Story Final Compile
Stitch scenes, voiceover, music and subtitles into one mp4
- trigger
- output_path
StoryFinalCompile is the payoff node of the Story JSON Nodes pack (stepan-bogatorjov/comfy_scenes_json_node). Everything else in the pipeline feeds it: per-scene videos, per-scene voiceover, one background music track, and a story JSON - and it returns a single finished MP4 with crossfades, mixed audio, optional burned-in subtitles and an optional global speed change. If you've built a multi-scene story, this is where it stops being a folder of loose renders and becomes a short.
It's also the node that makes you respect naming conventions. It matches scenes as <n>_<counter>.mp4, voiceovers as <n>_<counter>.wav (or mp3/flac/m4a/aac/ogg), the music track by its _<counter> suffix, and the story JSON as scene_<counter>.txt - all the outputs the pack's save nodes produce by default. Wire it right and the compile is fully automatic; get a name wrong and it either can't find scenes or silently skips audio.
How it works
It's a two-pass ffmpeg job under the hood, and the source comments are worth respecting: feeding scene videos into both the crossfade chain and the audio mix in one command deadlocks ffmpeg (the xfade buffer and the amix queue fight over the same inputs), so the node renders the crossfaded video first, then muxes the audio mix against it. Each scene's voiceover is delayed to its scene's timeline start (compressed with atempo only if it's longer than the scene), each scene's original audio is kept underneath at a low volume so background ambience survives, and the music bed is trimmed to the final duration. playback_speed retimes everything uniformly at the very end, so the subtitle windows and transition offsets all stay correct. Subtitles - off by default - come from the story JSON: italic for off-screen voiceover narration, regular for in-scene dialogue, with length-weighted timing when a scene has several lines.
The inputs that matter
- trigger - any socket, for execution order. The intended chain ends with CollectRunOutputs taking the returned
output_path. - scenes_folder / voiceover_folder / music_folder - subfolders under
output/; empty meansoutput/itself. - transition_duration - crossfade length (0–2s).
- playback_speed - 0.25–4×, applied to the whole cut.
- voiceover_volume, original_audio_volume (default 0.2 - the quiet layer under narration), music_volume (default 0.3).
- voiceover_subtitles / dialogue_subtitles - burn them in or keep the frame clean.
- filename_prefix - the output file is
final_00001.mp4by default. - Output output_path (STRING) - where the finished MP4 landed.
Install
The pack plus ffmpeg on PATH (or pip install imageio-ffmpeg into ComfyUI's Python - the node tries both):
cd ComfyUI/custom_nodes
git clone https://github.com/stepan-bogatorjov/comfy_scenes_json_node
Restart ComfyUI (or install via Manager under "Story JSON Nodes").
Troubleshooting
The most common error is a straight FileNotFoundError because no <n>_<counter>.mp4 files exist in the scenes folder - check that the save nodes ran, and that the folders line up. On non-Windows systems, the burned-in subtitle path assumes Arial fonts at the Windows location; without them the subtitle render falls back, so keep voiceover_subtitles/dialogue_subtitles off unless you're on Windows or have those fonts available. And don't run it before the save nodes finish - it reads files from disk, so ordering through the trigger socket is the whole point. If the final file appears with no voiceover, the voiceover folder path or the scene prefix is wrong - the compiler logs exactly which voiceovers it matched, so read the console.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| trigger | * | — | |
| scenes_folderopt | STRING | — | |
| voiceover_folderopt | STRING | — | |
| music_folderopt | STRING | — | |
| transition_durationopt | FLOAT | 0.200–2 | — |
| playback_speedopt | FLOAT | 1.000.25–4 | — |
| voiceover_volumeopt | FLOAT | 1.000–2 | — |
| original_audio_volumeopt | FLOAT | 0.200–1 | — |
| music_volumeopt | FLOAT | 0.300–2 | — |
| voiceover_subtitlesopt | BOOLEAN | false | — |
| dialogue_subtitlesopt | BOOLEAN | false | — |
| filename_prefixopt | STRING | final | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| output_path | STRING | — |