Files
2026-01-09 04:37:55 +02:00

105 lines
3.4 KiB
Python

# mstring.py
# string helpers (ONLY TWO NODES)
import re
# ====== M String Pick (index) ======
# In: idx (INT), lines (STRING multiline)
# Out: text (STRING)
class MStringPickIndex:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"idx": ("INT", {"default": 0, "min": -(2**31), "max": (2**31 - 1), "step": 1}),
"lines": ("STRING", {"default": "a\nb\nc", "multiline": True}),
}}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "go"
CATEGORY = "mnodes/string"
def go(self, idx: int, lines: str):
arr = [ln for ln in lines.splitlines() if ln.strip() != ""]
if not arr:
return ("",)
if idx < 0 or idx >= len(arr):
return ("",)
return (arr[int(idx)],)
# ====== M Regex Replace (picked option) ======
# NOTE: input is named "n" (not "seed") to avoid ComfyUI's extra "control after generate" seed UI.
# In order: n, text, pattern, options, then regex settings
# Out order: next_seed, out_text, picked_option, picked_index
class MRegexReplaceFromLines:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"n": ("INT", {"default": 0, "min": 0, "max": 2**63 - 1, "step": 1}),
# single-line input to save space
"text": ("STRING", {"default": "", "multiline": False}),
"pattern": ("STRING", {"default": "SKIN", "multiline": False}),
# ONLY this is multiline
"options": ("STRING", {"default": "pink\ngreen\nblue\n", "multiline": True}),
"case_insensitive": ("BOOLEAN", {"default": False}),
"multiline": ("BOOLEAN", {"default": False}),
"dotall": ("BOOLEAN", {"default": True}),
"max_replacements": ("INT", {"default": 0, "min": 0, "max": 999999, "step": 1}), # 0=all
}}
RETURN_TYPES = ("INT", "STRING", "STRING", "INT")
RETURN_NAMES = ("next_seed", "out_text", "picked_option", "picked_index")
FUNCTION = "go"
CATEGORY = "mnodes/string"
def go(self, n: int, text: str, pattern: str, options: str,
case_insensitive: bool, multiline: bool, dotall: bool, max_replacements: int):
opts = [ln for ln in options.splitlines() if ln.strip() != ""]
if not opts:
return (int(n), text, "", 0)
MASK64 = (1 << 64) - 1
MASK63 = (1 << 63) - 1
def splitmix64(x: int) -> int:
x = (x + 0x9E3779B97F4A7C15) & MASK64
z = x
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9 & MASK64
z = (z ^ (z >> 27)) * 0x94D049BB133111EB & MASK64
return (z ^ (z >> 31)) & MASK64
r = splitmix64(int(n) & MASK64)
picked_index = int(r % len(opts))
picked_option = opts[picked_index]
next_seed = int(r & MASK63)
flags = 0
if case_insensitive:
flags |= re.IGNORECASE
if multiline:
flags |= re.MULTILINE
if dotall:
flags |= re.DOTALL
rx = re.compile(pattern, flags)
out_text = rx.sub(picked_option, text, count=(max_replacements if max_replacements > 0 else 0))
return (next_seed, out_text, picked_option, picked_index)
NODE_CLASS_MAPPINGS = {
"MStringPickIndex": MStringPickIndex,
"MRegexReplaceFromLines": MRegexReplaceFromLines,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"MStringPickIndex": "M String Pick (index)",
"MRegexReplaceFromLines": "M Regex Replace (picked option)",
}