Files
mo-akkakk-ComfyUI-MiniMaxH3…/__init__.py
T

65 lines
4.8 KiB
Python

from comfy_api.latest import ComfyExtension, io
from .h3_windows import WindowsWrapper, apply, recording_clip
class MiniMaxH3TemporalWindow(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="MiniMaxH3TemporalWindow",
display_name="Minimax H3 Temporal Window & Spatial Tiles (mo-akkakk)",
category="model/patch/minimax",
search_aliases=["h3 temporal window", "h3 spatial tiles", "h3 long video", "minimax overlap", "context windows", "mo-akkakk"],
description="Put after the model, and route the CLIP through it into Reference to Video. "
"Samples the video as overlapping temporal windows, one after another: each window is a normal H3 run on its "
"slice of the plate, starts from the previous window's finished overlap frames (frame-0 guide), and the "
"overlap is crossfaded. Optional spatial tiles, each following its crop of the plate. Works with KSampler and SamplerCustomAdvanced.",
inputs=[
io.Model.Input("model"),
io.Clip.Input("clip", optional=True,
tooltip="Route the CLIP through here into Reference to Video so each window re-encodes the prompt with only "
"its own slice of <Video N> (timestamps from 0), like a manual batch. Strongly recommended."),
io.Boolean.Input("temporal_windows", default=True,
tooltip="Split the shot into overlapping temporal windows. Off = the whole length runs as one native H3 pass."),
io.Int.Input("window_frames", default=124, min=22, max=3600, step=17,
tooltip="Frames per window (17k+5). Keep it inside what works natively for you, e.g. <= 192."),
io.Int.Input("overlap_frames", default=22, min=5, max=1000, step=17,
tooltip="Frames shared by neighbouring windows (5, 22, 39...). Carried into the next window as its frame-0 guide, then crossfaded."),
io.Boolean.Input("short_final_window", default=False,
tooltip="Let the last window be shorter than window_frames instead of pulling it back to full length. "
"Saves the frames that would be generated twice at the end, which matters most with large windows. Off = previous behaviour."),
io.Int.Input("min_final_window_frames", default=22, min=5, max=3600, step=17,
tooltip="With short_final_window on: the last window is never shorter than this (17k+5: 5, 22, 39, 56...). "
"If the natural tail is shorter, the window is pulled back only far enough to reach it."),
io.Boolean.Input("carry_overlap", default=True,
tooltip="Give each window the previous window's finished overlap frames (video and audio) as its frame-0 guide."),
io.Boolean.Input("spatial_tiles", default=False,
tooltip="Split each window into overlapping tiles, for stage 1 or stage 2. Each tile follows its crop of the plate. Without a reference video, a full-denoise pass in tiles repeats the subject."),
io.Int.Input("tile_width", default=960, min=256, max=16384, step=32),
io.Int.Input("tile_height", default=544, min=256, max=16384, step=32),
io.Int.Input("tile_overlap", default=256, min=0, max=8192, step=32),
io.Boolean.Input("crop_ref_images_per_tile", default=True,
tooltip="With tiles, crop <Picture N> to each tile too. On when the picture is a frame of this shot; off for character/style references."),
],
outputs=[io.Model.Output(), io.Clip.Output()],
)
@classmethod
def execute(cls, model, temporal_windows, window_frames, overlap_frames, short_final_window, min_final_window_frames, carry_overlap, spatial_tiles, tile_width, tile_height,
tile_overlap, crop_ref_images_per_tile, clip=None) -> io.NodeOutput:
wrapper = WindowsWrapper(window_frames if temporal_windows else 0, overlap_frames, carry_overlap,
tile_height // 16 if spatial_tiles else 0, tile_width // 16 if spatial_tiles else 0,
tile_overlap // 16, crop_ref_images_per_tile,
min_final_window_frames if short_final_window else 0)
return io.NodeOutput(apply(model, wrapper), recording_clip(clip) if clip is not None else None)
class MiniMaxH3WindowExtension(ComfyExtension):
async def get_node_list(self):
return [MiniMaxH3TemporalWindow]
async def comfy_entrypoint() -> MiniMaxH3WindowExtension:
return MiniMaxH3WindowExtension()