Skip to main content

video-edit

Edit a two-camera Camtasia recording into a polished, feature-rich video. The workflow is mandatory: 1. Analyze the raw footage (find beats/sections). 2. Define structural segments (full-camera, screen-capture, etc.). 3. Generate the master composite (crosspix/blur/crop/anim). 4. Verification passes run through the final technical passes (audio/visually/system). This class of work requires meticulous time-stamping and architectural rigor. Triggers on: polish the video, final pass, full edit.

Source facts

Repository
tylerprogramming/claude-skills
Last source activity
October 9, 2026 at 21:33
Detected SKILL.md language
English
Stars
0
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
42 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
video-edit
description
Edit a two-camera Camtasia recording into a polished, feature-rich video. The workflow is mandatory: 1. Analyze the raw footage (find beats/sections). 2. Define structural segments (full-camera, screen-capture, etc.). 3. Generate the master composite (crosspix/blur/crop/anim). 4. Verification passes run through the final technical passes (audio/visually/system). This class of work requires meticulous time-stamping and architectural rigor. Triggers on: polish the video, final pass, full edit.
Edit the raw two-camera recording named in $ARGUMENTS into a finished cut. ## Workflow context Sits between filming and `/yt-chapters` in the pipeline: ``` /yt-package → [FILM] → **/yt-edit** → /yt-chapters → /yt-shorts → /yt-upload ``` `/yt-chapters` and `/yt-seo` both read the file this produces, so finish here first. ## The footage Tyler shoots **two frame-synced 1920x1080 / 30fps files** in `~/Movies/Camtasia/`: ``` <name> - CAMERA.mp4 him + the audio <name> - SCREEN.mp4 the capture ``` Identical durations, so one cut plan drives both. Work lives in `~/Movies/Camtasia/<name>/scripts/`, output in `<name>/<name> - FINAL.mp4`. **Default layout:** SCREEN full-frame, CAMERA circle-masked bottom-right. 480px circle, 50px inset, crop 980x980+490+100 for head-and-shoulders. The circle's left edge sits at **x=1390**, so any wide graphic must cap its width to clear it. ## Flow ### Step 1 — profile the noise floor. Never skip this. ```bash python3 ~/.claude/skills/yt-edit/lib/audio_floor.py "<name> - CAMERA.mp4" ``` **Never reuse the last video's `--noise` value.** The right threshold has swung 40dB between shoots and the wrong one silently eats speech: | shoot | floor | correct `--noise` | |---|---|---| | 17 claude code skills | -26dB | `0.05` | | 045 arcade.dev | -72dB | `0.005` | Carrying 0.05 to the arcade shoot cut **75 seconds of speech tails** and nobody would have noticed from the log. The floor is the p10-p50 cluster, speech is p80+; put the threshold between them. ### Step 2 — silence pass ```bash hyperframes silence "<name> - CAMERA.mp4" --noise=<value> --dry-run --json > silence.json ``` **Flags must use the `=` form.** `--noise 0.05` with a space is silently ignored and returns the default result with no warning, which looks exactly like the flag having no effect. This is Tyler's fork (`~/dev/hyperframes-studio`, bun-linked). `silence`, `caption` and `duplicates` exist there and **not** in the published npm CLI. Then prune slivers: drop keeps shorter than **0.60s** that are isolated by **3.0s** of silence on both sides. Those are clicks, chair creaks and clothing rustle, not speech. ### Step 3 — transcribe and find duplicate takes Transcribe, then intersect the word list with the keep ranges before using it for anything. Two Whisper failure modes make raw transcripts unusable: - **It hallucinates fluent sentences over long silences.** On 045 it invented a whole sentence across a stretch measuring -70dB. - **It drops real words on the long-file pass.** A repeat Tyler reported looked absent in the full transcript and was plainly there when the 18-second window was transcribed in isolation. So: **before telling him a reported repeat is not there, re-transcribe that window alone.** If the isolated pass and the full pass disagree, trust the isolated one. `lib/find_dups.py` helps, but on every video so far the real retakes were found by **reading the transcript for repeated beat structure**, not by any tool. Tyler also says "let me redo that section" and counts himself in with "3, 2, 1" — search the **source** transcript for those, not the recut one, because the count often falls inside a gated region and comes back mangled. **Rule: when a take is repeated, keep the second one.** ### Step 4 — snap the cut boundaries ```bash python3 ~/.claude/skills/yt-edit/lib/snap_cuts.py <in.json> <out.json> ``` Snaps each boundary to the lowest-RMS 20ms frame within ±0.45s. **Do not skip this.** Hand-placed boundaries clip syllables, and Whisper's word-end times are unreliable enough that one "about." was reported as spanning ten seconds. ### Step 5 — verify nothing was lost ```bash python3 ~/.claude/skills/yt-edit/lib/verify_cuts.py <final.mp4> <cuts.json> ``` Decodes to 8kHz mono, takes 50ms RMS frames, and flags any run above -40dB lasting 0.30s or more inside a cut region. **Target is 0.0s of speech lost.** This is independent of the transcript, which is the point. ### Step 6 — render Copy **`reference/058/recut.py`** into the project's `scripts/` and edit the config block at the top. It is the current reference implementation and supersedes `reference/recut_reference.py`, which predates the per-keep window handling, the secret blurring and the startup validation. `reference/058/README.md` maps what each part does. **Before the first render, read `reference/SILENT-FAILURES.md`.** Every entry in it produced a file that played, had the right duration, and was wrong. That is the normal failure mode here — not a crash. Keep the startup validation that exits when any config key stops matching a keep start. Trimming a keep moves its start, and a stale key means the graphic silently does not appear. **Batches of ~30 clips, 3 concurrent ffmpeg passes**, each batch input-seeking with `-ss` so it only decodes its own span. Intermediates are `.mov` with libx264 crf18 video and **PCM** audio, joined with the concat demuxer using `-c:v copy -c:a aac`. Video is encoded exactly once, and keeping audio PCM until the final pass means hundreds of clip boundaries add no AAC priming gaps. Two approaches that work on short reels and **fail at hour length**: - One `trim` filter per clip in a single graph: every decoded frame goes through 2N filters. At N=360 that runs at **0.19x realtime**. - One `select='between(t,a,b)+...'` expression: the parser dies with **"Cannot allocate memory"** past roughly 100 terms. `--minutes N` renders a prefix for review. ### Step 7 — creative pass Only after he has seen the silence cut. Ask before building; he has strong opinions and they are usually right. See `reference/MOTION-KIT.md` FIRST — the reusable motions, the zoom system, the pacing targets and the AI-video rule, all verified on 047. Then `reference/LESSONS.md` for the ffmpeg and animation gotchas — that file is the expensive part of this skill and is worth reading in full before building any graphic. ## Treatment — which picture is on screen The default is SCREEN full-frame with the camera circle bottom-right, but three departures from that are what make it a video rather than a screen share. All three are decided by **measurement**; see `reference/MEASUREMENTS.md` for the recipes. **1. Section intros go full-frame camera.** Where he introduces a new section with nothing happening on screen, the picture should be him. Find these by combining *static screen* (motion under 1.6 at 1s resolution) with *absence of pointing language* ("if we go", "right here", "you can see", "click"). Either test alone is wrong — static screen alone found 68 candidates on 058 and would have turned the film into a talking-head reel; both together found 5. Require ≥12s, keep-align, and check his gaze before committing. **End each span on the first pointing phrase** so the picture returns to the screen exactly when he starts demonstrating. Give those spans zooms. Alternate direction, carry the scale across cuts so no cut jumps. **2. Browser chrome gets cropped**, so open tabs naming unrelated work are not on screen. Detect a browser by correlating the bookmarks-bar rows against a known browser frame, not by looking for a brightness edge — a terminal floating over the browser kills the edge while the tab strip is still visible. If chrome appears or disappears mid-keep, bisect to 1/30s and **split the keep**; a split is lossless. **The crop must preserve aspect.** Cropping `1920x(1080-118)` and scaling back to `1920x1080` stretches everything vertically by 12%. Take a 16:9 window (`round((1080-CHROME_Y)*16/9)` wide) and choose *which* width to drop per keep by minimising deleted content weight. **3. The circle moves to the bottom-left** only where the right corner is measurably busier. Do not set this by eye: three of four hand-written spans on 058 were backwards, parking the circle on the content it was meant to avoid. ## Secrets Scan the **whole film** for credentials before delivering, not just the part under review. On 058 a `claude setup-token` OAuth code was on screen in three places at once — the terminal URL, the browser address bar, and the account email in the page footer. Blur with `boxblur` on a cropped region overlaid back, **before** the chrome crop so the boxes stay in original coordinates. Verify by measuring high-frequency detail inside the box against a reference strip of the same page; a properly destroyed line reads 0.00 against 0.09. ## Graphics Built as **transparent ProRes 4444 MOVs** via `hyperframes render <dir> --format mov` (WebM tags alpha but discards it), then overlaid inside the same batch pass, never as a second encode of the film. Prefer **registry components** over bespoke HTML: ```bash npx hyperframes@latest add bottom-up-letters --dir overlays/build/bul ``` Ones already in use: `bottom-up-letters`, `number-pop-in`, `caption-editorial-emphasis`, `code-scroll`, `lt-side-rule`, `editorial-flash-overlay`, `mk-clone-wall-transition`. That is **7 of 131** — see `reference/CATALOG.md` for the rest. `reference/047/` holds the complete working build for the 047 intro — the best worked example available. `build_intro.py` (assembly + the zoom keyframe system), `build_montage.py` (a synthetic scene set), `build_wall.py` / `build_auth.py` (full-frame cutaways with real brand marks), `build_codescroll.py` (driving a registry block by hand), `build_intro_gfx.py` (one timeline for every overlay beat), `preview2.py` (the 2-minute iteration loop — this is the one that makes creative passes cheap). Copy `reference/graphic_reference.py` as a minimal starting point. It shows the `grab()` helper that pulls a component's `<style>` and `<script>` into a page, and the pattern of pinning every beat to a word timestamp. **Fonts** are bundled @fontsource, no network: inter, montserrat, outfit, nunito, oswald, anton, league-gothic, archivo-black, space-mono, ibm-plex-mono, jetbrains-mono, eb-garamond, playfair-display, source-code-pro, roboto, open-sans, lato, poppins. Anton is the house display face. **Brand logos come from `simple-icons` (npm), never from a generative model** — asked for "the Gmail logo" a diffusion model invents a plausible wrong one. Note that **Slack, Salesforce and the Microsoft marks are not in the set**, removed at those companies' request, so check before promising a logo. For AI clips use `/kie-seedance-video`; for isolating a logo out of a photo use `/kie-nano-banana`. ## Editing an already-finished render If the source files have been overwritten by a re-record, the recut script cannot rebuild the cut and the rendered master is the only truth. Use `reference/apply_overlays_reference.py`: it re-encodes only the span that changes and stream-copies the rest from a keyframe. **`-ss <keyframe>` rounds down and falls back to the previous keyframe.** Ask for keyframe + 0.024 and size the head segment to `total - tail`, or you silently duplicate several seconds. Find real keyframes with: ```bash ffprobe -v error -select_streams v:0 -show_entries frame=key_frame,pts_time \ -of csv=p=0 <file> | awk -F, '$1==1 {print $2}' ``` ## Verification discipline **Measure, do not eyeball.** `reference/MEASUREMENTS.md` has every recipe as copy-paste code. Each one caught a real defect that looked fine on screen: | question | measurement | |---|---| | does the render match the plan? | duration vs sum of keep ranges — catches dropped batches | | is the cut plan valid? | inverted, zero-length, overlapping, out-of-order ranges | | is speech lost / is there dead air? | 50ms RMS runs above -40dB inside cuts; runs below -45dB inside keeps | | is the zoom visible? | percent change; under ~8% total reads as nothing | | **is the zoom smooth?** | **percent per frame; 1.9%/frame is the ceiling** | | did the zoom actually fire? | optical scale fit, but only trust correlation > 0.8 | | is anything stretched? | fit both axes independently; aspect error must be 0.00% | | which corner for the circle? | content density in both bottom corners | | which side to crop? | deleted content weight — edges AND flat panels | | is this a browser window? | bookmarks-bar correlation, not a brightness edge | | is the secret really gone? | high-frequency detail in the box vs the page | | is the border outside the camera? | mask extent vs ring extent, per row | Two habits that matter more than any single metric: - **Sanity-check the metric before trusting the number.** Three measurements on 058 were wrong the first time and produced a confidently wrong decision — an edge-only crop score that ignored a flat terminal panel, a corner-density test read at the wrong threshold, and an optical scale fit corrupted by the non-scaling circle. - **Verify overlays by looking at frames**, not by a colour metric. A green-channel test once "proved" no leak when the page underneath was dark magenta. Numbers tell you whether it works; contact sheets tell you what rendered. You need both. ## Working with Tyler - **His timestamps are approximate** — typically off by 2 to 4 seconds, and occasionally they point at nothing. Search a window around the mark, and if there is genuinely nothing there, say so plainly rather than cutting something to look responsive. - **Review in slices.** He wants to see 30 seconds before you build ten minutes. - **Whisper writes "cloud code".** He is saying Claude Code. Never ship that. - He often sends a follow-up refinement mid-build. Read the whole thread before rendering. - **His eye beats the metric.** He caught the harsh screen zooms, the mask border showing camera outside it, and the 12% aspect stretch — all after automated checks had passed. When he says something looks off, the measurement is missing, not him. - **He asks for one thing; check whether it is systemic.** "5:34 should be on the right" was one span, but re-deriving all of them found three more backwards. "Make 11:41 full camera" was one section; the same test found four more. Fix the class, then report what changed. - **Report timestamps, not descriptions.** When several things changed, a table of `time | duration | what starts it` is what he wants back. ## Background renders `nohup ... &` returns the launcher shell immediately, so a completion notification fires while the render is still going. Either run it in the foreground with a long timeout, or poll for the actual process: ```bash while pgrep -f "scripts/recut.py" > /dev/null; do sleep 20; done ``` `rm -rf` on scratch directories trips the approval gate mid-flow. Content-hash-keyed filenames mean you never need to delete anything; when you do need to remove one stale file, do it from Python rather than a shell `rm`. ## Output ``` ~/Movies/Camtasia/<name>/ <name> - FINAL.mp4 the deliverable scripts/ recut + per-graphic builders overlays/movs/ rendered transparent MOVs overlays/build/ registry components + generated HTML transcript/ recut_words.json (r = recut time, src = source time) ``` `transcript/recut_words.json` carries **both** timelines. `/yt-chapters` and `/yt-seo` want `r`. The recut script's own `SHOTS` list is in **source** time. Mixing them up is the single easiest mistake to make here. **Before building any graphic, read these:** 1. **`reference/SILENT-FAILURES.md`** — the ways a render comes out wrong with exit code 0. Start here; it is the most expensive file in the skill. 2. **`reference/MEASUREMENTS.md`** — every verification recipe as copy-paste code. 3. **`reference/CATALOG.md`** — all 383 registry items by category. Grep it before hand-building anything. 4. **`reference/MOTION-KIT.md`** — the card travel-from-below timing, the self-drawing glyph pattern, the strike-through hold, the zoom keyframe system, the measured pacing targets, and when AI video earns its place. 5. **`reference/058/`** — the current reference build, with `README.md` mapping it. Also deliver previews to a path **without spaces** (`~/Movies/Camtasia/<n>-previews/`); the desktop MEDIA card cannot open a path containing them. Verify the copy exists before sending the link — pasting a path you have not created is an easy way to hand over nothing. Afterwards, update `~/content/youtube/notes/status.md` **and** the ClickUp task — the file is the source of truth but they are meant to agree.
View on GitHub