youtube-downloader

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.

yaniv-golan/ytd-skill2 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
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"
---

# 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:

```bash
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:

```bash
pip install -U yt-dlp    # pure Python, works everywhere
pip install ffmpeg-static # pre-built ffmpeg binary, no sudo needed
```

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:
```bash
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:
```bash
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:
```bash
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:

1. Launch a browser and navigate to YouTube:
   ```
   Use browser_navigate to go to https://accounts.google.com
   ```

2. 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.

3. 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.

4. 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`.

5. Retry the yt-dlp command with `--cookies /tmp/yt-cookies.txt`.

6. 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.

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY