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

The 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 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, 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:

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():

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.

The fetch_captions Function

Located at lines 73-95 in skills/watch/scripts/download.py, this function handles caption-only extraction:

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

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:

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 consumes without additional I/O operations.

Integration with the Transcription Pipeline

The extracted VTT files flow directly into the transcription workflow. The transcribe.py module expects the subtitle_path value from the dictionary returned by 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 and skills/watch/scripts/transcribe.py.

Summary

  • 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

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 ensures all subtitle files are converted to VTT regardless of the source format (SRT, TTML, etc.). This standardization allows 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →