youtube_helper.streaming module

youtube_helper.streaming

Stream catalog, picker, and direct-URL resolution for yt-dlp-supported sites. Does NOT decode anything — the resolved URLs are meant to be consumed by:

  • video_helper.extract_frames for video frames

  • podcast_helper.iter_pcm (separate package) for audio PCM

What this module gives you, layered from low-level to high-level:

  • VideoStreamInfo — typed dict describing one video format.

  • list_video_streams() — enumerate every video format yt-dlp finds for a URL (one entry per resolution / codec / container).

  • pick_video_stream() — pick the single best video stream matching codec / format / fps / language constraints.

  • resolve_direct_url() — quick “give me ONE direct URL ready for ffmpeg” (audio or video flavour). Coarser than pick_*.

Audio stream catalog / picker intentionally NOT in this module — that responsibility lives in podcast-helper so the audio decoding path has a single owner.

Author

Warith HARCHAOUI — https://linkedin.com/in/warith-harchaoui

class youtube_helper.streaming.DirectMediaURL[source]

Bases: TypedDict

Resolved direct-media URL ready for ffmpeg.

Keys

urlstr

The direct media URL. Can be an HLS .m3u8 (live), DASH .mpd, or progressive .mp4 / .webm — whichever yt-dlp selected.

containerstr

Container format string ("hls", "dash", "mp4", "webm", "audio", …). Useful for ffmpeg’s -f hint if the URL extension is ambiguous.

is_livebool

True if the source is a live stream. Callers may want to skip -re (real-time pacing) since live is already real-time.

headersdict[str, str]

HTTP headers the player must send with the request. YouTube often requires a specific User-Agent and Referer to avoid 403s; pass them to ffmpeg via -headers.

container: str
headers: dict[str, str]
is_live: bool
url: str
class youtube_helper.streaming.VideoStreamInfo[source]

Bases: TypedDict

One video format’s metadata, normalised across extractors.

Keys

format_idstr

yt-dlp’s stable identifier for this format on this URL.

urlstr

The direct media URL — ready for ffmpeg / PyAV.

headersdict[str, str]

HTTP headers ffmpeg must send with the request (User-Agent, Referer, Cookie, …). Pass through to video_helper.extract_frames(http_headers=...) or to ffmpeg’s -headers flag.

extstr

File extension reported by yt-dlp ("mp4" / "webm" / "m3u8" / …).

containerstr

Normalised container hint ("mp4" / "webm" / "hls" / "dash") — useful when the ext is ambiguous.

vcodecstr

Video codec ("h264" / "vp9" / "av01" / "none" if audio-only — filtered out of this list).

acodecstr

Audio codec ("none" for video-only formats — common on YouTube DASH, paired with a separate audio-only format).

width, heightint

Pixels. 0 if unknown (rare).

fpsfloat

Frames per second. 0.0 if unknown.

vbr_kbpsfloat

Estimated video bitrate in kbit/s. 0.0 if unknown.

filesize_bytesint

Estimated file size in bytes. 0 if unknown (always 0 for live).

is_livebool

True if this is a live stream (URL is an HLS / DASH manifest).

protocolstr

yt-dlp’s protocol string ("https" / "m3u8_native" / "http_dash_segments" / …).

languagestr | None

Language code if the format carries an explicit one (rare for video; common for audio).

notestr

yt-dlp’s format_note — typically a human-readable label ("1080p60", "Premium", …).

acodec: str
container: str
ext: str
filesize_bytes: int
format_id: str
fps: float
headers: dict[str, str]
height: int
is_live: bool
language: str | None
note: str
protocol: str
url: str
vbr_kbps: float
vcodec: str
width: int
youtube_helper.streaming.extract_frames_stream(url, *, prefer_codec=None, prefer_format=None, max_fps=None, language=None, include_video_only=True, include_combined=True, cookies_from_browser=None, verbose=False, **extract_frames_kwargs)[source]

