# How download.py Extracts Video Captions via yt-dlp: A Complete Guide

> Learn how download.py uses yt-dlp to extract English video captions and convert them to VTT format. A complete guide to subtitle extraction.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: how-to-guide
- Published: 2026-07-12

---

**The [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) module in the bradautomates/claude-video repository orchestrates English caption extraction by invoking yt-dlp with specific subtitle flags, converting output to VTT format, and selecting the best available English subtitle file for downstream processing.**

The [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) script serves as the core download engine for the **claude-video** project, handling both full video downloads and caption-only extraction. This module leverages **yt-dlp**'s robust subtitle capabilities to fetch automatically-generated or manually-uploaded captions, standardizing them into WebVTT format for consistent processing by the transcription pipeline.

## How Caption Extraction Works in download.py

The caption extraction process follows a five-step pipeline that ensures reliable acquisition of English subtitles. According to the source code in [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py), the implementation prioritizes **VTT format** compatibility and **English language** variants while maintaining clean separation between video download and caption-only workflows.

### Preparing the Output Directory

Before invoking yt-dlp, the code ensures the destination directory exists and is writable. In `fetch_captions()` at lines 70-71, the script uses:

```python
out_dir.mkdir(parents=True, exist_ok=True)

```

This guarantees that yt-dlp has a valid location to drop subtitle files without raising filesystem errors.

### Configuring yt-dlp Subtitle Flags

The core of the extraction logic resides in the command-line arguments passed to yt-dlp. Whether performing a caption-only fetch or full video download, the code includes specific flags to target English subtitles:

- **`--write-subs`** – Requests manually uploaded subtitles
- **`--write-auto-subs`** – Requests automatically generated captions  
- **`--sub-langs en.*`** – Limits retrieval to English variants (e.g., `en`, `en-US`, `en-GB`)
- **`--sub-format vtt`** – Specifies the native output format
- **`--convert-subs vtt`** – Ensures conversion to WebVTT when source formats differ

For caption-only extraction, the `fetch_captions()` function (lines 73-81) appends `--skip-download` to avoid fetching the video file itself. When downloading both video and captions via `download_url()` (lines 28-38), these flags accompany the video download command.

### Executing the Subprocess

The module executes yt-dlp through Python's subprocess module. At line 87 in `fetch_captions()`:

```python
subprocess.run(cmd, stdout=sys.stderr, stderr=sys.stderr)

```

This streams yt-dlp's logs directly to the console while the tool fetches metadata and subtitle files, providing real-time feedback during the extraction process.

### Selecting the Best Subtitle File

After yt-dlp completes, the `_pick_subtitle()` function (lines 44-52) scans the output directory for `*.vtt` files. The selection logic prioritizes:

1. Files containing `.en.` in the filename
2. Files containing `.en-US.`, `.en-GB.`, or `.en-orig.` variants  
3. The first available VTT file if no English-specific match exists

This ensures the pipeline selects the most appropriate English subtitle track when multiple languages or variants are available.

## Code Implementation Details

The implementation exposes two primary functions for caption handling, each returning a structured dictionary consumed by downstream modules like [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py).

### The fetch_captions Function

Located at lines 73-95 in [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py), this function handles caption-only extraction:

```python
from pathlib import Path
from skills.watch.scripts.download import fetch_captions

url = "https://www.youtube.com/watch?v=abc123"
out_dir = Path("/tmp/watch_demo")
result = fetch_captions(url, out_dir)

print("Subtitle file:", result["subtitle_path"])
print("Video URL:", result["info"]["url"])

```

The function returns a dictionary containing:
- `video_path`: `None` (no video downloaded)
- `subtitle_path`: Path to the selected VTT file
- `info`: Metadata dictionary from [`video.info.json`](https://github.com/bradautomates/claude-video/blob/main/video.info.json)
- `downloaded`: Boolean flag indicating success

### Subtitle Selection Logic

The `_pick_subtitle()` helper implements intelligent file selection by scanning the output directory and ranking files based on language patterns. It prefers files matching English locale markers before falling back to any available VTT file, ensuring robust handling of yt-dlp's various naming conventions.

## Practical Usage Examples

The module supports two distinct workflows depending on whether you need standalone captions or video with transcription data.

### Extracting Captions Only

For scenarios requiring only subtitle text without the video payload:

```python
from pathlib import Path
from skills.watch.scripts.download import fetch_captions

url = "https://www.youtube.com/watch?v=abc123"
out_dir = Path("/tmp/caption_extraction")
result = fetch_captions(url, out_dir)

if result["subtitle_path"]:
    print(f"Captions saved to: {result['subtitle_path']}")

```

This approach uses `--skip-download` to minimize bandwidth and storage requirements.

### Downloading Video with Captions

For full pipeline processing that includes frame extraction:

```python
from pathlib import Path
from skills.watch.scripts.download import download

url = "https://www.youtube.com/watch?v=xyz789"
out_dir = Path("/tmp/full_download")
metadata = download(url, out_dir)

print("Video file:", metadata["video_path"])
print("Subtitle file:", metadata["subtitle_path"])

```

Both functions return compatible dictionaries that [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) consumes without additional I/O operations.

## Integration with the Transcription Pipeline

The extracted VTT files flow directly into the transcription workflow. The [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) module expects the `subtitle_path` value from the dictionary returned by [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py), parsing the WebVTT format to extract timestamped text segments. If no subtitle file exists (or if `_pick_subtitle()` returns `None`), the transcription pipeline falls back to Whisper-based audio transcription.

This architecture ensures that **caption extraction via yt-dlp** remains decoupled from text processing, allowing each component to evolve independently while maintaining clean interfaces between [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py) and [`skills/watch/scripts/transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/transcribe.py).

## Summary

- **[`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py)** centralizes caption extraction using yt-dlp's subtitle download capabilities
- **VTT format** is enforced through `--sub-format` and `--convert-subs` flags for compatibility with downstream parsers
- **English language** filtering uses `--sub-langs en.*` to capture all English variants
- **`_pick_subtitle()`** implements intelligent file selection prioritizing `.en.` patterns
- **Both** caption-only and full-video workflows return structured dictionaries consumed by [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py)

## Frequently Asked Questions

### What subtitle formats does download.py support?

The module specifically targets **WebVTT (VTT)** format. By passing `--sub-format vtt` and `--convert-subs vtt` to yt-dlp, [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) ensures all subtitle files are converted to VTT regardless of the source format (SRT, TTML, etc.). This standardization allows [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) to parse caption files consistently without implementing multiple format handlers.

### Can download.py extract captions in languages other than English?

The current implementation hardcodes `--sub-langs en.*` in the yt-dlp command arguments within `fetch_captions()` and `download_url()`. To support other languages, you would need to modify the command list in [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py) to replace `en.*` with your desired language code (e.g., `es.*` for Spanish or `fr.*` for French).

### How does the module handle videos without captions?

When yt-dlp cannot find subtitles matching the English language filter, or when `_pick_subtitle()` finds no VTT files in the output directory, the function returns `None` for the `subtitle_path` key in the result dictionary. The calling code in [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) checks this value and automatically falls back to audio-based transcription using Whisper when no subtitle file is available.

### Why does fetch_captions use --skip-download?

The `--skip-download` flag prevents yt-dlp from fetching the video payload when only metadata and subtitles are required. This optimization significantly reduces bandwidth usage and processing time for workflows that only need caption text without visual frame extraction. When the full pipeline requires both video and captions, the `download()` function omits this flag to retrieve the complete media file.