# How yt-dlp Integration Handles Video Downloading and Caption Fetching in Claude-Video

> Discover how Claude-Video leverages yt-dlp for seamless video downloading and VTT caption fetching. Learn about its unified Python wrapper for media retrieval and metadata extraction.

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

---

**The Claude-Video skill uses yt-dlp as its sole external binary to fetch videos and extract English VTT subtitles, orchestrating caption-only metadata retrieval and full media downloads through a unified Python wrapper in [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py).**

The `bradautomates/claude-video` repository implements a robust **yt-dlp integration** that isolates all external binary interactions to a single module. This design allows the skill to efficiently retrieve video metadata and subtitles without downloading the actual media file when possible, while gracefully falling back to full video extraction when needed. The integration supports both local file paths and remote URLs, handling format conversion, subtitle language filtering, and error recovery automatically.

## Detecting URLs vs. Local Files

The integration first determines whether the user provided a remote URL or a local file path. In [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py), the `is_url` function checks if the source string starts with `http` or `https`, while `resolve_local` handles filesystem paths.

```python
def is_url(source: str) -> bool:
    return source.startswith(("http://", "https://"))

def resolve_local(path: str) -> dict:
    # Returns dict with file path and downloaded=False

    return {"video_path": path, "downloaded": False, ...}

```

When `is_url` returns `False`, the skill bypasses yt-dlp entirely and processes the local file directly. This binary-agnostic approach allows the rest of the pipeline to operate identically on both downloaded and locally supplied media.

## Fetching Metadata and Captions Without Downloading

For URL-based sources, the skill first attempts to retrieve subtitles without downloading the video file. The [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) script calls `fetch_captions` (lines 98-100) to build a yt-dlp command that skips the media download but extracts metadata and subtitles.

The `fetch_captions` function (lines 65-95 in [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py)) constructs a subprocess command with the following flags:

- `--skip-download` – Prevents video retrieval
- `--write-info-json` – Saves metadata to a JSON file
- `--write-subs` and `--write-auto-subs` – Captures manual and auto-generated subtitles
- `--sub-langs en.*` – Filters for English language tracks only
- `--sub-format vtt --convert-subs vtt` – Enforces VTT format output

After execution, helper functions `_pick_subtitle` and `_read_info` locate the best VTT file and parse the metadata JSON, returning a dictionary containing the subtitle path (or `None`) and video information.

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

result = fetch_captions("https://youtube.com/watch?v=...", Path("./work"))
print(result["subtitle_path"])  # Path to VTT file or None

print(result["info"]["title"])  # Video metadata

```

## Full Video and Audio Downloads

When subtitle-only retrieval is insufficient—such as when frame extraction is required—the skill falls back to `download` (called at lines 120-126 in [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py)). This function delegates to `download_url` (lines 115-162), which constructs a comprehensive yt-dlp command for media retrieval.

The `download_url` implementation specifies:

- `-N 8` – Uses up to 8 parallel fragment workers for faster downloads
- Format selection limited to 720p video (or `ba/bestaudio` when `audio_only=True`)
- `--merge-output-format mp4` – Combines separate video/audio streams into MP4
- The same subtitle flags as `fetch_captions` to capture VTT files during download
- `--no-playlist --ignore-errors` – Skips playlists and continues on individual errors

After execution, `_pick_video` locates the downloaded media file while `_pick_subtitle` extracts the VTT path. The function returns a complete dictionary with `video_path`, `subtitle_path`, `info` metadata, and `downloaded=True`.

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

# For video + subtitles

result = download("https://vimeo.com/12345", Path("./work"), audio_only=False)

# For audio-only extraction

audio_result = download("https://youtube.com/...", Path("./work"), audio_only=True)

```

## Error Handling and Edge Cases

The integration handles several failure modes gracefully:

- **Missing binary**: Both `fetch_captions` and `download_url` verify yt-dlp existence and exit with `SystemExit` pointing to installation instructions (e.g., `brew install yt-dlp`)
- **Subtitle failures**: HTTP 429 or other subtitle download errors are ignored as long as the video file succeeds (`result.returncode` is not checked for video presence)
- **No available captions**: When `_pick_subtitle` finds no VTT files, it returns `None`; downstream code falls back to Whisper transcription if enabled
- **Audio-only mode**: The format selector switches to best audio, producing an audio file while maintaining the same return structure

The [`setup.py`](https://github.com/bradautomates/claude-video/blob/main/setup.py) module validates that both yt-dlp and ffmpeg/ffprobe are installed before the skill attempts any downloads.

## Orchestration Flow in watch.py

The high-level flow in [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) optimizes for minimal bandwidth:

1. **Initial check**: Create temporary working directory and call `fetch_captions` for metadata and subtitles
2. **Subtitle parsing**: If VTT files exist, `parse_vtt` converts them to transcript segments
3. **Conditional download**: If subtitles are missing or frames are needed, call `download` to fetch the actual media
4. **Post-processing**: Frame extraction and transcription modules operate on the resulting files

This two-phase approach ensures that simple transcript requests never trigger unnecessary video downloads, while maintaining a consistent interface for downstream processing.

## Summary

- **Centralized wrapper**: All yt-dlp interaction is isolated in [`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py), making the skill testable and binary-agnostic
- **Dual-mode operation**: `fetch_captions` retrieves metadata without video, while `download`/`download_url` handles full media extraction with parallel workers and 720p limits
- **VTT enforcement**: Both paths force English subtitles (`en.*`) into VTT format and parse them via `_pick_subtitle`
- **Resilient design**: The integration tolerates missing subtitles, network errors, and missing binaries with clear fallback paths
- **Audio support**: Setting `audio_only=True` switches the format selector to best audio while maintaining identical return structures

## Frequently Asked Questions

### What happens if yt-dlp is not installed on the system?

The skill checks for the yt-dlp binary at runtime in both `fetch_captions` and `download_url`. If the binary is missing, the functions raise `SystemExit` with a clear message directing the user to install yt-dlp via package managers like `brew install yt-dlp` or `pip install yt-dlp`, preventing cryptic failures.

### Can I download audio only without fetching the video file?

Yes. Pass `audio_only=True` to the `download` function. This switches the yt-dlp format selector from 720p video to `ba/bestaudio`, downloading only the audio stream while still attempting to retrieve VTT subtitles and metadata. The returned dictionary uses the same keys, with `video_path` pointing to the audio file.

### What subtitle formats does the Claude-Video integration support?

The integration exclusively uses **VTT format**. Both `fetch_captions` and `download_url` pass `--sub-format vtt --convert-subs vtt` to yt-dlp, ensuring all subtitle tracks (manual and auto-generated) are converted to VTT. The `_pick_subtitle` helper specifically searches for `.vtt` files in the output directory.

### How does the skill handle videos that have no captions available?

When yt-dlp produces no VTT files, `_pick_subtitle` returns `None` for the `subtitle_path` key. The [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) orchestration detects this and falls back to Whisper transcription if the user has enabled audio processing. If the operation requires frames or the user disabled transcription, the skill proceeds with video download but marks subtitles as unavailable.