Add BGM helper for H3 prompt curator
This commit is contained in:
@@ -57,11 +57,11 @@
|
||||
- Builds one H3 prompt block per beat, with quick controls for per-shot timing, continuity, ref behavior, anchor additions, soundscape, and music while staying compatible with direct text editing.
|
||||
|
||||
- `Dumas H3 Prompt Curator`
|
||||
- Inputs: `action_prompt`, `anatomy_guard`, `subject_count_guard`, optional `anchor`, optional `soundscape`, optional `ref_1` through `ref_9`
|
||||
- Inputs: `action_prompt`, `anatomy_guard`, `subject_count_guard`, optional `anchor`, optional `soundscape`, optional `bgm`, optional `ref_1` through `ref_9`
|
||||
- Outputs: `prompt`, `ref_image_1` through `ref_image_9`, `reference_count`, `debug`
|
||||
- Builds one standalone MiniMax H3 prompt from your final action text plus structured character/location references.
|
||||
- The action text can mention references by character/location name, alias, `<Picture N>`, or `<refN>`. Only mentioned references are emitted, and the output images are compacted/renumbered so skipped inputs do not leave gaps.
|
||||
- Adds curated reference context, anatomy guard text, optional subject-count guard text, anchor/style text, and `overall_soundscape:` text while respecting MiniMax H3's reference-generation shape: one prompt plus up to nine reference images.
|
||||
- Adds curated reference context, anatomy guard text, optional subject-count guard text, anchor/style text, `overall_soundscape:` text, and `background_music:` text while respecting MiniMax H3's reference-generation shape: one prompt plus up to nine reference images.
|
||||
|
||||
- `Dumas H3 Shot Length`
|
||||
- Inputs: `shot_seconds`, `fps`, optional `cap_to_h3_max`
|
||||
@@ -107,6 +107,11 @@
|
||||
- Output: `soundscape`
|
||||
- Matching soundscape helper for standalone H3 prompts. Pick a preset such as quiet interior, rainy street, cafe, city night, forest, industrial, or silent, then edit the text that flows into `Dumas H3 Prompt Curator`.
|
||||
|
||||
- `Dumas Background Music Helper`
|
||||
- Inputs: `bgm`, `bgm_description`
|
||||
- Output: `bgm`
|
||||
- Matching BGM helper for standalone H3 prompts. Pick a preset such as subtle tension, cinematic suspense, emotional piano, dark ambient, hopeful orchestral, retro synth, action pulse, lo-fi, or no vocals, then edit the text that flows into `Dumas H3 Prompt Curator`.
|
||||
|
||||
- `Dumas JSON String to Object`
|
||||
- Input: `json_string`
|
||||
- Output: parsed `JSON`
|
||||
@@ -263,7 +268,7 @@ decr -> use index - 1
|
||||
|
||||
`Dumas H3 Plan Attach Scene Images` and `Dumas H3 Plan Extract Scene Images` are a companion pair for `ComfyUI-MiniMaxH3-Contex-Loop` and the local `ref2v` lane. The upstream H3 plan node cannot dynamically grow nine new image sockets for every JSON-defined scene, so Dumas stores scene image bindings beside the plan using a lightweight token and an in-memory registry. That keeps `plan.json` archiving intact while still letting you wire up nine IMAGE sockets per scene through chained helper nodes.
|
||||
|
||||
`Dumas Character Helper` is the restored two-image/text helper for general H3 workflows, and `Dumas Location Helper` mirrors it for scene/environment references. Both helpers also emit structured `REFERENCE` sockets for the curator. The structured `Dumas Character Reference` and `Dumas Location Reference` nodes remain available separately for workflows that want a single `REFERENCE` socket. `Dumas H3 Prompt Curator` consumes those structured references, assigns the final `<Picture N>` numbering, and outputs only the compacted images the prompt actually mentions.
|
||||
`Dumas Character Helper` is the restored two-image/text helper for general H3 workflows, and `Dumas Location Helper` mirrors it for scene/environment references. Both helpers also emit structured `REFERENCE` sockets for the curator. The structured `Dumas Character Reference` and `Dumas Location Reference` nodes remain available separately for workflows that want a single `REFERENCE` socket. `Dumas H3 Prompt Curator` consumes those structured references plus optional anchor, soundscape, and BGM strings, assigns the final `<Picture N>` numbering, and outputs only the compacted images the prompt actually mentions.
|
||||
|
||||
`Dumas Strip Iteration Suffix` keeps the part before the first underscore and drops the rest. Names like `char123_pose_final.png` become `char123.png`, while names with no underscore such as `char123.png` are left untouched.
|
||||
|
||||
|
||||
+110
-1
@@ -398,6 +398,51 @@ _SOUNDSCAPE_PRESETS = OrderedDict(
|
||||
("custom", ""),
|
||||
]
|
||||
)
|
||||
_BGM_PRESETS = OrderedDict(
|
||||
[
|
||||
(
|
||||
"none",
|
||||
"",
|
||||
),
|
||||
(
|
||||
"subtle tension",
|
||||
"low, restrained tension bed with sparse pulses and no vocals",
|
||||
),
|
||||
(
|
||||
"cinematic suspense",
|
||||
"cinematic suspense score with muted strings, low drones, and controlled rising pressure",
|
||||
),
|
||||
(
|
||||
"emotional piano",
|
||||
"soft emotional piano underscoring with gentle space and no vocals",
|
||||
),
|
||||
(
|
||||
"dark ambient",
|
||||
"dark ambient music bed with deep drones, distant texture, and slow unease",
|
||||
),
|
||||
(
|
||||
"hopeful orchestral",
|
||||
"hopeful orchestral underscore with warm strings, gentle brass, and restrained lift",
|
||||
),
|
||||
(
|
||||
"retro synth",
|
||||
"retro synth score with analog pulses, warm pads, and steady momentum",
|
||||
),
|
||||
(
|
||||
"action pulse",
|
||||
"driving action pulse with percussion, rhythmic bass, and urgent forward motion",
|
||||
),
|
||||
(
|
||||
"lo-fi",
|
||||
"soft lo-fi instrumental bed with mellow rhythm and warm tape texture",
|
||||
),
|
||||
(
|
||||
"no vocals",
|
||||
"instrumental background music only, no singing, no lyrics, no vocal hooks",
|
||||
),
|
||||
("custom", ""),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _soundscape_options():
|
||||
@@ -406,6 +451,14 @@ def _soundscape_options():
|
||||
|
||||
def _soundscape_description(soundscape_name):
|
||||
return _SOUNDSCAPE_PRESETS.get(soundscape_name, "")
|
||||
|
||||
|
||||
def _bgm_options():
|
||||
return list(_BGM_PRESETS.keys())
|
||||
|
||||
|
||||
def _bgm_description(bgm_name):
|
||||
return _BGM_PRESETS.get(bgm_name, "")
|
||||
_LOAD_IMAGES_FOLDER_DEFAULT_STATE = {
|
||||
"version": 1,
|
||||
"folder": "",
|
||||
@@ -1187,6 +1240,7 @@ def curate_h3_prompt(
|
||||
action_prompt,
|
||||
anchor="",
|
||||
soundscape="",
|
||||
bgm="",
|
||||
refs=(),
|
||||
anatomy_guard="auto",
|
||||
subject_count_guard="auto",
|
||||
@@ -1209,6 +1263,7 @@ def curate_h3_prompt(
|
||||
):
|
||||
_append_prompt_section(prompt_parts, "Subject count guard", _subject_count_guard_text(selected))
|
||||
_append_prompt_section(prompt_parts, "overall_soundscape", soundscape)
|
||||
_append_prompt_section(prompt_parts, "background_music", bgm)
|
||||
|
||||
prompt = "\n\n".join(prompt_parts).strip()
|
||||
if len(prompt) > _H3_PROMPT_MAX_CHARS:
|
||||
@@ -2561,10 +2616,53 @@ class DumasSoundscapeHelperNode:
|
||||
return (text,)
|
||||
|
||||
|
||||
class DumasBackgroundMusicHelperNode:
|
||||
DESCRIPTION = (
|
||||
"Choose a background music preset, auto-fill its editable description, "
|
||||
"and pass the final BGM text downstream for MiniMax H3 prompts."
|
||||
)
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("bgm",)
|
||||
FUNCTION = "build_bgm"
|
||||
CATEGORY = "Dumas/MiniMax"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
default_bgm = "none"
|
||||
return {
|
||||
"required": {
|
||||
"bgm": (
|
||||
_bgm_options(),
|
||||
{
|
||||
"default": default_bgm,
|
||||
"tooltip": "Preset title used to seed the editable background music description.",
|
||||
},
|
||||
),
|
||||
"bgm_description": (
|
||||
"STRING",
|
||||
{
|
||||
"default": _bgm_description(default_bgm),
|
||||
"multiline": True,
|
||||
"tooltip": (
|
||||
"Editable background music description. Whatever text is here "
|
||||
"is what the node outputs to the bgm socket."
|
||||
),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
def build_bgm(self, bgm, bgm_description):
|
||||
text = str(bgm_description or "").strip()
|
||||
if not text:
|
||||
text = _bgm_description(bgm)
|
||||
return (text,)
|
||||
|
||||
|
||||
class DumasH3PromptCuratorNode:
|
||||
DESCRIPTION = (
|
||||
"Curate one MiniMax H3 prompt from an action textbox, anchor text, "
|
||||
"soundscape text, and up to nine structured references. References are "
|
||||
"soundscape/BGM text, and up to nine structured references. References are "
|
||||
"compacted so only mentioned names, aliases, or explicit <Picture N>/<refN> "
|
||||
"tags are sent onward."
|
||||
)
|
||||
@@ -2603,6 +2701,13 @@ class DumasH3PromptCuratorNode:
|
||||
"tooltip": "Optional soundscape text, usually from Dumas Soundscape Helper.",
|
||||
},
|
||||
),
|
||||
"bgm": (
|
||||
"STRING",
|
||||
{
|
||||
"forceInput": True,
|
||||
"tooltip": "Optional background music text, usually from Dumas Background Music Helper.",
|
||||
},
|
||||
),
|
||||
}
|
||||
for slot in range(1, _H3_PROMPT_REF_SLOTS + 1):
|
||||
optional[f"ref_{slot}"] = (
|
||||
@@ -2658,6 +2763,7 @@ class DumasH3PromptCuratorNode:
|
||||
subject_count_guard,
|
||||
anchor="",
|
||||
soundscape="",
|
||||
bgm="",
|
||||
ref_1=None,
|
||||
ref_2=None,
|
||||
ref_3=None,
|
||||
@@ -2672,6 +2778,7 @@ class DumasH3PromptCuratorNode:
|
||||
action_prompt,
|
||||
anchor=anchor,
|
||||
soundscape=soundscape,
|
||||
bgm=bgm,
|
||||
refs=(ref_1, ref_2, ref_3, ref_4, ref_5, ref_6, ref_7, ref_8, ref_9),
|
||||
anatomy_guard=anatomy_guard,
|
||||
subject_count_guard=subject_count_guard,
|
||||
@@ -2730,6 +2837,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"DumasCharacterReference": DumasCharacterReferenceNode,
|
||||
"DumasLocationReference": DumasLocationReferenceNode,
|
||||
"DumasSoundscapeHelper": DumasSoundscapeHelperNode,
|
||||
"DumasBackgroundMusicHelper": DumasBackgroundMusicHelperNode,
|
||||
"DumasH3PromptCurator": DumasH3PromptCuratorNode,
|
||||
"DumasAnchorStyle": DumasAnchorStyleNode,
|
||||
"DumasCharacterHelper": DumasCharacterHelperNode,
|
||||
@@ -2746,6 +2854,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DumasCharacterReference": "Dumas Character Reference",
|
||||
"DumasLocationReference": "Dumas Location Reference",
|
||||
"DumasSoundscapeHelper": "Dumas Soundscape Helper",
|
||||
"DumasBackgroundMusicHelper": "Dumas Background Music Helper",
|
||||
"DumasH3PromptCurator": "Dumas H3 Prompt Curator",
|
||||
"DumasAnchorStyle": "Dumas Anchor Style",
|
||||
"DumasCharacterHelper": "Dumas Character Helper",
|
||||
|
||||
@@ -440,6 +440,13 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(result[0], "steady rain, wet pavement, distant traffic hum")
|
||||
|
||||
def test_background_music_helper_defaults_to_selected_preset_description(self):
|
||||
node = self.image_nodes.DumasBackgroundMusicHelperNode()
|
||||
|
||||
result = node.build_bgm("subtle tension", "")
|
||||
|
||||
self.assertEqual(result[0], "low, restrained tension bed with sparse pulses and no vocals")
|
||||
|
||||
def test_h3_prompt_curator_compacts_named_references(self):
|
||||
node = self.image_nodes.DumasH3PromptCuratorNode()
|
||||
dave_image = FakeTensorBatch()
|
||||
@@ -472,6 +479,7 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
subject_count_guard="auto",
|
||||
anchor="grounded handheld thriller",
|
||||
soundscape="steady rain",
|
||||
bgm="low suspense music",
|
||||
ref_1=dave,
|
||||
ref_2=van,
|
||||
ref_3=cafe,
|
||||
@@ -483,6 +491,8 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
self.assertIn("Action: Dave runs from the Coffee Shop into the rain.", prompt)
|
||||
self.assertIn("Anatomy guard:", prompt)
|
||||
self.assertIn("Subject count guard:", prompt)
|
||||
self.assertIn("overall_soundscape: steady rain", prompt)
|
||||
self.assertIn("background_music: low suspense music", prompt)
|
||||
self.assertIn("exactly one named character: <Picture 1> Dave", prompt)
|
||||
self.assertIs(result[1], dave_image)
|
||||
self.assertIs(result[2], cafe_image)
|
||||
@@ -616,10 +626,12 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
self.assertIs(mappings["DumasCharacterHelper"], self.image_nodes.DumasCharacterHelperNode)
|
||||
self.assertIs(mappings["DumasLocationHelper"], self.image_nodes.DumasLocationHelperNode)
|
||||
self.assertIs(mappings["DumasSoundscapeHelper"], self.image_nodes.DumasSoundscapeHelperNode)
|
||||
self.assertIs(mappings["DumasBackgroundMusicHelper"], self.image_nodes.DumasBackgroundMusicHelperNode)
|
||||
self.assertIs(mappings["DumasH3PromptCurator"], self.image_nodes.DumasH3PromptCuratorNode)
|
||||
self.assertEqual(display["DumasCharacterHelper"], "Dumas Character Helper")
|
||||
self.assertEqual(display["DumasLocationHelper"], "Dumas Location Helper")
|
||||
self.assertEqual(display["DumasSoundscapeHelper"], "Dumas Soundscape Helper")
|
||||
self.assertEqual(display["DumasBackgroundMusicHelper"], "Dumas Background Music Helper")
|
||||
self.assertEqual(display["DumasH3PromptCurator"], "Dumas H3 Prompt Curator")
|
||||
|
||||
def test_h3_prompt_curator_uses_documented_reference_limits(self):
|
||||
|
||||
Reference in New Issue
Block a user