Install any skill in seconds. Free to start, no credit card required.
Get Started Free →When you want to extract content from a video — YouTube, Loom, Vimeo, Riverside, Zoom recording, local MP4, X/IG video, anything yt-dlp supports. Three depth modes user picks per invocation — transcript (just words, fast/free), visual (transcript + ffmpeg frame extraction + Claude vision pass on key moments), multimodal (Gemini native video ingestion if $GEMINI_API_KEY set, else dense Claude vision). Uses MLX-Whisper local on Mac for transcription, falls back to platform-provided transcripts whe
.claude/skills/coreyhaines31-watch-video/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 600% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 479% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 112% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 256% | 0% |
Replaces and broadens the prior youtube-transcript skill. YouTube is now one of many sources; depth is user-controlled.
Accept:
youtu.be/<id>, youtube.com/shorts/<id>, raw 11-char IDloom.com/share/<id> or loom.com/embed/<id>vimeo.com/<id>.mp4 from a downloaded recordingsocial-fetch for metadata, uses yt-dlp for the file.mp4 / .mov / .webm / .mkvDetect source from URL pattern or file extension. If ambiguous, ask.
| Invocation | Mode | What you get | |---|---|---| | /watch-video <url> | transcript (default) | Clean text, metadata, optional chapters | | /watch-video <url> transcript | transcript | Same as default | | /watch-video <url> visual | visual | Transcript + frames at intervals + Claude vision pass identifying key moments | | /watch-video <url> multimodal | multimodal | Native video to Gemini (if $GEMINI_API_KEY), else dense Claude vision frame-by-frame |
If the depth isn't specified and the video is >10 minutes, ask before defaulting (visual/multimodal cost real money on long videos).
For URL sources, use yt-dlp:
bashyt-dlp --print "%(title)s|%(uploader)s|%(duration_string)s|%(upload_date>%Y-%m-%d)s|%(description)s" \ --print "%(chapters)j" --skip-download "<url>"
Capture: title, uploader/channel, duration, upload date, description (first paragraph), chapters (JSON or null).
For local files, use ffprobe:
bashffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "<file>"
~/Documents/videos/<source>-<slug>-<date>/Where:
source: youtube / loom / vimeo / riverside / zoom / localslug: kebab-case of title (first 4–6 words, max 50 chars)date: YYYY-MM-DDBackend selection (in order):
yt-dlp --write-sub --write-auto-sub --skip-download --sub-lang en --sub-format vtthttps://www.loom.com/share/<id> page metadata or Loom API if $LOOM_API_KEY setbash # Install once: pip install mlx-whisper python3 -c "import mlx_whisper; mlx_whisper.transcribe('<file>', path_or_hf_repo='mlx-community/whisper-large-v3-turbo')" \ > "<workdir>/transcript-raw.json" Or via the CLI: mlx_whisper <file> --model mlx-community/whisper-large-v3-turbo --output-dir <workdir>
Download the video file first if it's a URL (use yt-dlp; Loom/Vimeo/YT all supported):
bashyt-dlp -f "bv*[height<=720]+ba/b[height<=720]" -o "<workdir>/video.%(ext)s" "<url>"
720p is plenty for transcription and frame analysis (smaller download, faster processing).
Clean the transcript (only needed for YouTube auto-subs which have rolling captions; Whisper output is already clean):
bash# YouTube VTT cleanup — de-dup rolling captions, strip tags, paragraph-break on cue gaps >2s awk ' /^WEBVTT/ || /^Kind:/ || /^Language:/ || /^NOTE/ { next } /-->/ { in_cue = 1; last = ""; next } /^$/ { if (last) print last; in_cue = 0; last = ""; next } in_cue { gsub(/<[^>]+>/, "", $0); last = $0 } END { if (last) print last } ' "<workdir>/transcript.en.vtt" | awk '!seen[$0]++' > "<workdir>/transcript.txt"
Save final to <workdir>/transcript.txt.
transcript mode: stop hereOutput:
transcript.txtmetadata.jsonvisual mode: extract frames + vision passCadence by source heuristic:
| Source type | Frame cadence | |---|---| | Screen-share / Loom / demo | 1 frame per 5s (UI changes fast) | | Talking head / podcast | 1 frame per 30s (slow change) | | Slide presentation | 1 frame per 10s + force a frame on each detected scene change | | Default if unsure | 1 frame per 15s |
bashmkdir -p "<workdir>/frames" ffmpeg -i "<workdir>/video.mp4" -vf "fps=1/15" "<workdir>/frames/frame-%04d.png" -y
For scene-change detection (slide decks especially):
bashffmpeg -i "<workdir>/video.mp4" -vf "select='gt(scene,0.3)',showinfo" -vsync vfr "<workdir>/frames/scene-%04d.png" 2> "<workdir>/scene-detection.log"
Pair each frame with the transcript chunk for the same timestamp window. Then batch-send to Claude vision for synthesis.
Per-frame batch prompt (up to ~10 frames per call):
> Here are N frames from a video at timestamps T1..TN. For each frame, describe what's on screen in 1–2 sentences. Flag: (a) UI changes from previous frame, (b) text visible on screen, (c) any moment that looks like a decision, action, or notable event. Also note the transcript text spoken during this window.
Save the output as <workdir>/moments.md:
markdown# Key moments — <title> ## 00:00:15 (frame-001.png) **On screen**: Login form, email field focused **Transcript**: "So you just open it up and..." **Note**: Beginning of UI demo ## 00:00:45 (frame-002.png) **On screen**: Dashboard with 4 cards **Transcript**: "And here's where you see all your projects." **Note**: Major view change — first time the dashboard appears
After moments are identified, synthesize the whole video into <workdir>/summary.md:
markdown# Summary — <title> **Source:** <source URL / file> **Duration:** <hh:mm:ss> **Watched at:** <date> **Mode:** visual ## TL;DR <2–4 sentences> ## Key moments - 00:00:15 — <one-line> - 00:00:45 — <one-line> ## Action items flagged - <item> [timestamp] ## Decisions flagged - <decision> [timestamp] — consider routing to /decide ## Quotes worth keeping - "..." [timestamp] ## Open questions - <question raised but not answered>
multimodal mode$GEMINI_API_KEY is set (much cheaper + faster than per-frame for long videos):Default model: gemini-3.5-flash (released May 2026, ~$1.50 input / $9 output per 1M tokens; ~$0.15/sec of video; beats 3.1 Pro on coding/agentic benchmarks at 4× the speed). Override to gemini-3.1-pro for brand audits / high-stakes analysis where details matter; gemini-2.5-flash-lite for bulk cheap processing.
bash # Step 1: Upload video via Files API FILE_URI=$(curl -s -X POST "https://generativelanguage.googleapis.com/upload/v1beta/files?key=$GEMINI_API_KEY" \ -H "X-Goog-Upload-Command: start, upload, finalize" \ -H "Content-Type: video/mp4" \ --data-binary "@<workdir>/video.mp4" | jq -r '.file.uri')
# Wait until file is ACTIVE (Gemini processes the video first) while true; do STATE=$(curl -s "$FILE_URI?key=$GEMINI_API_KEY" | jq -r '.state') "$STATE" = "ACTIVE" ] && break sleep 3 done
# Step 2: Generate content with the file + multimodal-analysis prompt curl -s -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent?key=$GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"contents\":{ \"parts\": {\"file_data\":{\"mime_type\":\"video/mp4\",\"file_uri\":\"$FILE_URI\"}}, {\"text\":\"<multimodal analysis prompt — see Step 7's summary template + use-case extensions>\"} ] }] }"
Files persist in Gemini Files API for ~48 hours — useful for re-querying the same video with different prompts.
Same summary.md template as Step 7 + an extended section:
markdown## Multimodal observations - **Body language / delivery**: <observations on talking-head video> - **Pacing**: <fast/slow/uneven> - **Visual style**: <brand audit, ad review, design observations> - **Audio quality / atmosphere**: <music, silence, background>
Exact extra sections depend on the use case (brand audit, ad review, talk delivery review, client-call read). Use case is inferred from the source + the user's verbal framing when invoking.
After any mode completes, offer:
> "Want to capture this to second-brain? I'll write a `call-<slug>.md` (or `meeting-` / `note-` / `resource-`) to `${SECOND_BRAIN_VAULT:-$HOME/Documents/SecondBrain}/raw/` with the summary, source URL, and transcript link."
Type prefix by source:
| Source | Prefix | |---|---| | Loom / Zoom / Riverside / Otter / call recording | call- | | Meeting (own notes, not a transcript) | meeting- | | Talk / keynote / conference | note- | | Ad / landing-page video / marketing reference / competitor video | resource- |
File body: 1-line source, the summary, link to full workdir.
In chat:
<source> · <title> · <duration> · <mode> · <word count> wordsvisual / multimodal: brief list of top 3 key moments| Source | Download | Built-in transcript | Notes | |---|---|---|---| | YouTube | yt-dlp | Auto-subs (--write-auto-sub) | Same as the prior youtube-transcript skill | | Loom | yt-dlp (Loom supported) | Yes — fetch via embed metadata or Loom API | Async screenshare focus — prime use case | | Vimeo | yt-dlp | Sometimes | Marketing/embed videos | | Riverside | Direct URL from export, or local file | Yes — Riverside generates them | Podcast episodes | | Zoom | Local .mp4 (downloaded recordings) | Sometimes (Zoom audio transcript file) | Client calls | | X / IG / TikTok | Defer to social-fetch for metadata, yt-dlp for file | No | Short-form | | Local file | n/a | n/a | Drop a path |
social-fetch — for X/IG/TikTok URL metadata (engagement, author, replies) before video processingsecond-brain — capture summary as raw/call-<slug>.md, meeting-, note-, or resource- per source typedecide — when a video contains a flagged decision, route to /decide for structured capturepm — action items flagged in summary can be triaged to project boardsslide-deck — talk recordings → outline extraction → deck draft (loop)jab-hook — quotes + clip-worthy moments from podcast/talk videos feed BIP/promo postsskillify from-video — primary use case for visual mode on process recordings. the user records themselves doing a workflow (Loom/screen-share), this skill extracts transcript + key visual moments, then skillify synthesizes the workflow into a SKILL.md. "Record once, AI converts to skill."| Failure | Response | |---|---| | Video unavailable / private / region-locked | Report and stop | | No subtitles + Whisper not installed | Tell the user: pip install mlx-whisper (Mac) | | ffmpeg missing (for visual/multimodal) | Tell the user: brew install ffmpeg | | Vision pass returns empty / unclear | Lower the frame count, retry, or fall back to transcript-only with a note | | Multimodal requested but no $GEMINI_API_KEY and >30min video | Warn cost, offer to fall back to visual mode | | yt-dlp binary missing | brew install yt-dlp |
yt-dlp -f "bv*[height<=720]+ba/b[height<=720]" is the default.ffmpeg -vf "select='gt(scene,0.3)'" to force a frame on each detected slide change — more reliable than pure time-based sampling.## Decisions flagged + ## Action items flagged sections signal /decide and /pm follow-ups. Downstream composability lives in the summary structure.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 12,366 | 4,263 | -66% | 1 | 1 | 0% | 1,615 | 4,568 | +183% | 0 | 0 | — |
case-02 | fail→pass | 6,166 | 17,184 | +179% | 1 | 1 | 0% | 920 | 6,436 | +600% | 0 | 0 | — |
case-07 | pass→pass | 15,878 | 11,460 | -28% | 1 | 1 | 0% | 2,734 | 5,986 | +119% | 0 | 0 | — |
case-03 | fail→fail | 17,591 | 6,644 | -62% | 1 | 1 | 0% | 2,642 | 4,457 | +69% | 0 | 0 | — |
case-04 | pass→pass | 5,959 | 3,183 | -47% | 1 | 1 | 0% | 1,050 | 4,564 | +335% | 0 | 0 | — |
case-05 | pass→pass | 10,341 | 4,453 | -57% | 1 | 1 | 0% | 1,644 | 4,786 | +191% | 0 | 0 | — |
case-06 | fail→pass | 4,445 | 2,524 | -43% | 1 | 1 | 0% | 767 | 4,438 | +479% | 0 | 0 | — |
case-08 | fail→pass | 16,337 | 5,141 | -69% | 1 | 1 | 0% | 2,556 | 4,815 | +88% | 0 | 0 | — |
case-09 | fail→fail | 14,522 | 10,843 | -25% | 1 | 1 | 0% | 2,396 | 5,716 | +139% | 0 | 0 | — |
case-10 | fail→pass | 15,496 | 8,289 | -47% | 1 | 1 | 0% | 2,500 | 5,297 | +112% | 0 | 0 | — |
case-11 | fail→pass | 7,053 | 2,793 | -60% | 1 | 1 | 0% | 1,234 | 4,390 | +256% | 0 | 0 | — |
case-12 | fail→pass | 14,385 | 3,453 | -76% | 1 | 1 | 0% | 2,061 | 4,578 | +122% | 0 | 0 | — |
case-13 | fail→pass | 12,852 | 3,271 | -75% | 1 | 1 | 0% | 2,072 | 4,543 | +119% | 0 | 0 | — |
case-14 | pass→pass | 10,728 | 4,283 | -60% | 1 | 1 | 0% | 1,561 | 4,706 | +201% | 0 | 0 | — |
case-15 | fail→pass | 7,549 | 2,910 | -61% | 1 | 1 | 0% | 1,258 | 4,508 | +258% | 0 | 0 | — |
case-16 | fail→pass | 9,890 | 2,600 | -74% | 1 | 1 | 0% | 1,559 | 4,481 | +187% | 0 | 0 | — |
case-17 | fail→pass | 11,887 | 2,313 | -81% | 1 | 1 | 0% | 1,855 | 4,409 | +138% | 0 | 0 | — |
case-18 | pass→pass | 6,204 | 2,915 | -53% | 1 | 1 | 0% | 1,029 | 4,468 | +334% | 0 | 0 | — |
case-19 | pass→pass | 3,702 | 1,541 | -58% | 1 | 1 | 0% | 605 | 4,216 | +597% | 0 | 0 | — |
case-20 | pass→pass | 13,001 | 3,977 | -69% | 1 | 1 | 0% | 2,142 | 4,795 | +124% | 0 | 0 | — |
case-21 | fail→pass | 9,882 | 4,103 | -58% | 1 | 1 | 0% | 1,694 | 4,688 | +177% | 0 | 0 | — |
case-22 | pass→pass | 11,478 | 10,933 | -5% | 1 | 1 | 0% | 2,105 | 6,002 | +185% | 0 | 0 | — |
case-23 | pass→pass | 17,543 | 17,699 | +1% | 1 | 1 | 0% | 3,419 | 7,417 | +117% | 0 | 0 | — |
case-24 | pass→pass | 14,842 | 14,162 | -5% | 1 | 1 | 0% | 2,539 | 6,401 | +152% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 23 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +46 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.