| 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): |
| |
| header = [] |
| if name: header.append(name) |
| if trigger_str: header.append(trigger_str) |
| |
| |
| |
| 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): |
| |
| 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): |
| |
| 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} |