Back to catalog

youtube-highlights-clipper

Clip YouTube highlights into vertical shorts on demand.

Category 🎬 Media
Version v1.0.0

YouTube Highlights Clipper

Extract specific highlight themes from previously-ingested YouTube videos as vertical (9:16) short clips.

Trigger

  • "extract highlights 1,3,5 from William Hockey"
  • "get me the s-curve highlight from Alex Sacerdote"
  • "clip themes 2 and 4 from Dianne Penn"
  • "make shorts from Nikita Bier highlights 1,2"

User picks highlights by number (as listed from the highlights page's Key Themes section). The agent lists available highlights first, user picks, agent clips.

Requirements

  • yt-dlp (installed via uv tool install yt-dlp into ~/.local/bin/)
  • ffmpeg (system package, usually pre-installed)
  • Wiki at /opt/data/wiki/ with highlights in consume/highlights/

Ensure yt-dlp is on PATH:

export PATH="$HOME/.local/bin:$PATH"

Storage

All clips go under /opt/data/clips/<highlight-slug>/:

/opt/data/clips/
├── william-hockey-bet-on-yourself/
│   ├── 01-vc-as-heroin.mp4
│   ├── 03-boring-edge.mp4
│   └── ...
├── alex-sacerdote-investing/
│   └── 02-s-curve-framework.mp4
└── ...

Naming: NN-theme-slug.mp4 where NN is the highlight number (zero-padded, 2 digits).

Workflow

Phase 1 — List Available Highlights

First, read the highlights index: /opt/data/wiki/consume/highlights/index.md

This page is the canonical browseable index of all highlights with themes, timestamps, and YouTube URLs. Present it to the user in the same outline format it's already in (NOT markdown tables — Slack renders tables poorly).

The index page is maintained alongside highlights and updated when new videos are ingested. Always check it first before falling back to reading individual highlight files.

Only fall back to reading individual highlight pages if the index is stale (missing recent ingestions) or the user asks for more detail on a specific highlight.

Phase 2 — Parse User Selection

User specifies highlights to clip. Formats supported:

  • "extract 1,3,5" — clips themes 1, 3, 5 from the most recently mentioned highlight
  • "extract 1,2 from William Hockey" — explicit speaker
  • "get me the s-curve highlight from Alex Sacerdote" — topic-based search
  • "clip all from Dianne Penn" — all themes

Phase 3 — Find Timestamps

For each selected theme number, find its timestamp in the highlight page:

1. Read the highlight markdown file

2. Find the theme section matching the number (e.g., ### 3. The Boring Edge)

3. Extract all timestamps from quotes in that section (e.g., ~40:34, ~37:53)

4. Use the earliest timestamp in the theme section as the clip start point

5. If no timestamp found in the theme section, scan nearby sections for one

Phase 4 — Download Clip

For each highlight, use yt-dlp to download a segment:

export PATH="$HOME/.local/bin:$PATH"

OUTDIR="/opt/data/clips/$SLUG"
mkdir -p "$OUTDIR"

Compute start (5s before timestamp) and end (85s after = 90s total)

START=$(python3 -c " ts='$TIMESTAMP' # e.g., '40:34' or '1:07:08' parts = ts.strip('~').split(':') if len(parts) == 2: total = int(parts[0]) * 60 + int(parts[1]) elif len(parts) == 3: total = int(parts[0]) 3600 + int(parts[1]) 60 + int(parts[2]) else: total = 0 start = max(0, total - 5) print(start) ") END=$(python3 -c " ts='$TIMESTAMP' parts = ts.strip('~').split(':') if len(parts) == 2: total = int(parts[0]) * 60 + int(parts[1]) elif len(parts) == 3: total = int(parts[0]) 3600 + int(parts[1]) 60 + int(parts[2]) else: total = 0 print(total + 85) ")

Download with yt-dlp

yt-dlp \ --download-sections "*${START}-${END}" \ -f "bestvideo[height<=1080]+bestaudio/best[height<=1080]" \ --merge-output-format mp4 \ -o "${OUTDIR}/raw_${NN}.mp4" \ --force-keyframes-at-cuts \ "$YOUTUBE_URL"

Phase 5 — Convert to Vertical (9:16 Shorts)

After download, use ffmpeg to crop/reframe to vertical:

ffmpeg -i "${OUTDIR}/raw_${NN}.mp4" \
  -vf "crop=ih*9/16:ih,scale=1080:1920:flags=lanczos" \
  -c:v libx264 -preset fast -crf 23 \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  "${OUTDIR}/${NN}-${THEME_SLUG}.mp4"

If the source is already vertical, skip the crop and just scale.

Phase 6 — Clean Up Raw

rm "${OUTDIR}/raw_${NN}.mp4"

Phase 7 — Report

After clipping, report:

Clipped 3 highlights from William Hockey → /opt/data/clips/william-hockey-bet-on-yourself/
  01-vc-as-heroin.mp4 (90s, ~20:36–22:06)
  03-boring-edge.mp4 (90s, ~40:29–41:59)
  05-dollar-as-weapon.mp4 (90s, ~57:56–59:26)

Ready to download.

Deletion

When the user says "delete clips from William Hockey" or "delete /opt/data/clips/...":

rm -rf /opt/data/clips/$SLUG/

Confirm: "Deleted /opt/data/clips/william-hockey-bet-on-yourself/ (3 clips)."

Listing Clips

When the user says "what clips do I have?":

find /opt/data/clips/ -name "*.mp4" -exec ls -lh {} \; 2>/dev/null || echo "No clips found."

Cross-Session Queries

The user may reference highlights by topic, not speaker name ("get me s-curve highlight from ..."). Use:

grep -rl "s-curve" /opt/data/wiki/consume/highlights/ --include="*.md" -l

To find the relevant highlight file, then proceed with the normal workflow.

Pitfalls

  • Never auto-extract. Only clip when the user explicitly asks. This is on-demand, not part of the youtube-content ingestion pipeline.
  • yt-dlp rate limits. YouTube may throttle if downloading multiple clips rapidly. Add --sleep-interval 5 between clips.
  • Some highlights have no timestamps. If a theme section has no ~MM:SS pattern, skip it and tell the user which themes were skipped.
  • Timestamps are approximate. Highlights use ~ (tilde) timestamps — they're directionally correct but may be ±10s off. The 5s pre-buffer helps.
  • Vertical crop may cut off faces. The center-crop approach assumes the speaker is centered. If the composition is off (two speakers side by side), the crop may cut one out. Use crop=ih9/16:ih:(iw-ih9/16)/2:0 to center-crop.
  • Anneka Gupta is a stub. It has no YouTube URL or timestamps. Needs re-extraction from the original podcast before it can be clipped.
  • Timothy Keller has no YouTube URL in the highlights page. The sermon "Your Plans: God's Plans" likely exists on YouTube but the URL wasn't captured during ingestion. Search for it or ask the user.
  • Audio-only sources. If the source is audio-only (podcast), skip video download and just extract audio segment. But most "shorts" platforms want video.
  • Long clips. Shorts/Reels platforms typically cap at 60s (sometimes 90s). The default 90s clip length may need reduction for strict Shorts compliance. Use 55s as safe default if uncertain.

Verification

After clipping, verify each file:

ffprobe -v error -show_entries format=duration,size -of csv=p=0 "${OUTDIR}/${NN}-${THEME_SLUG}.mp4"

Confirm duration is 55-95s and file size is reasonable (5-20 MB for 90s at 1080p).