diff --git a/__init__.py b/__init__.py index f83be0a..c39de71 100644 --- a/__init__.py +++ b/__init__.py @@ -1,75 +1,37 @@ # ComfyUI Model Downloader Bridge import server import folder_paths -import os import asyncio from aiohttp import web print("\033[95m[Model-Downloader] UI Extension Bridge active\033[0m") -# Erzwungene Pfad-Registrierung für den User-Pfad -USER_MODEL_PATH = r"C:\AI\comfyuidata\models" -if os.path.exists(USER_MODEL_PATH): - print(f"[Model-Downloader] Prüfe User-Modell-Pfad: {USER_MODEL_PATH}") - # Wir fügen den Pfad zu den Standard-Modell-Pfaden hinzu, falls er fehlt - # ComfyUI nutzt intern oft 'base_path' oder 'extra_model_paths' - try: - # Versuch, den Pfad als extra Modell-Pfad zu registrieren - # folder_paths.add_model_folder_path("custom", USER_MODEL_PATH) - pass - except: pass - @server.PromptServer.instance.routes.post("/kippster/model-downloaded") async def model_downloaded(request): - print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Erzwinge harten Backend-Reset...\033[0m") - + print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Leere Backend-Caches...\033[0m") + try: - # 1. Warte auf Dateisystem (besonders wichtig bei Windows/HDD) - await asyncio.sleep(1.0) - - # 2. Alle Caches in folder_paths radikal leeren + # 1. Kurze Pause für das Dateisystem (wichtig beim Entpacken/Verschieben) + await asyncio.sleep(0.5) + + # 2. Lösche gezielt die generierten Dateilisten aus dem ComfyUI Cache if hasattr(folder_paths, "filename_list_cache"): folder_paths.filename_list_cache.clear() - if hasattr(folder_paths, "cache_helper"): - if hasattr(folder_paths.cache_helper, "clear"): - folder_paths.cache_helper.clear() - - if hasattr(folder_paths, "cache"): - if hasattr(folder_paths.cache, "clear"): - folder_paths.cache.clear() - else: - folder_paths.cache = {} + if hasattr(folder_paths, "cache_helper") and hasattr(folder_paths.cache_helper, "clear"): + folder_paths.cache_helper.clear() - # 3. Aktiven Scan triggern für ALLE bekannten Kategorien - folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models", - "clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"] - - if hasattr(folder_paths, "folder_names"): - folders = list(folder_paths.folder_names.keys()) - - for folder in folders: - try: - # get_filename_list erzwingt bei leerem Cache einen neuen os.walk - folder_paths.get_filename_list(folder) - except: pass - - # 4. Den globalen object_info Cache im Server zurücksetzen - # Das zwingt den Server beim nächsten /object_info Aufruf alles neu zu berechnen + # 3. Zwinge den Server, beim nächsten API-Call die Daten frisch zu generieren server.PromptServer.instance.object_info = None - - print(f"[Model-Downloader] Backend-Scan & Object-Info Reset abgeschlossen.") + + # 4. Sende das Signal an unser JavaScript (welches dann app.refresh() auslöst) + server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"}) + + return web.json_response({"status": "success", "message": "Cache cleared and frontend notified"}) except Exception as e: print(f"[Model-Downloader] Fehler beim Backend-Reset: {e}") - - # 5. Signale an alle Browser senden - # Wir senden "refresh" (Standard für ComfyUI UI-Refresh) - server.PromptServer.instance.send_sync("refresh", {}) - # Unser spezieller Trigger für den Deep-UI-Sync - server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"}) - - return web.json_response({"status": "success", "message": "Backend reset and rescan complete"}) + return web.json_response({"status": "error", "message": str(e)}) WEB_DIRECTORY = "web" NODE_CLASS_MAPPINGS = {} diff --git a/src/main/resources/comfyui-bridge/__init__.py b/src/main/resources/comfyui-bridge/__init__.py index f83be0a..c39de71 100644 --- a/src/main/resources/comfyui-bridge/__init__.py +++ b/src/main/resources/comfyui-bridge/__init__.py @@ -1,75 +1,37 @@ # ComfyUI Model Downloader Bridge import server import folder_paths -import os import asyncio from aiohttp import web print("\033[95m[Model-Downloader] UI Extension Bridge active\033[0m") -# Erzwungene Pfad-Registrierung für den User-Pfad -USER_MODEL_PATH = r"C:\AI\comfyuidata\models" -if os.path.exists(USER_MODEL_PATH): - print(f"[Model-Downloader] Prüfe User-Modell-Pfad: {USER_MODEL_PATH}") - # Wir fügen den Pfad zu den Standard-Modell-Pfaden hinzu, falls er fehlt - # ComfyUI nutzt intern oft 'base_path' oder 'extra_model_paths' - try: - # Versuch, den Pfad als extra Modell-Pfad zu registrieren - # folder_paths.add_model_folder_path("custom", USER_MODEL_PATH) - pass - except: pass - @server.PromptServer.instance.routes.post("/kippster/model-downloaded") async def model_downloaded(request): - print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Erzwinge harten Backend-Reset...\033[0m") - + print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Leere Backend-Caches...\033[0m") + try: - # 1. Warte auf Dateisystem (besonders wichtig bei Windows/HDD) - await asyncio.sleep(1.0) - - # 2. Alle Caches in folder_paths radikal leeren + # 1. Kurze Pause für das Dateisystem (wichtig beim Entpacken/Verschieben) + await asyncio.sleep(0.5) + + # 2. Lösche gezielt die generierten Dateilisten aus dem ComfyUI Cache if hasattr(folder_paths, "filename_list_cache"): folder_paths.filename_list_cache.clear() - if hasattr(folder_paths, "cache_helper"): - if hasattr(folder_paths.cache_helper, "clear"): - folder_paths.cache_helper.clear() - - if hasattr(folder_paths, "cache"): - if hasattr(folder_paths.cache, "clear"): - folder_paths.cache.clear() - else: - folder_paths.cache = {} + if hasattr(folder_paths, "cache_helper") and hasattr(folder_paths.cache_helper, "clear"): + folder_paths.cache_helper.clear() - # 3. Aktiven Scan triggern für ALLE bekannten Kategorien - folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models", - "clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"] - - if hasattr(folder_paths, "folder_names"): - folders = list(folder_paths.folder_names.keys()) - - for folder in folders: - try: - # get_filename_list erzwingt bei leerem Cache einen neuen os.walk - folder_paths.get_filename_list(folder) - except: pass - - # 4. Den globalen object_info Cache im Server zurücksetzen - # Das zwingt den Server beim nächsten /object_info Aufruf alles neu zu berechnen + # 3. Zwinge den Server, beim nächsten API-Call die Daten frisch zu generieren server.PromptServer.instance.object_info = None - - print(f"[Model-Downloader] Backend-Scan & Object-Info Reset abgeschlossen.") + + # 4. Sende das Signal an unser JavaScript (welches dann app.refresh() auslöst) + server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"}) + + return web.json_response({"status": "success", "message": "Cache cleared and frontend notified"}) except Exception as e: print(f"[Model-Downloader] Fehler beim Backend-Reset: {e}") - - # 5. Signale an alle Browser senden - # Wir senden "refresh" (Standard für ComfyUI UI-Refresh) - server.PromptServer.instance.send_sync("refresh", {}) - # Unser spezieller Trigger für den Deep-UI-Sync - server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"}) - - return web.json_response({"status": "success", "message": "Backend reset and rescan complete"}) + return web.json_response({"status": "error", "message": str(e)}) WEB_DIRECTORY = "web" NODE_CLASS_MAPPINGS = {} diff --git a/src/main/resources/comfyui-bridge/web/downloader.js b/src/main/resources/comfyui-bridge/web/downloader.js index 788f3f1..875dacc 100644 --- a/src/main/resources/comfyui-bridge/web/downloader.js +++ b/src/main/resources/comfyui-bridge/web/downloader.js @@ -13,9 +13,10 @@ const initializeExtension = async () => { // 2. WebSocket Listener für den "Sync"-Trigger api.addEventListener("kippster-refresh-ui", async (event) => { - console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Erzwinge harten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;"); - + console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Starte echten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;"); + try { + // Zeige Feedback an const showToast = (text) => { const toast = document.createElement("div"); toast.style = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:rgba(255,204,0,0.9); color:black; padding:10px 20px; border-radius:5px; z-index:10001; font-weight:bold; font-family:sans-serif; pointer-events:none; transition: opacity 0.5s;"; @@ -23,77 +24,24 @@ const initializeExtension = async () => { document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = "0"; setTimeout(() => document.body.removeChild(toast), 500); }, 3000); }; - showToast("🔄 Erzwinge Refresh der Node-Definitionen..."); + showToast("🔄 Lade Node-Definitionen neu..."); - // A. Manager-spezifische Endpunkte triggern falls vorhanden - try { await fetch('/refresh_models', { method: 'POST' }); } catch(e) {} - try { await fetch('/manager/refresh_node_definitions', { method: 'POST' }); } catch(e) {} + // WICHTIG: Das ist der Gamechanger! Löscht den Frontend-Cache von ComfyUI. + // Ohne das hier lädt ComfyUI immer die alten Modell-Listen aus dem Browser-RAM. + api.nodeDefs = null; - // B. Pause für Backend-Stabilität - await new Promise(r => setTimeout(r, 1500)); + // Gib dem Windows-Dateisystem kurz Zeit, die kopierten/verschobenen Dateien zu registrieren + await new Promise(r => setTimeout(r, 800)); - // C. HARTER UI REFRESH (Der Kern-Befehl der das "Refresh Node Definitions" Button imitiert) - console.log("[Model-Downloader] Executing Deep Refresh..."); - - // 1. Hole frische Definitionen vom Server (triggert /object_info) - const nodeDefs = await api.getNodeDefs(); - - // 2. Aktualisiere die Custom Nodes Liste - if (api.getCustomNodes) await api.getCustomNodes(); - - // 3. Wenn ComfyUI V2 (app.refresh existiert) + // Ruft die offizielle ComfyUI Refresh-Funktion auf (das entspricht exakt dem Klick auf "Refresh" im Menü) + // app.refresh() zieht sich automatisch die neuen nodeDefs vom Server und "heilt" fehlerhafte Nodes. if (app.refresh) { await app.refresh(); } - let healedCount = 0; - - // 4. Jede Node auf dem Canvas zwingen, ihre Widgets neu zu evaluieren - if (app.graph && app.graph._nodes) { - for (const node of app.graph._nodes) { - const def = nodeDefs[node.type]; - if (def?.input?.required) { - for (const widgetName in def.input.required) { - const inputDef = def.input.required[widgetName]; - if (Array.isArray(inputDef[0])) { // Dropdown / Combo - const newValues = inputDef[0]; - const widget = node.widgets?.find(w => w.name === widgetName); - - if (widget && widget.type === "combo") { - const oldValue = widget.value; - widget.options.values = newValues; - - // Validierung & Heilung - if (newValues.includes(oldValue)) { - if (node.color === "#322" || node.color === "#a22") { - node.color = ""; - node.bgcolor = ""; - healedCount++; - } - } else if (oldValue && oldValue !== "None" && !newValues.includes(oldValue)) { - node.color = "#322"; - node.bgcolor = "#a22"; - } - } - } - } - } - // WICHTIG: Trigger node internal refresh - if (node.onRefresh) node.onRefresh(); - } - - // 5. Den Graphen zwingen, die Nodes neu zu zeichnen (entspricht graph.refreshNodes) - if (app.graph.refreshNodes) app.graph.refreshNodes(); - } - - // F. Canvas neu zeichnen + // Canvas zwingen, sich neu zu zeichnen app.graph.setDirtyCanvas(true, true); - - console.log(`[Model-Downloader] Deep-Sync abgeschlossen. Healed: ${healedCount}`); - - if (app.ui?.dialog?.show) { - app.ui.dialog.show("✅ Node-Definitionen wurden erzwungen aktualisiert!\nAlle Modell-Listen sind nun auf dem neuesten Stand."); - } + console.log("[Model-Downloader] Sync erfolgreich abgeschlossen."); } catch (e) { console.error("[Model-Downloader] Fehler beim erzwungenen Refresh:", e); diff --git a/web/downloader.js b/web/downloader.js index 788f3f1..875dacc 100644 --- a/web/downloader.js +++ b/web/downloader.js @@ -13,9 +13,10 @@ const initializeExtension = async () => { // 2. WebSocket Listener für den "Sync"-Trigger api.addEventListener("kippster-refresh-ui", async (event) => { - console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Erzwinge harten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;"); - + console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Starte echten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;"); + try { + // Zeige Feedback an const showToast = (text) => { const toast = document.createElement("div"); toast.style = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:rgba(255,204,0,0.9); color:black; padding:10px 20px; border-radius:5px; z-index:10001; font-weight:bold; font-family:sans-serif; pointer-events:none; transition: opacity 0.5s;"; @@ -23,77 +24,24 @@ const initializeExtension = async () => { document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = "0"; setTimeout(() => document.body.removeChild(toast), 500); }, 3000); }; - showToast("🔄 Erzwinge Refresh der Node-Definitionen..."); + showToast("🔄 Lade Node-Definitionen neu..."); - // A. Manager-spezifische Endpunkte triggern falls vorhanden - try { await fetch('/refresh_models', { method: 'POST' }); } catch(e) {} - try { await fetch('/manager/refresh_node_definitions', { method: 'POST' }); } catch(e) {} + // WICHTIG: Das ist der Gamechanger! Löscht den Frontend-Cache von ComfyUI. + // Ohne das hier lädt ComfyUI immer die alten Modell-Listen aus dem Browser-RAM. + api.nodeDefs = null; - // B. Pause für Backend-Stabilität - await new Promise(r => setTimeout(r, 1500)); + // Gib dem Windows-Dateisystem kurz Zeit, die kopierten/verschobenen Dateien zu registrieren + await new Promise(r => setTimeout(r, 800)); - // C. HARTER UI REFRESH (Der Kern-Befehl der das "Refresh Node Definitions" Button imitiert) - console.log("[Model-Downloader] Executing Deep Refresh..."); - - // 1. Hole frische Definitionen vom Server (triggert /object_info) - const nodeDefs = await api.getNodeDefs(); - - // 2. Aktualisiere die Custom Nodes Liste - if (api.getCustomNodes) await api.getCustomNodes(); - - // 3. Wenn ComfyUI V2 (app.refresh existiert) + // Ruft die offizielle ComfyUI Refresh-Funktion auf (das entspricht exakt dem Klick auf "Refresh" im Menü) + // app.refresh() zieht sich automatisch die neuen nodeDefs vom Server und "heilt" fehlerhafte Nodes. if (app.refresh) { await app.refresh(); } - let healedCount = 0; - - // 4. Jede Node auf dem Canvas zwingen, ihre Widgets neu zu evaluieren - if (app.graph && app.graph._nodes) { - for (const node of app.graph._nodes) { - const def = nodeDefs[node.type]; - if (def?.input?.required) { - for (const widgetName in def.input.required) { - const inputDef = def.input.required[widgetName]; - if (Array.isArray(inputDef[0])) { // Dropdown / Combo - const newValues = inputDef[0]; - const widget = node.widgets?.find(w => w.name === widgetName); - - if (widget && widget.type === "combo") { - const oldValue = widget.value; - widget.options.values = newValues; - - // Validierung & Heilung - if (newValues.includes(oldValue)) { - if (node.color === "#322" || node.color === "#a22") { - node.color = ""; - node.bgcolor = ""; - healedCount++; - } - } else if (oldValue && oldValue !== "None" && !newValues.includes(oldValue)) { - node.color = "#322"; - node.bgcolor = "#a22"; - } - } - } - } - } - // WICHTIG: Trigger node internal refresh - if (node.onRefresh) node.onRefresh(); - } - - // 5. Den Graphen zwingen, die Nodes neu zu zeichnen (entspricht graph.refreshNodes) - if (app.graph.refreshNodes) app.graph.refreshNodes(); - } - - // F. Canvas neu zeichnen + // Canvas zwingen, sich neu zu zeichnen app.graph.setDirtyCanvas(true, true); - - console.log(`[Model-Downloader] Deep-Sync abgeschlossen. Healed: ${healedCount}`); - - if (app.ui?.dialog?.show) { - app.ui.dialog.show("✅ Node-Definitionen wurden erzwungen aktualisiert!\nAlle Modell-Listen sind nun auf dem neuesten Stand."); - } + console.log("[Model-Downloader] Sync erfolgreich abgeschlossen."); } catch (e) { console.error("[Model-Downloader] Fehler beim erzwungenen Refresh:", e);