/watch — give Claude eyes on videoPaste a video URL or a file path, ask a question. Claude downloads it, looks at the frames, reads the timestamped transcript, and answers from what it actually saw and heard — not from the title.
Claude can read a page, run a script, browse a repo. It cannot watch a video. This gives it that sense — and it's the single highest-leverage skill we run, because so much of what matters (competitor launches, ad creative, bug repros, conference talks, YouTube tutorials) only exists as video.
| Surface | Install |
|---|---|
| Claude Code recommended |
/plugin marketplace add scrappylabsai/watch/plugin install watch@watch |
| claude.ai (web) | Download watch.skill
→ Settings → Capabilities → Skills → + |
| Codex | git clone https://github.com/scrappylabsai/watch.git ~/.codex/skills/watch |
| Manual / dev | git clone https://github.com/scrappylabsai/watch.git ~/.claude/skills/watch |
Zero config to start. First run preflights itself: ffmpeg and yt-dlp
auto-install via brew on macOS (Linux/Windows print the exact commands). Native captions cover most
public videos for free — transcription only kicks in when a video has none.
/watch https://youtu.be/dQw4w9WgXcQ what happens at the 30 second mark?
/watch bug-repro.mov what's going wrong here?
/watch https://youtu.be/<long-talk> summarize this
Frames are now chosen by content, not by clock. One ffmpeg pass keeps every real scene change, plus a density floor so slow screencasts still get covered, then a sliding-window pixel dedup drops shots Claude has already seen. A static slide collapses to one frame; a fast-cut reel keeps every beat. Net effect: a 27-second screencast went from 27 frames to ~10 with nothing lost — 2–3× cheaper on static content, and it now catches cuts that land between whole-second sample points. Falls back to the old fixed-interval sampling automatically if scene detection misbehaves, so no video regresses.
Re-running on an already-downloaded file now picks up the cached .vtt/.srt sitting
next to it instead of re-transcribing. And oversized audio is split into chunks, transcribed, and stitched back
with correct timestamp offsets — which fixed the hard failure on anything past ~21 minutes.
--no-frames — transcript-only mode
Skips frame extraction entirely. Saves roughly 50–80k image tokens on talking-head content. Two people on Zoom for 50 minutes costs a fortune in tokens to tell you "yep, two people on Zoom."
Forked from bradautomates/claude-video v0.1.2.
We rewired the audio path: upstream uploads every clip to a paid Whisper API. Ours prefers a local/self-hosted
ASR endpoint via a small listen wrapper, with cloud Whisper kept as an explicit opt-in. Same public
surface, four backends, all the upstream frame logic and security model preserved.
These are the field-tested habits — the difference between "it works" and "it works cheaply and answers the question you asked."
--no-framesPodcast, interview, fireside chat, AMA, conference panel, anything "in conversation with" — the visuals carry no signal. Go transcript-only. If the transcript then says "look at this graph", re-run with frames. We ran a 2h19m tutorial this way, got the verdict (90% skippable, one 10-minute section worth keeping) for almost nothing.
--start / --endA sparse 100-frame scan of a 40-minute video is worse than a dense pass over the 30 seconds you care about.
Focused mode gets a much denser per-second budget inside the range, and the transcript is auto-filtered to match.
Accepts SS, MM:SS, or HH:MM:SS.
/watch "$URL" --start 2:15 --end 2:45 # zoom into one moment
/watch "$URL" --start 1:12:00 # from 1h12m to the end
--resolution 1024Default frame width is 512px, which is fine for "what's happening." If you need to read code, a slide, or a
prompt someone typed, bump it. It roughly quadruples image tokens per frame, so pair it with
--start/--end rather than using it on a whole video. We've recovered full prompts
verbatim off someone's screen recording this way.
--scene 0.15 — more sensitive cut detection if subtle transitions are being missed (default 0.30)--dedup-threshold 15 — raise for noisy/handheld footage that isn't deduping; 0 disables dedup--max-frames 40 — hard-cap the token budget--fixed-interval — force the old evenly-spaced samplingIf Claude already watched the video this session, the frames and transcript are still in its context. Asking a follow-up question costs nothing extra — re-running the script pays the whole image-token bill a second time.
Three routes, tried in order. Most public videos never leave step 1.
| Route | When | Cost |
|---|---|---|
| Native captions | Source platform has manual or auto-generated subs. Tried first, always. | Free · instant · already timestamped |
Self-hosted ASR--backend local |
A listen wrapper is on your PATH. Points at any OpenAI-compatible ASR endpoint —
yours, or ours at api.scrappylabs.ai as fallback. |
$0 marginal — runs on hardware you own |
Cloud Whisper--backend groq|openai |
No local wrapper installed, or forced explicitly. Groq whisper-large-v3 preferred;
OpenAI whisper-1 as fallback. |
Pay per call (Groq is the cheap/fast one) |
Start with captions only — it covers the large majority of real usage at zero cost. When you hit
a video with no captions, drop a GROQ_API_KEY into ~/.config/watch/.env and you're done;
it's pennies. If you ever want the $0 path on your own box, the listen wrapper is a ~30-line shell
script in the repo (docs/listen.sh) that points at any OpenAI-compatible ASR endpoint — happy to help
wire it up. Or --no-asr to keep audio entirely local and go frames-only.
Token cost is dominated by frames. The transcript is cheap (a few thousand tokens for a 10-minute video). Roughly: 80 frames at 512px ≈ 50–80k image tokens.
| Duration | Frame budget (cap) | What you get |
|---|---|---|
| ≤ 30 s | ~30 | Dense — basically every key moment |
| 30 s – 1 min | ~40 | Still dense |
| 1 – 3 min | ~60 | Comfortable |
| 3 – 10 min | ~80 | Sparse but workable |
| > 10 min | 100 + warning | Sparse scan — re-run focused instead |
Those are ceilings. Since 1.3.0, static content (screencasts, slides, talking heads) typically lands 2–3× under them because dedup collapses repeated shots. Hard limits either way: 2 fps, 100 frames.
A regression in Python 3.14's re module segfaults yt-dlp mid-download. Fix:
uv tool install --python 3.13 yt-dlp — the shim shadows the system one and it's durable.
If you point ~/.claude/skills/watch at a dev directory, cleaning that directory silently breaks
the skill — it fails with no obvious error. Install a real directory, or audit your symlinks periodically.
yt-dlp will fail plainly. That's a real failure, not a retry case — don't loop on it.
Each run leaves a temp working directory with the video, frames, and audio. Claude removes it when you're done asking follow-ups; on a long session, check it did.
.mov of something broken.
Claude finds the frame where it goes wrong and often catches the cause before you open the file.Everything runs locally except transcription. The video itself is never uploaded anywhere —
only an extracted mono 16 kHz audio clip goes out, and only when native captions are missing and ASR isn't
disabled. No platform login, no session cookies, no posting. API keys live in ~/.config/watch/.env
at 0600, are never logged, and are never shared across providers. --no-asr keeps
everything on your machine.