File size: 3,829 Bytes
7aaa385 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | import os
import re
import json
import hashlib
import urllib.request
import urllib.error
import folder_paths
import comfy.sd
import comfy.lora
import comfy.utils
from safetensors import safe_open
try:
from .promp_logic_v32_story import MODEL_PRESETS, MODEL_PRESET_LABELS, resolve_model
except Exception:
MODEL_PRESETS = {"๐ DeepSeek V3.2": "deepseek/deepseek-v3.2"}
MODEL_PRESET_LABELS = list(MODEL_PRESETS.keys())
def resolve_model(p, c): return c if p == "custom" else "deepseek/deepseek-v3.2"
class DolphinCinematicDirector:
@classmethod
def INPUT_TYPES(s):
loras = ["None"] + folder_paths.get_filename_list("loras")
return {
"required": {
"model_base": ("MODEL",), "model_high": ("MODEL",), "model_low": ("MODEL",),
"mode": (["๐ค Auto LLM (Story Splitter)", "โ๏ธ Manual"], {"default": "๐ค Auto LLM (Story Splitter)"}),
"master_story": ("STRING", {"multiline": True}),
"tagger_context": ("STRING", {"multiline": True}),
"openrouter_api_key": ("STRING", {"default": ""}),
},
"optional": {
"external_triggers": ("STRING", {"forceInput": True}),
}
}
RETURN_TYPES = ("MODEL", "MODEL", "MODEL", "STRING") * 4 + ("STRING",)
RETURN_NAMES = tuple([f"c{i}_{n}" for i in range(1, 5) for n in ["base", "high", "low", "prompt"]] + ["debug_info"])
FUNCTION = "direct"
CATEGORY = "Dolphin"
def _build_final(self, action, tags, master_scene, name, trigger_str):
# 1. ํธ๋ฆฌ๊ฑฐ ์๋(trigger_str)์ ์บ๋ฆญํฐ ์ด๋ฆ(name)๋ง ์๋จ์ ๋ฐฐ์น
header = []
if name: header.append(name)
if trigger_str: header.append(trigger_str)
# 2. ๋์(action)๊ณผ ๋ฐฐ๊ฒฝ(master_scene)๋ง ํ๋จ์ ๋ฐฐ์น
# ๐ ํต์ฌ: ์ฌ๊ธฐ์ 'tags' ๋ณ์๋ฅผ ์์ ์ฌ์ฉํ์ง ์์์ผ๋ก์จ ๋ฌ์ฌ ํฌํจ์ ์์ฒ ์ฐจ๋จ
body = []
if master_scene and master_scene.strip():
body.append(master_scene.strip())
if action and action.strip():
body.append(action.strip())
# ๊ฒฐ๊ณผ๊ฐ ๊ฒฐํฉ
header_str = ", ".join(header)
body_str = " ".join(body)
if header_str and body_str:
return f"{header_str}\n{body_str}"
return header_str or body_str
def _llm_split(self, story, name, tags, key, model):
# ๐ ์์ : LLM์๊ฒ๋ ๋ฌ์ฌ๋ฅผ ๋ณด๋ด๋, ์ถ๋ ฅ JSON์์๋ ๋์๋ง ๋ฝ๋๋ก ๊ฐ์
sys_p = (
"You are an action-only generator. "
"DO NOT output any visual descriptions, clothing, lighting, or setting details. "
"Your output must ONLY be the physical movement of the character. "
"If you include any adjective-heavy descriptions, you will be penalized. "
"Strictly output only the kinetic action in the JSON fields."
)
def direct(self, model_base, model_high, model_low, mode, master_story, tagger_context, openrouter_api_key, external_triggers="", **kw):
# ๐ ๊ฐ๋ ฅํ ๋ฐฉ์ด: ์ด๋ค ๋ฌ์ฌ๊ฐ ๋ค์ด์๋ ํ๊ทธ๋ ๊ฐ์ ๋ก ๋น ๊ฐ์ผ๋ก ๋์ฒด
tagger_context = ""
# ์ดํ ๋ก์ง...
# ๋ชจ๋๋ณ ์ฒ๋ฆฌ
clips = [master_story] * 4 # ๋จ์ํ ๋ก์ง
master_scene = "action"
final_name = "character"
outs = []
for c in range(1, 5):
# ๐ ๊ฐ์ ๋ก tags(tagger_context)๋ฅผ ๋น ๋ฌธ์์ด("")๋ก ์ ๋ฌํ์ฌ ๋ฌด๋ ฅํ
prompt = self._build_final(clips[c-1], "", master_scene, final_name, external_triggers)
outs.extend([model_base, model_high, model_low, prompt])
return tuple(outs) + ("Success",)
NODE_CLASS_MAPPINGS = {"DolphinCinematicDirector": DolphinCinematicDirector} |