| namespace | aiwg |
| platforms | ["all"] |
| name | youtube-acquisition |
| description | yt-dlp patterns for acquiring content from YouTube and video platforms |
| category | media-curator |
YouTube Acquisition
Comprehensive yt-dlp command patterns for downloading video and audio content from YouTube, Vimeo, SoundCloud, and other supported platforms. Includes quality selection strategies, format filtering, the SABR 403 workaround, metadata extraction, and batch operations.
Overview
yt-dlp is the primary tool for media acquisition from YouTube and 1000+ other sites. This skill documents proven patterns from production use, including workarounds for the n-challenge / EJS gate, PO-token requirements, and the older SABR 403 issue.
Key Capabilities:
- Download best available quality (video + audio)
- Extract audio-only in multiple formats
- Handle playlists and channels
- Embed metadata and thumbnails
- Download subtitles and auto-captions
- Work around platform restrictions
Prerequisites (#1229)
As of 2026, basic format selectors return only image storyboards on most YouTube videos until two relatively new gates are satisfied. Read this section before any acquisition attempt — running yt-dlp without these prerequisites produces "Only images are available for download" failures that look like format-selector bugs but aren't.
1. n-challenge / EJS solver
YouTube returns ciphered streaming URLs whose n parameter must be transformed by JavaScript extracted from the player. yt-dlp delegates this to the yt-dlp-ejs plugin plus a JS runtime. Without both, all real formats are filtered out and only sb* storyboard formats remain.
pip install --user yt-dlp-ejs
which deno || sudo apt install -y deno
Reference: https://github.com/yt-dlp/yt-dlp/wiki/EJS
2. PO Token (Proof of Origin)
Many client variants (mweb, ios, web) demand a Proof-of-Origin token tied to a logged-in / browser-attested session. Without a PO token provider those clients are skipped, narrowing the available format list. Standard provider:
pip install --user bgutil-ytdlp-pot-provider
Reference: https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide
3. Plugin discovery — install yt-dlp via pip, NOT the standalone zipapp
This is the gotcha that bites operators most often. The standalone yt-dlp zipapp (the form distributed at /usr/local/bin/yt-dlp from the official static download) does not expose user site-packages to its plugin-discovery path. Even after pip install --user yt-dlp-ejs, a zipapp invocation will silently fail to find the EJS solver. Symptoms: n challenge solving failed warnings even after installing the plugin.
The fix is to install yt-dlp itself via pip so it shares site-packages with the EJS plugin, then ensure ~/.local/bin precedes /usr/local/bin on PATH:
pip install --user --break-system-packages yt-dlp-ejs
pip install --user --break-system-packages -U yt-dlp
export PATH="$HOME/.local/bin:$PATH"
~/.local/bin/yt-dlp -F "https://www.youtube.com/watch?v=VIDEO_ID"
After this, downloads typically succeed automatically — yt-dlp's EJS solver may even fall back to the android_vr client without needing the JS runtime.
Basic Download Patterns
Best Quality Video + Audio
Download highest quality video and audio, merge into single file.
yt-dlp "VIDEO_URL"
yt-dlp -f "bestvideo+bestaudio" "VIDEO_URL"
yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]" --merge-output-format mp4 "VIDEO_URL"
yt-dlp -f "bestvideo+bestaudio" \
--embed-metadata \
--embed-thumbnail \
--embed-subs \
"VIDEO_URL"
Failure-Mode Triage (#1229)
Match the symptom to the right gate before changing format selectors. Three distinct failures look similar but require different fixes:
| Symptom | Cause | Fix |
|---|
-F lists only sb0..sb3 storyboard formats; warning n challenge solving failed, Only images are available | n-challenge / EJS missing | Install yt-dlp-ejs and a JS runtime (see Prerequisites). Most failures resolve here. |
Client-specific 403s; warning mweb formats require a GVS PO Token or similar | PO token required | Install bgutil-ytdlp-pot-provider (see Prerequisites). |
Explicit-format 403 on bestvideo[ext=mp4]+bestaudio[ext=m4a] selectors against newer videos | SABR | Simplify to best[ext=mp4]/best (below). |
SABR 403 (legacy fix — keep for explicit-selector failures)
yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]" "VIDEO_URL"
yt-dlp -f "best[ext=mp4]/best" "VIDEO_URL"
yt-dlp -f "bestaudio/best" -x --audio-format mp3 "VIDEO_URL"
Why this still helps even with EJS in place: best[ext=mp4]/best lets yt-dlp pick a single combined stream rather than negotiating separate video/audio tracks, which sidesteps both SABR 403s and several PO-token edge cases.
Audio-Only Extraction
Extract audio track without video.
yt-dlp -f "bestaudio" -x --audio-format mp3 "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format flac "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format opus "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format m4a "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 --audio-quality 320K "VIDEO_URL"
yt-dlp -f "bestaudio/best" -x --audio-format mp3 "VIDEO_URL"
Audio Quality Ladder:
- FLAC - Lossless, large files, archival quality
- Opus - Best quality/size ratio, not universally supported
- M4A/AAC 256kbps - Excellent quality, wide compatibility
- MP3 320kbps - Good quality, universal compatibility
- MP3 192kbps - Acceptable quality, smaller files
Specific Resolution
Download specific video resolution.
yt-dlp -f "bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]" "VIDEO_URL"
yt-dlp -f "bestvideo[height<=720]+bestaudio" "VIDEO_URL"
yt-dlp -f "bestvideo[height<=2160]+bestaudio" "VIDEO_URL"
yt-dlp -f "best[height<=1080]/best" "VIDEO_URL"
Specific Format Codes
Use YouTube format codes directly (use yt-dlp -F URL to list available formats).
yt-dlp -F "VIDEO_URL"
yt-dlp -f 137+140 "VIDEO_URL"
yt-dlp -f "137+140/136+140/best" "VIDEO_URL"
Common YouTube Format Codes:
- 137: 1080p MP4 video
- 136: 720p MP4 video
- 140: M4A 128kbps audio
- 251: Opus 160kbps audio
- bestaudio: Highest quality audio available
Playlist Operations
Download Entire Playlist
yt-dlp "PLAYLIST_URL"
yt-dlp -o "%(playlist_index)s - %(title)s.%(ext)s" "PLAYLIST_URL"
yt-dlp -o "~/Music/%(playlist)s/%(title)s.%(ext)s" "PLAYLIST_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 "PLAYLIST_URL"
Playlist Range Selection
yt-dlp --playlist-start 1 --playlist-end 10 "PLAYLIST_URL"
yt-dlp --playlist-items "1,3,5,7,9" "PLAYLIST_URL"
yt-dlp --playlist-start 6 "PLAYLIST_URL"
yt-dlp --playlist-items "1,5,10,15" "PLAYLIST_URL"
Reverse Playlist Order
yt-dlp --playlist-reverse "PLAYLIST_URL"
Channel Downloads
Entire Channel
yt-dlp "https://www.youtube.com/@CHANNEL_HANDLE/videos"
yt-dlp -o "%(uploader)s/%(upload_date)s - %(title)s.%(ext)s" \
"https://www.youtube.com/@CHANNEL_HANDLE/videos"
yt-dlp -f "bestaudio" -x --audio-format mp3 \
"https://www.youtube.com/@CHANNEL_HANDLE/videos"
Channel Filtered by Date
yt-dlp --dateafter 20230101 "CHANNEL_URL"
yt-dlp --dateafter 20230101 --datebefore 20231231 "CHANNEL_URL"
yt-dlp --dateafter today "CHANNEL_URL"
yt-dlp --dateafter now-7days "CHANNEL_URL"
Channel Filtered by View Count
yt-dlp --min-views 100000 "CHANNEL_URL"
yt-dlp --max-views 1000000 "CHANNEL_URL"
Search and Discovery
YouTube Search
yt-dlp "ytsearch10:artist name song"
yt-dlp "ytsearch1:artist official video"
yt-dlp -f "bestaudio" -x --audio-format mp3 "ytsearch5:artist live"
yt-dlp "ytsearch:artist name intitle:official"
Search Options
yt-dlp "ytsearchsortorder:view_count:artist name"
yt-dlp "ytsearchsortorder:upload_date:artist name"
yt-dlp "ytsearchsortorder:rating:artist name"
yt-dlp -f "bestvideo[height>=720]+bestaudio" "ytsearch5:artist HD"
Metadata and Thumbnails
Embed Metadata
yt-dlp --embed-metadata "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 \
--embed-metadata \
--add-metadata \
"VIDEO_URL"
yt-dlp --parse-metadata "title:%(artist)s - %(track)s" \
--embed-metadata \
"VIDEO_URL"
Thumbnail Handling
yt-dlp --write-thumbnail "VIDEO_URL"
yt-dlp --embed-thumbnail "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 \
--embed-thumbnail \
--embed-metadata \
"VIDEO_URL"
yt-dlp --write-thumbnail --convert-thumbnails jpg "VIDEO_URL"
Write Metadata Files
yt-dlp --write-info-json "VIDEO_URL"
yt-dlp --write-description "VIDEO_URL"
yt-dlp --write-info-json \
--write-description \
--write-thumbnail \
--write-annotations \
"VIDEO_URL"
Subtitle Downloads
Basic Subtitle Download
yt-dlp --write-subs --all-subs "VIDEO_URL"
yt-dlp --write-auto-subs "VIDEO_URL"
yt-dlp --write-subs --sub-langs "en,es,fr" "VIDEO_URL"
yt-dlp --embed-subs "VIDEO_URL"
Subtitle Format Conversion
yt-dlp --write-subs --sub-format srt "VIDEO_URL"
yt-dlp --write-subs --sub-format vtt "VIDEO_URL"
yt-dlp --write-subs --sub-format "srt/vtt/best" "VIDEO_URL"
Output Templates
File Naming Patterns
yt-dlp -o "%(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "%(upload_date)s - %(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "%(uploader)s/%(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "%(playlist_index)02d - %(title)s.%(ext)s" "PLAYLIST_URL"
yt-dlp -o "%(uploader)s - %(upload_date)s - %(title)s [%(id)s].%(ext)s" "VIDEO_URL"
Directory Organization
yt-dlp -o "~/Downloads/%(uploader)s/%(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "~/Archive/%(upload_date>%Y)s/%(upload_date>%m)s/%(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "~/Music/%(playlist)s/%(playlist_index)02d - %(title)s.%(ext)s" "PLAYLIST_URL"
yt-dlp -o "%(uploader)s/%(upload_date)s/%(title)s.%(ext)s" "CHANNEL_URL"
Sanitization and Safety
yt-dlp --restrict-filenames -o "%(title)s.%(ext)s" "VIDEO_URL"
yt-dlp -o "%(title)s.%(ext)s" --replace-in-metadata "title" " " "_" "VIDEO_URL"
yt-dlp -o "%(title).50s.%(ext)s" "VIDEO_URL"
Quality Selection Strategies
Balanced Quality/Size
yt-dlp -f "bestvideo[height<=1080]+bestaudio/best[height<=1080]" "VIDEO_URL"
yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio" \
--merge-output-format mp4 \
"VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 --audio-quality 192K "VIDEO_URL"
Maximum Quality
yt-dlp -f "bestvideo+bestaudio" "VIDEO_URL"
yt-dlp -f "bestvideo[height<=2160]+bestaudio" "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format flac "VIDEO_URL"
Minimum File Size
yt-dlp -f "worst[height>=360]" "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 --audio-quality 128K "VIDEO_URL"
yt-dlp -f "bestvideo[ext=webm][height<=720]+bestaudio[ext=webm]" "VIDEO_URL"
Archive and Resumption
Download Archive
Prevent re-downloading already acquired content.
yt-dlp --download-archive downloaded.txt "PLAYLIST_URL"
yt-dlp --download-archive archive.txt "PLAYLIST_URL"
yt-dlp --download-archive "%(playlist_id)s.txt" "PLAYLIST_URL"
Resume Interrupted Downloads
yt-dlp -c "VIDEO_URL"
yt-dlp --no-continue "VIDEO_URL"
Advanced Filtering
File Size Limits
yt-dlp --max-filesize 500M "VIDEO_URL"
yt-dlp --min-filesize 10M "VIDEO_URL"
yt-dlp --min-filesize 10M --max-filesize 500M "PLAYLIST_URL"
Duration Filters
yt-dlp --match-filter "duration > 300" "CHANNEL_URL"
yt-dlp --match-filter "duration < 600" "PLAYLIST_URL"
yt-dlp --match-filter "duration > 180 & duration < 600" "CHANNEL_URL"
Content Filters
yt-dlp --match-filter "!is_live" "CHANNEL_URL"
yt-dlp --match-filter "is_live" "CHANNEL_URL"
yt-dlp --match-filter "!age_limit" "CHANNEL_URL"
yt-dlp --match-filter "view_count > 10000" "CHANNEL_URL"
Rate Limiting and Throttling
Speed Limits
yt-dlp -r 5M "VIDEO_URL"
yt-dlp -r 1M "PLAYLIST_URL"
yt-dlp -r 0 "VIDEO_URL"
Request Throttling
yt-dlp --sleep-interval 5 "PLAYLIST_URL"
yt-dlp --min-sleep-interval 3 --max-sleep-interval 8 "PLAYLIST_URL"
yt-dlp --sleep-requests 2 "CHANNEL_URL"
Authentication and Cookies
Login with Credentials
yt-dlp -u USERNAME -p PASSWORD "VIDEO_URL"
yt-dlp -n "VIDEO_URL"
Cookie Files
yt-dlp --cookies-from-browser firefox "VIDEO_URL"
yt-dlp --cookies cookies.txt "VIDEO_URL"
yt-dlp --cookies-from-browser chrome --cookies cookies.txt "VIDEO_URL"
Flatpak browser profiles (#1229)
--cookies-from-browser chromium does NOT find Flatpak Chromium installs (or any other Flatpak browser) — the binary's default profile path doesn't match the Flatpak sandbox layout. Pass an explicit profile path:
yt-dlp --cookies-from-browser "chromium:$HOME/.var/app/org.chromium.Chromium/config/chromium" "VIDEO_URL"
yt-dlp --cookies-from-browser "chromium:$HOME/.var/app/io.github.ungoogled_software.ungoogled_chromium/config/chromium" "VIDEO_URL"
yt-dlp --cookies-from-browser "firefox:$HOME/.var/app/org.mozilla.firefox/.mozilla/firefox" "VIDEO_URL"
The same pattern (<browser>:<profile-path>) applies to any Flatpak browser. Native (non-Flatpak) installs are auto-discovered without the explicit path.
Post-Processing
FFmpeg Operations
yt-dlp --recode-video mp4 "VIDEO_URL"
yt-dlp -x --audio-format mp3 --audio-quality 320K "VIDEO_URL"
yt-dlp --postprocessor-args "ffmpeg:-c:v libx264 -crf 23" "VIDEO_URL"
Thumbnail to Video
yt-dlp --write-thumbnail --skip-download "VIDEO_URL"
yt-dlp -f "bestaudio" -x --audio-format mp3 \
--embed-thumbnail \
"VIDEO_URL"
Batch Downloads
File-Based Batch
yt-dlp -a urls.txt
cat > urls.txt <<EOF
https://youtube.com/watch?v=VIDEO1
https://youtube.com/watch?v=VIDEO2
https://youtube.com/playlist?list=PLAYLIST_ID
EOF
yt-dlp -a urls.txt
yt-dlp -a urls.txt -f "bestaudio" -x --audio-format mp3
Scripted Batch
for url in $(cat urls.txt); do
yt-dlp -f "bestaudio/best" -x --audio-format mp3 "$url"
sleep 5
done
cat urls.txt | xargs -P 3 -I {} yt-dlp {}
Platform-Specific Patterns
SoundCloud
yt-dlp "https://soundcloud.com/artist/track"
yt-dlp "https://soundcloud.com/artist/sets/playlist"
yt-dlp "https://soundcloud.com/artist/tracks"
yt-dlp --embed-metadata \
-f "bestaudio" -x --audio-format mp3 \
"SOUNDCLOUD_URL"
Vimeo
yt-dlp "https://vimeo.com/VIDEO_ID"
yt-dlp --video-password PASSWORD "VIMEO_URL"
yt-dlp -f "bestvideo+bestaudio" "VIMEO_URL"
Bandcamp
yt-dlp "https://artist.bandcamp.com/album/album-name"
yt-dlp "https://artist.bandcamp.com/track/track-name"
yt-dlp -f "best" "BANDCAMP_URL"
Internet Archive
yt-dlp "https://archive.org/details/IDENTIFIER"
yt-dlp -f "FLAC/MP3/best" "ARCHIVE_URL"
yt-dlp "https://archive.org/download/IDENTIFIER/"
Troubleshooting Patterns
Only storyboards available / "n challenge solving failed"
Most common 2026 failure. The EJS solver is missing or invisible to a zipapp install. See Prerequisites above for the full fix.
pip install --user --break-system-packages yt-dlp-ejs yt-dlp
export PATH="$HOME/.local/bin:$PATH"
~/.local/bin/yt-dlp -F "VIDEO_URL"
"mweb formats require a GVS PO Token" / client-specific 403s
PO token provider missing. See Prerequisites above.
pip install --user bgutil-ytdlp-pot-provider
403 Forbidden on explicit format selectors (legacy SABR)
yt-dlp -f "best[ext=mp4]/best" "VIDEO_URL"
yt-dlp -f "bestaudio/best" -x --audio-format mp3 "VIDEO_URL"
pip install --user --break-system-packages -U yt-dlp yt-dlp-ejs
Slow Download Speed
yt-dlp --geo-bypass "VIDEO_URL"
yt-dlp --concurrent-fragments 4 "VIDEO_URL"
yt-dlp --external-downloader aria2c "VIDEO_URL"
Geo-Blocking
yt-dlp --geo-bypass "VIDEO_URL"
yt-dlp --geo-bypass-country US "VIDEO_URL"
Age-Restricted Content
yt-dlp --cookies-from-browser firefox "VIDEO_URL"
yt-dlp -u USERNAME -p PASSWORD "VIDEO_URL"
Performance Optimization
Parallel Downloads
yt-dlp --external-downloader aria2c \
--external-downloader-args "-x 16 -s 16 -k 1M" \
"VIDEO_URL"
Fragment Optimization
yt-dlp --concurrent-fragments 8 "VIDEO_URL"
yt-dlp --concurrent-fragments 4 "PLAYLIST_URL"
Complete Examples
Example 1: Music Video Collection
Download entire artist channel as 1080p MP4 with metadata.
yt-dlp -f "best[height<=1080][ext=mp4]/best[height<=1080]/best" \
--embed-metadata \
--embed-thumbnail \
--embed-subs \
-o "%(uploader)s/%(upload_date)s - %(title)s.%(ext)s" \
--download-archive downloaded.txt \
"https://www.youtube.com/@ArtistOfficial/videos"
Example 2: Audio-Only Discography
Extract audio from all videos in playlist as 320kbps MP3.
yt-dlp -f "bestaudio/best" \
-x --audio-format mp3 --audio-quality 320K \
--embed-metadata \
--embed-thumbnail \
-o "%(playlist)s/%(playlist_index)02d - %(title)s.%(ext)s" \
"PLAYLIST_URL"
Example 3: Live Concert Archive
Download concert with subtitles, thumbnail, and JSON metadata.
yt-dlp -f "bestvideo+bestaudio" \
--write-subs --embed-subs --all-subs \
--write-thumbnail --embed-thumbnail \
--write-info-json \
--write-description \
-o "Concerts/%(upload_date)s - %(title)s.%(ext)s" \
"VIDEO_URL"
Example 4: SABR-Safe Batch Download
Download multiple videos using SABR-compatible format selection.
cat > urls.txt <<EOF
https://youtube.com/watch?v=VIDEO1
https://youtube.com/watch?v=VIDEO2
https://youtube.com/watch?v=VIDEO3
EOF
yt-dlp -f "best[ext=mp4]/best" \
--embed-metadata \
--embed-thumbnail \
-o "%(title)s.%(ext)s" \
-a urls.txt
Best Practices
- Verify Prerequisites first — Install
yt-dlp-ejs + a JS runtime, and confirm yt-dlp itself was installed via pip (not the standalone zipapp) so plugins resolve. See the Prerequisites section above.
- Always use
--embed-metadata - Preserves video metadata in file
- Use download archive - Prevents re-downloading with
--download-archive
- Apply SABR workaround - Use
best[ext=mp4]/best for explicit-selector failures on newer videos
- Rate limit large downloads - Use
--sleep-interval for playlists/channels
- Organize with output templates - Use
-o for consistent file organization
- Install yt-dlp via pip, not the zipapp —
pip install --user --break-system-packages -U yt-dlp shares site-packages with yt-dlp-ejs; distros / package managers shipping the zipapp form will silently break plugin discovery.
- Test format selection - Use
-F to list formats before downloading; if you only see sb* storyboard formats, an EJS prerequisite is missing.
- Use cookies for member content -
--cookies-from-browser for restricted content; pass an explicit profile path for Flatpak browsers (see Authentication and Cookies > Flatpak browser profiles).
See Also
- Source Discoverer Agent:
@$AIWG_ROOT/agentic/code/frameworks/media-curator/agents/source-discoverer.md
- find-sources Command:
@$AIWG_ROOT/agentic/code/frameworks/media-curator/commands/find-sources.md
- Queue Manager Agent:
@$AIWG_ROOT/agentic/code/frameworks/media-curator/agents/queue-manager.md
References
- @$AIWG_ROOT/agentic/code/addons/aiwg-utils/rules/human-authorization.md — Seek explicit authorization before bulk channel downloads or overwriting existing files
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/find-sources/SKILL.md — Source discovery skill that identifies YouTube URLs for this skill to download
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/acquire/SKILL.md — General acquisition skill that delegates YouTube downloads to this skill
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/audio-extraction/SKILL.md — Audio extraction patterns used after YouTube video downloads
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/quality-filtering/SKILL.md — Quality filtering applied to select best available YouTube format