diff --git a/movie_editor/backend/nodes.py b/movie_editor/backend/nodes.py index d51addf6..93d187e5 100644 --- a/movie_editor/backend/nodes.py +++ b/movie_editor/backend/nodes.py @@ -194,7 +194,9 @@ ROLES: dict[str, dict] = { # Each entry: id, type, label, required, role_hint (role key to suggest adding), hint. # MiniMax H3 replaces two of these: its own EmptyMiniMaxH3LatentAV emits the video AND # audio streams in one node, so there is no separate audio-latent step and the latent is -# no longer optional (nothing else produces one). The audio VAE stays required — it decodes +# no longer optional — Studio does not generate one for this family, so a node has to. It +# needn't be the Empty node: fl2va / ref2va build their own AV latent alongside their +# conditioning, and satisfy this the same way. The audio VAE stays required — it decodes # the generated audio — and gains a second, optional job on H3 (encoding audio references). FAMILY_REQUIREMENTS: dict[str, dict] = { "minimax_h3": { @@ -206,7 +208,9 @@ FAMILY_REQUIREMENTS: dict[str, dict] = { "clip": {"hint": "Add the MiniMax H3 text encoder (Qwen3-VL-32B, truncated to 50 layers)."}, "init_latent": {"required": True, "label": "AV latent", "hint": "Add Empty MiniMax H3 AV Latent — it makes the video and audio " - "streams together and feeds the Chain Sampler directly."}, + "streams together and feeds the Chain Sampler directly. A " + "MiniMax H3 Image to Video (fl2va) or Reference to Video " + "(ref2va) node covers this too: each builds its own AV latent."}, }, }, } diff --git a/movie_editor/frontend/models.js b/movie_editor/frontend/models.js index 26501de6..27c02951 100644 --- a/movie_editor/frontend/models.js +++ b/movie_editor/frontend/models.js @@ -1007,9 +1007,23 @@ }); } - // For VAE: need two separate producers (video_vae + audio_vae roles). - // For others: at least one producer of the type (regardless of role). + // Does this slot SOURCE the type — output it without consuming one? A loader or a + // generator does; a patcher that only passes the type through (a LoRA on MODEL/CLIP) + // does not, and can't satisfy a requirement on its own. + function slotSourcesType(type, slot) { + const spec = specFor(slot); + if (!(spec?.outputs || []).some((o) => o.type === type)) return false; + return !(spec?.connection_inputs || []).some((ci) => ci.type === type); + } + + // VAE is asked for twice — video and audio — and only the role tells the two apart, so + // those stay role-bound (two separate producers required). Every other requirement is + // about whether the pipeline can GET that type from somewhere, so any node that really + // sources it counts, whatever role the user filed it under: MiniMax H3's fl2va / ref2va + // nodes emit their own AV latent, which is exactly what an Empty AV Latent would provide. function requirementSatisfied(req) { + if (req.type === "VAE") return slotProducesType(req.type, req.role_hint); + if (config.slots.some((s) => slotSourcesType(req.type, s))) return true; if (req.role_hint) return slotProducesType(req.type, req.role_hint); return slotProducesType(req.type, null); }