CoachBatePrepareShotContinuity
H3 shot 12 picks up from shot 11's actual output
- latent
- vae
- audio_vae
- conditioning_audio
- latent
- conditioning_audio
- trim_frames
The problem it solves is bookkeeping as much as sampling
If you're rendering an episodic video shot by shot, continuity is the thing that eats your weekend. Shot 12 has to start where shot 11 ended - same wardrobe, same lighting, same face, and on H3 with native audio, the same voice mid-sentence. The naive fix is to render shot 11, export it, load the last 39 frames by hand into shot 12, and repeat that forty times.
CoachBatePrepareShotContinuity removes the "by hand." It takes the new shot's latent, the vae
and audio_vae, the shot_payload from CoachBateShotLoader, and the output_directory of the
run, and returns a prepared latent, the conditioning_audio and a trim_frames integer. It
finds the source shot's output by reading the receipt that CoachBateRecordShotOutput wrote for it
- inside
.continuity/in the run folder - rather than scanning filenames, which is why it can hand you the exact file that shot produced.
What it delegates, and what it validates
Mechanically, the node does not reimplement H3 masking. It locates the receipt, decodes the tail
of the recorded clip through VHS's own loader (so frame and audio timelines agree), then calls the
installed H3 context nodes - MiniMaxH3ExistingVideoMaskedContext for the video case and
MiniMaxH3SongMaskedAVContext for the audio-master case - to do the actual conditioning. Its own
value is the validation around that, and there's a lot of it: it checks the recorded file's size
and mtime against the receipt and refuses if the file changed, checks the source shot still
occurs exactly once in the shotlist, checks the source really is a counted 24fps render, and
checks the conditioning grid covers the requested shot with less than 17 frames of padding - that
last one is the 17k+5 grid biting again.
no_master_audio (default true) is the switch worth understanding. Left on, the recorded clip is
the audio source and only a prefix of it is used: the node takes the canonical audio tail so the
model hears where the voice actually was, and refuses to fabricate missing dialogue rather than
padding silence into a line. Turn it off and the supplied conditioning_audio becomes the master
- that's music-video territory, where you have a song and you want the shot built on top of a specific rangy chunk of it. In that mode the conditioning audio gets padded at the tail to fit the model grid, and CoachBateTrimShotOutput discards that invented tail along with the extra picture frames.
It also returns float("NaN") from its change check, meaning it re-runs on every queue press no
matter what. That's correct here - the receipt and the source file can both change between runs -
but it's worth knowing that an always-dirty node defeats the cache for everything behind it, so
expect a slower graph than the node count suggests.
Inputs and outputs
Required: latent, vae, audio_vae, shot_payload, output_directory, no_master_audio.
Optional: conditioning_audio.
Outputs: latent (to the sampler), conditioning_audio (to the audio side), trim_frames (into
CoachBateTrimShotOutput's trim_frames input - and yes, it needs to go there; that integer is how
the grid padding gets cut back off).
The shotlist fields it reads are video_continuation (boolean), continuation_source_shot_id, and
continuation_context_frames - which must be 39, 90, 141, and so on (39 plus multiples of 51),
because the AV context has to land on the model's grid. It also refuses shots flagged
manual_input_required, which is a nice production feature: park a shot with a note and the queue
stops rather than rendering something wrong.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/CoachBate/ComfyUI-CoachBate.git
Or ComfyUI Manager → "CoachBate" → restart. Category is plain CoachBate. This node leans on ComfyUI's installed H3 nodes, Video Helper Suite (it decodes through VHS), and PyAV (which ComfyUI ships). The pack itself declares no pip dependencies.
Where people get burned
- "Render source shot X in this run first; no output receipt exists." - the source shot hasn't been rendered into this run folder, so there's nothing to continue from. Order matters: the receipts are written shot by shot.
- "Continuation requires a different, explicit source shot ID." -
video_continuationis true butcontinuation_source_shot_idis blank or points at the shot itself. - "AV continuation frames must be 39, 90, 141, …" - your
continuation_context_framesis off the grid. - "Recorded output was modified" - you re-encoded or trimmed the source clip after rendering. The receipt check is doing exactly what it's meant to; re-render the source shot.
- The conditioning audio is shorter than the shot - the node refuses instead of padding. A real failure to fix at the audio end, not in the graph.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | — | |
| vae | VAE | — | |
| audio_vae | VAE | — | |
| shot_payload | STRING | — | |
| output_directory | STRING | — | |
| no_master_audio | BOOLEAN | true | — |
| conditioning_audioopt | AUDIO | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |
| conditioning_audio | AUDIO | — |
| trim_frames | INT | — |