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}