From 6684c5f2e0f7a07cdd4434f5f5cd362a9a0dd44b Mon Sep 17 00:00:00 2001 From: Chris Dumas Date: Wed, 2 Sep 2026 13:06:25 +0000 Subject: [PATCH] Remove legacy H3 ref image aliases --- H3_LONG_VIDEOS_GUIDE.md | 6 -- dumas_h3_longvideos.py | 91 ++++++++++++------------------- tests/test_dumas_h3_longvideos.py | 14 ++--- 3 files changed, 42 insertions(+), 69 deletions(-) diff --git a/H3_LONG_VIDEOS_GUIDE.md b/H3_LONG_VIDEOS_GUIDE.md index 80b78d2..46e8c7a 100644 --- a/H3_LONG_VIDEOS_GUIDE.md +++ b/H3_LONG_VIDEOS_GUIDE.md @@ -734,12 +734,6 @@ Each slot has a stable picture number: - `ref_2` = `` - and so on -### `ref_image_1` To `ref_image_9` - -Legacy compatibility aliases for older graphs. - -You should prefer `ref_1` to `ref_9` in new work. - ### `ref_mode` Controls which beats receive the reference images. diff --git a/dumas_h3_longvideos.py b/dumas_h3_longvideos.py index f4ae3c5..6a20804 100644 --- a/dumas_h3_longvideos.py +++ b/dumas_h3_longvideos.py @@ -5910,14 +5910,14 @@ class H3LongVideos: "first_frame": ("IMAGE", {"tooltip": "Optional opening keyframe for shot 1. Leave empty to " "start from pure text/reference conditioning. On later shots the node normally uses the " "previous shot's last frame automatically; this socket is only the initial handoff."}), - # ref2va inputs. SOCKET NUMBER matters: prompt tags refer to these - # exact slots, even when some intermediate sockets are left empty. - # A shot using only ref_image_7 is still tagged as in the - # prompt and renumbered only for the per-shot tokenizer payload. - # When a mode uses all connected refs, they keep socket order. - # The tokenizer labels the carried refs .. in - # the order they are handed that shot. - # Refer to socket tags in the prompt if you want a reference bound to + # ref2va inputs. SOCKET NUMBER matters: prompt tags refer to these + # exact slots, even when some intermediate sockets are left empty. + # A shot using only ref_7 is still tagged as in the + # prompt and renumbered only for the per-shot tokenizer payload. + # When a mode uses all connected refs, they keep socket order. + # The tokenizer labels the carried refs .. in + # the order they are handed that shot. + # Refer to socket tags in the prompt if you want a reference bound to # a named character ("Kristy, , walks in"). "ref_1": ("REFERENCE", {"tooltip": "Reference slot . Feed a Dumas Character " "Reference or Dumas Location Reference here. The image is used for real H3 visual " @@ -5927,24 +5927,14 @@ class H3LongVideos: "ref_3": ("REFERENCE", {"tooltip": "Reference slot ."}), "ref_4": ("REFERENCE", {"tooltip": "Reference slot ."}), "ref_5": ("REFERENCE", {"tooltip": "Reference slot ."}), - "ref_6": ("REFERENCE", {"tooltip": "Reference slot ."}), - "ref_7": ("REFERENCE", {"tooltip": "Reference slot ."}), - "ref_8": ("REFERENCE", {"tooltip": "Reference slot ."}), - "ref_9": ("REFERENCE", {"tooltip": "Reference slot ."}), - "ref_image_1": ("*", {"tooltip": "Legacy alias for ref_1. Accepts old IMAGE wiring or a " - "REFERENCE payload. Keep old graphs working without rewiring."}), - "ref_image_2": ("*", {"tooltip": "Legacy alias for ref_2. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_3": ("*", {"tooltip": "Legacy alias for ref_3. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_4": ("*", {"tooltip": "Legacy alias for ref_4. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_5": ("*", {"tooltip": "Legacy alias for ref_5. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_6": ("*", {"tooltip": "Legacy alias for ref_6. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_7": ("*", {"tooltip": "Legacy alias for ref_7. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_8": ("*", {"tooltip": "Legacy alias for ref_8. Accepts old IMAGE wiring or a REFERENCE payload."}), - "ref_image_9": ("*", {"tooltip": "Legacy alias for ref_9. Accepts old IMAGE wiring or a REFERENCE payload."}), - "plan_only": ("BOOLEAN", {"default": False, - "tooltip": "Preview the shot split and timing WITHOUT sampling pixels. Uses this " - "node's current settings and returns the plan in `info` almost instantly. " - "Good for checking beat counts, shot ceilings, ref placement and clamps " + "ref_6": ("REFERENCE", {"tooltip": "Reference slot ."}), + "ref_7": ("REFERENCE", {"tooltip": "Reference slot ."}), + "ref_8": ("REFERENCE", {"tooltip": "Reference slot ."}), + "ref_9": ("REFERENCE", {"tooltip": "Reference slot ."}), + "plan_only": ("BOOLEAN", {"default": False, + "tooltip": "Preview the shot split and timing WITHOUT sampling pixels. Uses this " + "node's current settings and returns the plan in `info` almost instantly. " + "Good for checking beat counts, shot ceilings, ref placement and clamps " "before a long render."}), "fps": ("INT", {"default": 24, "min": 1, "max": 60, "tooltip": "DISPLAY ONLY -- H3 always renders 24 fps. The model's frame grid and its " @@ -6478,18 +6468,15 @@ class H3LongVideos: mute_nonspeech_audio=True, mute_fade_ms=40, watermark_text="", watermark_position="bottom-right", watermark_size=4.0, watermark_opacity=0.75, watermark_margin=3.0, - intro_text="", intro_position="center", intro_seconds=3.0, intro_fade=0.6, - intro_size=9.0, overlay_font="arial.ttf", overlay_stroke=0, - ref_1=None, ref_2=None, ref_3=None, ref_4=None, - ref_5=None, ref_6=None, ref_7=None, ref_8=None, - ref_9=None, - ref_image_1=None, ref_image_2=None, ref_image_3=None, ref_image_4=None, - ref_image_5=None, ref_image_6=None, ref_image_7=None, ref_image_8=None, - ref_image_9=None, - ref_mode="auto ref2v", ref_image_size="match", ref_noise_aug=0.95, - detail_pass=False, detail_sampler_name="euler", detail_scheduler="beta", - detail_steps=8, detail_denoise=0.4, - graph=None, node_id=None): + intro_text="", intro_position="center", intro_seconds=3.0, intro_fade=0.6, + intro_size=9.0, overlay_font="arial.ttf", overlay_stroke=0, + ref_1=None, ref_2=None, ref_3=None, ref_4=None, + ref_5=None, ref_6=None, ref_7=None, ref_8=None, + ref_9=None, + ref_mode="auto ref2v", ref_image_size="match", ref_noise_aug=0.95, + detail_pass=False, detail_sampler_name="euler", detail_scheduler="beta", + detail_steps=8, detail_denoise=0.4, + graph=None, node_id=None): # FIRST: detect a checkpoint swap since the previous execution and hard-flush. # A stale resident model from a different checkpoint would otherwise poison @@ -6503,24 +6490,16 @@ class H3LongVideos: # H3 renders 24 fps, always. Honor the widget only as a warning: a lower value # used to silently shorten every shot (10s -> 124f -> 5.2s of real time). - fps_note = ("" if int(fps) == H3_FPS else - f"fps widget is {int(fps)} but H3 always renders {H3_FPS} fps -- all durations " - f"computed at {H3_FPS}; set your video-save node to {H3_FPS} too") - fps = H3_FPS - w, h = parse_resolution(resolution) - legacy_ref_slots = ( - ref_image_1, ref_image_2, ref_image_3, ref_image_4, ref_image_5, - ref_image_6, ref_image_7, ref_image_8, ref_image_9, - ) - direct_ref_slots = tuple( - current if current is not None else legacy - for current, legacy in zip(( - ref_1, ref_2, ref_3, ref_4, ref_5, - ref_6, ref_7, ref_8, ref_9, - ), legacy_ref_slots) - ) - ref_slots = direct_ref_slots - normalized_ref_slots = _normalized_ref_slots(ref_slots) + fps_note = ("" if int(fps) == H3_FPS else + f"fps widget is {int(fps)} but H3 always renders {H3_FPS} fps -- all durations " + f"computed at {H3_FPS}; set your video-save node to {H3_FPS} too") + fps = H3_FPS + w, h = parse_resolution(resolution) + ref_slots = tuple(( + ref_1, ref_2, ref_3, ref_4, ref_5, + ref_6, ref_7, ref_8, ref_9, + )) + normalized_ref_slots = _normalized_ref_slots(ref_slots) connected_refs = [ref for ref in normalized_ref_slots if _reference_image(ref) is not None] direct_ref_count = len(connected_refs) explicit_character_memory = (character_memory or "").strip() diff --git a/tests/test_dumas_h3_longvideos.py b/tests/test_dumas_h3_longvideos.py index 1fb9abe..7846b97 100644 --- a/tests/test_dumas_h3_longvideos.py +++ b/tests/test_dumas_h3_longvideos.py @@ -609,11 +609,11 @@ class DumasH3LongVideosHelperTests(unittest.TestCase): ref_positions = [names.index(f"ref_{index}") for index in range(1, 10)] self.assertEqual(ref_positions, list(range(ref_positions[0], ref_positions[0] + 9))) - def test_input_types_keep_legacy_ref_image_aliases(self): + def test_input_types_do_not_expose_legacy_ref_image_aliases(self): optional = self.module.H3LongVideos.INPUT_TYPES()["optional"] for index in range(1, 10): - self.assertIn(f"ref_image_{index}", optional) + self.assertNotIn(f"ref_image_{index}", optional) def test_shot_seconds_tooltip_describes_ceiling_behavior(self): optional = self.module.H3LongVideos.INPUT_TYPES()["optional"] @@ -708,7 +708,7 @@ class DumasH3LongVideosHelperTests(unittest.TestCase): self.assertEqual(context, "") - def test_run_uses_legacy_ref_image_inputs_when_new_slots_are_empty(self): + def test_run_uses_reference_slots_directly(self): calls = {} original_parse_resolution = self.module.parse_resolution original_connected_refs = self.module._connected_refs @@ -772,13 +772,13 @@ class DumasH3LongVideosHelperTests(unittest.TestCase): scheduler="simple", seed=1, plan_only=True, - ref_image_1={"image": "legacy-1"}, - ref_image_3={"image": "legacy-3"}, + ref_1={"image": "live-1"}, + ref_3={"image": "live-3"}, ) - self.assertEqual(calls["refs"][0]["image"], "legacy-1") + self.assertEqual(calls["refs"][0]["image"], "live-1") self.assertIsNone(calls["refs"][1]) - self.assertEqual(calls["refs"][2]["image"], "legacy-3") + self.assertEqual(calls["refs"][2]["image"], "live-3") self.assertEqual(result[2].count("ref2va: 2 reference image(s)"), 1) finally: self.module.parse_resolution = original_parse_resolution