Files
TuonoMindCode-ComfyUI-Multi…/sequence_prompt_nodes.py
T
2025-12-06 15:16:30 +01:00

318 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
# Separatorer för intern kodning i pair_data
CLIP_SEP = "\n<<<MULTICLIP_CLIP_SEP>>>\n"
META_SEP = "\n<<<MULTICLIP_META_SEP>>>\n"
class MultiClipTextScriptMain:
"""
Multi-Clip Text Script: Main
Tar text från t.ex. Ollama eller någon annan källa + konstant prefix/suffix:
prefix_text:
Beskrivning som alltid ska vara med (personen, scenen, stil, etc).
multi_clip_script (exempel):
(clip01)
woman waves at the camera
(clip02)
woman gets angry
(clip03)
woman calls someone
suffix_text:
Text som läggs till efter varje klipp (t.ex. kamera-stil, kvalitet, osv).
negative_text:
Negativ prompt som är gemensam för alla klipp.
Scriptet parsas i formatet:
(clip01)
<text>
(clip02)
<text>
...
Och omvandlas till ett internt paket (MULTICLIP_PAIR_DATA) där vi sparar:
- prefix
- suffix
- alla clip-texter i ordning
Sedan kan "Multi-Clip Text Script: Clip Selector" plocka ut
en clip-text i taget och bygga en färdig prompt.
Output:
pair_data (MULTICLIP_PAIR_DATA) – intern sträng med all data
num_clips (INT) – hur många klipp som hittades
multi_clip_negative (MULTICLIP_NEGATIVE) – negativ prompt för alla klipp
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# 1. Prefix först
"prefix_text": (
"STRING",
{
"multiline": True,
"default": "",
},
),
# 2. Huvudfönstret för (clip01)(clip02)...-scriptet
"multi_clip_script": (
"STRING",
{
"multiline": True,
"default": "(clip01)\nwoman walks forward and waves to the camera\n\n"
"(clip02)\nwoman walks forward and gets angry\n\n"
"(clip03)\nwoman throws the phone",
},
),
# 3. Suffix sist
"suffix_text": (
"STRING",
{
"multiline": True,
"default": "",
},
),
},
"optional": {
"negative_text": (
"STRING",
{
"multiline": True,
"default": "",
},
),
},
}
RETURN_TYPES = ("MULTICLIP_PAIR_DATA", "INT", "MULTICLIP_NEGATIVE")
RETURN_NAMES = ("pair_data", "num_clips", "multi_clip_negative")
FUNCTION = "parse_script"
CATEGORY = "Text / Multi-Clip"
def parse_script(
self,
prefix_text: str,
multi_clip_script: str,
suffix_text: str,
negative_text: str = "",
):
# Hitta alla (clipXX) + text fram till nästa (clipYY) eller slutet
pattern = r"\(clip(\d+)\)\s*(.*?)(?=\n\(clip\d+\)|\Z)"
matches = re.findall(pattern, multi_clip_script, flags=re.DOTALL | re.IGNORECASE)
clips = []
for idx, text in matches:
text = text.strip()
if text:
clips.append((int(idx), text)) # "01" -> 1 osv, för sortering
# Sortera efter clip-nummer
clips.sort(key=lambda x: x[0])
prefix_clean = (prefix_text or "").strip()
suffix_clean = (suffix_text or "").strip()
negative_clean = (negative_text or "").strip()
if not clips:
# Inga klipp hittade – koda ändå prefix/suffix men utan clips
payload = META_SEP.join([prefix_clean, suffix_clean, ""])
return (payload, 0, negative_clean)
# Själva klipp-texterna i ordning
clip_texts = [t for _, t in clips]
clips_blob = CLIP_SEP.join(clip_texts)
# Kodning i pair_data:
# prefix <<META_SEP>> suffix <<META_SEP>> clip1 <<CLIP_SEP>> clip2 <<CLIP_SEP>> ...
payload = META_SEP.join([prefix_clean, suffix_clean, clips_blob])
num_clips = len(clip_texts)
return (payload, num_clips, negative_clean)
class MultiClipTextScriptClipSelector:
"""
Multi-Clip Text Script: Clip Selector
Tar emot pair_data från MultiClipTextScriptMain (typ MULTICLIP_PAIR_DATA)
och väljer EN specifik klipp-text baserat på clip_number (1-baserat).
clip_number (1-baserat i UI):
1 -> första klipp-texten (clip01)
2 -> andra (clip02)
3 -> tredje (clip03)
osv.
auto_format:
- False: prefix + tomrad + clip + tomrad + suffix
- True: "prefix, clip, suffix." (klassisk prompt-stil)
+ rensar bort extra kommatecken i slutet av varje del.
negative:
- Input av typen MULTICLIP_NEGATIVE (ingen egen text-ruta i denna nod).
Typisk kedja:
Multi-Clip Text Script: Main (multi_clip_negative)
→ Anything Everywhere
→ Multi-Clip Text Script: Clip Selector (negative)
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"pair_data": ("MULTICLIP_PAIR_DATA", {}),
"clip_number": (
"INT",
{
"default": 1,
"min": 1,
"max": 999,
"step": 1,
},
),
},
"optional": {
"auto_format": (
"BOOLEAN",
{
"default": False,
},
),
"log_to_console": (
"BOOLEAN",
{
"default": False,
},
),
"negative": (
"MULTICLIP_NEGATIVE",
{},
),
},
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("clip_text", "negative_text")
FUNCTION = "select_pair"
CATEGORY = "Text / Multi-Clip"
def select_pair(
self,
pair_data: str,
clip_number: int,
auto_format: bool = False,
log_to_console: bool = False,
negative: str = "",
):
if not pair_data:
return ("", negative)
# Förväntat format:
# prefix <<META_SEP>> suffix <<META_SEP>> clips_blob
parts = pair_data.split(META_SEP)
if len(parts) == 3:
prefix, suffix, clips_blob = parts
elif len(parts) == 2:
prefix, clips_blob = parts
suffix = ""
else:
prefix = ""
suffix = ""
clips_blob = parts[0] if parts else ""
clips_blob = clips_blob or ""
clips = clips_blob.split(CLIP_SEP) if clips_blob else []
internal_index = clip_number - 1
if not clips:
combined = self._combine(prefix, "", suffix, auto_format)
if log_to_console and combined:
print("[Multi-Clip Selector] (no clips) →", combined)
return (combined, negative)
if internal_index < 0:
internal_index = 0
if internal_index >= len(clips):
internal_index = len(clips) - 1
clip_raw = clips[internal_index]
combined = self._combine(prefix, clip_raw, suffix, auto_format)
if log_to_console and combined:
print(f"[Multi-Clip Selector] clip_number={clip_number} →", combined)
return (combined, negative)
@staticmethod
def _clean_piece(piece: str) -> str:
if not piece:
return ""
p = piece.strip()
while p and p[-1] in [",", ";", " "]:
p = p[:-1]
return p.strip()
@staticmethod
def _combine(prefix: str, clip: str, suffix: str, auto_format: bool) -> str:
prefix = prefix or ""
clip = clip or ""
suffix = suffix or ""
if not auto_format:
parts = []
if prefix.strip():
parts.append(prefix.strip())
if clip.strip():
parts.append(clip.strip())
if suffix.strip():
parts.append(suffix.strip())
return "\n\n".join(parts)
prefix_clean = MultiClipTextScriptClipSelector._clean_piece(prefix)
clip_clean = MultiClipTextScriptClipSelector._clean_piece(clip)
suffix_clean = MultiClipTextScriptClipSelector._clean_piece(suffix)
parts = []
if prefix_clean:
parts.append(prefix_clean)
if clip_clean:
parts.append(clip_clean)
if suffix_clean:
parts.append(suffix_clean)
if not parts:
return ""
text = ", ".join(parts)
text = text.strip()
if text and text[-1] not in [".", "!", "?"]:
text += "."
return text
# Registrering för ComfyUI
NODE_CLASS_MAPPINGS = {
"MultiClipTextScriptMain": MultiClipTextScriptMain,
"MultiClipTextScriptClipSelector": MultiClipTextScriptClipSelector,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"MultiClipTextScriptMain": "Multi-Clip Text Script: Main",
"MultiClipTextScriptClipSelector": "Multi-Clip Text Script: Clip Selector",
}