Give Claude the ability to watch and understand a local video file. The skill turns the video into two streams Claude can reason over directly:
- Sampled frame images on disk — read on demand via the Read tool (Claude is multimodal).
- A timestamped transcript from local Whisper — fully offline after the first model download.
Claude is the interpreter. The scripts are a deterministic perception layer.
When to use
Trigger whenever the user points at a video file and wants it understood: summarization, Q&A, captioning, finding a moment, explaining what's on screen, transcribing dialogue, or describing visuals. Also trigger when the user says "watch this" with a path, or drops a video path into the chat with any implied question.
Do not use for: streaming URLs (YouTube etc. — ask the user to download first with yt-dlp), live camera feeds, or pure audio files (use a transcription-only tool instead).
Requirements
ffmpegandffprobeon PATH (brew install ffmpeg/apt install ffmpeg)- Python 3.9+
pip install -U openai-whisper(first run downloads ~150 MB for thebasemodel to~/.cache/whisper)
If openai-whisper is missing, scripts/transcribe_audio.py exits with code 2 and prints the install command. Surface that to the user, offer to install, then continue.
How to run
The orchestrator runs frame extraction and transcription in parallel and prints a JSON manifest to stdout:
python3 scripts/analyze_video.py --video /abs/path/to/file.mp4 \
[--fps 1] [--max-width 1024] [--whisper-model base] [--no-transcribe]
Manifest shape:
{
"video": "/abs/path/file.mp4",
"duration_seconds": 612.4,
"frames": {
"out_dir": "/tmp/video_frames_XXXX",
"fps": 1.0,
"frame_count": 612,
"frames": [{"path": "...frame_00001.jpg", "t": 0.5}, ...]
},
"transcript": {
"model": "base",
"language": "en",
"text": "full concatenated transcript...",
"segments": [{"start": 0.0, "end": 3.2, "text": "..."}, ...]
}
}
For partial work, call the sub-scripts directly:
scripts/extract_frames.py --video X --fps 2 --max-width 1024— frames only.scripts/transcribe_audio.py --video X --model base— transcript only.
Choosing parameters
Do not blindly default. Read the user's intent first, then pick:
| Intent | --fps |
--max-width |
Whisper model |
|---|---|---|---|
| Summarize a long lecture / meeting (>20 min) | 0.25 |
768 |
base |
| General Q&A, short clip (<5 min) | 1 |
1024 |
base |
| Detailed visual question, reading on-screen text | 2 |
1280 |
base |
| Noisy audio, accents, multilingual | — | — | small or medium |
| Audio-only question ("what did they say about X?") | skip frames | — | base |
| Visual-only question ("what's on screen at 1:30?") | 1–2 |
1024 |
--no-transcribe |
Bumping Whisper to medium pulls ~1.5 GB; large-v3 is ~3 GB. Warn the user before escalating.
Reasoning over the manifest
- For visual questions at a specific time — look up
frames.frames[i]wheretis closest to the target timestamp, thenReadthat single frame path. Do not Read all frames; pull only what you need. - For audio questions — search
transcript.segments[].text. Quote with[MM:SS-MM:SS]timestamps. - For synthesis / summaries — skim the full
transcript.text, then sample 5–10 frames spread across the duration (out_dirglob or evenly-spaced indices). Cite timestamps in the answer. - Cross-reference — when the user asks "what is shown while they talk about X?", find the segment, then pull the frame whose
tfalls in[segment.start, segment.end].
Always cite timestamps as [MM:SS] so the user can verify.
Edge cases
- No audio track →
transcript.segmentsis empty. Proceed with frames only. - Silent stretches → expected; transcript will have gaps.
- Very long video (>1 hr) → start at
--fps 0.25 --whisper-model base. Re-sample a narrow window by pre-trimming:ffmpeg -ss 600 -to 720 -i in.mp4 -c copy clip.mp4. - Frame timestamps are the midpoint of each sampling interval (
(i + 0.5) / fps), not exact decode PTS. Good enough for grounding; don't claim frame-accurate precision. - Frames dir is ephemeral — lives under
$TMPDIR. The OS reaps it; delete manually if the user is privacy-sensitive. - Whisper model cache —
~/.cache/whisper. Safe to delete to reclaim disk.
Self-annealing
If something breaks (ffmpeg missing codec, Whisper OOM on a large model, unsupported container), read the stderr, fix the root cause (install codec, drop to smaller model, remux the container), and note the learning. Do not silently swallow the failure.