From b9fbc5eb3c1b420e66e11eec820f68b09000640a Mon Sep 17 00:00:00 2001 From: FaberVS <54268603+FaberVS@users.noreply.github.com> Date: Fri, 8 Aug 2025 17:52:52 +0300 Subject: [PATCH] Add files via upload --- ModelParamsNode.py | 49 +++++----- __init__.py | 19 +--- __pycache__/ModelParamsNode.cpython-312.pyc | Bin 0 -> 28900 bytes __pycache__/__init__.cpython-312.pyc | Bin 0 -> 680 bytes js/ModelParamsNode.js | 103 +++++++------------- js/MultiModelSwitch.js | 36 +------ 6 files changed, 64 insertions(+), 143 deletions(-) create mode 100644 __pycache__/ModelParamsNode.cpython-312.pyc create mode 100644 __pycache__/__init__.cpython-312.pyc diff --git a/ModelParamsNode.py b/ModelParamsNode.py index a4b5b00..5775bdb 100644 --- a/ModelParamsNode.py +++ b/ModelParamsNode.py @@ -12,7 +12,10 @@ import os # Додано для роботи з шляхами from server import PromptServer # Додано from aiohttp import web # Додано -import git +try: + import git +except ImportError: + git = None from typing import Any, Dict import yaml @@ -27,10 +30,12 @@ class AlwaysEqualProxy(str): comfy_ui_revision = None def get_comfyui_revision() -> Any: + if git is None: + return "Unknown" try: repo = git.Repo(os.path.dirname(folder_paths.__file__)) return len(list(repo.iter_commits('HEAD'))) - except: + except Exception: return "Unknown" def compare_revision(num: int) -> bool: @@ -39,7 +44,6 @@ def compare_revision(num: int) -> bool: comfy_ui_revision = get_comfyui_revision() return comfy_ui_revision == "Unknown" or int(comfy_ui_revision) >= num -WEB_DIRECTORY = "js" # Додано для JS файлу MAX_FLOW_NUM = 10 lazy_options = {"lazy": True} @@ -83,7 +87,6 @@ def get_style_names(style_filename): # Додаємо імена, пропускаючи порожні або None names.extend([item.get("name") for item in data if isinstance(item, dict) and item.get("name")]) except Exception as e: - print(f"Error loading style names from {filepath}: {e}") return [] # Повертаємо порожній список при помилці # Видаляємо дублікати (якщо є) і сортуємо. НЕ додаємо "None". @@ -178,7 +181,7 @@ class ModelParamsPipeNode: } pipe_data = (model, clip_skipped, final_vae, combined_params) - # Convert sampler_name and scheduler to string only for display purposes + # Return sampler_name and scheduler as values compatible with KSampler enum inputs return (model, clip_skipped, final_vae, steps, cfg, sampler_name, scheduler, safe_positive_tag, safe_negative_tag, pipe_data) @@ -189,7 +192,7 @@ class ParamsPipeUnpack: def INPUT_TYPES(cls): return { "required": { "params_pipe": (PIPE_TYPE_NAME,), } } - # Змінюємо типи повернення для sampler_name та scheduler + # Повертаємо типи для sampler_name та scheduler як enum-значення KSampler RETURN_TYPES = ("MODEL", "CLIP", "VAE", "INT", "FLOAT", comfy.samplers.KSampler.SAMPLERS, comfy.samplers.KSampler.SCHEDULERS, "STRING", "STRING", PIPE_TYPE_NAME) @@ -214,9 +217,9 @@ class ParamsPipeUnpack: positive_tag = params_dict.get("positive_tag", "") negative_tag = params_dict.get("negative_tag", "") - # Повертаємо оригінальні об'єкти, а не їх рядкові представлення + # Повертаємо значення, сумісні з enum-входами KSampler return (model, clip, vae, steps, cfg, - sampler_name, scheduler, # Без конвертації в str + sampler_name, scheduler, positive_tag, negative_tag, params_pipe) # --- Node 3: ListSelectorNode --- @@ -355,7 +358,13 @@ class PromptBuilderNode: @classmethod def INPUT_TYPES(cls): style_files = get_style_files() # Ця функція все ще повертає список з "None" - # Ми більше не використовуємо get_style_names для початкового списку тут + # Отримуємо всі можливі імена стилів для всіх файлів + all_style_names = [] + for style_file in style_files: + if style_file != "None": + all_style_names.extend(get_style_names(style_file)) + # Додаємо порожній рядок на початок списку + all_style_names = [""] + list(set(all_style_names)) return { "required": { @@ -366,8 +375,8 @@ class PromptBuilderNode: "optional": { "from_IMG2TXT": ("STRING", {"multiline": True, "default": "", "forceInput": True}), "style_file": (style_files, ), - # Змінюємо default на порожній рядок, оскільки "None" більше не буде опцією - "style_name": ("STRING", {"default": "", "multiline": False}), + # Тепер передаємо повний список можливих імен стилів + "style_name": (all_style_names, {"default": ""}), } } @@ -381,7 +390,7 @@ class PromptBuilderNode: # Використовуємо *args, **kwargs, щоб уникнути помилок з аргументами return float("NaN") - def build_prompts(self, params_pipe, positive_base, negative_base, from_IMG2TXT=None, style_file="None", style_name="None"): + def build_prompts(self, params_pipe, positive_base, negative_base, from_IMG2TXT=None, style_file="None", style_name=""): # 1. Розпаковка params_pipe if not isinstance(params_pipe, tuple) or len(params_pipe) != 4: raise ValueError("PromptBuilderNode: Invalid params_pipe structure.") @@ -404,7 +413,7 @@ class PromptBuilderNode: style_negative_prompt = "" # Default negative from style # 3. Обробка стилю (отримання шаблонів) - if style_file != "None" and style_name != "None": + if style_file and style_file != "None" and style_name: styles_dir = get_styles_dir() filepath = os.path.join(styles_dir, style_file) try: @@ -491,31 +500,19 @@ class ActiveModel: # --- API Endpoint for Dynamic Styles --- @PromptServer.instance.routes.post('/multi_model/get_style_names') async def get_style_names_endpoint(request): - # print("--- API Endpoint /multi_model/get_style_names called ---") # Keep this commented for less verbose logs try: json_data = await request.json() filename = json_data.get('filename') - if filename is None: # Check explicitly for None, as empty string might be valid (though unlikely here) - print("API Error: Filename not provided in request") + if filename is None: return web.Response(status=400, text="Filename not provided") style_names = get_style_names(filename) - # print(f"Returning style names for {filename}: {style_names}") # Keep commented return web.json_response({"style_names": style_names}) except json.JSONDecodeError as json_err: - try: - raw_body = await request.read() - body_text = raw_body.decode('utf-8', errors='ignore') - except Exception: - body_text = "[Could not read body]" - print(f"API Error: Invalid JSON received. Body: {body_text}. Error: {json_err}") return web.Response(status=400, text=f"Invalid JSON received: {json_err}") except Exception as e: - print(f"API Error in get_style_names_endpoint: {e}") - import traceback - print(traceback.format_exc()) return web.Response(status=500, text=f"Internal server error: {e}") # --- Helper definitions for MySwitchIndex --- diff --git a/__init__.py b/__init__.py index 07a18c1..f94b9e4 100644 --- a/__init__.py +++ b/__init__.py @@ -4,10 +4,6 @@ import traceback NODE_CLASS_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS = {} - - -print(f"--- Initializing MultiModel --- ") - try: # Спробуємо імпортувати ноди з ModelParamsNode.py from .ModelParamsNode import NODE_CLASS_MAPPINGS as MODEL_NODE_CLASS_MAPPINGS @@ -16,20 +12,11 @@ try: # Оновлення словників даними з ModelParamsNode.py NODE_CLASS_MAPPINGS.update(MODEL_NODE_CLASS_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(MODEL_NODE_DISPLAY_NAME_MAPPINGS) - - print("✅ MultiModel: Mappings imported and updated successfully.") except ImportError as e: - print(f"❌ MultiModel: Failed to import from ModelParamsNode. ImportError: {e}") - print(traceback.format_exc()) + pass except Exception as e: - print(f"❌ MultiModel: An unexpected error occurred during initialization: {e}") - print(traceback.format_exc()) - -# Видалено блок імпорту для logic.py, оскільки файли видалено + pass # Вказуємо ComfyUI, що в папці js є веб-файли -WEB_DIRECTORY = "js" - -# Цей рядок залишиться, щоб показати, чи дійшов __init__.py до кінця -print(f"--- MultiModel initialization finished (check for errors above) --- ") \ No newline at end of file +WEB_DIRECTORY = "js" \ No newline at end of file diff --git a/__pycache__/ModelParamsNode.cpython-312.pyc b/__pycache__/ModelParamsNode.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b754072789253532f83cb3b5d0b3cd38dd921a89 GIT binary patch literal 28900 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 R58;!fT^GO1$a%h!<3HeosgeKy literal 0 HcmV?d00001 diff --git a/js/ModelParamsNode.js b/js/ModelParamsNode.js index 8ce9077..3a16a13 100644 --- a/js/ModelParamsNode.js +++ b/js/ModelParamsNode.js @@ -1,14 +1,7 @@ import { app } from "/scripts/app.js"; -// Simple test log to confirm the file is loaded -// console.log("SIMPLE TEST: js/ModelParamsNode.js executed!"); - -console.log("%c[ModelParamsNode] JS файл завантажився! (v2-dynamic-inputs)", "color: white; background: #007acc; font-weight: bold; padding: 2px 8px; border-radius: 3px;"); -// --- Loading js/ModelParamsNode.js --- v2-dynamic-inputs - // Helper function to fetch style names from the backend async function fetchStyleNames(filename) { - // Return empty array immediately if filename is 'None' or invalid if (!filename || filename === "None") { return []; } @@ -24,94 +17,70 @@ async function fetchStyleNames(filename) { throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`); } const data = await response.json(); - // console.log("Received style names:", data); // Keep commented - return data.style_names || []; // Return empty array on failure or no names + return data.style_names || []; } catch (error) { - console.error(`[MultiModel Extension] Error fetching style names for ${filename}:`, error); - return []; // Return empty array on error + return []; } } const ext = { name: "MultiModel.PromptBuilderNode.StyleUpdater", - async setup(app) { - // console.log("MultiModel.PromptBuilderNode.StyleUpdater extension setup called"); - }, + async setup(app) {}, async nodeCreated(node) { - // console.log(`MultiModel Extension: nodeCreated called for node: ${node.getTitle()} (Type: ${node.constructor.type})`); - - // Check if this is the target node type we want to modify if (node.constructor.type === "PromptBuilder") { - // console.log(`MultiModel Extension: Found target node type 'PromptBuilderNode': ${node.getTitle()}`); - - const styleFileWidget = node.widgets.find(w => w.name === "style_file"); - const styleNameWidget = node.widgets.find(w => w.name === "style_name"); + const styleFileWidget = node.widgets?.find(w => w?.name === "style_file"); + const styleNameWidget = node.widgets?.find(w => w?.name === "style_name"); - if (!styleFileWidget) { - console.error("[MultiModel Extension] Could not find 'style_file' widget on PromptBuilderNode!"); - return; - } - if (!styleNameWidget) { - console.error("[MultiModel Extension] Could not find 'style_name' widget on PromptBuilderNode!"); + if (!styleFileWidget || !styleNameWidget) { return; } - // Force the widget type to combo, even if Python defined it as STRING - styleNameWidget.type = "combo"; - // Ensure options object exists for combo box properties - if (!styleNameWidget.options) { styleNameWidget.options = {}; } - - // Add a callback to handle selection changes for the style_name widget itself - styleNameWidget.callback = (value) => { - // Explicitly set the widget's value on selection - styleNameWidget.value = value; - console.log(`[MultiModel Extension] style_name selected and value set to: ${styleNameWidget.value}`); - // Optional: Force redraw if the value change doesn't update visually - // node.setDirtyCanvas(true, false); - }; - - // console.log("MultiModel Extension: Found 'style_file' and 'style_name' widgets."); + // Встановлюємо початкові значення + styleFileWidget.value = "None"; + styleNameWidget.value = ""; + styleNameWidget.options = { values: [] }; // Function to update style_name options const updateStyleNames = async (selectedFilename) => { - const names = await fetchStyleNames(selectedFilename); - if (styleNameWidget.options) { - // console.log(`[MultiModel Extension] Updating styleNameWidget.options.values to:`, names); - styleNameWidget.options.values = names; - - // Встановлюємо значення: або поточне (якщо воно є в новому списку), або перший елемент нового списку - // console.log(`[MultiModel Extension] Before setting value - current styleNameWidget.value: ${styleNameWidget.value}, styleNames includes current value: ${names.includes(styleNameWidget.value)}`); - styleNameWidget.value = names.includes(styleNameWidget.value) ? styleNameWidget.value : names[0]; - // console.log(`[MultiModel Extension] After setting value - new styleNameWidget.value: ${styleNameWidget.value}`); - - // Додаємо колбек, якщо його ще немає - if (!styleNameWidget.callback) { - styleNameWidget.callback = (value) => { - styleNameWidget.value = value; - console.log(`[MultiModel Extension] style_name selected and value set to: ${styleNameWidget.value}`); - }; + try { + if (!selectedFilename || selectedFilename === "None") { + styleNameWidget.options = { values: [] }; + styleNameWidget.value = ""; + node.setDirtyCanvas(true, false); + return; } - } else { - console.error("[MultiModel Extension] 'style_name' widget does not have accessible options."); + + const names = await fetchStyleNames(selectedFilename); + if (!names || !names.length) { + styleNameWidget.options = { values: [] }; + styleNameWidget.value = ""; + node.setDirtyCanvas(true, false); + return; + } + + styleNameWidget.options = { values: names }; + styleNameWidget.value = names[0] || ""; + node.setDirtyCanvas(true, false); + } catch (error) { + styleNameWidget.options = { values: [] }; + styleNameWidget.value = ""; + node.setDirtyCanvas(true, false); } }; + // Set up style_file callback const originalCallback = styleFileWidget.callback; - styleFileWidget.callback = async (value) => { - // console.log(`MultiModel Extension: style_file changed to: ${value}`); await updateStyleNames(value); if (originalCallback) { originalCallback.call(styleFileWidget, value); } }; - if (styleFileWidget.value) { - // console.log(`MultiModel Extension: Initializing style_names for file: ${styleFileWidget.value}`); - setTimeout(() => updateStyleNames(styleFileWidget.value), 100); // Keep timeout for initialization - } else { - console.warn("[MultiModel Extension] 'style_file' widget has no initial value on creation."); + // Initialize style names if needed + if (styleFileWidget.value && styleFileWidget.value !== "None") { + setTimeout(() => updateStyleNames(styleFileWidget.value), 100); } } } diff --git a/js/MultiModelSwitch.js b/js/MultiModelSwitch.js index 5d1778e..74dccc1 100644 --- a/js/MultiModelSwitch.js +++ b/js/MultiModelSwitch.js @@ -1,5 +1,4 @@ -import { app } from "../../../scripts/app.js"; -import { ComfyWidgets } from "../../../scripts/widgets.js"; +import { app } from "/scripts/app.js"; // Simple debounce function function debounce(func, delay) { @@ -20,9 +19,6 @@ app.registerExtension({ async beforeRegisterNodeDef(nodeType, nodeData, app) { // Compare with the class name if (nodeData.name === nodeClassName) { - console.log(`[ModelSwitch] Patching ${nodeClassName}`); - - // --- ФУНКЦІЇ --- function handleModeSwitch(modeWidget, indexWidget, app, value) { const updateVisibility = (modeValue) => { if (indexWidget) { @@ -41,14 +37,11 @@ app.registerExtension({ } function updateDynamicInputIndexes(node) { - console.log('[ModelSwitch] Оновлення індексів динамічних входів'); const prefix = node._input_prefix || "input_"; let dynamicInputs = node.inputs ? node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index") : []; - // Перейменовуємо динамічні входи по порядку for (let i = 0; i < dynamicInputs.length; i++) { const expectedName = `${prefix}${i + 1}`; if (dynamicInputs[i].name !== expectedName) { - console.log(`[ModelSwitch] Перейменування входу ${dynamicInputs[i].name} -> ${expectedName}`); dynamicInputs[i].name = expectedName; } } @@ -81,29 +74,18 @@ app.registerExtension({ return; } if (connected) { - console.log(`[ModelSwitch] Вхід "${inputName}" (${index}) підключено`); if (index === node.last_empty) { const prefix = node._input_prefix || "input_"; let dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); node.addInput(`${prefix}${dynamicInputs.length + 1}`, "*"); - console.log('[ModelSwitch] Додаємо ще один порожній динамічний вхід'); updateDynamicInputIndexes(node); - } else { - console.log('[ModelSwitch] Підключено не до останнього вільного входу, новий не додаємо'); } } else { - console.log(`[ModelSwitch] Вхід "${inputName}" (${index}) відключено`); - // Якщо відключено від останнього підключеного, прибираємо цей вхід, але лише якщо порожніх динамічних входів більше одного let dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); let emptyInputs = dynamicInputs.filter(inp => inp.link == null); if (index === node.last_connected && emptyInputs.length > 1) { node.removeInput(index); - console.log('[ModelSwitch] Відключено від останнього підключеного — видаляємо цей вхід'); updateDynamicInputIndexes(node); - } else if (index === node.last_connected) { - console.log('[ModelSwitch] Це останній порожній вхід — не видаляємо'); - } else { - console.log('[ModelSwitch] Відключено не від останнього підключеного — нічого не робимо'); } } node._updatingInputs = false; @@ -113,12 +95,7 @@ app.registerExtension({ // --- Віджети --- const modeWidget = node.widgets.find(w => w.name === "mode"); const indexWidget = node.widgets.find(w => w.name === "index"); - if (!modeWidget) { - console.error(`[ModelSwitch] ${nodeClassName}: 'mode' widget not found! Cannot apply dynamic visibility.`); - return; - } - if (!indexWidget) { - console.error(`[ModelSwitch] ${nodeClassName}: 'index' widget not found! Cannot apply dynamic visibility.`); + if (!modeWidget || !indexWidget) { return; } handleModeSwitch(modeWidget, indexWidget, app); @@ -127,24 +104,17 @@ app.registerExtension({ node._input_prefix = "input_"; const prefix = node._input_prefix; let dynamicInputs = node.inputs ? node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index") : []; - console.log('[ModelSwitch] dynamicInputs:', dynamicInputs.map(inp => ({name: inp.name, link: inp.link}))); - // --- Якщо входів немає, додаємо перший --- if (dynamicInputs.length === 0) { - console.log('[ModelSwitch] Входів немає, додаємо перший'); node.addInput(`${prefix}1`, "*"); dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } let connectedInputs = dynamicInputs.filter(inp => inp.link != null); let emptyInputs = dynamicInputs.filter(inp => inp.link == null); - console.log('[ModelSwitch] connectedInputs:', connectedInputs); - console.log('[ModelSwitch] emptyInputs:', emptyInputs); if (emptyInputs.length === 0) { - console.log('[ModelSwitch] Додаємо новий порожній вхід'); node.addInput(`${prefix}${connectedInputs.length + 1}`, "*"); dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } else if (emptyInputs.length > 1) { - console.log('[ModelSwitch] Видаляємо зайві порожні входи'); for (let i = node.inputs.length - 1; i >= 0; i--) { if (node.inputs[i].name !== "mode" && node.inputs[i].name !== "index" && node.inputs[i].link == null && emptyInputs.length > 1) { node.removeInput(i); @@ -153,7 +123,6 @@ app.registerExtension({ } dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } - console.log('[ModelSwitch] dynamicInputs після додавання/видалення:', dynamicInputs.map(inp => ({name: inp.name, link: inp.link}))); updateDynamicInputIndexes(node); // --- Зберігаємо попередній стан links --- node._prevLinks = []; @@ -175,7 +144,6 @@ app.registerExtension({ const onNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = function() { onNodeCreated?.apply(this, arguments); - console.log(`[ModelSwitch] ${nodeClassName} created`); setTimeout(() => { handleNodeCreation(this, app); }, 500);