""" H3 Shot Length (single model-free source for shot length) ========================================================== Holds ONE shot-length value and emits it as both seconds and a grid-aligned H3 frame count. Because it never reads the model, it can sit upstream of Kijai's Model Preview Override without creating a cycle -- unlike the preview node or the sampler, which read the model to compute their frame counts and so cannot feed anything that produces the model. Wire: H3 Shot Length (seconds) -> H3 Long Videos FL2VA (shot_seconds) H3 Shot Length (frames) -> Model Preview Override (preview_frames) One value, entered once here, drives both -- no manual re-entry, no cycle. Note: this is a FIXED shot length you choose. The sampler's *auto* (VRAM- picked) length can't be used for preview_frames, because computing it requires reading the model, which is the very dependency that creates the loop. Set the sampler's shot_seconds from this node's `seconds` output so the two agree. """ H3_MAX_FRAMES = 362 def align_up_grid(n): n = max(5, int(n)) while n % 17 != 5: n += 1 return n class H3ShotLength: CATEGORY = "MiniMax-H3/utils" FUNCTION = "emit" RETURN_TYPES = ("FLOAT", "INT", "STRING") RETURN_NAMES = ("seconds", "frames", "info") @classmethod def INPUT_TYPES(cls): return { "required": { "shot_seconds": ("FLOAT", {"default": 5.0, "min": 0.2, "max": 15.1, "step": 0.5, "tooltip": "Length of each shot. Feeds the sampler's shot_seconds AND (as frames) " "the preview override. Max ~15s (362 frames)."}), "fps": ("INT", {"default": 24, "min": 1, "max": 60}), }, "optional": { "cap_to_h3_max": ("BOOLEAN", {"default": True, "tooltip": "Clamp frames to 362 (~15s), H3's single-clip maximum."}), }, } def emit(self, shot_seconds, fps, cap_to_h3_max=True): fps = max(1, int(fps)) frames = align_up_grid(round(float(shot_seconds) * fps)) capped = cap_to_h3_max and frames > H3_MAX_FRAMES if capped: frames = H3_MAX_FRAMES info = (f"{shot_seconds:g}s/shot @ {fps}fps -> {frames} frames" f"{' (capped 362)' if capped else ''}") return (round(float(shot_seconds), 3), frames, info) NODE_CLASS_MAPPINGS = {"H3ShotLength": H3ShotLength} NODE_DISPLAY_NAME_MAPPINGS = {"H3ShotLength": "H3 Shot Length"} __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]