From 09991b2e8a19db25ee24aaf4ff498001f7e1bc3c Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Mon, 30 Sep 2019 18:06:30 +0200 Subject: [PATCH] Use a filesystem FIFO for YouTube media content instead of playing the *.googlevideo.com URL directly Google Video URLs now return 403 if played directly. Let youtube-dl handle the heavylifting and use a FIFO to stream the media --- platypush/plugins/media/__init__.py | 30 ++++++++++++++++++++++++++++- platypush/plugins/media/mpv.py | 18 ----------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/platypush/plugins/media/__init__.py b/platypush/plugins/media/__init__.py index 9015f30ccd..f6a33a975c 100644 --- a/platypush/plugins/media/__init__.py +++ b/platypush/plugins/media/__init__.py @@ -42,6 +42,7 @@ 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') @@ -137,6 +138,7 @@ class MediaPlugin(Plugin): self.media_dirs.add(self.download_dir) self._videos_queue = [] + self._youtube_proc = None @staticmethod def _torrent_event_handler(evt_queue): @@ -157,12 +159,20 @@ class MediaPlugin(Plugin): """ if resource.startswith('youtube:') \ + or resource.startswith('https://youtu.be/') \ or resource.startswith('https://www.youtube.com/watch?v='): + m = re.match('youtube:video:(.*)', resource) + if not m: + m = re.match('https://youtu.be/(.*)', resource) + if m: + resource = 'https://www.youtube.com/watch?v={}'.format(m.group(1)) + if self.__class__.__name__ == 'MediaChromecastPlugin': # The Chromecast has already its native way to handle YouTube return resource - resource = self.get_youtube_url(resource).output + self.stream_youtube_to_fifo(resource) + resource = 'file://' + self._youtube_fifo elif resource.startswith('magnet:?'): self.logger.info('Downloading torrent {} to {}'.format( resource, self.download_dir)) @@ -458,6 +468,24 @@ class MediaPlugin(Plugin): return results + 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() + @action def get_youtube_url(self, url): m = re.match('youtube:video:(.*)', url) diff --git a/platypush/plugins/media/mpv.py b/platypush/plugins/media/mpv.py index be82c1c1bf..a2a6504f19 100644 --- a/platypush/plugins/media/mpv.py +++ b/platypush/plugins/media/mpv.py @@ -1,5 +1,4 @@ import os -import re import threading from platypush.context import get_bus @@ -102,19 +101,6 @@ class MediaMpvPlugin(MediaPlugin): return callback - @staticmethod - def _get_youtube_link(resource): - base_url = 'https://youtu.be/' - regexes = ['^https://(www\.)?youtube.com/watch\?v=([^?&#]+)', - '^https://(www\.)?youtu.be.com/([^?&#]+)', - '^(youtube:video):([^?&#]+)'] - - for regex in regexes: - m = re.search(regex, resource) - if m: - return base_url + m.group(2) - return None - @action def execute(self, cmd, **args): """ @@ -150,10 +136,6 @@ class MediaMpvPlugin(MediaPlugin): if resource.startswith('file://'): resource = resource[7:] - else: - yt_resource = self._get_youtube_link(resource) - if yt_resource: - resource = yt_resource self._player.play(resource) return self.status()