ytdown
A Rust library (and companion CLI) mirroring yt-dlp's core: resolve a media URL into structured metadata and stream formats, select a format, and download it to disk.
Quickstart
use Path;
use Ytdown;
async
Collections (playlists / channels / search)
resolve returns MediaInfo::Collection for playlists, channels, and
ytsearch: queries. Its entries field is a futures::Stream that paginates
lazily, so consume it with futures::StreamExt (next, take, collect, …).
Add futures to your Cargo.toml to bring the extension trait into scope:
[]
= "0.3"
use StreamExt;
use ;
async
Supported URLs
YouTube is the only extractor registered by default (embedders can add their
own via the Extractor trait). Accepted hosts: youtube.com, www./m./
music.youtube.com, youtube-nocookie.com, and youtu.be.
| Kind | URL shapes |
|---|---|
| Video | …/watch?v=ID, youtu.be/ID, …/shorts/ID, …/embed/ID, …/v/ID, …/e/ID |
| Playlist | …/playlist?list=ID, any non-watch URL with ?list=ID |
| Channel | …/channel/UC…, …/@handle (streams the channel's Videos tab) |
| Search | ytsearch:QUERY pseudo-URL (mirrors yt-dlp) |
A watch URL that also carries &list= resolves to the video (the v=
parameter wins). Anything else fails fast with Error::UnsupportedUrl —
no network request is made.
Features
| Feature | Default | Description |
|---|---|---|
ffmpeg |
off | Mux separate DASH audio + video streams via the system ffmpeg binary. |
Architecture
src/
├── lib.rs # Public API: Ytdown client (builder), re-exports
├── error.rs # Error enum: Network, Extraction, Cipher, UnsupportedUrl, ...
├── types.rs # MediaInfo, Format, Thumbnail, Entry, enums (Container, ...)
├── extractor/
│ ├── mod.rs # Extractor trait + Registry (URL → extractor dispatch)
│ └── youtube/
│ ├── mod.rs # URL recognition + orchestration
│ ├── innertube.rs # InnerTube client: player/browse/search endpoints
│ ├── player.rs # JS player fetch + sig/nsig function extraction
│ └── pagination.rs # Continuation-token Stream for playlists/channels/search
├── jsi.rs # boa_engine wrapper: execute extracted cipher fns
├── download/
│ ├── mod.rs # Downloader: chunked GET, Range resume, retry/backoff
│ └── progress.rs # Progress events + callback plumbing
├── format.rs # FormatSelector: best/worst/filters
└── postprocess.rs # [feature "ffmpeg"] mux / convert via system ffmpeg
A Registry holds boxed Extractors; Ytdown::resolve dispatches to the first
matching extractor or returns Error::UnsupportedUrl. The shared reqwest::Client,
config, and caches travel through an ExtractorContext.
Testing
Unit and offline integration tests (wiremock-backed) run with:
Live tests in tests/live.rs hit the real YouTube network and are marked
#[ignore = "network"], so they are skipped by default and in CI. Run them
explicitly:
CLI
The ytdown-cli crate ships a ytdown binary over the same engine:
# Inspect available formats
# Download: best merged video+audio (needs ffmpeg), or best progressive
# Explicit formats: keywords, itags, or video+audio merge pairs
# Metadata as JSON, search from the terminal
|
# Playlists/channels download entry-by-entry
Subcommands
| Command | Description |
|---|---|
get <URL> |
Download a video, playlist, channel, or ytsearch: result set |
info <URL> |
Print resolved metadata as JSON (--pretty, --limit for collections) |
formats <URL> |
List a video's available formats as a table (--json) |
search <QUERY> |
List search results as a table (-n/--limit, default 10; --json) |
completions <SHELL> |
Generate shell completions (bash, zsh, fish, …) |
Global flags on every command: -v/-vv (info/debug logs), -q (silence logs),
--user-agent <UA>. RUST_LOG overrides the verbosity flags when set.
Format selection (-f)
| Value | Meaning |
|---|---|
| (omitted) | Best split video+audio merged via ffmpeg; best progressive without ffmpeg |
best |
Best progressive (muxed A+V) format |
bestvideo / bestaudio |
Best video-only / audio-only stream |
22 |
A specific format by itag |
137+140 |
Video itag + audio itag, merged via ffmpeg |
--max-height <H> and --container <mp4|webm> narrow the keyword selections;
combining them with explicit itags is rejected (exit 2). Merging needs ffmpeg
on PATH or via --ffmpeg <path>.
Output templates (-o, default {title}.{ext})
Placeholders: {title} {id} {ext} {height} {itag} {uploader} {index}
({index} is the 1-based position within a playlist/channel/search download).
Substituted values are sanitized to safe path components; literal / in the
template creates directories.
Downloads
--concurrency <N> parallel range chunks (with --chunk-size <BYTES>),
--retries <N>, and resume of partial files by default (--no-resume to
disable). Collections download entry-by-entry, honouring --skip <N> and
--limit <N>; per-entry failures are logged and reported at the end without
aborting the run.
Interactive picker
Run ytdown get on a TTY without -f and a format picker opens: arrows to
navigate, / to filter, enter to select (video-only formats offer pairing
with the best audio), q to quit. --no-tui (or piping) selects the best
format automatically. The picker and all progress/log output draw on stderr,
so stdout stays clean for piping.
Exit codes
0 success (including quitting the picker), 1 runtime errors (network,
extraction, download), 2 usage errors (bad flags, invalid -f/-o values).
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.