Stream frames from any yt-dlp-supported URL through video_helper.extract_frames().

One-call composition of:

  1. pick_video_stream() — resolves url to a single VideoStreamInfo matching the codec / format / fps / language constraints.

  2. video_helper.extract_frames() — opens the resolved direct media URL with the right http_headers and yields frames under the destination of your choice (numpy BGR, torch RGB, or PIL).

Why this exists

The two-step pattern is the published recipe (see ai-helpers/EXAMPLES.md and youtube-helper/EXAMPLES.md):

>>> stream = yth.pick_video_stream(url, prefer_codec="h264")
>>> for frame in vh.extract_frames(
...     stream["url"], http_headers=stream["headers"],
...     destination="torch", device="auto", batch_size=32,
... ):
...     model(frame)

extract_frames_stream collapses both calls into one, plumbs the headers automatically, and lets the caller forward any extract_frames kwarg (destination, device, batch_size, output_width, frame_step, …) verbatim.

param url:

Any URL yt-dlp can extract (YouTube / Vimeo / DailyMotion / Twitch VOD / …). Live streams are accepted as well — the returned iterator runs until the live source ends.

type url:

str

param prefer_codec:

Forwarded to pick_video_stream(). See its docstring for the matching semantics. Defaults pick yt-dlp’s best candidate.

type prefer_codec:

optional

param prefer_format:

Forwarded to pick_video_stream(). See its docstring for the matching semantics. Defaults pick yt-dlp’s best candidate.

type prefer_format:

optional

param max_fps:

Forwarded to pick_video_stream(). See its docstring for the matching semantics. Defaults pick yt-dlp’s best candidate.

type max_fps:

optional

param language:

Forwarded to pick_video_stream(). See its docstring for the matching semantics. Defaults pick yt-dlp’s best candidate.

type language:

optional

param include_video_only:

Forwarded to pick_video_stream(). Defaults keep both.

type include_video_only:

bool, optional

param include_combined:

Forwarded to pick_video_stream(). Defaults keep both.

type include_combined:

bool, optional

param cookies_from_browser:

Forwarded to pick_video_stream(). Required for age-gated / members-only content.

type cookies_from_browser:

str, optional

param verbose:

Forwarded to pick_video_stream().

type verbose:

bool, optional

param **extract_frames_kwargs:

Any keyword accepted by video_helper.extract_frames(): start_index / end_index / start_instant / end_instant / stabilize / frame_step / frame_interval / frame_indices / frame_times / backend / hwaccel / output_width / output_height / pad_color / destination / device / batch_size / layout.

Note: http_headers is wired automatically from the resolved stream — passing it yourself overrides the resolver’s headers (rare; mostly useful when the caller already authored a stable header dict).

returns:

The same iterator type that video_helper.extract_frames() returns — numpy / torch / PIL frames or batches per the passed-in destination and batch_size.

rtype:

Iterator

raises RuntimeError:

If yt-dlp can’t extract the URL (private / removed / geo-blocked).

raises ValueError:

If no stream matches the codec / format / fps / language constraints (the catalog itself was non-empty, but every entry got filtered out).

Examples

>>> import youtube_helper as yth
>>> # Sequential decode at 1 frame per second, 224×224 letterboxed.
>>> for frame in yth.extract_frames_stream(
...     "https://www.youtube.com/watch?v=YE7VzlLtp-4",
...     prefer_codec="h264", prefer_format="mp4", max_fps=30,
...     frame_interval=1.0, output_width=224, output_height=224,
... ):
...     model(frame)
>>> # Batched torch tensors on a GPU device — same downstream code as
>>> # the file-based path.
>>> for batch in yth.extract_frames_stream(
...     "https://vimeo.com/123",
...     destination="torch", device="auto", batch_size=32,
... ):
...     logits = model(batch.float() / 255.0)
Parameters:
  • url (str)

  • prefer_codec (str | None)

  • prefer_format (str | None)

  • max_fps (float | None)

  • language (str | None)

  • include_video_only (bool)

  • include_combined (bool)

  • cookies_from_browser (str | None)

  • verbose (bool)

  • extract_frames_kwargs (Any)

