134 lines
3.5 KiB
Python
134 lines
3.5 KiB
Python
import json
|
|
|
|
|
|
_DEFAULT_BEAT = "Describe this beat."
|
|
_DEFAULT_STATE = {"scene": "", "character_sheet": "", "beats": [{"text": _DEFAULT_BEAT}]}
|
|
_LEGACY_DIRECTIVE_PREFIXES = (
|
|
"seconds",
|
|
"duration",
|
|
"continuity",
|
|
"ref_mode",
|
|
"ref_noise_aug",
|
|
"anchor_add",
|
|
"overall_soundscape",
|
|
"soundscape",
|
|
"non_diegetic_music",
|
|
"music",
|
|
"wardrobe",
|
|
"enter",
|
|
"exit",
|
|
)
|
|
|
|
|
|
def _clone_default_state():
|
|
return {
|
|
"scene": "",
|
|
"character_sheet": "",
|
|
"beats": [{"text": _DEFAULT_BEAT}],
|
|
}
|
|
|
|
|
|
def _strip_legacy_directives(text):
|
|
"""Remove directives from the abandoned Dumas Long Videos fork.
|
|
|
|
The upstream Long Videos node sends unknown field labels to the model as text,
|
|
so this builder strips the old managed controls rather than emitting prompts
|
|
that ask H3 to draw labels such as "seconds:" or "music:" in the frame.
|
|
"""
|
|
kept = []
|
|
for line in str(text or "").splitlines():
|
|
lowered = line.strip().lower()
|
|
if any(lowered.startswith(f"{name}:") for name in _LEGACY_DIRECTIVE_PREFIXES):
|
|
continue
|
|
kept.append(line)
|
|
return "\n".join(kept).strip()
|
|
|
|
|
|
def _parse_beat_prompt_state(value):
|
|
if isinstance(value, dict):
|
|
raw = value
|
|
else:
|
|
text = str(value or "").strip()
|
|
if not text:
|
|
return _clone_default_state()
|
|
try:
|
|
raw = json.loads(text)
|
|
except Exception:
|
|
return _clone_default_state()
|
|
|
|
scene = str(raw.get("scene") or "")
|
|
character_sheet = str(raw.get("character_sheet") or "")
|
|
beats = []
|
|
for item in list(raw.get("beats") or []):
|
|
if isinstance(item, dict):
|
|
text = str(item.get("text") or "")
|
|
else:
|
|
text = str(item or "")
|
|
beats.append({"text": text})
|
|
|
|
if not beats:
|
|
beats = [{"text": _DEFAULT_BEAT}]
|
|
return {
|
|
"scene": scene,
|
|
"character_sheet": character_sheet,
|
|
"beats": beats,
|
|
}
|
|
|
|
|
|
def _assemble_beat_prompt(state):
|
|
parsed = _parse_beat_prompt_state(state)
|
|
chunks = []
|
|
scene = str(parsed.get("scene") or "").strip()
|
|
if scene:
|
|
chunks.append(scene)
|
|
character_sheet = str(parsed.get("character_sheet") or "").strip()
|
|
if character_sheet:
|
|
chunks.append(character_sheet)
|
|
for beat in parsed["beats"]:
|
|
text = _strip_legacy_directives(beat.get("text") or "")
|
|
if text:
|
|
chunks.append(text)
|
|
return "\n\n".join(chunks)
|
|
|
|
|
|
class DumasH3BeatPromptNode:
|
|
DESCRIPTION = (
|
|
"Build an upstream MiniMax H3 Long Videos prompt: optional scene paragraph, "
|
|
"optional character sheet, then one blank-line-separated textbox per beat. "
|
|
"Per-beat helpers only emit directives the upstream node understands."
|
|
)
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("prompt",)
|
|
FUNCTION = "build_prompt"
|
|
CATEGORY = "Dumas/MiniMax"
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {},
|
|
"hidden": {
|
|
"H3BeatPromptState": ("STRING", {"default": json.dumps(_DEFAULT_STATE)}),
|
|
},
|
|
}
|
|
|
|
def build_prompt(self, H3BeatPromptState=""):
|
|
return (_assemble_beat_prompt(H3BeatPromptState),)
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"DumasH3BeatPrompt": DumasH3BeatPromptNode,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"DumasH3BeatPrompt": "Dumas H3 Beat Prompt",
|
|
}
|
|
|
|
|
|
__all__ = [
|
|
"DumasH3BeatPromptNode",
|
|
"NODE_CLASS_MAPPINGS",
|
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
|
"_assemble_beat_prompt",
|
|
"_parse_beat_prompt_state",
|
|
]
|