---
name: post-production
description: Turn a raw recording into publishable assets, silence-cut, captions, timestamps, final description, social posts. Use after recording, when the creator says "post" / "edit" / "post-production".
---
<!-- Adapted from the hijoseromero production repo (Claude Code skill). Generic version: personal rules, private paths and names removed.
   Assumes a repo laid out as content/videos/NNN-slug/, content/ideas.md, pipeline/, tools/ (auto-editor, faster-whisper, Playwright).
   Drop this file at.claude/skills/post-production/SKILL.md and invoke with /post-production. -->

# Post-production

Work inside the video's folder. Commands from `pipeline/daily-sop.md` block 3:

0. **Audio mastering is OPT-IN ONLY, do NOT run it by default.** the creator's ear test (2026-08-14) rejected the processed sound; the native OBS chain is the approved sound. `pipeline/bin/audio-master` exists for future experiments (audit + findings in `setup/audio-fix-2026-08/`), only when the creator explicitly asks.
1. `auto-editor recording.mkv --margin 0.3sec -b:a 256k -o edit.mp4` (adjust input name; `-b:a 256k` always, the 128k default downgraded every final until 025, this fix stays).
2. Whisper captions → `edit.srt` (see `pipeline/tools.md` for the installed variant).
  - **Name/term spelling pass is automatic:** `tools/whisper_srt.py` runs `tools/srt_fix.py` on every segment (dictionary `tools/srt_fixes.json`: Qwen, Unsloth, llama.cpp, MTP, DeepSeek, Strix Halo, ROCm, FastAPI, CachyOS, GMKtec…). For an.srt made any other way, run `python3 tools/srt_fix.py final.srt` (keeps `.asr-orig`, prints every changed line). When a review finds a new mis-hearing, ADD it to the JSON so it never recurs. Judgment fixes (garbled phrases like "token's best line" → "tokens baseline") stay manual: correct them with confidence, list them for the creator.
3. From the.srt: real chapter timestamps → replace 0X:XX in `description.md`. Chapters = the section boundaries, phrased as curiosity, not labels.
4. All copy from what was actually SAID (parse the.srt), never invent claims that aren't in the video:
  - Final YouTube description (update `description.md`)
  - `posts.md`, 3 X/Twitter variations for **Buffer** (X + IG scheduler; see `content/social/README.md`). Use a `<PRIMARY LINK>` placeholder for the CTA, funnel priority is Substack post URL (email capture) > YouTube (reach) > website. LinkedIn dropped 2026-07-27. Voice: `brand/identity.md`; zero corporate speak.
  - Pin-comment draft (the CTA question)
5. Verify against the ⚠️ shot-list that nothing private is visible in `edit.mp4`, spot-check frames at each screen-share chapter (ffmpeg frame grabs).
6. **Thumbnail refresh from the actual footage** (so every video's thumb shows current look, hoodie, haircut, expression that's really in the video):
  a. Grab webcam-box crops densely in the hook + CTA windows (that's where the creator looks at the lens): `ffmpeg -ss T -i final.mp4 -frames:v 1 -vf "crop=W:H:X:Y"...` every 2-3 s, montage, pick a calm/confident eye-contact frame (rule 4 in `brand/thumbnails.md`, no mid-blink, no mid-word mouth).
  b. Cutout: `rembg` python API with the `u2net_human_seg` session (person-only; model cached in `tools/.cache/u2net`), then `magick -brightness-contrast 10x8`, webcam stills run dim. Save as `thumbnail/still-<t>-cutout.png`.
  b2. ⚠️ **Portrait-crop the cutout (rule added 2026-07-27 after the creator rejected 007 v1):** full-frame webcam cutouts keep the chair/mic and leave the face small and low, it ends up UNDER the text. Crop to a portrait window around the subject first (`magick still-X-cutout.png -crop 1500x1860+1020+300 +repage -trim +repage`, adjust window to the framing), then build. Gate before showing the creator: face large, anchored right, head fully inside frame, and **zero text-over-face overlap** at full size.
  c. Point the video's `thumbnail/spec-*.json` cutouts at the new still, rebuild via `tools/build_thumb.py`, and gate with the 168px shrink test. Thumbnail text/badges must only promise what the video actually says (rule 10, cash every promise; verify against the transcript).
6b. **A/V sync correction (calibrated 2026-07-28; STANDARD STEP).** The home-rig webcam/capture pipeline delays VIDEO relative to audio by a measured **~0.26 s** (you hear speech/clap ~0.26 s before the mouth/hands move). Confirmed with an in-frame clap test: two claps gave +250 ms and +267 ms (mean ~258 ms). **Apply this to every home-rig recording** as part of postprod.
  - **Apply (fast, no re-encode):** `ffmpeg -i final.mp4 -itsoffset 0.26 -i final.mp4 -map 0:v -map 1:a -c copy -shortest final-synced.mp4` (delays audio to meet the late video), then shift `final.srt` by **+0.26 s**. Back up the pre-sync file as `final-presync.mp4`.
  - **Verify/re-measure per recording when the clap is in frame** : audio clap = argmax|samples| near the clap; visual clap = the 60fps frame where hands meet; offset = visual - audio. If it differs from 0.26 s by >1 frame (~17 ms), use the measured value. This keeps it honest without assuming the constant is eternal.
  - ⚠️ **Non-home recordings (phone, travel, other rig): the 0.26 s does NOT apply, the creator will flag these.** Re-measure from that video's in-frame clap, or if there's no delay in that pipeline, skip the correction. Never blindly apply 0.26 s to a non-home-rig file.
  - v9 note: corrected with 0.29 s (measured before the in-frame test), within tolerance of the true 0.26 s, looked great, shipped as-is.
7. Check off Edited/Captions in brief.md. Remind: thumbnail pick 15-min cap, then Block 5 publish steps are MANUAL (human approves everything public). Note:.srt is a sidecar, captions are never burned in; players (VLC/mpv) auto-load a same-named.srt, YouTube shows it only when the viewer turns CC on.
8. **Run `/video-check`**, grade this video against "the basics" and write `review.md` + append the trend row to `content/videos/REVIEW-LOG.md`. Surface any 🔧 fix-now items (cheap, pre-publish) and the ⏭️ next-time list. Never blocks the publish (ship daily > perfection); it's how the videos improve week over week.
