forked from platypush/platypush
Use youtube-dl to extract the video URL instead of streaming its content to a local sock file
This commit is contained in:
parent
43f71ed47b
commit
711ea543bb
3 changed files with 31 additions and 28 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue