forked from platypush/platypush
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
This commit is contained in:
parent
f69a7e422b
commit
09991b2e8a
2 changed files with 29 additions and 19 deletions
|
@ -42,6 +42,7 @@ class MediaPlugin(Plugin):
|
||||||
# A media plugin can either be local or remote (e.g. control media on
|
# A media plugin can either be local or remote (e.g. control media on
|
||||||
# another device)
|
# another device)
|
||||||
_is_local = True
|
_is_local = True
|
||||||
|
_youtube_fifo = os.path.join(tempfile.gettempdir(), 'youtube_video.sock')
|
||||||
|
|
||||||
_NOT_IMPLEMENTED_ERR = NotImplementedError(
|
_NOT_IMPLEMENTED_ERR = NotImplementedError(
|
||||||
'This method must be implemented in a derived class')
|
'This method must be implemented in a derived class')
|
||||||
|
@ -137,6 +138,7 @@ class MediaPlugin(Plugin):
|
||||||
|
|
||||||
self.media_dirs.add(self.download_dir)
|
self.media_dirs.add(self.download_dir)
|
||||||
self._videos_queue = []
|
self._videos_queue = []
|
||||||
|
self._youtube_proc = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _torrent_event_handler(evt_queue):
|
def _torrent_event_handler(evt_queue):
|
||||||
|
@ -157,12 +159,20 @@ class MediaPlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if resource.startswith('youtube:') \
|
if resource.startswith('youtube:') \
|
||||||
|
or resource.startswith('https://youtu.be/') \
|
||||||
or resource.startswith('https://www.youtube.com/watch?v='):
|
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':
|
if self.__class__.__name__ == 'MediaChromecastPlugin':
|
||||||
# The Chromecast has already its native way to handle YouTube
|
# The Chromecast has already its native way to handle YouTube
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
resource = self.get_youtube_url(resource).output
|
self.stream_youtube_to_fifo(resource)
|
||||||
|
resource = 'file://' + self._youtube_fifo
|
||||||
elif resource.startswith('magnet:?'):
|
elif resource.startswith('magnet:?'):
|
||||||
self.logger.info('Downloading torrent {} to {}'.format(
|
self.logger.info('Downloading torrent {} to {}'.format(
|
||||||
resource, self.download_dir))
|
resource, self.download_dir))
|
||||||
|
@ -458,6 +468,24 @@ class MediaPlugin(Plugin):
|
||||||
|
|
||||||
return results
|
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
|
@action
|
||||||
def get_youtube_url(self, url):
|
def get_youtube_url(self, url):
|
||||||
m = re.match('youtube:video:(.*)', url)
|
m = re.match('youtube:video:(.*)', url)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
|
@ -102,19 +101,6 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
|
|
||||||
return callback
|
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
|
@action
|
||||||
def execute(self, cmd, **args):
|
def execute(self, cmd, **args):
|
||||||
"""
|
"""
|
||||||
|
@ -150,10 +136,6 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
|
|
||||||
if resource.startswith('file://'):
|
if resource.startswith('file://'):
|
||||||
resource = resource[7:]
|
resource = resource[7:]
|
||||||
else:
|
|
||||||
yt_resource = self._get_youtube_link(resource)
|
|
||||||
if yt_resource:
|
|
||||||
resource = yt_resource
|
|
||||||
|
|
||||||
self._player.play(resource)
|
self._player.play(resource)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
Loading…
Reference in a new issue