Skip to project content
Yulai FanAbout
Product

Voiceover Cutter

I designed and built a script-driven media tool that aligns recordings to text, generates AI narration, and assembles finished demo voiceovers.

Voiceover Cutter

Overview

Built with sales teammates to automate a repeated editing task, saving approximately 20 minutes per demo video based on team feedback. A Next.js interface connects to a Python processing service, with visible job progress and reusable intermediate clips.

My contribution
Interface, workflow, and AI-assisted implementation
Stack
Next.js, TypeScript, Python, FastAPI, and FFmpeg
Speech services
ElevenLabs Scribe and text-to-speech, with optional Whisper transcription
Deployment
Vercel frontend and containerized API on Google Cloud Run

Separating UI from processing

The browser handles script input, segment previews, voice selection, and job status. FastAPI handles the expensive work: transcription, alignment, cutting, synthesis, and verification. This keeps long-running media work outside the frontend request lifecycle.

A typed API contract exposes the job ID, status, processing stage, progress, errors, and output availability. The React interface polls the active job once a second and stops at a terminal state. Keeping the ID in localStorage lets a page reload reconnect to a job while the backend still has it.

Matching speech to a script

The difficult part is choosing the right take from a recording with repeated phrases. Word-level transcription feeds a dynamic-programming aligner that matches script words in order. Fuzzy matching tolerates transcription differences, while a cost for skipped transcript words favors compact passages over matches scattered across unrelated takes.

Cross-segment deduplication prevents two script lines from claiming the same audio. Boundary refinement uses silence and retake checks before FFmpeg cuts the selected spans. These decisions connect a text edit to a specific, inspectable piece of media.

Implementation

A recoverable pipeline, from script segments to a verified media file.

  1. 01

    Parse an ordered segment model

    Split human narration by non-empty line and keep AI VOICE blocks intact. Speaker labels select an ElevenLabs voice. Both generation and recording-edit modes assemble their clips in the original script order.

  2. 02

    Expose job state and cancellation

    A backend worker advances through named processing stages. The UI shows progress and logs, tolerates temporary polling failures, and reconnects after a reload. Cancellation is cooperative and takes effect at the next processing checkpoint.

  3. 03

    Reuse completed work

    A segment manifest records the previous cut. When human segments still match, the next job reuses those clips and regenerates the AI voice blocks. Changes to human text take the re-cut path using the existing source media.

  4. 04

    Verify the assembled output

    Re-transcribe the final file and compare it with the script. The activity panel exposes missing words, extras, and matched spans so a completed export can be reviewed before use.

  5. 05

    Keep the recovery boundary explicit

    Jobs run in a single-worker executor with in-memory state and temporary workspace files. Reload recovery works while that backend job exists; durable recovery across a server restart would require persistent job and artifact storage.

Voiceover system

Production paths

Paste script
No recording

Generate voices with ElevenLabs

Export M4A

Upload recording

Match recording to script

Remove mistakes and retakes

Insert generated agent voices

Export MP4 or M4A

Script parsing and voice assignment

Script inputProduct behavior
Normal textNarrator voice, or recorded speech in cut mode
Agent 1:ElevenLabs Agent 1 voice
Agent 2:ElevenLabs Agent 2 voice
Unlabeled AI blockAgent 2 fallback voice
Input to output

Normal textNarrator clip

Agent 1:Agent 1 clip

Agent 2:Agent 2 clip

Ordered clipsFinal file

Media processing architecture

The interface submits jobs and polls status; the Python service owns the media pipeline.

Next.js on VercelFastAPI worker on Cloud RunScribe / Whisper + alignment + FFmpeg + TTSVerified M4A or MP4

Re-run and recovery decisions

ConditionProcessing path
No recording suppliedGenerate ordered narration clips with text-to-speech
Only AI voice content changedReuse human clips; regenerate AI blocks and reassemble
Human narration changedReuse source media and run alignment and cutting again
Browser reloads during a jobRestore the job ID and reconnect to status polling
Backend no longer has the jobClear the stale job reference and allow a fresh run
Output verification

Finished mediaRe-transcribe

TranscriptCompare to script

DifferencesMissing / extra words

Activity panelReview the result

End-to-end production workflow

How a script moves from source setup through assembly, review, selective regeneration, and export.

End-to-end voiceover production workflowA teammate pastes a script, chooses generated narration or an uploaded recording, applies voice rules to each segment, arranges the clips, reviews the result, and selectively regenerates only the content that changed.Set up sourceInterpret scriptAssembleReview and reuseNo, defaultYes, optionalYesNoNormal textAgent 1Agent 2UnlabeledGenerateCutOpenVoiceover CutterPaste script inoriginal orderUpload arecording?Generate VoicemodeDefault narrator orElevenLabs voice IDCut ExistingSpeech modeVoice isolationenabled?Clean recordingvoice trackUse originalrecordingParse script inoriginal orderSegmenttype?Normal textCurrentmode?Narrator voiceMatch and cut wordsfrom recordingAI VOICE withAgent 1 labelAI VOICE withAgent 2 labelUnlabeledAI VOICE blockElevenLabsAgent 1 voiceElevenLabsAgent 2 voiceAgent 2fallback voiceCreate and arrangeclips in script orderShow live progresswhile generating, cutting,and stitchingSuccessErrorUser stopsDoneCut anotherEdit and re-runYesNoReuse human clipsReuse cleaned mediaProcessingresult?Verify output andactivity detailsDownload M4A,MP4, or M4A cutNextaction?CompleteClear job andstart freshShow error detailsand allow resetCancel at next stageboundary and unlockOnly AI VOICEtext changed?Regenerate onlyagent voicesRe-cut changedhuman text