fix: even more aggressive refresh with toast notification and longer timeouts

This commit is contained in:
Thomas
2026-05-10 19:01:49 +02:00
parent 5fa6ce0290
commit 2d52be86a9
4 changed files with 146 additions and 118 deletions
+19 -16
View File
@@ -12,13 +12,13 @@ async def model_downloaded(request):
print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Erzwinge harten Backend-Reset...\033[0m")
try:
# 1. Dateisystem-Cache löschen (Kurze Pause für Windows-Dateisystem-Handles)
await asyncio.sleep(0.5)
# 1. Warte kurz auf das Dateisystem (besonders wichtig unter Windows)
await asyncio.sleep(0.8)
# Alle Caches in folder_paths radikal leeren
# 2. Alle Caches in folder_paths radikal leeren
# Wir löschen hier wirklich alles, was ComfyUI im RAM behält
if hasattr(folder_paths, "folder_names"):
for k in list(folder_paths.folder_names.keys()):
folder_paths.folder_names[k] = []
folder_paths.folder_names.clear()
if hasattr(folder_paths, "base_path_layers"):
for layer in folder_paths.base_path_layers.values():
@@ -28,30 +28,33 @@ async def model_downloaded(request):
if hasattr(folder_paths, "cache"):
folder_paths.cache.clear()
# 2. Den Definitions-Cache von ComfyUI löschen
# 3. Den Definitions-Cache von ComfyUI löschen
# Dies zwingt ComfyUI, die Node-Metadaten (inkl. Modell-Listen) neu zu generieren
server.PromptServer.instance.object_info = None
# 3. Aktiven Scan triggern (Offizieller Weg via get_filename_list)
# Wir decken hier alle gängigen Modell-Typen ab
folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models",
"clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"]
for folder in folders:
# 4. Aktiven Scan triggern
# Wir rufen get_filename_list für JEDEN bekannten Ordner auf
# Das zwingt das Backend, die Festplatte wirklich neu zu lesen
all_folders = list(folder_paths.folder_names.keys()) if hasattr(folder_paths, "folder_names") else []
# Fallback auf Standardordner falls Liste leer
if not all_folders:
all_folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models",
"clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"]
for folder in all_folders:
try:
folder_paths.get_filename_list(folder)
except: pass
print(f"[Model-Downloader] Backend-Reset abgeschlossen. Gescannte Ordner: {len(folders)}")
print(f"[Model-Downloader] Backend-Reset abgeschlossen. {len(all_folders)} Ordner neu indiziert.")
except Exception as e:
print(f"[Model-Downloader] Fehler beim Reset: {e}")
# 4. Signale an alle Browser
# "refresh_models" wird von vielen Extensions abgefangen
# 5. Signale an alle Browser
# Wir senden mehrere Signale um sicherzugehen, dass alle Extension-Typen reagieren
server.PromptServer.instance.send_sync("refresh_models", {})
# "refresh" ist der Standard für ComfyUI
server.PromptServer.instance.send_sync("refresh", {})
# Unser spezieller Trigger
server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"})
return web.json_response({"status": "success", "message": "Backend reset and rescan complete"})
+19 -16
View File
@@ -12,13 +12,13 @@ async def model_downloaded(request):
print("\033[92m🔥 [Model-Downloader] PING ERHALTEN! Erzwinge harten Backend-Reset...\033[0m")
try:
# 1. Dateisystem-Cache löschen (Kurze Pause für Windows-Dateisystem-Handles)
await asyncio.sleep(0.5)
# 1. Warte kurz auf das Dateisystem (besonders wichtig unter Windows)
await asyncio.sleep(0.8)
# Alle Caches in folder_paths radikal leeren
# 2. Alle Caches in folder_paths radikal leeren
# Wir löschen hier wirklich alles, was ComfyUI im RAM behält
if hasattr(folder_paths, "folder_names"):
for k in list(folder_paths.folder_names.keys()):
folder_paths.folder_names[k] = []
folder_paths.folder_names.clear()
if hasattr(folder_paths, "base_path_layers"):
for layer in folder_paths.base_path_layers.values():
@@ -28,30 +28,33 @@ async def model_downloaded(request):
if hasattr(folder_paths, "cache"):
folder_paths.cache.clear()
# 2. Den Definitions-Cache von ComfyUI löschen
# 3. Den Definitions-Cache von ComfyUI löschen
# Dies zwingt ComfyUI, die Node-Metadaten (inkl. Modell-Listen) neu zu generieren
server.PromptServer.instance.object_info = None
# 3. Aktiven Scan triggern (Offizieller Weg via get_filename_list)
# Wir decken hier alle gängigen Modell-Typen ab
folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models",
"clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"]
for folder in folders:
# 4. Aktiven Scan triggern
# Wir rufen get_filename_list für JEDEN bekannten Ordner auf
# Das zwingt das Backend, die Festplatte wirklich neu zu lesen
all_folders = list(folder_paths.folder_names.keys()) if hasattr(folder_paths, "folder_names") else []
# Fallback auf Standardordner falls Liste leer
if not all_folders:
all_folders = ["checkpoints", "loras", "vae", "controlnet", "diffusion_models", "upscale_models",
"clip", "unet", "gligen", "embeddings", "configs", "hypernetworks", "style_models"]
for folder in all_folders:
try:
folder_paths.get_filename_list(folder)
except: pass
print(f"[Model-Downloader] Backend-Reset abgeschlossen. Gescannte Ordner: {len(folders)}")
print(f"[Model-Downloader] Backend-Reset abgeschlossen. {len(all_folders)} Ordner neu indiziert.")
except Exception as e:
print(f"[Model-Downloader] Fehler beim Reset: {e}")
# 4. Signale an alle Browser
# "refresh_models" wird von vielen Extensions abgefangen
# 5. Signale an alle Browser
# Wir senden mehrere Signale um sicherzugehen, dass alle Extension-Typen reagieren
server.PromptServer.instance.send_sync("refresh_models", {})
# "refresh" ist der Standard für ComfyUI
server.PromptServer.instance.send_sync("refresh", {})
# Unser spezieller Trigger
server.PromptServer.instance.send_sync("kippster-refresh-ui", {"message": "ready"})
return web.json_response({"status": "success", "message": "Backend reset and rescan complete"})
@@ -16,26 +16,38 @@ const initializeExtension = async () => {
console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Erzwinge harten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;");
try {
// A. Offiziellen ComfyUI Refresh-Endpoint triggern (wie der Button im Manager)
// A. Benutzer informieren (kleiner Toast/Notification)
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;";
toast.textContent = text;
document.body.appendChild(toast);
setTimeout(() => { toast.style.opacity = "0"; setTimeout(() => document.body.removeChild(toast), 500); }, 3000);
};
showToast("🔄 Synchronisiere Modelle...");
// B. Offiziellen ComfyUI Refresh-Endpoint triggern
console.log("[Model-Downloader] Trigger /refresh_models...");
await fetch('/refresh_models', { method: 'POST' });
// B. Kurze Pause für das Backend
await new Promise(r => setTimeout(r, 1000));
// C. Großzügige Pause für das Dateisystem und Backend-Indizierung
await new Promise(r => setTimeout(r, 1500));
// C. Node-Definitionen neu laden (Zwingt den Browser die neuen Listen anzufragen)
// D. Node-Definitionen neu laden (Bypass Cache falls möglich)
console.log("[Model-Downloader] Re-fetching Node Definitions...");
const nodeDefs = await api.getNodeDefs();
let updatedCount = 0;
// D. Nodes auf dem Canvas manuell aktualisieren
let healedCount = 0;
let archivedCount = 0;
// E. Nodes auf dem Canvas manuell aktualisieren
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])) { // Es ist ein Dropdown (Combo)
if (Array.isArray(inputDef[0])) { // Dropdown
const newValues = inputDef[0];
const widget = node.widgets?.find(w => w.name === widgetName);
@@ -43,17 +55,19 @@ const initializeExtension = async () => {
const oldValue = widget.value;
widget.options.values = newValues;
// Check ob das ehemals rote Modell jetzt da ist
// Check ob das Modell jetzt da ist oder verschwunden ist
if (newValues.includes(oldValue)) {
// Modell vorhanden (oder wiederhergestellt)
if (node.color === "#322" || node.color === "#a22") {
node.color = "";
node.bgcolor = "";
updatedCount++;
healedCount++;
}
} else if (oldValue && oldValue !== "None" && !newValues.includes(oldValue)) {
// Modell fehlt immer noch -> rot markieren
// Modell fehlt (oder archiviert) -> rot markieren
node.color = "#322";
node.bgcolor = "#a22";
archivedCount++;
}
}
}
@@ -63,20 +77,18 @@ const initializeExtension = async () => {
}
}
// E. ComfyUI Standard-Refresh (lädt Sidebars etc. neu)
// F. ComfyUI Standard-Refresh & Canvas Update
if (app.refresh) await app.refresh();
// F. Canvas neu zeichnen
app.graph.setDirtyCanvas(true, true);
console.log(`[Model-Downloader] Sync abgeschlossen. ${updatedCount} Nodes aktualisiert.`);
console.log(`[Model-Downloader] Sync fertig. Healed: ${healedCount}, Archived: ${archivedCount}`);
const msg = updatedCount > 0
? `✅ Deep-Sync abgeschlossen!\n${updatedCount} Nodes wurden aktualisiert.`
: "✅ Synchronisation abgeschlossen! Die Modell-Listen sind aktuell.";
let finalMsg = "✅ Synchronisation abgeschlossen!";
if (healedCount > 0) finalMsg += `\n- ${healedCount} Modelle jetzt verfügbar.`;
if (archivedCount > 0) finalMsg += `\n- ${archivedCount} Modelle ins Archiv verschoben.`;
if (app.ui?.dialog?.show) app.ui.dialog.show(msg);
else alert(msg);
if (app.ui?.dialog?.show) app.ui.dialog.show(finalMsg);
else alert(finalMsg);
} catch (e) {
console.error("[Model-Downloader] Fehler beim erzwungenen Refresh:", e);
@@ -96,31 +108,30 @@ const initializeExtension = async () => {
await loadConfig();
const addUI = () => {
if (!document.getElementById("tki-downloader-fab")) {
const fab = document.createElement("div");
fab.id = "tki-downloader-fab";
fab.innerHTML = "🚀";
fab.title = "An Model Downloader senden";
fab.style = "position:fixed; bottom:30px; right:30px; z-index:10000; cursor:pointer; font-size:30px; background:#ffcc00; border-radius:50%; width:60px; height:60px; display:flex; align-items:center; justify-content:center; box-shadow:0 0 20px rgba(0,0,0,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 = async () => {
if (!app?.graph) return;
try {
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(app.graph.serialize())
});
if (response.ok) {
if (app.ui?.dialog?.show) app.ui.dialog.show("🚀 Workflow an Downloader gesendet!");
}
} catch (e) {
alert("❌ Downloader App nicht erreichbar!");
if (document.getElementById("tki-downloader-fab")) return;
const fab = document.createElement("div");
fab.id = "tki-downloader-fab";
fab.innerHTML = "🚀";
fab.title = "An Model Downloader senden";
fab.style = "position:fixed; bottom:30px; right:30px; z-index:10000; cursor:pointer; font-size:30px; background:#ffcc00; border-radius:50%; width:60px; height:60px; display:flex; align-items:center; justify-content:center; box-shadow:0 0 20px rgba(0,0,0,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 = async () => {
if (!app?.graph) return;
try {
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(app.graph.serialize())
});
if (response.ok) {
if (app.ui?.dialog?.show) app.ui.dialog.show("🚀 Workflow an Downloader gesendet!");
}
};
document.body.appendChild(fab);
}
} catch (e) {
alert("❌ Downloader App nicht erreichbar!");
}
};
document.body.appendChild(fab);
};
addUI();
};
+54 -43
View File
@@ -16,26 +16,38 @@ const initializeExtension = async () => {
console.log("%c🔥 [Model-Downloader] SIGNAL ERHALTEN! Erzwinge harten UI-Refresh...", "background: #ff0000; color: #fff; font-size: 14px;");
try {
// A. Offiziellen ComfyUI Refresh-Endpoint triggern (wie der Button im Manager)
// A. Benutzer informieren (kleiner Toast/Notification)
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;";
toast.textContent = text;
document.body.appendChild(toast);
setTimeout(() => { toast.style.opacity = "0"; setTimeout(() => document.body.removeChild(toast), 500); }, 3000);
};
showToast("🔄 Synchronisiere Modelle...");
// B. Offiziellen ComfyUI Refresh-Endpoint triggern
console.log("[Model-Downloader] Trigger /refresh_models...");
await fetch('/refresh_models', { method: 'POST' });
// B. Kurze Pause für das Backend
await new Promise(r => setTimeout(r, 1000));
// C. Großzügige Pause für das Dateisystem und Backend-Indizierung
await new Promise(r => setTimeout(r, 1500));
// C. Node-Definitionen neu laden (Zwingt den Browser die neuen Listen anzufragen)
// D. Node-Definitionen neu laden (Bypass Cache falls möglich)
console.log("[Model-Downloader] Re-fetching Node Definitions...");
const nodeDefs = await api.getNodeDefs();
let updatedCount = 0;
// D. Nodes auf dem Canvas manuell aktualisieren
let healedCount = 0;
let archivedCount = 0;
// E. Nodes auf dem Canvas manuell aktualisieren
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])) { // Es ist ein Dropdown (Combo)
if (Array.isArray(inputDef[0])) { // Dropdown
const newValues = inputDef[0];
const widget = node.widgets?.find(w => w.name === widgetName);
@@ -43,17 +55,19 @@ const initializeExtension = async () => {
const oldValue = widget.value;
widget.options.values = newValues;
// Check ob das ehemals rote Modell jetzt da ist
// Check ob das Modell jetzt da ist oder verschwunden ist
if (newValues.includes(oldValue)) {
// Modell vorhanden (oder wiederhergestellt)
if (node.color === "#322" || node.color === "#a22") {
node.color = "";
node.bgcolor = "";
updatedCount++;
healedCount++;
}
} else if (oldValue && oldValue !== "None" && !newValues.includes(oldValue)) {
// Modell fehlt immer noch -> rot markieren
// Modell fehlt (oder archiviert) -> rot markieren
node.color = "#322";
node.bgcolor = "#a22";
archivedCount++;
}
}
}
@@ -63,20 +77,18 @@ const initializeExtension = async () => {
}
}
// E. ComfyUI Standard-Refresh (lädt Sidebars etc. neu)
// F. ComfyUI Standard-Refresh & Canvas Update
if (app.refresh) await app.refresh();
// F. Canvas neu zeichnen
app.graph.setDirtyCanvas(true, true);
console.log(`[Model-Downloader] Sync abgeschlossen. ${updatedCount} Nodes aktualisiert.`);
console.log(`[Model-Downloader] Sync fertig. Healed: ${healedCount}, Archived: ${archivedCount}`);
const msg = updatedCount > 0
? `✅ Deep-Sync abgeschlossen!\n${updatedCount} Nodes wurden aktualisiert.`
: "✅ Synchronisation abgeschlossen! Die Modell-Listen sind aktuell.";
let finalMsg = "✅ Synchronisation abgeschlossen!";
if (healedCount > 0) finalMsg += `\n- ${healedCount} Modelle jetzt verfügbar.`;
if (archivedCount > 0) finalMsg += `\n- ${archivedCount} Modelle ins Archiv verschoben.`;
if (app.ui?.dialog?.show) app.ui.dialog.show(msg);
else alert(msg);
if (app.ui?.dialog?.show) app.ui.dialog.show(finalMsg);
else alert(finalMsg);
} catch (e) {
console.error("[Model-Downloader] Fehler beim erzwungenen Refresh:", e);
@@ -96,31 +108,30 @@ const initializeExtension = async () => {
await loadConfig();
const addUI = () => {
if (!document.getElementById("tki-downloader-fab")) {
const fab = document.createElement("div");
fab.id = "tki-downloader-fab";
fab.innerHTML = "🚀";
fab.title = "An Model Downloader senden";
fab.style = "position:fixed; bottom:30px; right:30px; z-index:10000; cursor:pointer; font-size:30px; background:#ffcc00; border-radius:50%; width:60px; height:60px; display:flex; align-items:center; justify-content:center; box-shadow:0 0 20px rgba(0,0,0,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 = async () => {
if (!app?.graph) return;
try {
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(app.graph.serialize())
});
if (response.ok) {
if (app.ui?.dialog?.show) app.ui.dialog.show("🚀 Workflow an Downloader gesendet!");
}
} catch (e) {
alert("❌ Downloader App nicht erreichbar!");
if (document.getElementById("tki-downloader-fab")) return;
const fab = document.createElement("div");
fab.id = "tki-downloader-fab";
fab.innerHTML = "🚀";
fab.title = "An Model Downloader senden";
fab.style = "position:fixed; bottom:30px; right:30px; z-index:10000; cursor:pointer; font-size:30px; background:#ffcc00; border-radius:50%; width:60px; height:60px; display:flex; align-items:center; justify-content:center; box-shadow:0 0 20px rgba(0,0,0,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 = async () => {
if (!app?.graph) return;
try {
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(app.graph.serialize())
});
if (response.ok) {
if (app.ui?.dialog?.show) app.ui.dialog.show("🚀 Workflow an Downloader gesendet!");
}
};
document.body.appendChild(fab);
}
} catch (e) {
alert("❌ Downloader App nicht erreichbar!");
}
};
document.body.appendChild(fab);
};
addUI();
};