A simple Python CLI to:
- Read a YouTube URL and download the video (yt-dlp)
- Detect highlight/punchline moments from the transcript (no API keys)
- Cut them into short MP4 clips (moviepy + ffmpeg)
Optimized for talk/podcast-style videos. Transcript-based heuristics (exclamation, laughter tokens, excitement words, speaking-rate spikes) are used to propose highlights.
- Python 3.10+
- Internet access (to fetch transcript and download video)
- ffmpeg: moviepy uses imageio-ffmpeg to auto-download a binary on first use. If that fails, install ffmpeg manually.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtpython app.py --url "https://www.youtube.com/watch?v=VIDEO_ID" \
--duration 45 --top-k 3 --dry-run
# Remove --dry-run to download and cut
python app.py --url "https://www.youtube.com/watch?v=VIDEO_ID" --duration 45 --top-k 3
# Use a local file directly (no download step)
python app.py --url "/path/to/local_video.mp4" --duration 45 --top-k 2Outputs go to ./outputs. Downloads go to ./downloads (for YouTube URLs).
You can output vertical shorts with up to 3 stacked tiles (faces). Works for YouTube URLs (after download) or local files.
# Analyze and cut in 9:16 with up to 3 tiles
python app.py --url "/path/to/local_video.mp4" --duration 45 --top-k 3 \
--aspect 9:16 --max-tiles 3
# Analyze only (no cutting)
python app.py --url "/path/to/local_video.mp4" --duration 45 --top-k 3 \
--aspect 9:16 --max-tiles 3 --dry-runHeuristics:
- If 2–3 distinct faces are found in a highlight, tiles will stack vertically (top→bottom) centered around faces.
- If fewer faces are found, remaining tiles repeat the main crop.
- If no faces are detected, falls back to a centered 9:16 crop.
Prefer a single view that follows the active speaker, like a smart PTZ camera? Use:
python app.py --url "/path/to/local_video.mp4" --duration 45 --top-k 3 \
--aspect 9:16 --focus-speakerHow it works:
- MediaPipe Face Mesh estimates per-face mouth openness as a proxy for speaking.
- Faces are tracked across time; the dominant speaker drives the crop.
- A smooth pan/zoom path keeps the person centered with proportional framing (no jitters).
- Will temporarily switch to 2–3 tiles when multiple faces are active, or show full view with blurred bars when appropriate.
Captions are burned-in by default:
- Color: yellow by default (customizable)
- Outline: black stroke (thicker for readability)
- Placement: ~25% from bottom, bottom-center aligned
- Padding: left/right margins (~7% width) and automatic word-wrapping to avoid edge overlap
Change caption color (named or hex):
python app.py --url "/path/to/local_video.mp4" --aspect 9:16 --focus-speaker \
--caption-color yellow # or white/red/green/blue/cyan/magenta/black or #RRGGBBNotes:
- If a transcript is available, it’s used. If not, the tool attempts automatic transcription (Whisper) for the rendered clip.
- To disable captions entirely, pass
--no-captions(to be added if desired) or I can wire this flag on request.
--durationtarget short length in seconds (default: 45)--top-knumber of clips to export (default: 3)--pre-rollseconds before the highlight window (default: 2)--post-rollseconds after the highlight window (default: 2)--language-prioritytranscript language order (default:id,en)--dry-runonly print detected segments without downloading/cutting--aspectoutput aspect:originalor9:16(default:9:16)--max-tilescap stacked tiles (faces) in 9:16 mode, 1–3 (default: 3)--focus-speakerenable speaker-focused auto-framing (smooth pan/zoom, adaptive layout)--caption-colorcaption color (named: yellow/white/red/green/blue/cyan/magenta/black or hex#RRGGBB)--downloadsdownloads directory (default:./downloads)--outdiroutputs directory (default:./outputs)--cookies-from-browseruse cookies from a local browser for restricted YouTube videos (e.g.,safari,chrome)--cookies-filepath to a cookies.txt (Netscape) file for yt-dlp
- Transcript is fetched via
youtube-transcript-api(no API key). If unavailable, the tool falls back to an audio-based highlight detector; captions then rely on Whisper ASR. - Each transcript line is scored via:
- Laughter tokens (e.g., "[laughter]", "haha", "wkwk", "ngakak")
- Emphasis/punctuation (! and ?)
- Excitement keywords (Indo + EN)
- Words-per-second spikes (delivery bursts)
- Minor preference for concise lines
- The top entries form windows around their midpoints, expanded to match
--duration, then de-duplicated to avoid overlaps. - If transcript is missing, an audio envelope peak detector proposes windows, then for captions the tool attempts Whisper ASR on the final rendered clip.
Output filenames:
short_{idx}_{start}-{end}.mp4for original aspectshort_{idx}_{start}-{end}_v916.mp4for 9:16 tiles modeshort_{idx}_{start}-{end}_v916_focus.mp4for 9:16 speaker-focused mode- When captions are burned, a
_capsuffix is added (e.g.,_v916_focus_cap.mp4)
9:16 vertical clip with burned-in captions:
short_01_575-630_v916_cap.mp4
- Restricted/DRM YouTube videos may fail to download even with cookies; local files are recommended.
- Audio-only fallback is used when transcripts are missing; results vary by content.
- Optional burn-in subtitles could be added using transcript +
ffmpeg drawtext. - Further refinements possible: active speaker via audio-visual sync, crossfades between layout changes.
MIT