downloader: Add youtube support

Adds very simple youtube stream support where we only ever use the "best" quality,
which we call "source" for consistency with twitch.

We use yt-dlp to do the heavy lifting of getting the playlist url out of youtube.
pull/355/head
Mike Lang 1 year ago committed by Mike Lang
parent 05349a62df
commit 18b71e6f5f

@ -643,6 +643,8 @@ def main(channels, base_dir=".", qualities="source", metrics_port=8001, backdoor
provider = TwitchProvider(channel, auth_token=twitch_auth_token) provider = TwitchProvider(channel, auth_token=twitch_auth_token)
elif type == "url": elif type == "url":
provider = URLProvider(url) provider = URLProvider(url)
elif type == "youtube":
provider = YoutubeProvider(url)
else: else:
raise ValueError(f"Unknown type {type!r}") raise ValueError(f"Unknown type {type!r}")
manager = StreamsManager(provider, channel, base_dir, qualities, important=important) manager = StreamsManager(provider, channel, base_dir, qualities, important=important)

@ -1,6 +1,8 @@
import json
import logging import logging
import random import random
import subprocess
from common.requests import InstrumentedSession from common.requests import InstrumentedSession
@ -51,6 +53,36 @@ class URLProvider(Provider):
return {"source": master_playlist.playlists[0].uri} return {"source": master_playlist.playlists[0].uri}
class YoutubeProvider(Provider):
"""Provider that takes a youtube live stream (NOT a video).
Doesn't support multiple renditions, quality must be "source".
Requires yt-dlp to be available in $PATH.
"""
# Youtube links expire after 6h, so roll workers at 5h
MAX_WORKER_AGE = 5 * 60 * 60
def __init__(self, youtube_url):
self.youtube_url = youtube_url
def get_media_playlist_uris(self, qualities, session=None):
if qualities != ["source"]:
raise ValueError("Cannot provide non-source qualities")
# We outsource the work of going from a youtube URL to a media playlist at the best available quality.
# We do this by telling yt-dlp to dump the json info for the video.
output = subprocess.check_output([
"yt-dlp",
"--dump-json",
"--output", "infojson:-", # json dump to stdout (just "-o -" will write it to stderr)
self.youtube_url,
])
try:
data = json.loads(output)
except Exception:
raise Exception(f"Invalid JSON from yt-dlp: {output!r}")
return {"source": data["url"]}
class TwitchProvider(Provider): class TwitchProvider(Provider):
"""Provider that takes a twitch channel.""" """Provider that takes a twitch channel."""
# Twitch links expire after 24h, so roll workers at 20h # Twitch links expire after 24h, so roll workers at 20h

Loading…
Cancel
Save