DiffusionGemma H3 Shot Assembler
Stitches your separate H3 lanes back into one master, audio untouched
- final_audio
- shot_1_images
- shot_2_images
- shot_3_images
- shot_4_images
- assembled_images
- final_audio
- assembly_report_json
- status
- ready
The planner divided your long prompt into H3 lanes. H3 rendered them separately. Now someone has to put the frames back together into one continuous master - and that job is more delicate than a naive concat, because each lane was trimmed to a retained segment and the master clock has to line up with the soundtrack to the frame. DiffusionGemma H3 Shot Assembler is the node that lazily requests only the planned lanes, trims every decoded segment at its retained cut, verifies the master clock and soundtrack duration, and concatenates exact frames - passing the pristine audio object through unchanged.
Read the docstring and you get the whole philosophy in one line: "Lazily join only planned H3 image batches, trim every segment to its master timing, and pass the exact locked audio object unchanged." The assembled video is frame-exact against the locked soundtrack excerpt; the audio itself is never re-encoded or touched.
Inputs that matter
plan_json- from the Audio-Aware Multi-Shot Planner. The assembler validates that it's a ready, schema-matching shot plan before touching anything; a stale or malformed plan raises instead of silently mis-assembling.final_audio- the locked soundtrack (the hash-locked pristine excerpt from the selector).target_duration_seconds(default 15) andfps(default 24) - the master clock the plan was built against.shot_1_images…shot_4_images- lazy IMAGE inputs, one per planned lane. "Lazy" here is doing real work: the node'scheck_lazy_statusasks for only the image batches the plan actually requires, so unplanned lanes never get decoded upstream.
Outputs: assembled_images (the exact-frame master), final_audio (the same pristine object, passed through), assembly_report_json (with the trim/clock verification record), status, and ready.
The frame-accuracy contract
Because each H3 lane was rendered at the generator's own length and the planner recorded a retained cut per lane, the assembler trims every decoded segment to its retained cut - dropping the tail frames the planner already budgeted as overlap/continuity - and verifies the total against the master clock and the soundtrack's duration. If the plan's frame math doesn't add up to the target, you get a ready=false and a report explaining the discrepancy rather than a subtly-shifted video. This is the node that makes the whole multi-lane trick trustable: exactly N frames, matching the audio, every time.
Where it fits
Downstream of the lanes themselves and upstream of whatever writes your final file. In the advertisement stack there's a governed sibling (DiffusionGemma Advertisement Master Assembler) that also enforces the campaign's contracted aspect tolerance and lanes; this node is the general music-video version. Either way the audio story is the same - the workflow deliberately discards H3-generated audio and muxes only the selected, hash-locked soundtrack, and this node is where the pristine audio rides along untouched.
Installing it
Part of exportAnything/ComfyUI-DiffusionGemmaPromptBuilder, under prompt/diffusiongemma/production-planning:
cd ComfyUI/custom_nodes
git clone https://github.com/exportAnything/ComfyUI-DiffusionGemmaPromptBuilder
or ComfyUI Manager → DiffusionGemma Prompt Builder → restart. Frame concatenation over numpy/PIL - no extra models. The model weight you need for anything in this graph is the DiffusionGemma folder (ComfyUI/models/LLM/diffusiongemma-26B-A4B-it-NVFP4) plus whatever H3 and audio models your lanes used.
Common issues
- "plan_json is not a ready audio-aware H3 shot plan." The plan didn't validate - wrong schema/version, or
readyis false. Regenerate the plan from the planner rather than hand-editing. - Output shorter or longer than expected. Every frame count is against
target_duration_secondsandfps. If those don't match what the lanes actually rendered at, the assembly report will show it - fix the clock inputs, not the assembler. - Wrong audio in the master. Make sure the
final_audioinput is the locked excerpt from the selector, not a raw generated candidate. This node passes through exactly what you give it.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| final_audio | AUDIO | — | |
| plan_json | STRING | — | |
| target_duration_seconds | FLOAT | 15.000.1–60 | — |
| fps | FLOAT | 241–120 | — |
| shot_1_imagesopt | IMAGE | — | |
| shot_2_imagesopt | IMAGE | — | |
| shot_3_imagesopt | IMAGE | — | |
| shot_4_imagesopt | IMAGE | — |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| assembled_images | IMAGE | — |
| final_audio | AUDIO | — |
| assembly_report_json | STRING | — |
| status | STRING | — |
| ready | BOOLEAN | — |