Nodes/comfyui-obvpm-timeline/H3 MCtx Save Video (obvpm)
ComfyUI Node

H3 MCtx Save Video (obvpm)

The save node that keeps an H3 clip extendable forever

By chanon·Created 21 days ago·Updated 2 days ago· 33
H3 MCtx Save Video (obvpm)
  • samples
  • images
  • audio
  • pins
  • untrimmed_images
  • untrimmed_audio
  • conditioning
  • path
base_folderproject1
filename_prefixclip
crf19
save_conditioningtrue
metadata

Every pack that extends video eventually asks you to make a choice: save your clip normally, or save it in a special way that keeps it continuable. H3 MCtx Save Video answers both at once, and it's the node most graphs here end with.

It writes an MP4 plus a .mctx.safetensors sidecar holding the clip's full raw latents and lineage. Any clip saved here can later seed an extension through its sidecar - including months later, at full quality, because the latents are the generation's own, not a re-encode of the delivery file.

What it's actually storing

Two things, and it's worth understanding both.

The MP4 is the delivery copy. It's lossy, it's what you watch and upload, and its crf is the setting that matters most on this node - assembly later stream-copies these frames and matches this crf for the few it re-encodes, so the take's own crf is what your final cut looks like.

The sidecar is the master. It holds the sampler's raw output latent (stored whole - pinned head and tail included, mapped out via the header) plus the header: identity, lineage, and the generation recipe. Pairing is by content hash, and the write order is deliberate. MP4 first, hash it, then the sidecar via a temp file and an atomic replace. Existence of the sidecar is the commit point, so a crash in between leaves a plain playable video rather than a corrupt pair.

The encode itself pipes raw frames to an ffmpeg subprocess when a binary is available (~2.2x faster, the VideoHelperSuite mechanism) and otherwise falls back to core's in-process PyAV path. Either way the same node owns the transaction - it is deliberately not downstream of VHS, so the pairing hash is trustworthy.

The inputs you set

samples takes the sampler's raw output latent, images takes the delivered frames (after the trim node removed pinned scaffolding), and base_folder / filename_prefix decide where it lands. Use the same base_folder value you gave the Timeline node and one setting drives both.

save_conditioning (default on) writes the wired conditioning beside the clip as a .cond.safetensors. This costs real disk - comparable to the latents, dominated by the reference images' share of the prompt - so turn it off for throwaway takes. But understand the asymmetry: it costs nothing now, and it cannot be recovered afterwards, because the conditioning only exists while the graph that built it is still wired up. No .cond means that take can never be refined by the joint upscale pass.

Then the optional wires: audio (leave it off for a silent MP4 - the sidecar still stores the audio latent), pins (the resolved pins from H3 MCtx Apply Pins; this is what derives the lineage edge, so a Save without it records a root), conditioning (the .cond above), and untrimmed_images / untrimmed_audio - the decode output before the trim node. Only the pinned head of that is kept, and it's what lets assembly crossfade this take's own re-render of the join against its parent's, turning a visible level step into a handover. Takes saved without it simply aren't crossfaded. There's no drama, just a slightly harder join.

metadata is free-form JSON stored verbatim in the header. Well-known keys the browser UI looks for: prompt, seed, steps, refs_note. That last one exists because nothing can fingerprint reference images automatically - writing "refs: the two redhead photos + audio_00003" is the mitigation. Invalid JSON is refused, so mind your commas.

Output: path, the written MP4, with the sidecar next to it. Wire that into H3 MCtx Result Preview and you get the seamless join check after every run.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm-timeline

Restart ComfyUI. No Python dependencies; the pack's pyproject declares none because ComfyUI already ships torch, safetensors and av. ComfyUI 2026-08-13 or later. Manager users: search comfyui-obvpm-timeline, or obvpm in the node menu since every node name ends with it.

Troubleshooting

  • Sidecar written, but you still can't refine. Check save_conditioning and whether conditioning is actually wired. It does nothing when unconnected.
  • MP4 exists, no sidecar. That's the crash-safe ordering doing its job - you get a playable video and no lineage. Re-run.
  • ffmpeg "exited" error. The fast path needs an ffmpeg on PATH; if it's missing or mismatched you'll see the subprocess error rather than a silent fallback.
  • Frame-count error about frames written vs expected. The save verifies what it wrote. Byte-level mismatches show up here, not three clips later.
  • A take that won't extend in the timeline. Either no sidecar, or the file was touched after saving and the hash no longer pairs. Treat finished takes as immutable.

For the one-node version of trim + save, see H3 MCtx Trim and Save Video. Use this one when the trim is already happening upstream and you want the saving step explicit.

Categoryobvpm/h3

Inputs (12)

NameTypeDefaultDescription
samplesLATENTThe sampler's RAW output latent for this take (the same one you decode). Stored whole in the sidecar; the pinned head/tail stay in it and are mapped out via the header.
imagesIMAGEDelivered frames (AFTER the trim node removed pinned scaffolding).
base_folderSTRINGproject1Output-relative folder to save into. Same meaning as the Timeline's base_folder, so one value can drive both. Empty = the output root.
filename_prefixSTRINGclipFilename prefix within base_folder, like core save nodes. Numbering is appended automatically.
crfINT190–51H.264 quality (lower = better, bigger). THE one that matters: assembly stream-copies these frames and matches this crf for the few it re-encodes, so the take's own setting is what the delivered cut looks like. The MP4 is the delivery copy; the sidecar keeps the lossless latents regardless.
save_conditioningBOOLEANtrueWrite the wired conditioning beside the clip. It is a real amount of disk -- comparable to the latents, and dominated by the reference images' share of the prompt -- so turn this off for throwaway takes and leave it on for anything you may want to upscale later. Off costs nothing now and cannot be recovered afterwards: the conditioning only exists while the graph that built it is still wired up. Does nothing when the conditioning input is unconnected.
audiooptAUDIODelivered audio (after the same trim). Leave unwired for a silent MP4; the sidecar still stores the audio latent.
pinsoptOBVPM_H3_PINSThe resolved pins from H3MCtxApplyPins. Carries the authoritative generation recipe for the sidecar and derives the lineage edge. Unconnected = root clip.
metadataoptSTRINGOptional provenance as JSON, stored verbatim in the sidecar header (user_meta). Well-known keys browser UI looks for: prompt, seed, steps, refs_note (nothing can fingerprint reference images automatically -- a refs_note is the mitigation). Any other keys are yours. Empty is fine; invalid JSON is refused.
untrimmed_imagesoptIMAGEOptional: the decode output BEFORE the trim node. Only its pinned head is kept, stored in the sidecar so assembly can crossfade this take's own re-render of the join against its parent's. Unwired = the take still saves, but cannot be crossfaded.
untrimmed_audiooptAUDIOOptional: the matching untrimmed audio, so the crossfade covers sound as well as picture.
conditioningoptCONDITIONINGOptional: the CONDITIONING this take was sampled with, stored beside the clip as .cond.safetensors. An upscale/refine pass has to sample the same clip again at a larger size, and it needs the same conditioning to do it -- text, reference images and audio, and any refmods applied on top. Rebuilding that from the prompt graph would mean re-running everything that made it, which can silently differ. Unwired = the take saves normally and simply cannot be refined later.

Outputs (1)

NameTypeDescription
pathSTRINGPath of the written MP4; the sidecar sits next to it.