From 1f87f9535a01be95a57e2f4def0110a95c94a24a Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Sun, 10 Sep 2023 18:10:50 +0100 Subject: [PATCH] Give MPV stream URLs rather than relying on ytdl_hook to fetch This is an initial implementation of #3 however it doesn't add all the metadata that ytdl_hook does. --- friends_queue/video_queue.py | 51 ++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/friends_queue/video_queue.py b/friends_queue/video_queue.py index db643cf..9dd2eb2 100644 --- a/friends_queue/video_queue.py +++ b/friends_queue/video_queue.py @@ -18,12 +18,12 @@ class VideoQueueItem: uploader: str = None duration: int = None duration_str: str = None - stream_url: str = None + video_url: str = None + audio_url: str = None thumbnail: str = None # TODO: Lock queue -# TODO: Async ytdl fetch class VideoQueue(List[VideoQueueItem]): def __init__( self, player: mpv.MPV, ytdl: yt_dlp.YoutubeDL, thumbnails: ThumbnailCache @@ -36,10 +36,17 @@ def __init__( def append(self, item: VideoQueueItem): assert item is not None super().append(item) - self._player.loadfile(item.url, mode="append-play") + + args = {} + if item.audio_url is not None: + args["audio_file"] = item.audio_url + if item.title is not None: + args["force_media_title"] = item.title + self._player.loadfile(item.video_url or item.url, mode="append-play", **args) def append_url(self, url: str): - self.append(fetch_video(self._ytdl, self._thumbs, url)) + """Fetch video URL and asyncronously append to queue""" + fetch_video(self._ytdl, self._thumbs, self, url) def move(self, item: int, to: int): assert item >= 0 and item < len(self) @@ -70,13 +77,33 @@ def choose_thumbnail(thumbnails): return None +def get_stream_urls(info): + video = None + audio = None + + streams = info.get("requested_formats") + if streams is not None: + for stream in streams: + if stream.get("video_ext", "none") != "none": + video = stream + elif stream.get("audio_ext", "none") != "none": + audio = stream + + return (video, audio) + + class FetchVideoThread(Thread): def __init__( - self, ytdl: yt_dlp.YoutubeDL, thumbnails: ThumbnailCache, item: VideoQueueItem + self, + ytdl: yt_dlp.YoutubeDL, + thumbnails: ThumbnailCache, + queue: VideoQueue, + item: VideoQueueItem, ): super().__init__(daemon=True) self._ytdl = ytdl self._thumbs = thumbnails + self._queue = queue self._item = item def run(self): @@ -84,10 +111,18 @@ def run(self): info = self._ytdl.extract_info(self._item.url, download=False) self._item.title = info.get("fulltitle") self._item.uploader = info.get("uploader") - self._item.stream_url = None # TODO self._item.duration = info.get("duration") self._item.duration_str = info.get("duration_string") + video, audio = get_stream_urls(info) + self._item.video_url = video.get("url") + self._item.audio_url = audio.get("url") + # TODO: Add other metadata added by ytdl_hook e.g. subtitles, chapters, bitrate + + # Append before fetching thumbnail as that requires another request and is not required to + # play the video + self._queue.append(self._item) + # Fetch video thumbnail (as base64) thumbnail = choose_thumbnail(info.get("thumbnails")) if thumbnail is not None: @@ -95,10 +130,10 @@ def run(self): def fetch_video( - ytdl: yt_dlp.YoutubeDL, thumbnails: ThumbnailCache, url: str + ytdl: yt_dlp.YoutubeDL, thumbnails: ThumbnailCache, queue: VideoQueue, url: str ) -> VideoQueueItem: item = VideoQueueItem(url) - FetchVideoThread(ytdl, thumbnails, item).start() + FetchVideoThread(ytdl, thumbnails, queue, item).start() return item