From 82bd383447fa86c7c8c3babb7739a059de7c0e9b Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 30 Apr 2026 21:16:04 +0200 Subject: [PATCH] feat: include ComfyUI custom node bridge in repository --- comfyui-model-downloader/__init__.py | 5 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 361 bytes comfyui-model-downloader/web/downloader.js | 190 ++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 comfyui-model-downloader/__init__.py create mode 100644 comfyui-model-downloader/__pycache__/__init__.cpython-312.pyc create mode 100644 comfyui-model-downloader/web/downloader.js diff --git a/comfyui-model-downloader/__init__.py b/comfyui-model-downloader/__init__.py new file mode 100644 index 0000000..c3b2092 --- /dev/null +++ b/comfyui-model-downloader/__init__.py @@ -0,0 +1,5 @@ +# ComfyUI Model Downloader Bridge +print("\033[95m[Model-Downloader] UI Extension Bridge active\033[0m") +WEB_DIRECTORY = "web" +NODE_CLASS_MAPPINGS = {} +__all__ = ["WEB_DIRECTORY", "NODE_CLASS_MAPPINGS"] diff --git a/comfyui-model-downloader/__pycache__/__init__.cpython-312.pyc b/comfyui-model-downloader/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b38ecf368a8f0e915758f15e44807196a70eb15 GIT binary patch literal 361 zcmX@j%ge<81ldKOGtUF*#~=<2Fhd!iRe+4?3``8E3@Hq&86l#K3@MCJjF}8kOest$ z%;`)~%#|#ftW{>x(UzvU(Z2a9sX4kX`Q>>z`H3m1MX?H@o(irNC8>GEnfZANPDPn1 z>8T2d$t9U(snXE~xwn|hQVQ{@6AlkrtLrmfVgYXS*sS7fO7r2ZX*^4-U F3IT0{Xd3_k literal 0 HcmV?d00001 diff --git a/comfyui-model-downloader/web/downloader.js b/comfyui-model-downloader/web/downloader.js new file mode 100644 index 0000000..413f015 --- /dev/null +++ b/comfyui-model-downloader/web/downloader.js @@ -0,0 +1,190 @@ +// Try different ways to get the ComfyUI app object +let app = null; + +const tryImports = async () => { + const paths = [ + "../../scripts/app.js", + "/scripts/app.js", + "../../../scripts/app.js" + ]; + + for (const path of paths) { + try { + const mod = await import(path); + if (mod.app) { + app = mod.app; + console.log(`[Model-Downloader] Successfully imported app from ${path}`); + break; + } + } catch (e) {} + } +}; + +await tryImports(); + +console.log("%c[Model-Downloader] DEBUG: JS File Loaded", "background: #222; color: #ff69b4; font-size: 20px;"); + +// Define setup logic that can be called by extension or as fallback +const initializeExtension = async () => { + console.log("%c[Model-Downloader] DEBUG: Initializing Extension Logic", "background: #222; color: #ff69b4; font-size: 16px;"); + + let apiToken = null; + + // Load token from local config.json (generated during installation) + const loadConfig = async () => { + try { + // Use import.meta.url to get the correct relative path for the extension + const baseUrl = new URL(".", import.meta.url).href; + const configUrl = `${baseUrl}config.json?v=${Date.now()}`; + + console.log("[Model-Downloader] Fetching config from:", configUrl); + const response = await fetch(configUrl); + if (response.ok) { + const config = await response.json(); + apiToken = config.token; + if (apiToken) { + console.log("[Model-Downloader] Pre-shared Token loaded."); + } else { + console.warn("[Model-Downloader] Token in config.json is empty!"); + } + } else { + console.warn("[Model-Downloader] config.json not found (Status: " + response.status + "). Did you run the installer in the Java App?"); + } + } catch (e) { + console.error("[Model-Downloader] Failed to load config.json:", e); + } + }; + + // Trigger config load in background + loadConfig(); + + const sendWorkflow = async () => { + if (!app || !app.graph) { + console.error("[Model-Downloader] App or Graph not ready."); + const notify = (msg) => { + if (window.comfyAPI?.dialog?.show) window.comfyAPI.dialog.show(msg); + else alert(msg); + }; + notify("❌ Fehler: ComfyUI Graph nicht bereit."); + return; + } + const workflow = app.graph.serialize(); + console.log("[Model-Downloader] Workflow:", workflow); + + const notify = (msg) => { + if (app.ui?.dialog?.show) app.ui.dialog.show(msg); + else if (window.comfyAPI?.dialog?.show) window.comfyAPI.dialog.show(msg); + else alert(msg); + }; + + if (!apiToken) { + notify("❌ Sicherheits-Fehler: Kein API-Token gefunden. Bitte führe den Installer in der Java-App aus."); + return; + } + + try { + console.log("[Model-Downloader] Sending request to 127.0.0.1:12345..."); + const response = await fetch("http://127.0.0.1:12345/import", { + method: "POST", + mode: "cors", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${apiToken}` + }, + body: JSON.stringify(workflow) + }); + + if (response.ok) { + notify("🚀 Workflow erfolgreich gesendet!"); + } else if (response.status === 401) { + notify("❌ Sicherheits-Fehler: Token ungültig. Bitte Downloader neu starten."); + apiToken = null; + } else { + notify("❌ Fehler: Downloader abgelehnt (Status: " + response.status + ")"); + } + } catch (e) { + notify("❌ Keine Verbindung zum Downloader-Tool! Läuft die Java-App?"); + } + }; + + // Strategy 1: The Modern FAB (Always works if DOM is ready) + const addFAB = () => { + if (document.getElementById("tki-downloader-fab")) return; + console.log("[Model-Downloader] Adding FAB..."); + const fab = document.createElement("div"); + fab.id = "tki-downloader-fab"; + fab.innerHTML = "🚀"; + fab.title = "Send to Model Downloader"; + fab.style = "position:fixed; bottom:30px; right:30px; z-index:10000; cursor:pointer; font-size:30px; background:#ff69b4; border-radius:50%; width:60px; height:60px; display:flex; align-items:center; justify-content:center; box-shadow:0 0 20px rgba(255,105,180,0.5); border: 2px solid white; transition: transform 0.2s;"; + fab.onmouseover = () => fab.style.transform = "scale(1.1)"; + fab.onmouseout = () => fab.style.transform = "scale(1.0)"; + fab.onclick = sendWorkflow; + document.body.appendChild(fab); + }; + + // Strategy 2: Primary Action (V2 UI) + const addV2Action = () => { + if (app?.ui?.menu?.addPrimaryAction) { + console.log("[Model-Downloader] Adding to V2 Menu..."); + app.ui.menu.addPrimaryAction({ + id: "tki.model-downloader.send", + icon: "pi pi-download", + label: "Send to Downloader", + callback: sendWorkflow + }); + } + if (app?.ui?.toolbar?.addPrimaryAction) { + console.log("[Model-Downloader] Adding to V2 Toolbar..."); + app.ui.toolbar.addPrimaryAction({ + id: "tki.model-downloader.send-toolbar", + icon: "pi pi-download", + label: "Send to Downloader", + callback: sendWorkflow + }); + } + }; + + // Strategy 3: Classic Menu + const addClassic = () => { + const menu = document.querySelector(".comfy-menu"); + if (menu) { + if (document.getElementById("tki-downloader-classic")) return; + console.log("[Model-Downloader] Adding to Classic Menu..."); + const btn = document.createElement("button"); + btn.id = "tki-downloader-classic"; + btn.textContent = "🚀 Downloader"; + btn.style.background = "#ff69b4"; + btn.style.color = "white"; + btn.onclick = sendWorkflow; + menu.appendChild(btn); + } + }; + + // Run immediately + addFAB(); + addClassic(); + addV2Action(); + + // Retry with delays to ensure UI is ready + setTimeout(addFAB, 1000); + setTimeout(addFAB, 5000); + setTimeout(addClassic, 2000); + setTimeout(addV2Action, 3000); +}; + +if (app) { + app.registerExtension({ + name: "TKI.ModelDownloader", + async setup() { + await initializeExtension(); + } + }); +} else { + console.warn("[Model-Downloader] App object not found. Falling back to manual initialization."); + // Fallback if app is not found or registration fails + if (document.readyState === "complete") { + initializeExtension(); + } else { + window.addEventListener("load", initializeExtension); + } +}