H3 MCtx Save (obvpm)
Keep your own video saver, still get the sidecar
- samples
- pins
- conditioning
- sidecar_path
So you're attached to your Video Combine node. Or you want a specific container, or a filename pattern, or your graph already saves through something else and rewiring it is a pain. H3 MCtx Save exists for exactly that: it writes only the .mctx.safetensors sidecar and pairs it to a video that some other node already saved.
The pack's authors are honest about the tradeoff in their own docs - the all-in-one save nodes are the simplest guaranteed-consistent route. This one is for when you want a specific saver and are willing to accept that you now own the pairing.
How it works
You give it video_path, either absolute or relative to your output folder. It's a string, so any node that emits a filename can feed it through a to-string conversion - the classic case being VHS Video Combine's filenames output. If that output is a list, the last path is the one used.
Then it writes the sidecar next to that file, storing the raw latent you handed to samples, the lineage derived from pins, and (optionally) the conditioning. Pairing is by content hash of the video, which is the whole point: the sidecar claims "these latents belong to this file", and any later load verifies that claim.
The catch is stated plainly in the node's own tooltip: the video must be the DELIVERED (trimmed) clip. If you've saved the untrimmed decode - pinned scaffolding still on the front or back - the latents the sidecar stores will be mapped out against the wrong frames, and every later extension will be off by the pin length.
Inputs and outputs
video_path- where the other saver put the file. Empty means an error, not a no-op.samples- the sampler's raw output latent for this take, same as the all-in-one save.save_conditioning- on by default, and the same blunt truth applies: the.condis only writable now. Turn it off for throwaway takes, leave it on for anything you may want to upscale later, because the conditioning exists only while the graph that built it is still wired.- Optional:
pins(unconnected = root clip, which is a decision, not a default - it means no crossfade and no recorded parent),metadata(free-form JSON, stored verbatim asuser_meta), andconditioning(the CONDITIONING this take was sampled with, stored beside the clip as.cond.safetensors).
Output is sidecar_path, the path of what it wrote. Useful for logging; not usually wired anywhere.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm-timeline
Restart ComfyUI. No extra Python dependencies - the pack declares none, because torch, safetensors and av are already in ComfyUI's own requirements. Needs ComfyUI 2026-08-13 or later for the core H3 nodes. In Manager, search the pack title comfyui-obvpm-timeline; every node ends in (obvpm), so obvpm in the node menu finds the whole pack.
If you're using it with VHS, install that too - it isn't part of this pack and isn't in the pack's dependency table, because nothing else here needs it.
Troubleshooting
- "video_path is empty." Your upstream saver didn't emit a usable path, or the conversion produced an empty string. Check what the wire actually carries before blaming the node.
- Sidecar written, loads refuse it. Re-encoding the video after saving breaks the hash pairing. So does anything that rewrites the file - a player that "fixes" the container, a trim, a re-mux. Save once, then leave the file alone.
- Wrong list element. A saver that outputs several filenames gets the last one used. If your saver writes multiple variants, make sure the last is the one you want paired.
- Saved the untrimmed clip. The most expensive mistake with this node. The fix is to put H3 MCtx Trim Pinned upstream of your saver, or to swap to H3 MCtx Trim and Save Video, which trims and saves in one step so the two can't disagree.
- Where did the metadata go? It goes in the sidecar header as
user_meta. Nothing downstream in ComfyUI reads it automatically - it's provenance for you and for the pack's browser UI, which looks for keys likeprompt,seed,stepsandrefs_note.
Reach for this node when the saving is a solved problem in your graph and you only need the continuation data bolted onto it.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| video_path | STRING | Path of the saved video: absolute, or relative to the output folder. Accepts a VHS filenames output wired via any to-string conversion; the LAST path is used. | |
| samples | LATENT | The sampler's RAW output latent for this take (same as the all-in-one Save). | |
| save_conditioning | BOOLEAN | true | Write 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. |
| pinsopt | OBVPM_H3_PINS | The resolved pins from H3MCtxApplyPins. Unconnected = root clip. | |
| metadataopt | STRING | Optional provenance JSON, stored verbatim as user_meta (see the all-in-one Save). | |
| conditioningopt | CONDITIONING | Optional: 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)
| Name | Type | Description |
|---|---|---|
| sidecar_path | STRING | Path of the written sidecar. |