Use youtube-dl to extract the video URL instead of streaming its content to a local sock file

This commit is contained in:
Fabio Manganiello 2020-11-11 03:07:23 +01:00
parent 43f71ed47b
commit 711ea543bb
3 changed files with 31 additions and 28 deletions

View file

@ -1,7 +1,7 @@
import os
import subprocess
import threading
from typing import Callable, Optional, List, Tuple
from typing import Callable, Optional, List
from platypush.plugins import Plugin, action

View file

@ -42,7 +42,6 @@ class MediaPlugin(Plugin):
# A media plugin can either be local or remote (e.g. control media on
# another device)
_is_local = True
_youtube_fifo = os.path.join(tempfile.gettempdir(), 'youtube_video.sock')
_NOT_IMPLEMENTED_ERR = NotImplementedError('This method must be implemented in a derived class')
# Supported audio extensions
@ -159,6 +158,12 @@ class MediaPlugin(Plugin):
evt_queue.put(event.args['files'])
return handler
@staticmethod
def _is_youtube_resource(resource):
return resource.startswith('youtube:') \
or resource.startswith('https://youtu.be/') \
or resource.startswith('https://www.youtube.com/watch?v=')
def _get_resource(self, resource):
"""
:param resource: Resource to play/parse. Supported types:
@ -169,9 +174,7 @@ class MediaPlugin(Plugin):
* Torrents (format: Magnet links, Torrent URLs or local Torrent files)
"""
if resource.startswith('youtube:') \
or resource.startswith('https://youtu.be/') \
or resource.startswith('https://www.youtube.com/watch?v='):
if self._is_youtube_resource(resource):
m = re.match('youtube:video:(.*)', resource)
if not m:
m = re.match('https://youtu.be/(.*)', resource)
@ -182,8 +185,7 @@ class MediaPlugin(Plugin):
# The Chromecast has already its native way to handle YouTube
return resource
self.stream_youtube_to_fifo(resource)
resource = 'file://' + self._youtube_fifo
resource = self.get_youtube_video_url(resource)
elif resource.startswith('magnet:?'):
self.logger.info('Downloading torrent {} to {}'.format(
resource, self.download_dir))
@ -201,11 +203,16 @@ class MediaPlugin(Plugin):
else:
raise RuntimeError('No media file found in torrent {}'.format(resource))
assert resource, 'Unable to find any compatible media resource'
return resource
def _stop_torrent(self):
torrents = get_plugin(self.torrent_plugin)
torrents.quit()
# noinspection PyBroadException
try:
torrents = get_plugin(self.torrent_plugin)
torrents.quit()
except:
pass
@action
def play(self, resource, *args, **kwargs):
@ -467,23 +474,12 @@ class MediaPlugin(Plugin):
# noinspection PyProtectedMember
return YoutubeMediaSearcher()._youtube_search_html_parse(query)
def stream_youtube_to_fifo(self, url):
if self._youtube_proc:
self.logger.info('Terminating existing YouTube process')
self._youtube_proc.terminate()
self._youtube_proc = None
if os.path.exists(self._youtube_fifo):
os.unlink(self._youtube_fifo)
os.mkfifo(self._youtube_fifo, 0o644)
def _youtube_dl_thread():
self._youtube_proc = subprocess.Popen(['youtube-dl', '-f', 'best', '-o', self._youtube_fifo, url])
self._youtube_proc.wait()
self._youtube_proc = None
threading.Thread(target=_youtube_dl_thread).start()
@staticmethod
def get_youtube_video_url(url):
youtube_dl = subprocess.Popen(['youtube-dl', '-f', 'best', '-g', url], stdout=subprocess.PIPE)
url = youtube_dl.communicate()[0].decode()
youtube_dl.wait()
return url
@staticmethod
def get_youtube_id(url: str) -> Optional[str]:

View file

@ -85,7 +85,7 @@ class MediaMpvPlugin(MediaPlugin):
self._post_event(MediaPauseEvent, resource=self._get_current_resource(), title=self._player.filename)
elif evt == Event.UNPAUSE:
self._post_event(MediaPlayEvent, resource=self._get_current_resource(), title=self._player.filename)
elif evt == Event.SHUTDOWN or evt == Event.IDLE or (
elif evt == Event.SHUTDOWN or (
evt == Event.END_FILE and event.get('event', {}).get('reason') in
[EndFile.EOF_OR_INIT_FAILURE, EndFile.ABORTED, EndFile.QUIT]):
playback_rebounced = self._playback_rebounce_event.wait(timeout=0.5)
@ -135,10 +135,10 @@ class MediaMpvPlugin(MediaPlugin):
args['sub_file'] = self.get_subtitles_file(subtitles)
resource = self._get_resource(resource)
if resource.startswith('file://'):
resource = resource[7:]
assert self._player, 'The player is not ready'
self._player.play(resource)
if self.volume:
self.set_volume(volume=self.volume)
@ -417,4 +417,11 @@ class MediaMpvPlugin(MediaPlugin):
return ('file://' if os.path.isfile(self._player.stream_path)
else '') + self._player.stream_path
def _get_resource(self, resource):
if self._is_youtube_resource(resource):
return resource # mpv can handle YouTube streaming natively
return super()._get_resource(resource)
# vim:sw=4:ts=4:et: