Give it one recording and a cut list of the pieces to keep, in the order they play;
get the rough cut, its filter graph and a timeline. The pieces come from the sound:
the recording is split into utterances, each is transcribed on its own, and you pick
the takes from those transcripts. For many takes in several files, or a screen
recording paired with a webcam, the text paper edit in python-cut-phrases-from-takes
is the route; guide-cut-best-takes weighs the two.
Inputs
- The source movie, probed (
ffprobe-inspect-media): its fps, and the audio stream'sstart(screen and webcam recordings often start their sound 0.1–0.25 s in). - Its 16 kHz mono WAV (
whisper-cli-transcribe-with-word-times, Run it), the clock every time below is on. The script adds the audiostartback for you. speech_map.pyfrompython-find-speech-pauses, in the job'sscripts/.
Run it
- Utterances:
exec python scripts/speech_map.py runs work/source.wav --gap 0.45 --out-dir work/runs. - Transcribe each run on its own with large-v3-turbo
(
whisper-cli-transcribe-with-word-times; start them all withexec(wait: false)). A short file's transcript is exact about which words were said; word times from the whole file drift up to 1 s, so never cut on them. - Pick the takes into
work/cut.json(references/islands_cut.mdhas the format):- per line, the last complete take, usually the cleanest and fastest;
- drop false starts, filler "OK"s, coughs, and runs that read only "Thank you." or "Yes." (whisper's words for breath and room);
- a take that restarts mid-sentence, or ends just before a word you don't want: cut
at an energy dip inside the run (
speech_map.py maparound it with--db -30) and mark that edgetight_startortight_end.
- Check every cut inside a run by transcribing that window on its own, ±0.5 s around the joint. A clipped last word is the usual note: a short tail can be the end of the word, not the start of the next one.
- Build: write
references/islands_cut.md's script into the job'sscripts/asislands_cut.py, as is, and runexec python scripts/islands_cut.py --src source/take.mov --wav work/source.wav --cut work/cut.json --fps 30 --out work/rough_cut.mp4. - Verify: transcribe the cut and read it against the lines; list the longest
pauses left (
speech_map.py pauses work/rough_cut.wav --min-gap 0.25). Then the person reviews it (guide-cut-best-takes, Process).
What the build does
- Each piece starts 0.05 s before its first speech and ends 0.09 s after its last; a
tight_edge stays exactly where you put it. - Inside a piece, every pause over 0.14 s keeps 0.06 s after the speech before it and 0.05 s before the speech after it, about 0.11 s in all.
- Every span is cut on the frame grid, picture and sound on the same frame times
(
ffmpeg-frame-exact-trim, The frame grid), with a 6 ms fade in and a 10 ms fade out on the sound, all joined in one-/filter_complexgraph.
What you get
rough_cut.mp4 (H.264, AAC), rough_cut.graph.txt, and rough_cut.timeline.json:
every kept span with its piece, note, WAV times, source frames and its start and end in
the cut, for re-cueing captions and graphics after a re-cut.
Tips and traps
- A 70 s webcam read with retakes became 23 runs; 3 pieces from them cut to 640 frames in 12 spans, picture and sound both 21.333 s, and its transcript read the kept lines word for word.
- A run that flows into an unwanted word ("…thinking, but") has no pause to find at the
adaptive threshold;
map --db -30found the dip and atight_endthere removed the "but", which the transcript of the cut then punctuated as a sentence end. - Pauses at the joins between pieces come out about 0.14 s (tail plus pre-roll); lists of "longest pauses" on the cut are mostly joins and the quiet ends of words.
- The graph is one line per span; hundreds of spans are fine in one graph.
Check it
- Frame count of the cut against its audio length:
ffprobeboth; they match to the frame. - A transcript of the cut (
whisper-cli-transcribe-with-word-times, Check it) against the lines picked.


