Imported from JulesDups/claude-config-public (
skills/analyze-video/SKILL.md). Install upstream withnpx skills add JulesDups/claude-config-public --skill analyze-video. Copyright stays with the author.
name: analyze-video description: Download videos (YouTube/Vimeo/mp4), transcribe locally, extract frames, then dispatch parallel sub-agents to write one Markdown synthesis per video + aggregate recap. disable-model-invocation: true allowed-tools: Read, Write, Edit, TodoWrite, Glob, Grep, Agent, Monitor, Bash(yt-dlp *), Bash(ffmpeg *), Bash(ffprobe *), Bash(python *), Bash(trash *), Bash(powershell *), Bash(gio *), Bash(osascript *), Bash(code *), Bash(start *), Bash(open *), Bash(xdg-open *), Bash(nvidia-smi *), Bash(bash *), Bash(tail *) argument-hint: [url1 url2 ...] | [--file urls.txt] | [path/to/urls.txt]
Download one or many videos (YouTube, Vimeo, direct mp4, S3/CloudFront, etc.), transcribe locally on GPU when available, extract frames, then dispatch cheap parallel Haiku/Sonnet sub-agents to write one exhaustive Markdown synthesis per video. After all per-video syntheses land, a single reporter agent writes a cross-video aggregate synthesis. Only the synthesis files survive cleanup.
This skill is NOT:
- A translator — syntheses match each video's dominant language. No translation unless user asks.
- A summarizer — syntheses are exhaustive on visible UI, verbatim on on-screen text.
- A transcription service for audio-only — frame sampling mandatory; pure-audio URLs rejected.
- A live narrator — see Silence Policy below.
- A tool for private/login-gated URLs — yt-dlp will fail; mark URL skipped and move on.
Silence Policy (CRITICAL)
The orchestrator (main Claude) MUST emit ZERO user-facing text between Step 2 (env check pass) and the final report of Step 16. Tool calls are silent by design. Status updates, "now doing X", per-frame observations, post-cleanup confirmations — all forbidden. The user reads the final report and the synthesis files; nothing else.
Permitted user-facing output:
- One-line fatal error if Step 2 env check fails (single line, then stop).
- Final aggregate report at Step 16 (single Markdown block: table + reporter output path + cleanup status).
Sub-agents follow the same silence policy: they only emit their per-video summary at the very end of their work; their internal tool calls are not surfaced to the user.
If the user explicitly requests progress updates ("verbose mode", "narrate"), the orchestrator may emit one short caveman-style line per major phase transition (download done / transcribe done / synthesis done / cleanup done). Default = silent.
Usage
/analyze-video [url1] [url2] ...
/analyze-video --file path/to/urls.txt
/analyze-video path/to/urls.txt # auto-detected if single arg ends in .txt
Examples:
/analyze-video https://www.youtube.com/watch?v=E6ncPDcIb2k
/analyze-video https://www.youtube.com/watch?v=aaa https://vimeo.com/bbb
/analyze-video C:/Users/jules/Desktop/videos_urls.txt
/analyze-video --file ./formation_urls.txt
A urls.txt file may contain one URL per line, with optional comment headers (lines starting with # or text-only lines without http). Blank lines and === separators are ignored. Optional ## Title lines preceding a URL are captured as the human title for that video.
If no URL is passed, stop and emit the expected usage on a single line.
Required environment
| Tool | Why | Fallback |
|---|---|---|
yt-dlp |
Download videos / fetch metadata / fetch auto-subs when present | none — fatal |
ffmpeg + ffprobe |
Extract audio + frames + read duration | none — fatal |
python (3.10+) with faster_whisper + ctranslate2 |
Local transcription when no auto-sub is available | If missing: skip transcription, frames-only synthesis with explicit warning footer |
NVIDIA GPU + CUDA-enabled ctranslate2 |
Fast transcription (large-v3 float16). Detected via nvidia-smi + ctranslate2.get_cuda_device_count() |
CPU fallback (base int8) — much slower; warn in final report |
trash (trash-cli) |
Safe scratch cleanup | Recycle Bin shell verb (Windows) / gio trash (Linux) / osascript Finder delete (macOS) |
code CLI |
Open syntheses in VS Code | start (Windows) / open (macOS) / xdg-open (Linux) |
Instructions
-
Parse arguments — Tokenize
$ARGUMENTS. Branches:- 0 tokens → stop, emit usage line.
- 1 token ending in
.txtOR starting with--file→ file mode (read URL list). - All other tokens → URL mode (positional URLs).
- In file mode, parse the file: keep lines starting with
http/https, capture the immediately preceding non-empty non-URL line as the candidate human title. Strip leading numbering like1.,01). Build[(title, url), ...]queue. - Build the queue. If empty → stop with "no valid URL found" line.
-
Validate environment — Single batched check via Bash:
yt-dlp --versionandffmpeg -version— fatal if either missing.python -c "import faster_whisper, ctranslate2; print(ctranslate2.get_cuda_device_count())"— recordgpu_count. If import fails: settranscription_mode=skipand continue. Ifgpu_count >= 1: settranscription_mode=cuda. Elsetranscription_mode=cpu.nvidia-smi --query-gpu=name,memory.total --format=csv,noheader— record GPU name (informational).- On fatal failure, emit single line
FATAL: <missing tool>and stop.
-
Provision the pipeline script — Use
~/.claude/skills/analyze-video/scripts/process_video.py(absolute path). The file ships with the skill; do not write inline. If missing, abort withFATAL: skill scripts/process_video.py missing — reinstall skill. The script:- Args:
<url> <video_id> <scratch_dir> [fps_div]. - Steps: yt-dlp download → ffprobe duration → fps_div auto-bump for >20min → ffmpeg audio (
16k mono pcm_s16le) → ffmpeg frames (fps=1/{div}, scale=800:-1) → faster-whisper transcription (large-v3float16 CUDA, fallbackbaseint8 CPU; language=auto with bias to user-default). - Writes:
video.mp4,audio.wav,frames/frame_NNN.jpg,transcript.srt,meta.json. - Exits 0 on success.
- Args:
-
Provision the batch driver — Write a per-run bash driver
<perm-root>/.batch-<run-id>.shthat loops the queue sequentially and invokesprocess_video.pyper URL. Logs to<perm-root>/.batch-<run-id>.log. Each loop iteration emits=== START chNN ===and=== END chNN ===markers (used by the Monitor watch). The Python invocation must end with|| trueso a non-zero exit from the script does not abort the entire batch loop. -
Per-video naming — For each
(title, url)in the queue:video_id = chNN(zero-padded index from queue order). Used for scratch dir name.slug= slugify(title) → lowercase ASCII, non-alnum →-, collapse repeats, trim 60 chars. If batch from file with a common course name in headings, prefix slug with{course-slug}-NN-for natural sort.synthesis_filename = {YYYY-MM-DD}-{slug}.md(single video) or{YYYY-MM-DD}-{course-slug}-{NN}-{slug}.md(batch from file).- If a file with the same name exists today, append
-2,-3before.md.
-
Set up locations — Permanent root:
/c/Users/jules/Documents/pro/hegoatek/creation contenu/syntheses videos.mkdir -pit. Per-video scratch:<perm-root>/.work-{video-id}/. Quote every path (it contains spaces). -
Launch the batch driver in background —
Bash run_in_background: trueinvokingbash <batch-driver>.sh. Capture the background task ID. -
Arm a Monitor watch —
Monitorwithtail -F <batch-log> | grep -E --line-buffered "===|FAILED|BATCH DONE|TRANSCRIBING|Error|Traceback". Coverage rule: include progress + failure signatures. Usetimeout_ms≈120000 + 900000 * N(2 min base + 15 min/URL — accounts for GPU transcription of long videos). -
Wait silently — While the batch runs, do not narrate. The Monitor surfaces
START/END/FAILEDevents automatically. Do NOT poll, do NOT sleep, do NOT issue intermediate report lines. -
On
FAILED chNNevents — Inspect the scratch dir AFTEREND chNN. Ifmeta.json+transcript.srtexist, treat as false positive (Windows exit-code quirk) and log internally as OK. Only mark as real failure if scratch dir is missing artifacts. -
On
BATCH DONE— Probe each scratch dir formeta.json. Build the per-video manifest:[{video_id, title, url, scratch, frames, duration_sec, srt_size, status: ok|incomplete}, ...]incompletevideos are skipped from synthesis dispatch and recorded asskippedin the final report. -
Dispatch parallel synthesis sub-agents — Decide cohort size based on video count
N:- N == 1: 1 sub-agent (Haiku) for the synthesis. Orchestrator does NOT write the synthesis itself — to keep the main context clean.
- 2 ≤ N ≤ 5: 1 sub-agent per video (Haiku each).
- 6 ≤ N ≤ 12: 2–3 sub-agents, each handling a contiguous chunk (
ceil(N / 3)videos per agent). Default model: Haiku. Use Sonnet only for chunks where average video duration > 25 min (denser content benefits from stronger reasoning). - N > 12: 4 sub-agents (Haiku), chunks of ≈ N/4. Hard cap.
- Send all sub-agent calls in a single message with multiple Agent tool uses so they run concurrently.
- Each sub-agent prompt MUST be self-contained (see "Sub-agent prompt template" below). Sub-agents are general-purpose. Override model per agent via the
modelparameter (haikudefault,sonnetfor dense content).
-
Sub-agent contract — Each sub-agent: a. For each assigned chapter: read
meta.json, readtranscript.srtfully, readframes/in batches of 5–6 via Read tool (vision auto-render), cross-reference timestamps, write the synthesis Markdown at the canonical output path, runcode "<absolute path>", thentrash "<scratch dir>"(max 2 attempts, fall back to PowerShell Recycle Bin verb on Windows). b. Report back ONLY a per-chapter Markdown table (chapter, filename, frames, size in KB, cleanup status). No prose. -
Wait for all synthesis sub-agents — They run in background. The orchestrator receives one notification per agent on completion. Do NOT narrate while waiting. Do NOT spawn new work.
-
Dispatch reporter agent — After all synthesis agents complete:
- Skip if
N == 1. - Otherwise: dispatch ONE Sonnet sub-agent to read every per-video synthesis file (titled
{YYYY-MM-DD}-*.mdmatching this run's prefix) and produce one aggregate cross-video synthesis at<perm-root>/{YYYY-MM-DD}-{course-slug}-00-RECAP.md. Sections (adapt to detected language):# Récapitulatif global — {course title}## Vue d'ensemble— 5–10 lignes denses sur l'arc narratif global.## Carte des chapitres— table (n°, titre, durée, 1-ligne résumé, 2 takeaways).## Concepts récurrents— concepts qui apparaissent dans 2+ chapitres, avec liste des chapitres source.## Stack et outils mentionnés (agrégé)— dédupliqué.## Pricing / chiffres clés (agrégé).## URLs et liens (agrégé).## Hooks et formules transversales.## Plan d'action recommandé— 7–15 actions priorisées synthétisées des takeaways individuels.## Génération— date, nombre de chapitres, modèle whisper, nombre total de frames.
- Reporter then opens the recap in VS Code.
- Skip if
-
Final report (orchestrator, single Markdown block) — After reporter completes, emit one message containing:
- Brief one-line header:
Processed N URLs. - One table:
| Chapter | URL host/path | Filename | Frames | Size (KB) | Status |. - If reporter ran:
Recap: <path to RECAP md>. - If any video failed:
Failed: chNN — <one-line reason>rows. - If any cleanup failed:
Cleanup-failed: <path>lines. - Nothing else. No prose, no congratulations.
- Brief one-line header:
Cohort sizing reference
| N URLs | Sub-agents | Model | Chunk size |
|---|---|---|---|
| 1 | 1 | Haiku | 1 |
| 2–5 | N | Haiku | 1 |
| 6–8 | 3 | Haiku (or Sonnet if avg dur > 25min) | ⌈N/3⌉ |
| 9–12 | 3 | Haiku | ⌈N/3⌉ |
| 13–20 | 4 | Haiku | ⌈N/4⌉ |
| > 20 | 4 | Haiku | ⌈N/4⌉ (offer to split in two runs) |
Token-cost rationale: Opus is forbidden for sub-agents in this skill. Haiku reads vision frames at a fraction of the cost. Reporter uses Sonnet because cross-chapter synthesis benefits from stronger reasoning.
Sub-agent prompt template (self-contained)
Each Agent call uses subagent_type=general-purpose and a model override (haiku or sonnet). The prompt must include:
- The hard rule that this is silent mode — no narration, only final per-chapter table at end.
- The chapter assignment table:
| chNN | original title | absolute scratch dir path |. - The exact synthesis output path pattern.
- The slug-to-NN mapping.
- The synthesis sections in the target language (French by default, autodetect if non-FR videos).
- The cleanup contract (
trashfirst, PowerShell Recycle Bin verb fallback, hard cap 2 attempts, neverrm -rf). - The final per-chapter report table format.
Standard French sections (the default for the canonical destination):
# {Titre original} — {Course title} (Chapitre {N}/{Total})## Métadonnées(URL source, formation, chapitre, durée, langue, date)## Résumé express(3–5 lignes denses)## Plan détaillé / Structure narrative(timestamps[mm:ss]+ écran + discours)## Concepts clés(numérotée, verbatim quand définition explicite)## Listes, tableaux et données affichés (verbatim)(recopié mot pour mot)## Hooks, formules et phrases marquantes(citations entre « »)## Pricing, chiffres et dates mentionnés## Outils, plateformes et stack technique observés## URLs, liens et mentions## Identité visuelle## Takeaways actionnables(impératif, numérotée)## Génération(frames, modèle whisper, langue, date)
For non-French videos, the sub-agent translates the section headings to the dominant language but keeps quoted content verbatim in the original language.
Output Layout
Canonical root: /c/Users/jules/Documents/pro/hegoatek/creation contenu/syntheses videos/
syntheses videos/
2026-05-01-formation-affiliation-00-RECAP.md <- kept (reporter output)
2026-05-01-formation-affiliation-01-introduction.md <- kept
2026-05-01-formation-affiliation-02-trouver-niches.md <- kept
...
.work-ch01/ <- deleted at cleanup
video.mp4
audio.wav
transcript.srt
meta.json
frames/frame_001.jpg ...
.batch-<run-id>.sh <- deleted at end
.batch-<run-id>.log <- deleted at end
Per-video synthesis + final RECAP survive. Everything else trashed.
Cleanup Policy
Per-video (sub-agent responsibility):
| Artifact | Reason |
|---|---|
.work-{video-id}/video.mp4 |
Large, re-downloadable |
.work-{video-id}/audio.wav |
Intermediate |
.work-{video-id}/transcript.srt |
Folded into synthesis |
.work-{video-id}/meta.json |
Stats already in synthesis Génération section |
.work-{video-id}/frames/*.jpg |
Visual content transcribed |
.work-{video-id}/ (whole dir) |
One trash call |
Per-run (orchestrator responsibility, Step 16 prelude):
| Artifact | Action |
|---|---|
.batch-<run-id>.sh |
trash |
.batch-<run-id>.log |
trash |
Restrictions
- NEVER
cdinto the scratch directory. All Bash commands targeting scratch use absolute paths.cdleaves a handle that blockstrashon Windows. - NEVER spawn long-running or background processes on scratch files (no
tail -f, no watchers on scratch). Monitor watches the batch LOG only. - NEVER open a scratch file in an editor before synthesis is written.
- Before any cleanup, confirm no Bash invocation is still running against scratch paths.
- Never use
rm -rfor any destructive unix command. Alwaystrash. - Never skip cleanup. Only
.mddeliverables survive. - Never write a synthesis outside the canonical destination. The path is fixed.
- Never upload videos / transcripts to third-party services. Process locally.
- Do not write a synthesis in a language different from the video unless the user explicitly requests translation.
- Process URLs sequentially in the batch driver. Do not parallelize downloads — disk IO contention corrupts frame numbering.
- Sub-agent synthesis writing IS parallel (see cohort sizing) — they read disk, not network.
- Opus is FORBIDDEN as a sub-agent model in this skill. Haiku for synthesis, Sonnet for reporter.
- The orchestrator (main Claude) MUST NOT write synthesis content itself when N ≥ 1 — always dispatch. This keeps main context clean and uses cheaper tokens.
Examples
Single URL (silent → 1 sub-agent → final table):
/analyze-video https://www.youtube.com/watch?v=E6ncPDcIb2k
Output (only thing printed):
Processed 1 URL.
| Chapter | URL | Filename | Frames | Size | Status |
|---|---|---|---:|---:|---|
| ch01 | youtube.com/watch?v=E6ncPDcIb2k | 2026-04-16-contentpreneur-club-meilleure-formation.md | 105 | 15.8KB | OK |
Batch from file (silent → 3 parallel Haiku sub-agents → 1 Sonnet reporter → final table + recap path):
/analyze-video C:/Users/jules/Desktop/videos_urls.txt
Output:
Processed 11 URLs.
| Chapter | URL | Filename | Frames | Size | Status |
|---|---|---|---:|---:|---|
| ch01 | cloudfront.net/.../1.Introduction.mp4 | 2026-05-01-formation-affiliation-01-introduction.md | 200 | 18.2KB | OK |
| ch02 | cloudfront.net/.../2.Trouverdesniches... | 2026-05-01-formation-affiliation-02-trouver-niches.md | 164 | 16.0KB | OK |
| ... | ... | ... | ... | ... | OK |
Recap: /c/Users/jules/Documents/pro/hegoatek/creation contenu/syntheses videos/2026-05-01-formation-affiliation-00-RECAP.md
Verbose mode (opt-in)
If user adds --verbose (or says "narrate", "verbose"), the orchestrator MAY emit one caveman-style line per phase:
env OK gpu=RTX4080batch start N=11batch done — 11 OK / 0 failsynth dispatch — 3 agents (haiku)synth done — 11 filesrecap done
Caveman style only — no full sentences. Tool calls still silent.
Related Skills
/graphify— Build a knowledge graph from generated syntheses.
Failure modes
| Symptom | Action |
|---|---|
yt-dlp returns 401/403 |
Mark URL skipped with reason auth-required |
ffmpeg returns 0 frames |
Mark video skipped with no-frames; do not fabricate visual content |
faster_whisper import fails |
Set transcription_mode=skip; sub-agents write frame-only synthesis with explicit ## Avertissement section noting absence of transcript |
| GPU OOM mid-transcription | Script auto-falls-back to CPU base model on RuntimeError containing out of memory |
| Sub-agent times out | Re-dispatch the same chunk to another Haiku agent; hard cap 1 retry |
| Reporter agent times out | Skip recap, mark Recap: skipped (timeout) in final report |
Both trash attempts fail |
Record cleanup-failed: <abs path> in final report; do not delete via shell |
Monitor timeout before BATCH DONE |
Probe each scratch dir for meta.json directly; if all present treat as complete. If some missing, re-run process_video.py for the missing chNN manually via Bash, then continue to Step 11. |