| name | youtube-downloader |
| description | Download YouTube videos with interactive resolution and subtitle selection using yt-dlp. Use when the user wants to download a YouTube video, save a video from YouTube, get video subtitles/captions, or mentions youtube download, yt-dlp, video resolution, or SRT subtitles from YouTube. |
| license | MIT |
| metadata | {"author":"Yaniv Golan","version":"0.1.0","compatibility":"Requires yt-dlp and ffmpeg. Auto-installs via pip if missing."} |
YouTube Downloader
Downloads YouTube videos interactively — lists available resolutions, checks for subtitles, and lets the user choose exactly what to download.
Prerequisites (auto-install)
Before doing anything else, silently check if both tools are available:
yt-dlp --version 2>/dev/null; echo "yt-dlp:$?"
ffmpeg -version 2>/dev/null | head -1; echo "ffmpeg:$?"
If anything is missing, install it automatically — no need to ask the user. Both tools are pip-installable and require no sudo or system package manager:
pip install -U yt-dlp
pip install ffmpeg-static
If pip is not found, try pip3. If ffmpeg-static fails, try imageio[ffmpeg] as a fallback which bundles its own ffmpeg binary. As a last resort on macOS try brew install ffmpeg, on Linux try apt-get install -y ffmpeg (may need sudo — only then ask the user).
Also check for a JavaScript runtime — yt-dlp uses it for some format detection:
node --version 2>/dev/null || deno --version 2>/dev/null
If missing, install one silently: apt-get install -y nodejs 2>/dev/null || brew install node 2>/dev/null. This is optional — yt-dlp works without it but may warn or miss some formats.
After installation, verify all tools work before proceeding. Tell the user what was installed.
Workflow
Follow these steps in order when the user provides a YouTube URL.
Step 1: List available formats
Run yt-dlp -F "URL" to get the format table. Parse the output and present a summary to the user — do not dump the raw table. Group the results into:
- Video+Audio (single files): list resolution, codec, and approximate size
- Video-only: list resolution, codec, and approximate size
- Audio-only: list quality, codec, and approximate size
Use AskUserQuestion to let the user pick. Offer sensible defaults:
| Option | Description |
|---|
| Best quality (Recommended) | Best video + best audio, merged automatically |
| 1080p MP4 | Cap at 1080p, force MP4 container |
| 720p MP4 | Smaller file, good for mobile |
| Audio only | Best audio, no video |
| Custom | User specifies resolution or format code |
Step 2: Check for subtitles
Run:
yt-dlp --list-subs "URL" 2>&1
Parse the output and tell the user:
- Which manual (creator-uploaded) subtitle languages are available
- Which auto-generated caption languages are available
- Whether no subtitles are available at all
Use AskUserQuestion to let the user choose:
| Option | Description |
|---|
| No subtitles | Download video only |
| English SRT (Recommended) | Download English subtitles, convert to SRT |
| All available languages | Download all subtitle tracks as SRT |
| Specific language(s) | User picks from the available list |
If the user's preferred language is only available as auto-generated, inform them and ask if that's acceptable.
Step 3: Confirm and download
Before downloading, show the user a summary of their selections:
- Video format and resolution
- Subtitle choice (if any)
- The full yt-dlp command that will be run
Use AskUserQuestion to confirm: "Start download?" with options "Yes (Recommended)" and "Change settings".
Construct the yt-dlp command based on user choices. Use these format selectors:
| User choice | Format selector |
|---|
| Best quality | -f "bv*+ba/b" |
| 1080p MP4 | -f "bv*[height<=1080][ext=mp4]+ba[ext=m4a]/b[ext=mp4]" |
| 720p MP4 | -f "bv*[height<=720][ext=mp4]+ba[ext=m4a]/b[ext=mp4]" |
| Audio only | -f "ba/b" --extract-audio --audio-format mp3 |
For subtitles, append these flags:
| Subtitle choice | Flags |
|---|
| Manual subs | --write-subs --sub-langs "LANG" --convert-subs srt |
| Auto-generated | --write-auto-subs --sub-langs "LANG" --convert-subs srt |
| Both manual + auto | --write-subs --write-auto-subs --sub-langs "LANG" --convert-subs srt |
| All languages | --write-subs --write-auto-subs --sub-langs "all" --convert-subs srt |
Always add --convert-subs srt when downloading subtitles — YouTube serves VTT/srv3 natively, and SRT is more universally compatible.
Run the command and show the user the download progress.
Step 4: Confirm results
After download completes, list the downloaded files with their sizes:
ls -lh FILENAME*
Tell the user:
- The video file path and size
- Any subtitle files downloaded
- The actual resolution and codec of the downloaded video
Format Selector Reference
| Selector | Meaning |
|---|
bv* | Best video (may include audio) |
ba | Best audio-only |
b | Best single file |
+ | Merge two streams |
/ | Fallback if first unavailable |
[height<=720] | Filter by max resolution |
[ext=mp4] | Filter by container format |
Authentication via Browser (for restricted videos)
Some videos require authentication: age-restricted, private, or members-only content. When yt-dlp reports an error like "Sign in to confirm your age", "Private video", or "Join this channel to get access", use this workflow:
On desktop (Claude Code, Cursor)
Add --cookies-from-browser chrome (or firefox, edge, safari) to the yt-dlp command. This reads cookies from the user's local browser profile. The user must be logged into YouTube in that browser.
In sandboxed environments (Cowork, cloud)
There's no local browser profile, so use Playwright to get cookies:
-
Launch a browser and navigate to YouTube:
Use browser_navigate to go to https://accounts.google.com
-
Tell the user to log in. Wait for them to complete the Google login flow — use browser_snapshot to check if they've reached the YouTube homepage or their Google account page.
-
Once logged in, extract cookies:
Use browser_evaluate to run: JSON.stringify(await (async () => {
const cookies = document.cookie.split('; ').map(c => {
const [name, ...rest] = c.split('=');
return { name, value: rest.join('='), domain: '.youtube.com' };
});
return cookies;
})())
Better approach — if the Playwright MCP exposes a cookies API, use that directly to get all cookies for .youtube.com and .google.com domains.
-
Write cookies to a Netscape cookie file:
# Netscape cookie file format (one line per cookie):
# domain\tinclude_subdomains\tpath\tsecure\texpiry\tname\tvalue
.youtube.com TRUE / TRUE 0 COOKIE_NAME COOKIE_VALUE
Save to a temporary file, e.g., /tmp/yt-cookies.txt.
-
Retry the yt-dlp command with --cookies /tmp/yt-cookies.txt.
-
Clean up the cookie file after download completes.
What authentication CANNOT fix
DRM-protected content (rented/purchased movies, YouTube Premium originals) cannot be downloaded even with authentication. The video stream is encrypted. If yt-dlp reports a DRM error, inform the user that this content is not downloadable.
Error Handling
When yt-dlp fails, parse its stderr and respond with actionable advice:
| Error pattern | Meaning | Action |
|---|
Sign in to confirm / not a bot | Bot detection | Trigger the browser auth workflow above |
Private video | Video is private | Trigger browser auth; if already authenticated, tell user they lack access |
Join this channel | Members-only | Trigger browser auth; user must be a channel member |
Video unavailable | Deleted or removed | Inform user, nothing can be done |
is not available in your country | Geo-restricted | Suggest VPN or --proxy URL |
DRM / cannot decrypt | DRM-protected | Tell user this content cannot be downloaded — the stream is encrypted |
Requested format not available | Format mismatch | Fall back to -f "bv*+ba/b" and inform user |
ffmpeg not found | Missing ffmpeg | Run the prerequisite install step |
WARNING: Unable to extract.*nsig / JS player | Missing JS runtime | Install nodejs and retry |
Do not show raw yt-dlp error output to the user. Translate it into a clear message with a next step.
Troubleshooting
Slow download: YouTube may throttle. Add --concurrent-fragments 4 for parallel fragment downloads.
Geo-restricted video: Requires a VPN or proxy. Use --proxy URL if the user provides one.