Purpose
Execute an edl.json (Edit Decision List) into a finished master.mp4. The EDL encodes all creator-grammar decisions — clip order, pacing, transitions, audio mix, and captions — produced by generate-edit-decision-list. This atom is the assembly step: it reads the contract and renders it deterministically without any interactive prompts.
Supports both source modes:
- conform (
source.mode = "file") — trims ranges from existing video files. - generative (
source.mode = "generate") — locates pre-rendered scene clips under<project_dir>/clips/<id>.mp4.
Inputs
edl(required) — path toedl.json. Must conform to the EDL schema (schema version 1.0).output(required) — path for the outputmaster.mp4.project_dir(optional) — base directory for resolving relative asset paths. Defaults tometa.project_dirfrom the EDL; falls back to the directory containingedl.json.crf(optional, default18) — x264 quality setting; lower = higher quality.
Workflow
- Load and validate EDL — parse
edl.json, checktimelineis non-empty and contiguous (each clip'starget_out== next clip'starget_inwithin ±1 frame), confirm exactly onepayoff-holdrole, and thatmeta.target_durationis within ±0.5s of the last clip'starget_out. - Resolve and trim clips — for each
timeline[]entry:source.mode = "file": trimfilefromintooutwith ffmpeg (re-encoded, frame-accurate).source.mode = "generate": locate<project_dir>/clips/<id>.mp4; if missing, FAIL listing every missing asset.
- Apply speed — if
speed != 1.0, applysetpts=PTS/<speed>(video) + chainedatempo(audio). - Apply motion — support
zoom-punch(stepped crop/scale to thetozoom factor) andpush-in(gradualzoompanfromfromtoto). Null motion is a no-op. - Normalise — scale every clip to
1080x1920(9:16) or the appropriate resolution formeta.aspectatmeta.fps. - Concatenate — hard-cut concat of all processed clips using ffmpeg
concatfilter. Non-hard-cuttransition_invalues are treated as hard cuts in v1; a note is written tomanifest.json. - Build audio bed — mix
audio.music+audio.vo+ per-clip SFX (placed atsfx[].aton the master timeline) + apply eachaudio_beats[]silence-beat by muting music atatfordurseconds. Whenaudio.ducking = "sidechain-20:1", compress music under VO with ffmpegsidechaincompress. - Burn captions — build an ASS subtitle file from all
timeline[].captions[]cues using the preset fromcaption-style-presets.json.static-sentenceholds the full cue;karaoke-poprenders per-word highlights by splitting the cue text into equal-time words. Falls back togrammar.caption_presetwhen a cue has no per-cue preset. - Mux and deliver — combine video, audio, and subtitle tracks into
output(master.mp4). Writemanifest.jsonto the same directory, including a recomputedpacing_curve.
CLI:
python3 skills/atoms/assembly/render-edl/scripts/render_edl.py \
--edl <edl.json> --output <master.mp4> [--project-dir <dir>] [--crf 18]
Output
master.mp4— finished video atmeta.fps, normalised resolution, with audio and burned captions.manifest.json(sibling tomaster.mp4) — run metadata including:skill_name,run_id,edl_path,output,created_at,duration_secondsclips_rendered— per-clip records:id,source_mode,file(resolved path),duration,statustransitions_logged— list of non-hard-cuttransition_invalues treated as hard cutspacing_curve— recomputedmean_shot_len,cuts_per_10s,payoff_hold_ratiostatus—pass|fail|needs human reviewwarnings,errors
Quality Checks
master.mp4exists and is non-zero;ffprobereports both video and audio streams.- Duration of
master.mp4is within ±0.5s ofmeta.target_duration. - Resolution matches the expected value for
meta.aspect(1080×1920 for 9:16). - All caption cues are present in the ASS file with correct timing.
manifest.jsonexists withstatus: passand allclips_renderedpaths resolve on disk.pacing_curve.payoff_hold_ratiois between 2.0 and 4.0 (warn if outside range, don't fail).
Failure Modes
- EDL validation failure — non-contiguous timeline, missing payoff-hold, or
target_durationmismatch; script exits with a clear message listing every violation. - Missing generate-mode clips — script fails immediately listing every
<project_dir>/clips/<id>.mp4that does not exist. Re-run after generating the missing clips. - Missing conform-mode source file — script exits with a clear error naming the file.
ffmpegnot installed — install viabrew install ffmpegorapt install ffmpeg.- No audio streams — if
audio.music,audio.vo, and all SFX are null/absent, the master is produced with a silent audio track; a warning is written to the manifest. - Caption preset not found — if the cue's preset does not exist in
caption-style-presets.json, the fallback presetplatform-defaultis used and a warning is written. - Speed out of range —
speedvalues < 0.1 or > 8.0 are rejected with a clear error.