# Extension points — yt-dlp/yt-dlp

> Canonical page: https://codevetter.com/unpack/yt-dlp/extension-points

# Extension points — yt-dlp/yt-dlp

Scanned commit c7fb478d21e9e59524befbe23f7801bb267fb880 (2026-09-16). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

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.
- InfoExtractor contract: subclass `InfoExtractor`, define `_VALID_URL` (regex or list; `False` for embed-only), implement `_real_extract(url)`; optional hooks `_initialize_pre_login`, `_perform_login` (gated on `_NETRC_MACHINE`), `_real_initialize`, `_GEO_COUNTRIES`/`_GEO_IP_BLOCKS`, `_TESTS`. — [yt_dlp/extractor/common.py#L532-592](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/common.py#L532-L592) [yt_dlp/extractor/common.py#L817-831](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/common.py#L817-L831)
- Built-in extractor registration is manual: add an import to `yt_dlp/extractor/_extractors.py`; `extractors.py` then populates the global `extractors` Indirect dict where iteration order determines match precedence. — [yt_dlp/extractor/_extractors.py](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/_extractors.py) [yt_dlp/extractor/extractors.py#L17-36](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/extractors.py#L17-L36) [yt_dlp/extractor/__init__.py#L17-29](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/__init__.py#L17-L29)
- Plugin extractors/post-processors: any `yt_dlp_plugins.extractor`/`yt_dlp_plugins.postprocessor` module exposing a public class ending in `IE`/`PP` is discovered via the `PluginFinder` meta-path finder and prepended into the core registries. — [yt_dlp/plugins.py#L33-34](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/plugins.py#L33-L34) [yt_dlp/plugins.py#L175-234](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/plugins.py#L175-L234) [yt_dlp/extractor/__init__.py#L9-14](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/__init__.py#L9-L14) [yt_dlp/postprocessor/__init__.py#L55-60](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/postprocessor/__init__.py#L55-L60)
- New request handlers: subclass `RequestHandler` (name must end in 'RH'), implement `validate`/`_send`; module-level `_REQUEST_HANDLERS` registration asserts unique `RH_KEY`, and `@register_preference` adds per-request ordering functions. — [yt_dlp/networking/common.py#L37-48](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/networking/common.py#L37-L48) [yt_dlp/networking/common.py#L133-140](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/networking/common.py#L133-L140) [yt_dlp/networking/common.py#L369-376](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/networking/common.py#L369-L376)
- YouTube PO-token provider framework: `PoTokenProvider` (suffix 'PTP') with `@register_provider`/`@register_preference`, plus cache providers 'PCP'/'PCSP' — a documented plugin-facing API in `pot/README.md`. — [yt_dlp/extractor/youtube/pot/provider.py#L109](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/youtube/pot/provider.py#L109) [yt_dlp/extractor/youtube/pot/cache.py#L28-57](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/youtube/pot/cache.py#L28-L57) [yt_dlp/extractor/youtube/pot/README.md#L24-52](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/youtube/pot/README.md#L24-L52)
- JS challenge providers: `JsChallengeProvider` (suffix 'JCP') under `extractor/youtube/jsc`, with a builtin quickjs provider — the extension point for pluggable signature/n challenge solvers. — [yt_dlp/extractor/youtube/jsc/provider.py#L95](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/youtube/jsc/provider.py#L95) [yt_dlp/extractor/youtube/jsc/_builtin/quickjs.py](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/youtube/jsc/_builtin/quickjs.py)
- External downloaders: `ExternalFD` subclasses (`CurlFD`, `AxelFD`, `WgetFD`, `Aria2cFD`, `HttpieFD`, `FFmpegFD`) declare `SUPPORTED_PROTOCOLS`/`SUPPORTED_FEATURES` and are selectable via `--downloader`; `list_external_downloaders` enumerates them for CLI help. — [yt_dlp/downloader/external.py#L37-38](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/downloader/external.py#L37-L38) [yt_dlp/downloader/external.py#L195-379](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/downloader/external.py#L195-L379) [yt_dlp/downloader/external.py#L586](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/downloader/external.py#L586)
- New download protocols plug into `PROTOCOL_MAP` in `downloader/__init__.py`; `get_suitable_downloader` resolves protocol strings (including '+'-joined multi-format protocols) to FD classes. — [yt_dlp/downloader/__init__.py#L4-58](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/downloader/__init__.py#L4-L58)
- JS runtimes are a named registry: `supported_js_runtimes` maps names to `JsRuntime` subclasses (deno/node/bun/quickjs registered in `__init__.py`), each probing executable versions with `MIN_SUPPORTED_VERSION`. — [yt_dlp/globals.py#L36-41](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/globals.py#L36-L41) [yt_dlp/__init__.py#L1098-1103](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/__init__.py#L1098-L1103) [yt_dlp/utils/_jsruntime.py#L89-153](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/utils/_jsruntime.py#L89-L153)
- Embedding hooks for library use: `add_progress_hook`, `add_post_hook`, `add_close_hook`, `add_postprocessor_hook`, plus `add_post_processor(pp, when=...)` for custom PP stages — the documented surface for API consumers. — [yt_dlp/YoutubeDL.py#L945-969](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/YoutubeDL.py#L945-L969)
- Extractor helpers for result shaping: `url_result`, `playlist_result`, `playlist_from_matches`, `url_transparent` results let an IE delegate to another IE without downloading — the composition mechanism between extractors. — [yt_dlp/extractor/common.py#L1275-1300](https://github.com/yt-dlp/yt-dlp/blob/c7fb478d21e9e59524befbe23f7801bb267fb880/yt_dlp/extractor/common.py#L1275-L1300)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
