From 1214029f1c8024ab474acf8d5ead4e276b44496d Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Sun, 7 Jun 2026 13:24:38 +0300 Subject: [PATCH] Movie Editor: scene/timeline image counts as an IMAGE source A node's IMAGE input (e.g. ImageTransform) reported 'no node outputs IMAGE' because the scene image comes from the timeline (injected as a LoadImage), not a node output. Now the builder always materialises the scene image as media_load and registers it as an IMAGE producer, so unwired IMAGE inputs auto-wire to the scene image. The Models modal no longer flags required IMAGE inputs (the timeline can always feed them). Co-Authored-By: Claude Opus 4.8 --- movie_editor/backend/builder.py | 11 +++++++++-- movie_editor/frontend/models.js | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/movie_editor/backend/builder.py b/movie_editor/backend/builder.py index fe378bf6..fc2e33b7 100644 --- a/movie_editor/backend/builder.py +++ b/movie_editor/backend/builder.py @@ -312,9 +312,11 @@ def build(object_info: dict, models_config: dict, params: dict, media: dict | No # scene.source.target provides a legacy explicit target; "timeline" input_sources # (step 3b) may have already created media_load — this step adds any extra targets. if media and media.get("filename"): + # Always materialise the scene image as a LoadImage so it can feed IMAGE inputs + # (it's the timeline's image — there's no node that "outputs" it otherwise). + graph.setdefault("media_load", { + "class_type": "LoadImage", "inputs": {"image": media["filename"]}}) if media.get("target"): - graph.setdefault("media_load", { - "class_type": "LoadImage", "inputs": {"image": media["filename"]}}) dst = _resolve_target(media["target"], port_to_core, slot_node_id) if dst and dst[0] in graph: graph[dst[0]]["inputs"][dst[1]] = ["media_load", 0] @@ -361,6 +363,11 @@ def build(object_info: dict, models_config: dict, params: dict, media: dict | No # 5. auto-wire remaining unbound typed inputs by unique producer. producers = _producers(graph, slots, slot_node_id, slot_def, object_info) + # The timeline scene image (LoadImage) is an IMAGE producer too, so a node's IMAGE + # input (e.g. an ImageTransform) auto-wires to the scene image instead of reporting + # 'no node outputs IMAGE'. + if "media_load" in graph: + producers.setdefault("IMAGE", []).append(("media_load", 0)) # A slot only matters if it's wired into the pipeline (an output wire, or referenced # as a source by a core override). Inert/unused slots are NOT validated or auto-wired, # so an unused node's required input never blocks generation. diff --git a/movie_editor/frontend/models.js b/movie_editor/frontend/models.js index 960daaee..d647d5e2 100644 --- a/movie_editor/frontend/models.js +++ b/movie_editor/frontend/models.js @@ -159,6 +159,7 @@ // that feeds nothing must not be flagged or block generation. if (slotIsActive(slot)) (spec.connection_inputs || []).forEach((ci) => { if (!ci.required) return; + if (ci.type === "IMAGE") return; // a scene/timeline image can always feed an IMAGE input const src = (slot.input_sources || {})[ci.name]; if (src && src !== "auto") return; // explicitly sourced (incl. "timeline") const incoming = (config.slots || []).some((s2) =>