# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"2": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "video/video/MiniMax_H3_i2v_768P",
"format": "auto",
"format.codec": "auto",
"video": [
"24",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"7": {
"class_type": "MinimaxHailuo03ContextIRNode",
"inputs": {
"model": "MiniMax H3",
"model.duration": 5,
"model.prompt": [
"14",
0
],
"model.ratio": "adaptive",
"model.reference_images.image_1": [
"25",
0
]
},
"_meta": {
"title": "MiniMax H3 Context IR (Prompt Enhancer)"
}
},
"8": {
"class_type": "SaveText",
"inputs": {
"filename_prefix": "H3_prompt",
"format": "txt",
"text": [
"12",
0
]
},
"_meta": {
"title": "Save Text"
}
},
"10": {
"class_type": "MinimaxHailuo03RegenerateNode",
"inputs": {
"model": "MiniMax H3",
"model.prompt": [
"8",
0
],
"model.reference_images.image_1": [
"25",
0
],
"model.resolution": "2K",
"video": [
"2",
0
],
"watermark": False
},
"_meta": {
"title": "MiniMax H3 Regenerate to 2K"
}
},
"11": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "video/Minimax_h3_max",
"format": "auto",
"format.codec": "auto",
"video": [
"13",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"12": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"14",
0
],
"on_true": [
"7",
0
],
"switch": [
"18",
0
]
},
"_meta": {
"title": "If/Else Switch"
}
},
"13": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"24",
0
],
"on_true": [
"10",
0
],
"switch": [
"19",
0
]
},
"_meta": {
"title": "If/Else Switch"
}
},
"14": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": "[Product] An all-black chunky sneaker (chunky sole, all-black body, black laces) on black velvet, cold-white top-light system, photoreal product photography.\n\n[First frame A (Shot 1, use the reference image or generate per this)]\nAn all-black chunky sneaker floating over black velvet, one cold-white beam from the upper left, the silhouette rimmed with faint edge light, slightly-off frontal view, the shoe centered, vast black negative space around it, 16:9, no text.\n\n[First frame B (Shot 2, with background text layer)]\nThe same all-black chunky sneaker on the same black velvet with the same cold-white top-light system, restaged as a low-angle hero shot looking up: the sole tilted toward the lens, tread pattern and cold steel-gray edge highlight crisp, laces dropping into frame from above. Deep in the blurred darkness behind the shoe, a giant dark-gray sans-serif line \"BUILT IN THE DARK\" is barely visible: far larger than the product, partially occluded by the shoe, out of focus, extremely low luminance, as if printed on the background velvet. 16:9, photoreal product photography.\n\n[First frame C (Shot 3)]\nThe same all-black chunky sneaker on the same black velvet with the same cold-white top-light system, restaged at a 45-degree overhead-side angle: the shoe occupying the right two-thirds, a large area of black negative space on the left, 16:9, no text.\n\n[Shot 1 motion layer (I2V, first frame A)]\nStart from the first frame, inherit its content and lighting; motion only. Action: [0s-2s] Dust drifts upward in the beam, the key light breathing subtly; [2s-5s] The shoe rotates slowly by about 20 degrees as one rim highlight sweeps along the chunky sole from heel to toe, the velvet rippling once. No text. Camera: one slow constant push-in, subtle handheld breathing, no cuts. Audio: a low drone, one muffled kick at 2s, faint velvet rustle.\n\n[Shot 2 motion layer (I2V, first frame B)]\n\nStart from the first frame, inherit its content and lighting; motion only. Action: [0s-1.5s] One highlight sweeps across the tread while the giant dark-gray background type \"BUILT IN THE DARK\" slides very slowly right to left, always behind the shoe, defocused and low-luminance; [1.5s-3.5s] The shoe flips slowly along a diagonal from sole view toward its side profile, briefly occluding more of the background type mid-flip, creating a foreground-silhouette versus background-type interplay; [3.5s-5s] It settles into a breathing hover as the background type slides off the left frame edge, cropping out and fading. Camera: locked macro, no cuts. Audio: the drone continues, one woody click at 1.5s, subtle fabric friction during the flip. Background text rules: the type never crosses in front of the shoe, never covers any part of it, stays defocused and low-contrast throughout, existing purely as a depth layer.\n\n[Shot 3 motion layer (I2V, first frame C)]\nStart from the first frame, inherit its content and lighting; motion only. Action: [0s-1.5s] The shoe hovers with breathing motion as the rim light builds; [1.5s-2.5s] The first half of the copy fades into the left negative space; [2.5s-3.2s] The second half fades in while the first half shifts gently left within the same line; [3.2s-5s] The copy locks, the shoe settles, the frame holds. Text spelled out verbatim: \"Darkness in Motion\", SF Pro Display Semibold, one line, no wrapping, vertically centered left-aligned; \"Darkness in\" in white fading in at 1.5s, \"Motion\" in cold steel gray at 2.5s. Camera: one slow push-in, no cuts. Audio: the drone peaks, one crisp pluck at 1.5s; at 4.5s everything stops leaving the pluck tail.\n\n[Edit & finishing]\nThree 5-second I2V segments, two hard cuts aligned to the kick ending Shot 1 and the pluck opening Shot 3; one unified cool dark LUT across the film, an optional light grain overlay to hide batch differences. The product stays fully black throughout; only the lighting and the shoe's motion change. Copy rhythm: Shot 1 no text, Shot 2 background defocused type only, Shot 3 the main copy; at most one readable text layer on screen at any moment.\n\n[Negative list (all three segments)]\nno product color change, no extra light sources, no readable text besides the Shot 3 copy line, background text in Shot 2 must never overlap or occlude the shoe, no two text rows, no cuts within any single segment, no mirror floor reflections, no dissolve transitions, no logo."
},
"_meta": {
"title": "Text (Prompt)"
}
},
"18": {
"class_type": "PrimitiveBoolean",
"inputs": {
"value": False
},
"_meta": {
"title": "Boolean (Prompt Enhance)"
}
},
"19": {
"class_type": "PrimitiveBoolean",
"inputs": {
"value": False
},
"_meta": {
"title": "Boolean (Upscale to 2K)"
}
},
"24": {
"class_type": "MinimaxHailuo03FirstLastFrameNode",
"inputs": {
"first_frame": [
"25",
0
],
"model": "MiniMax H3 Max",
"model.duration": 5,
"model.prompt": [
"12",
0
],
"model.prompt_expansion_mode": "balanced",
"model.resolution": "768P",
"seed": 791669044,
"watermark": False
},
"_meta": {
"title": "MiniMax H3 First-Last-Frame to Video"
}
},
"25": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "Load Image"
}
}
})
# Replace each example.com URL with a public URL of your file.
workflow.set_input("25", "image", client.assets.from_url("https://example.com/replace-with-a-url/image"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)