91 lines
4.4 KiB
Python
91 lines
4.4 KiB
Python
"""Submit a prompt-only MiniMax H3 T2V graph to ComfyUI."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import time
|
|
from urllib.request import Request, urlopen
|
|
|
|
|
|
def post_json(url: str, payload: dict) -> dict:
|
|
request = Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
|
|
with urlopen(request, timeout=30) as response:
|
|
return json.loads(response.read().decode())
|
|
|
|
|
|
def get_json(url: str) -> dict:
|
|
with urlopen(url, timeout=30) as response:
|
|
return json.loads(response.read().decode())
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--server", default="http://127.0.0.1:8188")
|
|
parser.add_argument("--prompt", required=True)
|
|
parser.add_argument("--filename-prefix", required=True)
|
|
parser.add_argument("--seed", type=int, default=440407)
|
|
parser.add_argument("--width", type=int, default=960)
|
|
parser.add_argument("--height", type=int, default=544)
|
|
parser.add_argument("--frames", type=int, default=124)
|
|
parser.add_argument("--steps", type=int, default=12)
|
|
parser.add_argument("--upscale", type=float)
|
|
args = parser.parse_args()
|
|
|
|
graph = {
|
|
"1": {"class_type": "UNETLoader", "inputs": {"unet_name": "minimax_h3_fl2va_pruned_nvfp4.safetensors", "weight_dtype": "default"}},
|
|
"3": {"class_type": "CLIPLoader", "inputs": {"clip_name": "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors", "type": "minimax"}},
|
|
"4": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_video_vae_fp16.safetensors"}},
|
|
"5": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_audio_vae_fp32.safetensors"}},
|
|
"8": {"class_type": "MiniMaxH3ImageToVideo", "inputs": {"clip": ["3", 0], "vae": ["4", 0], "prompt": args.prompt, "width": args.width, "height": args.height, "length": args.frames}},
|
|
"9": {"class_type": "BasicGuider", "inputs": {"model": ["1", 0], "conditioning": ["8", 0]}},
|
|
"10": {"class_type": "RandomNoise", "inputs": {"noise_seed": args.seed}},
|
|
"11": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "res_multistep"}},
|
|
"12": {"class_type": "BasicScheduler", "inputs": {"model": ["1", 0], "scheduler": "beta", "steps": args.steps, "denoise": 1.0}},
|
|
"13": {"class_type": "SamplerCustomAdvanced", "inputs": {"noise": ["10", 0], "guider": ["9", 0], "sampler": ["11", 0], "sigmas": ["12", 0], "latent_image": ["8", 1]}},
|
|
}
|
|
decoded_latent = ["13", 0]
|
|
if args.upscale is not None:
|
|
graph.update({
|
|
"18": {"class_type": "LTXVSeparateAVLatent", "inputs": {"av_latent": ["13", 0]}},
|
|
"19": {"class_type": "MinimaxH3LatentUpscaler3D", "inputs": {
|
|
"latent": ["18", 0],
|
|
"model_name": "minimax_h3_latent_upscaler_3d_fp16.safetensors",
|
|
"mode": "scale by multiplier",
|
|
"mode.scale": args.upscale,
|
|
"align": 32,
|
|
"keep_proportion": True,
|
|
"device": "cuda",
|
|
"precision": "fp16",
|
|
}},
|
|
"20": {"class_type": "LTXVConcatAVLatent", "inputs": {"video_latent": ["19", 0], "audio_latent": ["18", 1]}},
|
|
})
|
|
decoded_latent = ["20", 0]
|
|
graph.update({
|
|
"14": {"class_type": "VAEDecode", "inputs": {"samples": decoded_latent, "vae": ["4", 0]}},
|
|
"15": {"class_type": "VAEDecodeAudio", "inputs": {"samples": decoded_latent, "vae": ["5", 0]}},
|
|
"16": {"class_type": "CreateVideo", "inputs": {"images": ["14", 0], "audio": ["15", 0], "bit_depth": 8, "fps": 24.0}},
|
|
"17": {"class_type": "SaveVideo", "inputs": {"video": ["16", 0], "filename_prefix": args.filename_prefix, "format": "mp4", "codec": "auto"}},
|
|
})
|
|
|
|
started = time.perf_counter()
|
|
response = post_json(f"{args.server}/prompt", {"prompt": graph})
|
|
prompt_id = response["prompt_id"]
|
|
while True:
|
|
time.sleep(1)
|
|
history = get_json(f"{args.server}/history/{prompt_id}").get(prompt_id)
|
|
if history is None:
|
|
continue
|
|
status = history.get("status", {})
|
|
if status.get("completed") or status.get("status_str") in {"success", "error"}:
|
|
break
|
|
print(json.dumps({
|
|
"prompt_id": prompt_id,
|
|
"wall_seconds": time.perf_counter() - started,
|
|
"status": history.get("status", {}),
|
|
"outputs": history.get("outputs", {}),
|
|
}, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|