Return type:

Any

youtube_helper.streaming.list_video_streams(url, *, include_video_only=True, include_combined=True, cookies_from_browser=None, verbose=False)[source]

Enumerate every video format yt-dlp finds for this URL.

Results are ordered by yt-dlp’s quality ranking (best last). Pure video-only formats and combined (video + audio) formats are both included by default — adjust with the boolean flags.

Parameters:
  • url (str) – Any URL yt-dlp can extract.

  • include_video_only (bool, optional) – Keep formats with acodec="none" (DASH-style streams that need a separate audio mux). Default True.

  • include_combined (bool, optional) – Keep formats with both video and audio (legacy progressive streams; common on Vimeo, rare on YouTube high-quality). Default True.

  • cookies_from_browser (str, optional) – Pass through to yt-dlp’s --cookies-from-browser (e.g. "firefox" / "chrome" / "safari") for age-gated or login-required content.

  • verbose (bool, optional) – Echo yt-dlp’s output to stderr (off by default).

Returns:

Possibly empty if the URL has no video (audio-only podcast, for example).

Return type:

list[VideoStreamInfo]

Raises:

RuntimeError – If yt-dlp could not extract info at all (private video, geo-blocked without bypass, removed, …).

youtube_helper.streaming.pick_video_stream(url, *, prefer_codec=None, prefer_format=None, max_fps=None, language=None, include_video_only=True, include_combined=True, cookies_from_browser=None, verbose=False)[source]

Pick a single best video stream matching the given constraints.

Among the catalog returned by list_video_streams(), applies each non-None constraint as a hard filter, then returns the highest-ranked remaining candidate (yt-dlp’s native order — typically sorts by height × bitrate).

Parameters:
  • url (str) – Source URL (any yt-dlp-supported site).

  • prefer_codec (str, optional) – Substring match against vcodec ("h264" matches both "avc1.640028" and "h264" once normalised; "vp9" matches "vp09.00.40.08"; "av1" matches "av01...").

  • prefer_format (str, optional) – Equality match against ext ("mp4" / "webm").

  • max_fps (float, optional) – Drop formats with fps > max_fps.

  • language (str, optional) – Equality match against the format’s language (rarely set for video — primarily useful when the source carries multi-track video).

  • include_video_only (bool, optional) – See list_video_streams().

  • include_combined (bool, optional) – See list_video_streams().

  • cookies_from_browser (same as list_video_streams().)

  • verbose (same as list_video_streams().)

Returns:

The chosen stream.

Return type:

VideoStreamInfo

Raises:
  • RuntimeError – If yt-dlp can’t extract the URL.

  • ValueError – If no stream matches the constraints (the catalog itself was non-empty, but every entry got filtered out).

youtube_helper.streaming.resolve_direct_url(url, *, prefer='audio', live='auto')[source]

Resolve url to a direct, ffmpeg-readable media URL.

Parameters:
  • url (str) – Any URL yt-dlp can extract (YouTube / Vimeo / DailyMotion / Twitch / …).

  • prefer ("audio" | "video") – Format preference. "audio" selects bestaudio — cheapest path for ASR + diarization. "video" selects best (video + audio muxed), useful for the /video view (Phase 7) that needs the visual track too.

  • live ("auto" | "force_live" | "force_vod") – "auto" propagates whatever the info dict says (the usual case). The other two override — handy for testing or when the site mislabels a stream.

Returns:

See class docstring.

Return type:

DirectMediaURL

Raises:

RuntimeError – If yt-dlp could not extract a usable URL (private video, geo-blocked without bypass, removed, …).