CodeVetter
← All unpacks
CodeVetter · Repo Unpack evidence

yt-dlp/yt-dlp

Scanned commit c7fb478d21 dated 2026-09-16 — last upstream commit. Generated by codevetter 1.12.0 on 2026-09-22.

github.com/yt-dlp/yt-dlp ↗ · 1229 files · 11.4 MB

yt-dlp is a feature-rich command-line audio/video downloader with support for thousands of sites.

Deep analysis by swe-2 via the devin CLI on 2026-09-22 (runtime not recorded) — every claim below links to its cited source files.

Overview

yt-dlp is a Python 3.10+ command-line audio/video downloader (also usable as the `yt_dlp` library) that routes URLs through a registry of ~1750 site-specific InfoExtractor classes to produce metadata 'info dicts', then dispatches downloads to protocol-specific FileDownloaders and runs ffmpeg-based post-processors. It is a youtube-dl fork distinguished by zero required dependencies, a plugin system, pluggable network request handlers (urllib/requests/curl_cffi/websockets) with browser impersonation, and a built-in self-updater that pulls verified binaries from GitHub releases.

System map

yt-dlp is a single-process Python CLI/package. Execution enters through `yt_dlp.main()` (console script or `python -m yt_dlp`), parses optparse options plus config files into a `ydl_opts` params dict, then constructs one `YoutubeDL` orchestrator that owns the extractor table, post-processor lists, cookiejar, cache, and the RequestDirector. The codebase is layered: `extractor/` produces info dicts, `networking/` abstracts HTTP/WebSocket transports, `downloader/` writes bytes to disk per protocol, and `postprocessor/` runs ffmpeg and other mutations. Shared mutable state lives in `globals.py` `Indirect` cells, which is also how plugins merge into the core registries. Build artifacts (zipapp, PyInstaller binaries, lazy_extractors.py) are produced by the Makefile and bundle/devscripts.

All 13 system map claims →

Feature catalog

The feature surface is one large optparse CLI plus the `YoutubeDL` Python API. Options are organized into ~15 groups (General, Network, Geo, Selection, Download, Filesystem, Thumbnail, Verbosity, Workarounds, Video Format, Subtitles, Authentication, Post-Processing, SponsorBlock, Extractor). Major features: per-site extraction with playlist/search support, format selection DSL, output templates, download archive dedup, browser-cookie and netrc authentication, TLS/browser impersonation, ffmpeg post-processing incl. SponsorBlock, external downloaders, plugins, JS runtimes/remote components for YouTube challenge solving, self-update, and extensive listing/simulation/JSON dump modes.

All 14 feature catalog claims →

Data flow

Data flows: CLI args + config files -> `ydl_opts` params dict -> URL list -> extractor selection by `_VALID_URL` regex -> `ie.extract()` performs HTTP via the shared RequestDirector and returns an info dict -> `process_ie_result` normalizes and recursively resolves playlists/URL results -> format selection computes `requested_formats` -> `process_info` writes sidecars and calls `dl()` -> a protocol FileDownloader streams bytes to `.part`/final files -> post-processors mutate files -> archive/cache/sidecar JSON are the persistence outputs. The info dict is the single mutable data structure threaded through the whole pipeline.

Behavior traces

Three end-to-end flows were traced in code. Startup: `__main__` -> `main` -> `_real_main` (options, plugins, updater, YoutubeDL init). Primary action: `download` -> `extract_info` -> IE regex match -> `ie.extract` -> `process_ie_result` -> `process_video_result` -> `process_info` -> `dl` -> FileDownloader -> post_process -> archive write. Persistence/self-update: `Updater.query_update` fetches `_update_spec` + `SHA2-256SUMS`, verifies sha256, atomically swaps the executable, and `restart()` re-invokes the original argv.

Testing signals

Tests live in `test/` (pytest + unittest hybrid). Two tiers: 'core' tests (utils, networking, jsinterp, postprocessors, options) run offline under `-m 'not download'`, and 'download' tests that hit live sites, generated dynamically from each extractor's `_TESTS`/`_WEBPAGE_TESTS`. CI runs core tests across CPython 3.10-3.15 and PyPy on Linux+Windows with reruns; lint is ruff + autopep8. Coverage is explicitly unimplemented. The extractor download tests depend on live third-party sites and are therefore inherently flaky and mostly excluded from core CI.

Risk map

Highest-sensitivity areas: the self-updater downloads and replaces the running executable from GitHub releases (sha256-verified but not signature-verified); `ExecPP` and external downloaders shell out to user-supplied commands; `cookies.py` decrypts browser credential stores; the plugin system executes arbitrary code from config dirs by default; and `jsinterp.py`/ejs remote components execute third-party JavaScript. Fragility concentrates in the ~1752 regex-driven extractors and the first-match-wins registry ordering. Note the repo carries an explicit NO AI / NO LLM contribution policy.

Extension points

yt-dlp is built around registries. New site support = a new `InfoExtractor` subclass ending in `IE` registered by importing it in `_extractors.py`. Post-processors are `PP`-suffixed classes in the `postprocessors` dict. Request handlers are `RH`-suffixed classes auto-registered in `_REQUEST_HANDLERS` with optional preference functions. Third parties plug in via `yt_dlp_plugins` namespace packages (extractor/postprocessor), the documented YouTube PO-token provider framework (PTP/PCP/PCSP suffixes), JS challenge providers (JCP), external downloader FDs, and JS runtime registrations. API hooks (progress/post/close/postprocessor) support library embedding.

Contributor handoff

Read CONTRIBUTING.md (developer conventions) and .NO_AI/README.md (contribution policy) first. Style is enforced by ruff + autopep8: 120-col limit, single quotes, banned `from X import Y` for many stdlib modules. Tests run via `python -m devscripts.run_tests` (core/download/<IE name>) or `make test`; download tests need network. The main traps: extractor order is load-bearing, the params dict is the config contract, `lazy_extractors.py` is a generated artifact, plugins load from user dirs by default, and the public extractor/postprocessor API surface is intentionally stable while plugin internals are not.

Languages

LanguageFilesShare of source
Python1126100%
YAML32docs/config
Markdown10docs/config
JSON3docs/config
Shell30%
TOML10%

Start reading here

Probable source entrypoint: yt_dlp/__init__.py — inferred from the directory tree and manifest, not the upstream docs.

Source roots

yt_dlp/ (1045 files) devscripts/ (30 files) bundle/ (23 files)

Source layout — yt_dlp/

PathFiles
yt_dlp/__pyinstaller/2
yt_dlp/compat/8
yt_dlp/dependencies/2
yt_dlp/downloader/17
yt_dlp/extractor/322
yt_dlp/networking/10
yt_dlp/postprocessor/10
yt_dlp/utils/10
yt_dlp/__init__.py1
yt_dlp/aes.py1
yt_dlp/cache.py1
yt_dlp/cookies.py1
yt_dlp/globals.py1
yt_dlp/options.py1

Repository structure

Top-level pathFiles
yt_dlp/1045
test/81
.github/31
devscripts/30
bundle/23
.NO_AI/1

Docs found: CONTRIBUTING.mdREADME.md

Package

pyproject.toml — yt-dlp, 53 declared dependencies.

defaultbrotlibrotlicfficertifi"mutagen"pycryptodomex"requests>=2.32.2,<3"urllib3>=2.0.2,<3"websockets>=13.0"yt-dlp-ejs==0.8.0"]curl-cffi

CI & config entrypoints

Structural health signals

Average score 5.9/10 across 96 analyzed source files; 94 hotspots surfaced.

These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.

Recent history