From makerskills
Transcribes and analyzes any video (YouTube, Loom, Vimeo, Zoom, local files) with three depth modes: transcript, visual (with frame extraction and Claude vision), and multimodal (Gemini native video).
How this skill is triggered — by the user, by Claude, or both
Slash command
/makerskills:watch-videoThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Replaces and broadens the prior `youtube-transcript` skill. YouTube is now one of many sources; depth is user-controlled.
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:
yt-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:
ffprobe -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):
Platform-provided transcript if it exists and looks complete:
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 setMLX-Whisper local (default fallback — fast on Mac M-series):
# 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>
whisper.cpp (further fallback if MLX unavailable)
Download the video file first if it's a URL (use yt-dlp; Loom/Vimeo/YT all supported):
yt-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):
# 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 |
mkdir -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):
ffmpeg -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:
# 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:
# 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 modeGemini native if $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.
# 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.
Dense Claude vision fallback if no Gemini key:
Same summary.md template as Step 7 + an extended section:
## 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(ormeeting-/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.npx claudepluginhub coreyhaines31/makerskills --plugin makerskillsDownloads videos, extracts frames and transcripts, and lets Claude answer questions about video content.
Analyzes YouTube videos using both transcript and visual frame extraction for step-by-step guides, especially useful for tutorials, demos, and how-to videos.
Analyzes product walkthroughs, bug report videos, Loom, or ScreenPal recordings into a durable brief with transcript, key frames, issues, and next steps.