Video editing with ffmpeg
You are an editor who works with a transcript and filtergraphs rather than a mouse and a timeline. The pipeline is deterministic: everything is edited as text and rebuilt with one command.
footage → transcript with word-level timings → EDL (edit list) → ffmpeg → finished video
↑ ↑
faster-whisper edited by hand — this IS the edit
Core principle: the video is never analysed pixel by pixel — the edit is driven by timed text. Speech defines the structure; the picture serves it.
One reel or many?
Decide this first — it changes everything downstream.
- One or two reels → follow the step order below, conversationally.
- Three or more → switch to the batch pipeline: references/batch-pipeline.md. Each stage runs once over the whole project and caches by input hash. Doing ten reels conversationally costs roughly ten times the context and buys nothing for it.
Order of work
Always follow the steps, never skip ahead. Each step produces the input for the next.
- Check the environment. If ffmpeg / faster-whisper / Chrome are missing → references/setup.md. On a clean machine this takes ten minutes.
- Get the source. If the user handed you a URL instead of a file, fetch it first with
download.py. - Inspect the source — ffprobe, scene detection, a contact sheet. Look at the frames with your own eyes via Read. Never edit blind from text alone.
- Transcribe with word-level timings and proofread the result. Speech recognition gets words wrong even on clean audio. No local model?
transcribe_cloud.pygets the same output from Groq/OpenAI. - Agree on the concept with the user if they have not specified one: format, what goes into graphics, target platform. One question, not a questionnaire.
- Build the EDL — cut pauses, never cut ideas.
- Build
base, then verify the cuts frame by frame. - Generate subtitles and overlays, build
final. - Run the acceptance checklist and deliver the file with SendUserFile.
Full commands for each step — references/pipeline.md.
What this pipeline can and cannot do
Can: semantic cutting, pause removal, punch-in on cuts, colour grading (including .cube LUTs), word-level subtitles, arbitrarily complex infographics via HTML/CSS, stabilisation, loudness normalisation for social platforms, batch processing. With the Remotion plugin installed, also animated graphics rendered from React — see references/remotion.md.
Cannot at all — say so up front if the user asks for it: object tracking (pinning a caption to a moving hand) and masked grading. Those still need Premiere / DaVinci / After Effects.
Cannot alone, but can with FableCut installed: frame-accurate creative cutting, keyframed motion, transitions, speed ramps, chroma key, cutting to a music beat. FableCut is a browser timeline an agent drives through MCP — see references/fablecut.md. It has no speech recognition and its export needs a human click, so speech-driven structure and unattended batches stay in this pipeline.
Ready-made tools
assets/tools/ holds twelve working scripts — copy them into the project, do not rewrite them from scratch:
| Script | What it does |
|---|---|
download.py | fetch source footage from a URL (yt-dlp) — skip if you already have a local file |
transcribe.py | faster-whisper → transcript.json with word-level timings + .srt |
transcribe_cloud.py | same output, via Groq/OpenAI Whisper API — no model download, needs an API key |
pauses.py | lists pauses in speech — the basis for the EDL |
dump_words.py | every word with its index and confidence — for proofreading |
make_ass.py | word-level ASS subtitles, re-timed to the post-cut timeline |
render_overlays.py | HTML/CSS → headless Chrome → 1080×1920 PNG with alpha |
build.py | assembly: base (cut + grade), final (overlays + subtitles + audio) |
build_vo.py | same, but for a separate voiceover track — video from several sources fitted to the audio |
batch_vo.py | transcribe every voiceover in one model load |
mute_subs.py | drop subtitles where a full-screen card is on screen |
pipeline.py | batch driver — every stage over every reel, with caching |
assets/templates/ holds starter edl.json, overlays.json and corrections.json.
Lay the project out like this:
<folder with the footage>/
├── IMG_XXXX.MOV
└── edit/
├── tools/ ← the copied scripts
├── work/ ← intermediates, never shipped
├── overlays/ ← rendered PNGs
├── out/ ← finished videos
├── edl.json overlays.json corrections.json subs.ass
Reference files
Load these on demand, not all at once:
- references/setup.md — installing on a clean machine (Windows/macOS), verifying the ffmpeg build
- references/pipeline.md — the workflow step by step, cutting rules, commands
- references/storyboard.md — video formats, panel types, how to storyboard a talking head
- references/design-system.md — palette, type scale, safe zones, overlay techniques
- references/batch-pipeline.md — running many reels as stages, and where the context actually goes
- references/remotion.md — animated graphics tier: when React-rendered overlays earn their cost
- references/fablecut.md — timeline tier: when to hand the edit to FableCut, and how the two pipelines meet
- references/ffmpeg-cookbook.md — grades, effects, transitions, audio, encoding
- references/troubleshooting.md — read this before your first ffmpeg run; seventeen failures, each of which cost an hour
Rules that make or break the video
Learned on real builds, not from documentation.
- Look at the frames. A transcript will not tell you the speaker is standing up or fixing their hair at that exact moment. Those are precisely the places where cuts fall apart.
- A card goes above the face, never on it. The first attempt always drops the panel into the middle of the frame, covering the eyes. The speaker's gaze is half the trust the video earns.
- Cut at the end of a phrase, not on a stopwatch. Cutting "every N seconds" slices ideas in half.
- Leave 0.2–0.3 s of every pause. A pause collapsed to zero sounds like a stutter.
- Never touch dramatic pauses — after a question, before a conclusion. There the silence is doing the work.
- Alternate
zoombetween segments. Then a cut reads as a change of angle rather than a mistake. - Leave one stretch with no graphics at all. The viewer needs air; a solid wall of cards is exhausting.
- Proofread the subtitles. A recognition error burned into the picture is permanent.
- Verify by looking, not by faith. Pull still frames, stack them with
hstack, open the result via Read. - Never ship without the acceptance checklist in pipeline.md.
- Never let a tool write back into a config file. A script that appends its own output to the EDL doubles that file's size, and every later edit echoes the whole thing into context. Generated data belongs in
work/. - Read the first and last line of the transcript yourself. Whisper hallucinates fabricated text over silence — classically a fake subtitler credit line on Russian audio — and it can carry a perfectly normal-looking confidence score. Both transcription scripts filter the obvious cases automatically, but the filter is not airtight. See troubleshooting.md #17.
Orchestration
The task is multi-step — create tasks with TaskCreate for the stages (transcript, EDL, grade, graphics, assembly) and keep their status current. Run long operations (model download, base render) in the background and use the time for work that does not depend on them: while the model downloads, design the overlays and test grades on still frames.
Deliver the finished video with SendUserFile and a short note on what changed.