---
name: "podcast_video_clips"
description: "Cut long podcast or episode footage into hook-first vertical clips for Shorts, Reels, and TikTok: score moments from the transcript, reframe 16:9 to 9:16, burn word-level captions, and export upload-ready MP4s. Triggers on 'make clips from this episode', 'podcast to shorts', 'cut vertical highlights', 'add karaoke captions to a clip'."
---

# Podcast Video Clips

## Purpose

Turn a long video episode into 3-6 standalone vertical clips (21-60s each)
that hold the feed: pick the strongest moments from the transcript, reframe
16:9 to 9:16, burn in word-level highlighted captions, and export
1080x1920 MP4s. Every command below is real; the `bin/` helpers are tested
with py_compile plus an end-to-end ffmpeg run.

## Workflow

### 1. Transcribe with word timestamps

If you already have a transcript without timestamps, generate one with
word-level timing. faster-whisper on CPU is the practical default:

```bash
ffmpeg -hide_banner -y -i episode.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 episode.wav
python3 -m faster_whisper episode.wav --language en --word_timestamps True --output_format json --output_dir .
```

### 2. Score the moments

Read the transcript and score candidate segments against
[references/scoring-rubric.md](references/scoring-rubric.md): hook strength,
emotion/energy, quotability, concrete value. Ship only clips that clear the
bar and pass the standalone veto. Write the result as a clip plan:

```json
[
  {"start": 862.5, "end": 905.0, "title": "second-hire-decides-company"},
  {"start": 1360.0, "end": 1395.0, "title": "40k-retainer-mistake"}
]
```

Times are seconds (or `HH:MM:SS`). Each clip must start on its strongest
line and end on a resolved thought.

### 3. Build captions (optional but strongly recommended)

Clips without burned captions underperform in the feed. Convert word
timestamps into karaoke ASS:

```bash
python3 bin/words_to_ass.py words.json clip1.ass --chunk-size 3
```

Current word highlights yellow while the sweep advances; style lives in the
script's style args (`--font`, `--fontsize`, `--margin-v`).

### 4. Cut, reframe, and burn

Dry-run first (prints exact ffmpeg commands), then run:

```bash
python3 bin/clip.py episode.mp4 plan.json --outdir clips --ass clip1.ass
python3 bin/clip.py episode.mp4 plan.json --outdir clips --ass clip1.ass --run
```

Reframe strategies: `--reframe center` (default, talking head),
`--reframe center-offset --x-offset 100` (subject off-center),
`--reframe blur` (blurred-background letterbox, keeps full frame).
Full recipe details: [references/ffmpeg-recipes.md](references/ffmpeg-recipes.md).

### 5. Verify and ship

```bash
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration \
  -of csv=p=0 clips/second-hire-decides-company.mp4
```

Expect `1080,1920,<clip duration>`. Watch the first 3 seconds of each
clip: the hook line must land immediately. If a clip feels soft, re-score
it instead of posting it.

## Output Contract

- One MP4 per clip in `--outdir`, named `<title>.mp4`
- 1080x1920, H.264 (`libx264`), AAC 128k, `yuv420p`, `+faststart`
- Captions burned in (ASS/karaoke) when `--ass` was given
- Each clip standalone, 21-60s, opening on its strongest line

## Operating Rules

- Never invent timestamps. Every `start`/`end` comes from the real transcript.
- The standalone veto is absolute: a moment that needs earlier context
  does not ship, no matter how it scores.
- Re-encode clips (`libx264`); never `-c copy` for cuts, it lands on
  keyframes and slips timing.
- Keep captions in the middle 70% vertically; do not cover the speaker's face.
- No em dashes or AI-speak in captions or titles. Write like a person.
