diff --git a/web/comfyui/usage_stats.js b/web/comfyui/usage_stats.js index cce7011b..776313bb 100644 --- a/web/comfyui/usage_stats.js +++ b/web/comfyui/usage_stats.js @@ -1,6 +1,15 @@ // ComfyUI extension to track model usage statistics import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; +import { showToast } from "./utils.js"; + +// Define target nodes and their widget configurations +const PATH_CORRECTION_TARGETS = [ + { comfyClass: "CheckpointLoaderSimple", widgetName: "ckpt_name", modelType: "checkpoints" }, + { comfyClass: "UNETLoader", widgetName: "unet_name", modelType: "checkpoints" }, + { comfyClass: "LoraLoader", widgetName: "lora_name", modelType: "loras" }, + { comfyClass: "easy loraStack", widgetNamePattern: "lora_\\d+_name", modelType: "loras" } +]; // Register the extension app.registerExtension({ @@ -80,5 +89,100 @@ app.registerExtension({ } catch (error) { console.error("Error refreshing registry:", error); } + }, + + async loadedGraphNode(node) { + // Check if this node type needs path correction + const target = PATH_CORRECTION_TARGETS.find(t => t.comfyClass === node.comfyClass); + if (!target) { + return; + } + + await this.correctNodePaths(node, target); + }, + + async correctNodePaths(node, target) { + try { + if (target.widgetNamePattern) { + // Handle pattern-based widget names (like lora_1_name, lora_2_name, etc.) + const pattern = new RegExp(target.widgetNamePattern); + const widgetIndexes = []; + + if (node.widgets) { + node.widgets.forEach((widget, index) => { + if (pattern.test(widget.name)) { + widgetIndexes.push(index); + } + }); + } + + // Process each matching widget + for (const widgetIndex of widgetIndexes) { + await this.correctWidgetPath(node, widgetIndex, target.modelType); + } + } else { + // Handle single widget name + if (node.widgets) { + const widgetIndex = node.widgets.findIndex(w => w.name === target.widgetName); + if (widgetIndex !== -1) { + await this.correctWidgetPath(node, widgetIndex, target.modelType); + } + } + } + } catch (error) { + console.error("Error correcting node paths:", error); + } + }, + + async correctWidgetPath(node, widgetIndex, modelType) { + if (!node.widgets_values || !node.widgets_values[widgetIndex]) { + return; + } + + const currentPath = node.widgets_values[widgetIndex]; + if (!currentPath || typeof currentPath !== 'string') { + return; + } + + // Extract filename from path (after last separator) + const fileName = currentPath.split(/[/\\]/).pop(); + if (!fileName) { + return; + } + + try { + // Search for current relative path + const response = await api.fetchApi(`/${modelType}/relative-paths?search=${encodeURIComponent(fileName)}&limit=2`); + const data = await response.json(); + + if (!data.success || !data.relative_paths || data.relative_paths.length === 0) { + return; + } + + const foundPaths = data.relative_paths; + const firstPath = foundPaths[0]; + + // Check if we need to update the path + if (firstPath !== currentPath) { + // Update the widget value + // node.widgets_values[widgetIndex] = firstPath; + node.widgets[widgetIndex].value = firstPath; + + if (foundPaths.length === 1) { + // Single match found - success + showToast(`Updated path for ${fileName}: ${firstPath}`, 'info'); + } else { + // Multiple matches found - warning + showToast(`Multiple paths found for ${fileName}, using: ${firstPath}`, 'warning'); + } + + // Mark node as modified + if (node.setDirtyCanvas) { + node.setDirtyCanvas(true); + } + } + } catch (error) { + console.error(`Error correcting path for ${fileName}:`, error); + } } }); diff --git a/web/comfyui/utils.js b/web/comfyui/utils.js index 4d1341c8..dda3814a 100644 --- a/web/comfyui/utils.js +++ b/web/comfyui/utils.js @@ -23,6 +23,28 @@ export function getComfyUIFrontendVersion() { return window['__COMFYUI_FRONTEND_VERSION__'] || "0.0.0"; } +/** + * Show a toast notification + * @param {string} message - The message to display + * @param {string} type - The type of toast (success, error, info, warning) + */ +export function showToast(message, type = 'info') { + if (app && app.extensionManager && app.extensionManager.toast) { + app.extensionManager.toast.add({ + severity: type, + summary: type.charAt(0).toUpperCase() + type.slice(1), + detail: message, + life: 3000 + }); + } else { + console.log(`${type.toUpperCase()}: ${message}`); + // Fallback alert for critical errors only + if (type === 'error') { + alert(message); + } + } +} + // Dynamically import the appropriate widget based on app version export async function dynamicImportByVersion(latestModulePath, legacyModulePath) { // Parse app version and compare with 1.12.6 (version when tags widget API changed)