diff --git a/Backups/__init__(Working Backup 29-6).py b/Backups/__init__(Working Backup 29-6).py deleted file mode 100644 index 23f333e..0000000 --- a/Backups/__init__(Working Backup 29-6).py +++ /dev/null @@ -1,109 +0,0 @@ -import os -import json -import base64 -from PIL import Image -from io import BytesIO - - -class EnhancedCharacterPromptNode: - """ComfyUI node: kies character + action, toon preview image, output prompt + conditioning""" - - # JSON-bestanden met characters en actions - CHARACTER_JSON_FILES = [ - os.path.join(os.path.dirname(__file__), f"output_{i}.json") for i in range(1, 12) - ] - ACTION_JSON = os.path.join(os.path.dirname(__file__), "action.json") - - # Class-variabelen voor data en dropdowns - char_data = [] - action_data = {} - CHARACTERS = [] - ACTIONS = [] - - # Laad JSON data zodra class geladen wordt - try: - for path in CHARACTER_JSON_FILES: - with open(path, "r", encoding="utf-8") as f: - char_data.extend(json.load(f)) - except Exception as e: - print(f"⚠️ Error loading {path}: {e}") - - try: - with open(ACTION_JSON, "r", encoding="utf-8") as f: - action_data = json.load(f) - except Exception as e: - print("❌ Error loading action.json:", e) - - # Vul dropdown lijsten op basis van geladen data - CHARACTERS = [list(entry.keys())[0] for entry in char_data if isinstance(entry, dict) and len(entry) >= 1] - ACTIONS = list(action_data.keys()) - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "character": (cls.CHARACTERS,), - "action": (cls.ACTIONS,), - "extra_prompt": ("STRING", {"multiline": True, "default": ""}), - "clip": ("CLIP",), # optioneel, voor conditioning - } - } - - RETURN_TYPES = ("STRING", "IMAGE", "CONDITIONING") - RETURN_NAMES = ("prompt", "preview_image", "CONDITIONING") - FUNCTION = "build_prompt" - CATEGORY = "Prompting/Anime Character" - - def build_prompt(self, character, action, extra_prompt, clip): - char_prompt = "" - action_prompt = EnhancedCharacterPromptNode.action_data.get(action, "") - preview_image = None - - for entry in EnhancedCharacterPromptNode.char_data: - if isinstance(entry, dict) and character in entry: - value = entry[character] - if isinstance(value, str) and value.startswith("data:image"): - char_prompt = character - preview_data = value - else: - char_prompt = value - preview_data = entry.get("preview", "") - - if isinstance(preview_data, str) and preview_data.startswith("data:image"): - try: - base64_data = preview_data.split("base64,", 1)[1] - preview_image = self.decode_base64_to_image(base64_data) - except Exception as e: - print(f"⚠️ Base64 decode failed for {character}: {e}") - break - - final_prompt = ", ".join(p for p in [char_prompt, action_prompt, extra_prompt] if p).strip() - - conditioning_output = [] - if clip: - try: - cross_attn = clip.encode(final_prompt) - pooled = clip.encode_pooled(final_prompt) - cond_dict = {"pooled_output": pooled} - conditioning_output = [(cross_attn, cond_dict)] - except Exception as e: - print(f"⚠️ CLIP encode failed: {e}") - - return (final_prompt, preview_image, conditioning_output) - - def decode_base64_to_image(self, base64_str): - data = base64.b64decode(base64_str) - try: - img = Image.open(BytesIO(data)).convert("RGB") - except Exception as e: - raise ValueError("Failed to decode base64 image") from e - return img - - -NODE_CLASS_MAPPINGS = { - "EnhancedCharacterPromptNode": EnhancedCharacterPromptNode, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "EnhancedCharacterPromptNode": "Character + Action Prompt + Preview", -} diff --git a/Backups/__init__(latest 30-6).py b/Backups/__init__(latest 30-6).py deleted file mode 100644 index f4162a1..0000000 --- a/Backups/__init__(latest 30-6).py +++ /dev/null @@ -1,128 +0,0 @@ -import os -import json -import base64 -from PIL import Image -from io import BytesIO - -import nodes # Register JS directory -nodes.EXTENSION_WEB_DIRS["ComfyUI_AnimeCharacterSelect"] = os.path.join(os.path.dirname(__file__), "js") - - -class EnhancedCharacterPromptNode: - """ComfyUI node: select character + action, show preview image, output prompt + conditioning""" - - # JSON files with characters and actions - CHARACTER_JSON_FILES = [ - os.path.join(os.path.dirname(__file__), f"output_{i}.json") for i in range(1, 12) - ] - ACTION_JSON = os.path.join(os.path.dirname(__file__), "action.json") - - # Class variables for data and dropdowns - char_data = [] - action_data = {} - CHARACTERS = [] - ACTIONS = [] - - try: - for path in CHARACTER_JSON_FILES: - with open(path, "r", encoding="utf-8") as f: - char_data.extend(json.load(f)) - except Exception as e: - print(f"⚠️ Error loading {path}: {e}") - - try: - with open(ACTION_JSON, "r", encoding="utf-8") as f: - action_data = json.load(f) - except Exception as e: - print("❌ Error loading action.json:", e) - - CHARACTERS = [list(entry.keys())[0] for entry in char_data if isinstance(entry, dict) and len(entry) >= 1] - ACTIONS = list(action_data.keys()) - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "default": ""}), - "Select to add Character": (["Select the character to insert"] + cls.CHARACTERS,), - "Select to add Action": (["Select the action to insert"] + cls.ACTIONS,), - "clip": ("CLIP",), - } - } - - RETURN_TYPES = ("STRING", "IMAGE", "CONDITIONING") - RETURN_NAMES = ("prompt", "preview_image", "conditioning") - FUNCTION = "build_prompt" - CATEGORY = "Prompting/Anime Character" - - def build_prompt(self, prompt, **kwargs): - selected_char = kwargs.get("Select to add Character", None) - selected_act = kwargs.get("Select to add Action", None) - clip = kwargs.get("clip") - - preview_image = None - char_prompt = "" - action_prompt = "" - - for entry in self.char_data: - if isinstance(entry, dict) and selected_char in entry: - value = entry[selected_char] - if isinstance(value, str) and value.startswith("data:image"): - char_prompt = selected_char - preview_data = value - else: - char_prompt = value - preview_data = entry.get("preview", "") - - if isinstance(preview_data, str) and preview_data.startswith("data:image"): - try: - base64_data = preview_data.split("base64,", 1)[1] - preview_image = self.decode_base64_to_image(base64_data) - except Exception as e: - print(f"⚠️ Base64 decode failed for {selected_char}: {e}") - break - - action_prompt = self.action_data.get(selected_act, "") - - print("[DEBUG] char_prompt:", char_prompt) - print("[DEBUG] action_prompt:", action_prompt) - print("[DEBUG] input prompt:", prompt) - - # Build the live-updated editable prompt - new_prompt_parts = [char_prompt, action_prompt] - combined_prompt = ", ".join([p for p in new_prompt_parts if p]) - - if prompt.strip(): - if combined_prompt: - prompt = combined_prompt + ", " + prompt - else: - prompt = combined_prompt - - print("[DEBUG] final prompt:", prompt) - - conditioning_output = [] - if clip: - try: - cross_attn = clip.encode(prompt) - conditioning_output = [(cross_attn, {})] - except Exception as e: - print(f"⚠️ CLIP encode failed: {e}") - - return (prompt, preview_image, conditioning_output) - - def decode_base64_to_image(self, base64_str): - data = base64.b64decode(base64_str) - try: - from comfy.utils import pil_to_tensor - return pil_to_tensor(Image.open(BytesIO(data)).convert("RGB")) - except Exception as e: - raise ValueError("Failed to decode base64 image") from e - - -NODE_CLASS_MAPPINGS = { - "EnhancedCharacterPromptNode": EnhancedCharacterPromptNode, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "EnhancedCharacterPromptNode": "Character + Action Prompt (Live Insert)", -} diff --git a/Backups/__init__(working pt2).py b/Backups/__init__(working pt2).py deleted file mode 100644 index 3741dea..0000000 --- a/Backups/__init__(working pt2).py +++ /dev/null @@ -1,118 +0,0 @@ -import os -import json -import base64 -from PIL import Image -from io import BytesIO - - -class EnhancedCharacterPromptNode: - """ComfyUI node: select character + action, show preview image, output prompt + conditioning""" - - # JSON files with characters and actions - CHARACTER_JSON_FILES = [ - os.path.join(os.path.dirname(__file__), f"output_{i}.json") for i in range(1, 12) - ] - ACTION_JSON = os.path.join(os.path.dirname(__file__), "action.json") - - # Class variables for dropdown content - char_data = [] - action_data = {} - CHARACTERS = [] - ACTIONS = [] - - # Load JSON data once when class is loaded - try: - for path in CHARACTER_JSON_FILES: - with open(path, "r", encoding="utf-8") as f: - char_data.extend(json.load(f)) - except Exception as e: - print(f"⚠️ Error loading {path}: {e}") - - try: - with open(ACTION_JSON, "r", encoding="utf-8") as f: - action_data = json.load(f) - except Exception as e: - print("❌ Error loading action.json:", e) - - # Populate dropdowns - CHARACTERS = [list(entry.keys())[0] for entry in char_data if isinstance(entry, dict) and len(entry) >= 1] - ACTIONS = list(action_data.keys()) - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "character": (cls.CHARACTERS,), - "action": (cls.ACTIONS,), - "extra_prompt": ("STRING", {"multiline": True, "default": ""}), - "custom_prompt": ("STRING", {"multiline": True, "default": ""}), - "clip": ("CLIP",), - } - } - - RETURN_TYPES = ("STRING", "IMAGE", "CONDITIONING") - RETURN_NAMES = ("prompt", "preview_image", "conditioning") - FUNCTION = "build_prompt" - CATEGORY = "Prompting/Anime Character" - - def build_prompt(self, character, action, extra_prompt, custom_prompt, clip): - char_prompt = "" - action_prompt = EnhancedCharacterPromptNode.action_data.get(action, "") - preview_image = None - - for entry in EnhancedCharacterPromptNode.char_data: - if isinstance(entry, dict) and character in entry: - value = entry[character] - if isinstance(value, str) and value.startswith("data:image"): - char_prompt = character - preview_data = value - else: - char_prompt = value - preview_data = entry.get("preview", "") - - if isinstance(preview_data, str) and preview_data.startswith("data:image"): - try: - base64_data = preview_data.split("base64,", 1)[1] - preview_image = self.decode_base64_to_image(base64_data) - except Exception as e: - print(f"⚠️ Base64 decode failed for {character}: {e}") - break - - # Build editable prompt string - base_prompt = ", ".join(p for p in [char_prompt, action_prompt, extra_prompt] if p).strip() - final_prompt = custom_prompt if custom_prompt.strip() else base_prompt - - # Encode prompt for conditioning - conditioning_output = [] - if clip: - try: - if hasattr(clip, "encode_with_transformer"): - cross_attn, pooled = clip.encode_with_transformer(final_prompt) - cond_dict = {"pooled_output": pooled} - conditioning_output = [(cross_attn, cond_dict)] - else: - cross_attn = clip.encode(final_prompt) - pooled = clip.encode_pooled(final_prompt) - cond_dict = {"pooled_output": pooled} - conditioning_output = [(cross_attn, cond_dict)] - except Exception as e: - print(f"⚠️ CLIP encode failed: {e}") - - return (final_prompt, preview_image, conditioning_output) - - def decode_base64_to_image(self, base64_str): - data = base64.b64decode(base64_str) - try: - img = Image.open(BytesIO(data)).convert("RGB") - except Exception as e: - raise ValueError("Failed to decode base64 image") from e - return img - - -NODE_CLASS_MAPPINGS = { - "EnhancedCharacterPromptNode": EnhancedCharacterPromptNode, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "EnhancedCharacterPromptNode": "Character + Action Prompt + Preview", -} diff --git a/Backups/enhanced_prompt.js b/Backups/enhanced_prompt.js deleted file mode 100644 index a70c4fd..0000000 --- a/Backups/enhanced_prompt.js +++ /dev/null @@ -1,48 +0,0 @@ -console.log("EnhancedPromptNode JS loaded!") -function updatePromptValue(textarea, value) { - if (!textarea || !value || value.startsWith("Select")) return; - const existing = textarea.value.trim(); - const parts = existing.length > 0 ? existing.split(",").map(p => p.trim()) : []; - if (!parts.includes(value)) { - parts.push(value); - textarea.value = parts.join(", "); - textarea.dispatchEvent(new Event("input", { bubbles: true })); - } -} - -function setupLiveCharacterActionPromptHook() { - const observer = new MutationObserver(() => { - document.querySelectorAll(".comfy-control").forEach(container => { - const label = container.querySelector("label"); - const select = container.querySelector("select"); - - if (!label || !select) return; - - const labelText = label.innerText.trim(); - if ( - (labelText.includes("Select to add Character") || labelText.includes("Select to add Action")) && - !select.dataset._enhanced_hooked - ) { - select.dataset._enhanced_hooked = "true"; - select.addEventListener("change", () => { - const node = container.closest(".node"); - if (!node) return; - - const textarea = node.querySelector("textarea"); - if (!textarea) return; - - const selectedValue = select.value; - updatePromptValue(textarea, selectedValue); - }); - } - }); - }); - - observer.observe(document.body, { childList: true, subtree: true }); -} - -if (document.readyState !== "loading") { - setupLiveCharacterActionPromptHook(); -} else { - document.addEventListener("DOMContentLoaded", setupLiveCharacterActionPromptHook); -} diff --git a/Backups/extension.js b/Backups/extension.js deleted file mode 100644 index 8f54bcc..0000000 --- a/Backups/extension.js +++ /dev/null @@ -1,53 +0,0 @@ -// comfyui-animecharacterselect/js/enhanced_prompt.js - -console.log("EnhancedPrompt JS Loaded"); - -function insertIntoPrompt(node, dropdownName) { - // Zoek het promptveld (textarea) en de dropdown - const promptWidget = node.widgets.find(w => w.name === "prompt"); - const dropdownWidget = node.widgets.find(w => w.name === dropdownName); - if (!promptWidget || !dropdownWidget) return; - - const promptInput = promptWidget.inputEl; - const selectInput = dropdownWidget.inputEl; - if (!promptInput || !selectInput) return; - - selectInput.addEventListener("change", () => { - const value = selectInput.value; - if (value && !value.startsWith("Select")) { - let current = promptInput.value.trim(); - // Voeg toe als niet al aanwezig - if (!current.includes(value)) { - if (current.length > 0) { - current += ", " + value; - } else { - current = value; - } - promptInput.value = current; - promptWidget.value = current; - promptInput.dispatchEvent(new Event("input", { bubbles: true })); - } - // Reset dropdown - selectInput.value = selectInput.options[0].value; - selectInput.dispatchEvent(new Event("change")); - } - }); -} - -// Hook in op Node create/update -app.registerExtension({ - name: "ComfyUI_AnimeCharacterSelect", - nodeCreated(node) { - // Je node heet "EnhancedCharacterPromptNode" - if (node.type === "EnhancedCharacterPromptNode") { - insertIntoPrompt(node, "Select to add Character"); - insertIntoPrompt(node, "Select to add Action"); - } - }, - nodeRenamed(node) { - if (node.type === "EnhancedCharacterPromptNode") { - insertIntoPrompt(node, "Select to add Character"); - insertIntoPrompt(node, "Select to add Action"); - } - } -}); diff --git a/pyproject.toml b/pyproject.toml index 9814dbe..9a3ca45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "animecharacterselect" description = "WAI's Character select ported to ComfyUI with a few tweaks" -version = "1.1.0" +version = "1.1.1" license = {file = "LICENSE"} # classifiers = [ # # For OS-independent nodes (works on all operating systems)