slides · git:20260424.80235db · 2026-04-24 · sha256 d2c383dd203f194d
slides git:20260424.80235dbB
Immutable. This exact content never changes and is served at /api/v1/blob/d2c383dd203f194d.
--- description: Extract relevant frames from a video or animated image (GIF, APNG, animated WebP) so the model can view them as a timeline. Use when the user shares a video or an animated image. DO NOT use for static images — the model can already read those natively. --- # Slides Static images (JPG/PNG/static WebP) can be read natively. **This skill is only for video and animated images** — anything that has multiple frames across time. It uses `peepshow` (ffmpeg under the hood) to extract a sequence of relevant still frames so they can be viewed as a timeline. ## Input The user may provide the video (or animated image — GIF, APNG, animated WebP) in any of these forms: - A local file path (absolute, relative, or a network mount like `/Volumes/share/...`) - An `http://` or `https://` URL - A `data:video/...;base64,...` or `data:image/(gif|apng|webp|png);base64,...` URI pasted into the prompt - The literal `-` to read bytes from stdin (you would pipe via `Bash`) If `$ARGUMENTS` contains the video reference, use it directly. Otherwise ask the user to share one of the above. ## Steps 1. **Run the CLI** with the `Bash` tool. JSON is the most reliable output for parsing: ``` peepshow "$ARGUMENTS" --emit json ``` (If the path contains spaces, quote it. Use `--emit paths` if you prefer reading the human-readable list.) 2. **Parse the output**. In JSON mode, `frames[].path` is the ordered list of absolute paths. The `video` object gives you container, codec, resolution, fps, duration, and file size — useful context for the user's question without extra prompting. The `video.tags` object carries container-level metadata embedded in the file (title, artist, album_artist, director, producer, publisher, copyright, genre, description, creation_time, show, episode_id, season_number, etc.) — use it to ground your answer in what the video *says it is* before describing what you see. The `extraction` object tells you which strategy (`scene` vs `fps`) was used and how many frames were pruned. 3. **Read the audio transcript**, if present. When the input had an audio track, the JSON payload's `audio` object has: - `audio.path` — the extracted `audio.m4a` on disk. - `audio.durationSeconds`, `audio.codec`, `audio.peakDbfs`, `audio.silenceRatio` — audio metadata. - `audio.transcript` — when transcription ran (whisper.cpp or a cloud provider), this contains `text` (full concatenated transcript) and `segments[]` (each with `start` / `end` / `text` in seconds). Use the transcript to understand **what was said** in addition to what was shown. For conversational or narration-heavy videos the transcript is often more informative than the frames alone. Cross-reference segment timestamps against frame indices so you can quote who said what when. If `audio.path === null` the input had no audio track (GIF, APNG, animated WebP, silent video). Skip the audio step cleanly. If `audio.transcript === null` or `audio.transcript.skippedReason` is set, transcription didn't run — just work with the frames. 4. **Read each frame as an image** with the `Read` tool, **in order**. They are named `frame_0001.jpg`, `frame_0002.jpg`, etc. and represent the timeline from earliest to latest. 5. **Answer the user's question** using both the frames and the transcript together. Reference timestamps when helpful (derive from `video.durationSeconds`, frame ordering, and transcript `segments[].start`). A useful pattern for longer clips: summarise the visual timeline in 2-3 beats, then weave in direct quotes from the transcript to ground what was said at those moments. ## Useful flags Pass these after the input when the defaults are not right: - `--max 20` — cap the number of frames returned (default 40) - `--min 6` — ensure at least this many (falls back from scene detection to fps sampling) - `--threshold 0.2` — more sensitive scene detection (default 0.3; lower = more frames) - `--fps 0.5` — skip scene detection and sample at a fixed rate - `--width 960` — max output width in pixels (default 1280) - `--format png` — output PNG instead of JPG - `--stats full` — include the full stats block (video + extraction details) - `--stats off` — suppress stats entirely if context is tight ## Troubleshooting - **`ffmpeg not found`**: run `npm install` inside the plugin directory once to fetch the bundled ffmpeg binary. - **Zero frames returned**: lower `--threshold` or force `--fps 1`. - **Too many frames**: raise `--threshold` or lower `--max`.