Fix MiniMax R2V queue assets
This commit is contained in:
@@ -15,9 +15,11 @@ non-Heretic `AWQ` text-encoder path.
|
|||||||
## Workflow asset
|
## Workflow asset
|
||||||
|
|
||||||
- `workflow.json`
|
- `workflow.json`
|
||||||
|
- `api-prompt.json`
|
||||||
- `prompt-guide.md`
|
- `prompt-guide.md`
|
||||||
- `prompt-template.md`
|
- `prompt-template.md`
|
||||||
- `references/default-character-reference.jpg`
|
- `references/default-character-reference.jpg`
|
||||||
|
- `references/default-location-reference.jpg`
|
||||||
- `references/default-location-reference.url`
|
- `references/default-location-reference.url`
|
||||||
|
|
||||||
## This pass
|
## This pass
|
||||||
@@ -65,6 +67,16 @@ non-Heretic `AWQ` text-encoder path.
|
|||||||
- starter continuation plan defaults: `10s` clips, `8` plan steps
|
- starter continuation plan defaults: `10s` clips, `8` plan steps
|
||||||
- RTX upscale: `2x`, `ULTRA`
|
- RTX upscale: `2x`, `ULTRA`
|
||||||
|
|
||||||
|
## API submission
|
||||||
|
|
||||||
|
- `workflow.json` is the maintained front-end graph and still includes helper
|
||||||
|
nodes that ComfyUI rejects when you post the raw graph directly to `/prompt`.
|
||||||
|
- `api-prompt.json` is the server-safe prompt-style export for direct API
|
||||||
|
submission.
|
||||||
|
- `scripts/queue-minimax-r2v-duration-sweep.mjs` uploads the bundled scene +
|
||||||
|
character refs and queues the `2s/5s/8s/12s/15s/17s/20s` duration sweep with
|
||||||
|
first-pass steps set to `8` by default.
|
||||||
|
|
||||||
## When to use this
|
## When to use this
|
||||||
|
|
||||||
- use this first when the goal is: keep one or more reference images stable and
|
- use this first when the goal is: keep one or more reference images stable and
|
||||||
@@ -83,7 +95,9 @@ non-Heretic `AWQ` text-encoder path.
|
|||||||
starter built around the reusable prompt block and quality passes.
|
starter built around the reusable prompt block and quality passes.
|
||||||
- Use `prompt-guide.md` for the short authoring rules and `prompt-template.md`
|
- Use `prompt-guide.md` for the short authoring rules and `prompt-template.md`
|
||||||
for a copy-ready starter block that matches this maintained workflow.
|
for a copy-ready starter block that matches this maintained workflow.
|
||||||
- Default location reference URL for `<Picture 1>`:
|
- Default bundled `<Picture 1>` scene reference:
|
||||||
`https://www.juliettesinteriors.co.uk/wp-content/uploads/2020/03/luxury-designer-italian-button-upholstered-sofa-4.jpg`
|
`references/default-location-reference.jpg`
|
||||||
|
- Source URL preserved for the scene reference:
|
||||||
|
`references/default-location-reference.url`
|
||||||
- Default bundled Subject 1 character reference:
|
- Default bundled Subject 1 character reference:
|
||||||
`references/default-character-reference.jpg`
|
`references/default-character-reference.jpg`
|
||||||
|
|||||||
@@ -0,0 +1,908 @@
|
|||||||
|
{
|
||||||
|
"500": {
|
||||||
|
"class_type": "ModelPreviewOverrideKJ",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:498",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"max_resolution": 1024,
|
||||||
|
"jpeg_quality": 80,
|
||||||
|
"suppress_default_preview": true,
|
||||||
|
"preview_frames": 240,
|
||||||
|
"preview_fps": 9,
|
||||||
|
"tiny_vae": "taeh3.safetensors"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ModelPreviewOverrideKJ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"513": {
|
||||||
|
"class_type": "RTXVideoSuperResolution",
|
||||||
|
"inputs": {
|
||||||
|
"images": [
|
||||||
|
"571:522",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"resize_type": "scale by multiplier",
|
||||||
|
"quality": "HIGH",
|
||||||
|
"resize_type.scale": [
|
||||||
|
"723",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "RTXVideoSuperResolution"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"514": {
|
||||||
|
"class_type": "PixaromaResolution",
|
||||||
|
"inputs": {
|
||||||
|
"ResolutionState": "{\"mode\":\"custom\",\"ratio\":\"16:9\",\"w\":1088,\"h\":608,\"custom_w\":1088,\"custom_h\":608,\"custom_ratio_w\":17,\"custom_ratio_h\":9,\"snap\":16}"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "PixaromaResolution"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"539": {
|
||||||
|
"class_type": "RandomNoise",
|
||||||
|
"inputs": {
|
||||||
|
"noise_seed": 6406
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "RandomNoise"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"541": {
|
||||||
|
"class_type": "KSamplerSelect",
|
||||||
|
"inputs": {
|
||||||
|
"sampler_name": "euler_ancestral"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "KSamplerSelect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"548": {
|
||||||
|
"class_type": "PrimitiveBoolean",
|
||||||
|
"inputs": {
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Boolean 2 Pass"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"549": {
|
||||||
|
"class_type": "PrimitiveBoolean",
|
||||||
|
"inputs": {
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Boolean - Distilled"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"566": {
|
||||||
|
"class_type": "INTConstant",
|
||||||
|
"inputs": {
|
||||||
|
"value": 8
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Steps"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"572": {
|
||||||
|
"class_type": "LoadVideoUI",
|
||||||
|
"inputs": {
|
||||||
|
"video": "1.mp4",
|
||||||
|
"start_time": 0,
|
||||||
|
"end_time": 33.6,
|
||||||
|
"duration": 33.6,
|
||||||
|
"start_frame": 0,
|
||||||
|
"end_frame": 806,
|
||||||
|
"duration_frames": 806,
|
||||||
|
"resize_method": "maintain aspect ratio",
|
||||||
|
"custom_width": 0,
|
||||||
|
"custom_height": 0,
|
||||||
|
"frame_rate": 24,
|
||||||
|
"display_mode": "seconds",
|
||||||
|
"crop_x": 0,
|
||||||
|
"crop_y": 0,
|
||||||
|
"crop_w": 1,
|
||||||
|
"crop_h": 1
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadVideoUI"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"573": {
|
||||||
|
"class_type": "LoadAudioUI",
|
||||||
|
"inputs": {
|
||||||
|
"audio": "リアルもエーアイもこんなんばっか(ソウル).wav",
|
||||||
|
"start_time": 17,
|
||||||
|
"end_time": 35,
|
||||||
|
"duration": 18,
|
||||||
|
"audioUI": null
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadAudioUI"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"574": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "default-location-reference.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"575": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "default-character-reference.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"576": {
|
||||||
|
"class_type": "MiniMaxH3ReferenceToVideo",
|
||||||
|
"inputs": {
|
||||||
|
"clip": [
|
||||||
|
"680:487",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"680:488",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"audio_vae": [
|
||||||
|
"680:489",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"prompt": [
|
||||||
|
"591",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"width": 1088,
|
||||||
|
"height": 608,
|
||||||
|
"length": 243,
|
||||||
|
"ref_image_size": "max",
|
||||||
|
"ref_images.ref_image_0": [
|
||||||
|
"574",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"ref_images.ref_image_1": [
|
||||||
|
"575",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxH3ReferenceToVideo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"577": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "alig.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"578": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "alig.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"579": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "alig.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"580": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "alig.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"581": {
|
||||||
|
"class_type": "LoadImage",
|
||||||
|
"inputs": {
|
||||||
|
"image": "alig.jpg"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "LoadImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"591": {
|
||||||
|
"class_type": "StringConcatenate",
|
||||||
|
"inputs": {
|
||||||
|
"string_a": "<Picture 1> is the master scene, environment and composition reference. Preserve its futuristic apartment, panoramic city window, lighting direction, camera perspective, room geometry, built-in display wall, furniture placement and overall spacious near-future interior mood. Ignore the watermark in <Picture 1>.\n\n<Picture 2> is the primary identity reference for Subject 1. Use it to preserve the same woman, platinum-blonde tousled hair, blue eyes, fair skin, facial structure, makeup look and fitted silver-grey long-sleeve top. Ignore the beach background, magazine watermark, close-up framing and original environment from <Picture 2>.\n\nPlace Subject 1 inside the environment defined by <Picture 1>. Do not copy the background from <Picture 2>.",
|
||||||
|
"string_b": "A clean 10-second cinematic shot inside the luminous futuristic penthouse. Subject 1 stands near the panoramic window, turns toward camera, then walks slowly across the room with calm confidence before resting a hand on the back of the couch and looking out over the city. The camera makes a gentle floating lateral move with subtle handheld realism, soft lens breathing and controlled parallax. Keep the lighting cool and elegant from the city glow and interior practicals. Preserve the same identity, hair, face, outfit and proportions throughout. No dialogue, no text overlays, no extra people appearing in the room. Ambient sound is limited to soft room tone, distant city hush and subtle fabric movement.",
|
||||||
|
"delimiter": ""
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "StringConcatenate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"601": {
|
||||||
|
"class_type": "CreateVideo",
|
||||||
|
"inputs": {
|
||||||
|
"images": [
|
||||||
|
"718",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"fps": 24,
|
||||||
|
"audio": [
|
||||||
|
"571:519",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"bit_depth": 8
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "CreateVideo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"602": {
|
||||||
|
"class_type": "SaveVideo",
|
||||||
|
"inputs": {
|
||||||
|
"video": [
|
||||||
|
"601",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"filename_prefix": "MiniMax/v6-int8-ref2v-1088x608",
|
||||||
|
"format": "auto",
|
||||||
|
"codec": "auto"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SaveVideo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"703": {
|
||||||
|
"class_type": "KSamplerSelect",
|
||||||
|
"inputs": {
|
||||||
|
"sampler_name": "euler_ancestral_cfg_pp"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "KSamplerSelect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"714": {
|
||||||
|
"class_type": "Switch",
|
||||||
|
"inputs": {
|
||||||
|
"on_true": [
|
||||||
|
"703",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"541",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"switch": [
|
||||||
|
"715",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Switch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"715": {
|
||||||
|
"class_type": "PrimitiveBoolean",
|
||||||
|
"inputs": {
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "HIGH QUALITY"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"718": {
|
||||||
|
"class_type": "Switch",
|
||||||
|
"inputs": {
|
||||||
|
"on_true": [
|
||||||
|
"513",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"571:522",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"switch": [
|
||||||
|
"715",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Switch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"723": {
|
||||||
|
"class_type": "FloatConstant",
|
||||||
|
"inputs": {
|
||||||
|
"value": 1.5
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Upscale Multiplier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:492": {
|
||||||
|
"class_type": "DiffusionModelLoaderKJ",
|
||||||
|
"inputs": {
|
||||||
|
"model_name": "minimax_h3_ref2va_pruned_int8_convrot.safetensors",
|
||||||
|
"weight_dtype": "default",
|
||||||
|
"compute_dtype": "default",
|
||||||
|
"patch_cublaslinear": false,
|
||||||
|
"sage_attention": "auto",
|
||||||
|
"enable_fp16_accumulation": true
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "DiffusionModelLoaderKJ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:493": {
|
||||||
|
"class_type": "SolAttnPatch",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:492",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"tau": 1.3,
|
||||||
|
"start_percent": 0.2,
|
||||||
|
"end_percent": 0.9,
|
||||||
|
"min_tokens": 4096,
|
||||||
|
"int8_qk": true,
|
||||||
|
"sink_conditioning": "exact_kv_and_rows",
|
||||||
|
"morton": false,
|
||||||
|
"morton_curve": "2d_frame",
|
||||||
|
"int8_pv": true,
|
||||||
|
"verbose": false,
|
||||||
|
"use_tma": false,
|
||||||
|
"dense_blocks": ""
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SolAttnPatch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:494": {
|
||||||
|
"class_type": "ComfySwitchNode",
|
||||||
|
"inputs": {
|
||||||
|
"switch": true,
|
||||||
|
"on_false": [
|
||||||
|
"680:492",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_true": [
|
||||||
|
"680:493",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfySwitchNode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:491": {
|
||||||
|
"class_type": "MiniMaxH3SigmaShift",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:494",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"shift_video": 12,
|
||||||
|
"shift_audio": 4
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxH3SigmaShift"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:490": {
|
||||||
|
"class_type": "SpectrumApplyMiniMaxH3",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:491",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"enabled": false,
|
||||||
|
"blend_weight": 0.5,
|
||||||
|
"degree": 1,
|
||||||
|
"ridge_lambda": 0.1,
|
||||||
|
"window_size": 2,
|
||||||
|
"flex_window": 0.75,
|
||||||
|
"warmup_steps": 1,
|
||||||
|
"tail_actual_steps": 1,
|
||||||
|
"max_history": 8,
|
||||||
|
"debug": false,
|
||||||
|
"history_storage": "system_ram",
|
||||||
|
"bootstrap_first_forecast": true,
|
||||||
|
"anchor_residual_feedback": false,
|
||||||
|
"selective_rollback_correction": false,
|
||||||
|
"offline_smoothing_replay": true,
|
||||||
|
"audio_blend_weight": 0,
|
||||||
|
"offline_archive_storage": "system_ram",
|
||||||
|
"model_aware_mode": "off",
|
||||||
|
"model_aware_risk_threshold": 0.65,
|
||||||
|
"model_aware_trust_shrinkage": false,
|
||||||
|
"model_aware_replay_generic_correction": false,
|
||||||
|
"generic_correction_mode": "coordinate_rls",
|
||||||
|
"generic_correction_limiter": "hard_clip",
|
||||||
|
"generic_correction_limit": 0.4,
|
||||||
|
"generic_correction_attenuation": "no_attenuation"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SpectrumApplyMiniMaxH3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:704": {
|
||||||
|
"class_type": "MiniMaxH3TurboLoRA",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:490",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"lora_name": "minimax\\minimax_h3_ref2v_lightx2v_turbo_4step_v0.1_resized_avg_rank_20_bf16.safetensors",
|
||||||
|
"strength": 1,
|
||||||
|
"low_vram": false
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxH3TurboLoRA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:499": {
|
||||||
|
"class_type": "Power Lora Loader (rgthree)",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:704",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Power Lora Loader (rgthree)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:700": {
|
||||||
|
"class_type": "ModelAttentionBackend",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:499",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"attention": "comfy kitchen attention"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ModelAttentionBackend"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:497": {
|
||||||
|
"class_type": "MiniMaxLowVRAMAttention",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:700",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"head_chunks": 4
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxLowVRAMAttention"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:498": {
|
||||||
|
"class_type": "MiniMaxChunkFeedForward",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"680:497",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"chunks": 4,
|
||||||
|
"seq_threshold": 4096
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxChunkFeedForward"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:487": {
|
||||||
|
"class_type": "CLIPLoader",
|
||||||
|
"inputs": {
|
||||||
|
"clip_name": "qwen3vl_32b_heretic_minimax_h3_nvfp4.safetensors",
|
||||||
|
"type": "minimax",
|
||||||
|
"device": "default"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "CLIPLoader"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:488": {
|
||||||
|
"class_type": "VAELoader",
|
||||||
|
"inputs": {
|
||||||
|
"vae_name": "minimax_h3_video_vae_int8_convrot.safetensors"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "VAELoader"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"680:489": {
|
||||||
|
"class_type": "VAELoader",
|
||||||
|
"inputs": {
|
||||||
|
"vae_name": "minimax_h3_audio_vae_fp32.safetensors"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "VAELoader"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"550:537": {
|
||||||
|
"class_type": "easy int",
|
||||||
|
"inputs": {
|
||||||
|
"value": 10
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "Int - Duration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"550:538": {
|
||||||
|
"class_type": "ComfyMathExpression",
|
||||||
|
"inputs": {
|
||||||
|
"expression": "max(5, round(a * 24)) + (5 - (max(5, round(a * 24)) % 17)) % 17",
|
||||||
|
"values.a": [
|
||||||
|
"550:537",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfyMathExpression"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:525": {
|
||||||
|
"class_type": "BasicGuider",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"conditioning": [
|
||||||
|
"576",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "BasicGuider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:535": {
|
||||||
|
"class_type": "ConditioningZeroOut",
|
||||||
|
"inputs": {
|
||||||
|
"conditioning": [
|
||||||
|
"576",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ConditioningZeroOut"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:526": {
|
||||||
|
"class_type": "CFGGuider",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"positive": [
|
||||||
|
"576",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"571:535",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"cfg": 1
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "CFGGuider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:527": {
|
||||||
|
"class_type": "ComfySwitchNode",
|
||||||
|
"inputs": {
|
||||||
|
"switch": [
|
||||||
|
"549",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"571:525",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_true": [
|
||||||
|
"571:526",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfySwitchNode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:521": {
|
||||||
|
"class_type": "BasicScheduler",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"scheduler": "simple",
|
||||||
|
"steps": [
|
||||||
|
"566",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"denoise": 1
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "BasicScheduler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:523": {
|
||||||
|
"class_type": "ComfyMathExpression",
|
||||||
|
"inputs": {
|
||||||
|
"expression": "a / 2",
|
||||||
|
"values.a": [
|
||||||
|
"566",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfyMathExpression"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:530": {
|
||||||
|
"class_type": "SplitSigmas",
|
||||||
|
"inputs": {
|
||||||
|
"sigmas": [
|
||||||
|
"571:521",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"step": [
|
||||||
|
"571:523",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SplitSigmas"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:534": {
|
||||||
|
"class_type": "ComfySwitchNode",
|
||||||
|
"inputs": {
|
||||||
|
"switch": [
|
||||||
|
"548",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"571:521",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_true": [
|
||||||
|
"571:530",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfySwitchNode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:540": {
|
||||||
|
"class_type": "SamplerCustomAdvanced",
|
||||||
|
"inputs": {
|
||||||
|
"noise": [
|
||||||
|
"539",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"guider": [
|
||||||
|
"571:527",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sampler": [
|
||||||
|
"714",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sigmas": [
|
||||||
|
"571:534",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"latent_image": [
|
||||||
|
"576",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SamplerCustomAdvanced"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:520": {
|
||||||
|
"class_type": "DisableNoise",
|
||||||
|
"inputs": {},
|
||||||
|
"_meta": {
|
||||||
|
"title": "DisableNoise"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:531": {
|
||||||
|
"class_type": "MiniMaxH3LatentUpscaleCombined",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"571:540",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"scale_by": 1.5,
|
||||||
|
"method": "bilinear",
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"noise": [
|
||||||
|
"539",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sigmas": [
|
||||||
|
"571:530",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"audio_denoise": 0.35,
|
||||||
|
"positive": [
|
||||||
|
"576",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"571:535",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"learned_model": "h3_clean_latent_upscaler_film_epoch200.safetensors",
|
||||||
|
"noise_resample": "independent"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "MiniMaxH3LatentUpscaleCombined"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:533": {
|
||||||
|
"class_type": "BasicGuider",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"conditioning": [
|
||||||
|
"571:531",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "BasicGuider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:528": {
|
||||||
|
"class_type": "CFGGuider",
|
||||||
|
"inputs": {
|
||||||
|
"model": [
|
||||||
|
"500",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"positive": [
|
||||||
|
"571:531",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"571:531",
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"cfg": 1
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "CFGGuider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:529": {
|
||||||
|
"class_type": "ComfySwitchNode",
|
||||||
|
"inputs": {
|
||||||
|
"switch": [
|
||||||
|
"548",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"571:533",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_true": [
|
||||||
|
"571:528",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfySwitchNode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:532": {
|
||||||
|
"class_type": "SamplerCustomAdvanced",
|
||||||
|
"inputs": {
|
||||||
|
"noise": [
|
||||||
|
"571:520",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"guider": [
|
||||||
|
"571:529",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sampler": [
|
||||||
|
"714",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sigmas": [
|
||||||
|
"571:530",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"latent_image": [
|
||||||
|
"571:531",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "SamplerCustomAdvanced"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:536": {
|
||||||
|
"class_type": "ComfySwitchNode",
|
||||||
|
"inputs": {
|
||||||
|
"switch": [
|
||||||
|
"548",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_false": [
|
||||||
|
"571:540",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"on_true": [
|
||||||
|
"571:532",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "ComfySwitchNode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:519": {
|
||||||
|
"class_type": "VAEDecodeAudio",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"571:536",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"680:489",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "VAEDecodeAudio"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"571:522": {
|
||||||
|
"class_type": "VAEDecode",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"571:536",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"680:488",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"title": "VAEDecode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,11 @@ matching the local ComfyUI workflow that Chris is actually using.
|
|||||||
Bundled default Subject 1 character reference image:
|
Bundled default Subject 1 character reference image:
|
||||||
`references/default-character-reference.jpg`
|
`references/default-character-reference.jpg`
|
||||||
|
|
||||||
Default location reference for `<Picture 1>`:
|
Bundled default location reference for `<Picture 1>`:
|
||||||
`https://www.juliettesinteriors.co.uk/wp-content/uploads/2020/03/luxury-designer-italian-button-upholstered-sofa-4.jpg`
|
`references/default-location-reference.jpg`
|
||||||
|
|
||||||
|
Source URL for that location reference:
|
||||||
|
`references/default-location-reference.url`
|
||||||
|
|
||||||
## Official MiniMax doc takeaways
|
## Official MiniMax doc takeaways
|
||||||
|
|
||||||
@@ -42,11 +45,13 @@ Key points worth preserving:
|
|||||||
|
|
||||||
- Treat `<Picture 1>` as the environment / master-scene reference when an
|
- Treat `<Picture 1>` as the environment / master-scene reference when an
|
||||||
environment reference is used.
|
environment reference is used.
|
||||||
- Use the Juliette's Interiors sofa image above as the default `<Picture 1>`
|
- Use `references/default-location-reference.jpg` as the default `<Picture 1>`
|
||||||
location reference until a shot-specific environment image replaces it.
|
location reference until a shot-specific environment image replaces it.
|
||||||
- Treat `references/default-character-reference.jpg` as the default file to
|
- Treat `references/default-character-reference.jpg` as the default file to
|
||||||
load into `<Picture 2>` when you need a starter Subject 1 full-body
|
load into `<Picture 2>` when you need a starter Subject 1 full-body
|
||||||
reference and have not chosen a different one yet.
|
reference and have not chosen a different one yet. Duplicate that same file
|
||||||
|
into `<Picture 3>` when you want a face-reinforcement slot but only have the
|
||||||
|
single bundled starter identity image available.
|
||||||
- Group all images belonging to the same character explicitly.
|
- Group all images belonging to the same character explicitly.
|
||||||
- State what each reference controls and what it must not control.
|
- State what each reference controls and what it must not control.
|
||||||
- Ignore the backgrounds of character references unless a reference is
|
- Ignore the backgrounds of character references unless a reference is
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ Use this as a starter for the maintained `MiniMax H3` `r2v` master.
|
|||||||
Default bundled Subject 1 primary character reference:
|
Default bundled Subject 1 primary character reference:
|
||||||
`references/default-character-reference.jpg`
|
`references/default-character-reference.jpg`
|
||||||
|
|
||||||
Default location reference for `<Picture 1>`:
|
Default bundled location reference for `<Picture 1>`:
|
||||||
`https://www.juliettesinteriors.co.uk/wp-content/uploads/2020/03/luxury-designer-italian-button-upholstered-sofa-4.jpg`
|
`references/default-location-reference.jpg`
|
||||||
|
|
||||||
|
Source URL for the bundled location image:
|
||||||
|
`references/default-location-reference.url`
|
||||||
|
|
||||||
```text
|
```text
|
||||||
## Reference Assignment
|
## Reference Assignment
|
||||||
|
|||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 385 KiB After Width: | Height: | Size: 381 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -501,7 +501,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char256_Turnaround.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -540,7 +540,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char256_Headshots.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -2668,7 +2668,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Scn037.png",
|
"references/default-location-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -5597,7 +5597,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char093_Turnaround.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -5636,7 +5636,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char093_Headshots.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -5675,7 +5675,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char065_Turnaround.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -5714,7 +5714,7 @@
|
|||||||
"Node name for S&R": "LoadImage"
|
"Node name for S&R": "LoadImage"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"Char065_Headshots.png",
|
"references/default-character-reference.jpg",
|
||||||
"image"
|
"image"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const REPO_ROOT = path.resolve(__dirname, '..');
|
||||||
|
const WORKFLOW_ROOT = path.join(REPO_ROOT, 'optimized/image-to-video/minimax-h3-r2v-master');
|
||||||
|
const BASE_PROMPT = path.join(WORKFLOW_ROOT, 'api-prompt.json');
|
||||||
|
const DEFAULT_SCENE_REF = path.join(WORKFLOW_ROOT, 'references/default-location-reference.jpg');
|
||||||
|
const DEFAULT_CHARACTER_REF = path.join(WORKFLOW_ROOT, 'references/default-character-reference.jpg');
|
||||||
|
const DEFAULT_SERVER = process.env.COMFYUI_URL || 'http://192.168.1.202:8188';
|
||||||
|
const DEFAULT_OUT_ROOT = '/home/node/.openclaw/workspace/tmp/minimax-r2v-duration-sweep';
|
||||||
|
const DEFAULT_WIDTH = 1088;
|
||||||
|
const DEFAULT_HEIGHT = 608;
|
||||||
|
const DEFAULT_STEPS = 8;
|
||||||
|
const DEFAULT_DURATION_SECONDS = [2, 5, 8, 12, 15, 17, 20];
|
||||||
|
|
||||||
|
function printUsage() {
|
||||||
|
console.log(`Usage:
|
||||||
|
node scripts/queue-minimax-r2v-duration-sweep.mjs [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--server <url> ComfyUI base URL. Default: ${DEFAULT_SERVER}
|
||||||
|
--scene-image <path> Local scene reference image. Default: ${DEFAULT_SCENE_REF}
|
||||||
|
--character-image <path> Local character reference image. Default: ${DEFAULT_CHARACTER_REF}
|
||||||
|
--durations <csv> Comma-separated duration seconds. Default: ${DEFAULT_DURATION_SECONDS.join(',')}
|
||||||
|
--width <int> Output width. Default: ${DEFAULT_WIDTH}
|
||||||
|
--height <int> Output height. Default: ${DEFAULT_HEIGHT}
|
||||||
|
--steps <int> First-pass steps. Default: ${DEFAULT_STEPS}
|
||||||
|
--high-quality <bool> Enable HIGH QUALITY branch. Default: false
|
||||||
|
--prefix <text> Output filename prefix root. Default: MiniMax/r2v-duration-sweep
|
||||||
|
--out-dir <path> Run artifact directory. Default: ${DEFAULT_OUT_ROOT}/<timestamp>
|
||||||
|
--dry-run Resolve prompts locally but do not submit
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBoolean(raw) {
|
||||||
|
const value = String(raw).trim().toLowerCase();
|
||||||
|
if (['1', 'true', 'yes', 'on'].includes(value)) return true;
|
||||||
|
if (['0', 'false', 'no', 'off'].includes(value)) return false;
|
||||||
|
throw new Error(`Invalid boolean value: ${raw}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const options = {
|
||||||
|
server: DEFAULT_SERVER,
|
||||||
|
sceneImage: DEFAULT_SCENE_REF,
|
||||||
|
characterImage: DEFAULT_CHARACTER_REF,
|
||||||
|
durations: [...DEFAULT_DURATION_SECONDS],
|
||||||
|
width: DEFAULT_WIDTH,
|
||||||
|
height: DEFAULT_HEIGHT,
|
||||||
|
steps: DEFAULT_STEPS,
|
||||||
|
highQuality: false,
|
||||||
|
prefix: 'MiniMax/r2v-duration-sweep',
|
||||||
|
outDir: '',
|
||||||
|
dryRun: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < argv.length; i += 1) {
|
||||||
|
const arg = argv[i];
|
||||||
|
if (arg === '--help' || arg === '-h') {
|
||||||
|
printUsage();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
if (arg === '--dry-run') {
|
||||||
|
options.dryRun = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const next = argv[i + 1];
|
||||||
|
const take = () => {
|
||||||
|
if (next === undefined) throw new Error(`Missing value for ${arg}`);
|
||||||
|
i += 1;
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
switch (arg) {
|
||||||
|
case '--server': options.server = take(); break;
|
||||||
|
case '--scene-image': options.sceneImage = take(); break;
|
||||||
|
case '--character-image': options.characterImage = take(); break;
|
||||||
|
case '--durations':
|
||||||
|
options.durations = take().split(',').map((value) => Number(value.trim())).filter((value) => Number.isFinite(value) && value > 0);
|
||||||
|
break;
|
||||||
|
case '--width': options.width = Math.trunc(Number(take())); break;
|
||||||
|
case '--height': options.height = Math.trunc(Number(take())); break;
|
||||||
|
case '--steps': options.steps = Math.trunc(Number(take())); break;
|
||||||
|
case '--high-quality': options.highQuality = parseBoolean(take()); break;
|
||||||
|
case '--prefix': options.prefix = take(); break;
|
||||||
|
case '--out-dir': options.outDir = take(); break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.durations.length) throw new Error('At least one duration is required.');
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clone(value) {
|
||||||
|
return JSON.parse(JSON.stringify(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeJson(filePath, value) {
|
||||||
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||||
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondsToFrames(seconds) {
|
||||||
|
return Math.round(seconds * 24) + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrompt(basePrompt, variant, uploadedRefs) {
|
||||||
|
const prompt = clone(basePrompt);
|
||||||
|
const frameLength = secondsToFrames(variant.durationSeconds);
|
||||||
|
|
||||||
|
prompt['574'].inputs.image = uploadedRefs.scene;
|
||||||
|
prompt['575'].inputs.image = uploadedRefs.character;
|
||||||
|
prompt['576'].inputs.width = variant.width;
|
||||||
|
prompt['576'].inputs.height = variant.height;
|
||||||
|
prompt['576'].inputs.length = frameLength;
|
||||||
|
prompt['514'].inputs.ResolutionState = JSON.stringify({
|
||||||
|
mode: 'custom',
|
||||||
|
ratio: 'custom',
|
||||||
|
w: variant.width,
|
||||||
|
h: variant.height,
|
||||||
|
custom_w: variant.width,
|
||||||
|
custom_h: variant.height,
|
||||||
|
custom_ratio_w: variant.width,
|
||||||
|
custom_ratio_h: variant.height,
|
||||||
|
snap: 16,
|
||||||
|
});
|
||||||
|
prompt['550:537'].inputs.value = variant.durationSeconds;
|
||||||
|
prompt['566'].inputs.value = variant.steps;
|
||||||
|
prompt['715'].inputs.value = variant.highQuality;
|
||||||
|
prompt['539'].inputs.noise_seed = variant.seed;
|
||||||
|
prompt['591'].inputs.string_b = prompt['591'].inputs.string_b.replace(
|
||||||
|
/A clean 10-second cinematic shot/,
|
||||||
|
`A clean ${variant.durationSeconds}-second cinematic shot`,
|
||||||
|
);
|
||||||
|
prompt['602'].inputs.filename_prefix = `${variant.prefix}/${variant.slug}`;
|
||||||
|
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
function guessContentType(filePath) {
|
||||||
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg';
|
||||||
|
if (ext === '.webp') return 'image/webp';
|
||||||
|
return 'image/png';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadImage(server, filePath) {
|
||||||
|
const absPath = path.resolve(filePath);
|
||||||
|
const form = new FormData();
|
||||||
|
form.set('type', 'input');
|
||||||
|
form.set('overwrite', 'true');
|
||||||
|
form.set('image', new Blob([fs.readFileSync(absPath)], { type: guessContentType(absPath) }), path.basename(absPath));
|
||||||
|
|
||||||
|
const response = await fetch(`${server.replace(/\/$/, '')}/upload/image`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: form,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Failed to upload ${absPath} (${response.status}): ${text}`);
|
||||||
|
}
|
||||||
|
const payload = await response.json();
|
||||||
|
return String(payload?.name || path.basename(absPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitPrompt(server, prompt) {
|
||||||
|
const response = await fetch(`${server.replace(/\/$/, '')}/prompt`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
client_id: crypto.randomUUID(),
|
||||||
|
prompt,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Prompt submission failed (${response.status}): ${text}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const options = parseArgs(process.argv.slice(2));
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const outDir = path.resolve(options.outDir || path.join(DEFAULT_OUT_ROOT, timestamp));
|
||||||
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
|
|
||||||
|
const basePrompt = JSON.parse(fs.readFileSync(BASE_PROMPT, 'utf8'));
|
||||||
|
const uploadedRefs = {
|
||||||
|
scene: await uploadImage(options.server, options.sceneImage),
|
||||||
|
character: await uploadImage(options.server, options.characterImage),
|
||||||
|
};
|
||||||
|
|
||||||
|
const variants = options.durations.map((durationSeconds, index) => ({
|
||||||
|
slug: `${String(index + 1).padStart(2, '0')}-${durationSeconds}s`,
|
||||||
|
durationSeconds,
|
||||||
|
width: options.width,
|
||||||
|
height: options.height,
|
||||||
|
steps: options.steps,
|
||||||
|
highQuality: options.highQuality,
|
||||||
|
seed: 8200 + index,
|
||||||
|
prefix: options.prefix,
|
||||||
|
}));
|
||||||
|
|
||||||
|
writeJson(path.join(outDir, 'run.json'), {
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
server: options.server,
|
||||||
|
basePrompt: BASE_PROMPT,
|
||||||
|
uploadedRefs,
|
||||||
|
options: {
|
||||||
|
sceneImage: path.resolve(options.sceneImage),
|
||||||
|
characterImage: path.resolve(options.characterImage),
|
||||||
|
width: options.width,
|
||||||
|
height: options.height,
|
||||||
|
steps: options.steps,
|
||||||
|
highQuality: options.highQuality,
|
||||||
|
durations: options.durations,
|
||||||
|
prefix: options.prefix,
|
||||||
|
},
|
||||||
|
variants,
|
||||||
|
});
|
||||||
|
|
||||||
|
const summary = [];
|
||||||
|
for (const variant of variants) {
|
||||||
|
const prompt = buildPrompt(basePrompt, variant, uploadedRefs);
|
||||||
|
const variantDir = path.join(outDir, variant.slug);
|
||||||
|
fs.mkdirSync(variantDir, { recursive: true });
|
||||||
|
writeJson(path.join(variantDir, 'prompt.json'), prompt);
|
||||||
|
|
||||||
|
if (options.dryRun) {
|
||||||
|
summary.push({
|
||||||
|
slug: variant.slug,
|
||||||
|
durationSeconds: variant.durationSeconds,
|
||||||
|
frameLength: prompt['576'].inputs.length,
|
||||||
|
dryRun: true,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submission = await submitPrompt(options.server, prompt);
|
||||||
|
const promptId = String(submission?.prompt_id || '').trim();
|
||||||
|
if (!promptId) throw new Error(`Missing prompt_id for ${variant.slug}: ${JSON.stringify(submission)}`);
|
||||||
|
writeJson(path.join(variantDir, 'submission.json'), {
|
||||||
|
at: new Date().toISOString(),
|
||||||
|
promptId,
|
||||||
|
submission,
|
||||||
|
variant,
|
||||||
|
});
|
||||||
|
summary.push({
|
||||||
|
slug: variant.slug,
|
||||||
|
durationSeconds: variant.durationSeconds,
|
||||||
|
frameLength: prompt['576'].inputs.length,
|
||||||
|
promptId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJson(path.join(outDir, 'summary.json'), summary);
|
||||||
|
console.log(JSON.stringify({ outDir, uploadedRefs, summary }, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error(error?.stack || error?.message || String(error));
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user