From 8743133c43e737c3985ddd9a8c5442e1eab1adf8 Mon Sep 17 00:00:00 2001 From: Andre Date: Sat, 21 Mar 2026 20:26:34 +0100 Subject: [PATCH] AudioManager: Auto-Refresh nach jeder Generierung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dateiliste wird jetzt per API (/audiocraft/list) geladen statt über Node-Execution. Aktualisiert sich automatisch nach jeder Generierung. Refresh-Button für manuelles Update. Co-Authored-By: Claude Opus 4.6 --- comfyui-audio/comfyui_audiocraft/__init__.py | 21 ++ .../comfyui_audiocraft/web/js/audioManager.js | 195 +++++++++++------- 2 files changed, 136 insertions(+), 80 deletions(-) diff --git a/comfyui-audio/comfyui_audiocraft/__init__.py b/comfyui-audio/comfyui_audiocraft/__init__.py index c56c6a1..9fd4a48 100644 --- a/comfyui-audio/comfyui_audiocraft/__init__.py +++ b/comfyui-audio/comfyui_audiocraft/__init__.py @@ -27,4 +27,25 @@ async def delete_audio_file(request): return web.json_response({"ok": True, "deleted": filename}) + +# API-Endpoint zum Auflisten aller Audio-Dateien +@server.PromptServer.instance.routes.get("/audiocraft/list") +async def list_audio_files(request): + audio_dir = "/app/ComfyUI/output/audio" + if not os.path.exists(audio_dir): + return web.json_response({"files": [], "summary": "Kein Audio-Ordner"}) + + files = [] + total_size = 0 + for f in sorted(os.listdir(audio_dir)): + if f.endswith(".wav"): + fpath = os.path.join(audio_dir, f) + size = os.path.getsize(fpath) + total_size += size + files.append({"name": f, "size_mb": round(size / 1024 / 1024, 2)}) + + summary = f"{len(files)} Dateien | {total_size / 1024 / 1024:.1f} MB" + return web.json_response({"files": files, "summary": summary}) + + __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] diff --git a/comfyui-audio/comfyui_audiocraft/web/js/audioManager.js b/comfyui-audio/comfyui_audiocraft/web/js/audioManager.js index 244b7a5..95bf231 100644 --- a/comfyui-audio/comfyui_audiocraft/web/js/audioManager.js +++ b/comfyui-audio/comfyui_audiocraft/web/js/audioManager.js @@ -2,45 +2,6 @@ import { app } from "../../../scripts/app.js"; import { api } from "../../../scripts/api.js"; let loopEnabled = false; - -app.registerExtension({ - name: "AudioCraft.AudioManager", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeData.name === "AudioManager") { - const onExecuted = nodeType.prototype.onExecuted; - nodeType.prototype.onExecuted = function (message) { - onExecuted?.apply(this, arguments); - - if (!message?.text) return; - - const summary = message.text[0]?.content || ""; - const files = message.files || []; - - if (this._managerWidget) { - this._managerContainer.innerHTML = ""; - buildContent(this._managerContainer, summary, files); - } else { - const container = document.createElement("div"); - container.style.cssText = "padding:4px;overflow-y:auto;"; - buildContent(container, summary, files); - - this._managerContainer = container; - this._managerWidget = this.addDOMWidget("audio_manager", "dom", container, { - serialize: false, - hideOnZoom: false, - }); - } - - const listHeight = Math.min(40 + files.length * 26, 800); - this._managerWidget.computeSize = () => [this.size[0], listHeight]; - this.setSize([Math.max(this.size[0], 500), listHeight + 80]); - app.graph.setDirtyCanvas(true); - }; - } - }, -}); - -// Track currently playing audio globally so only one plays at a time let activeAudio = null; let activeBtn = null; @@ -56,30 +17,107 @@ function stopActive() { } } +async function refreshFileList(node) { + try { + const resp = await api.fetchApi("/audiocraft/list"); + const data = await resp.json(); + const container = node._managerContainer; + container.innerHTML = ""; + buildContent(container, data.summary, data.files); + + const listHeight = Math.min(40 + data.files.length * 26, 800); + node._managerWidget.computeSize = () => [node.size[0], listHeight]; + node.setSize([Math.max(node.size[0], 500), listHeight + 80]); + app.graph.setDirtyCanvas(true); + } catch (e) { + console.error("Refresh failed:", e); + } +} + +app.registerExtension({ + name: "AudioCraft.AudioManager", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData.name === "AudioManager") { + const origOnNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + origOnNodeCreated?.apply(this, arguments); + + const container = document.createElement("div"); + container.style.cssText = "padding:4px;overflow-y:auto;"; + + const loadingMsg = document.createElement("div"); + loadingMsg.textContent = "Klicke \u21BB Refresh um Dateien zu laden"; + loadingMsg.style.cssText = "color:#888;font-size:12px;padding:8px;"; + container.appendChild(loadingMsg); + + this._managerContainer = container; + this._managerWidget = this.addDOMWidget("audio_manager", "dom", container, { + serialize: false, + hideOnZoom: false, + }); + this._managerWidget.computeSize = () => [this.size[0], 60]; + + // Auto-refresh on creation + setTimeout(() => refreshFileList(this), 500); + }; + + // Also refresh when node is executed via Queue + const onExecuted = nodeType.prototype.onExecuted; + nodeType.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + refreshFileList(this); + }; + } + }, +}); + +// Listen for any prompt execution to auto-refresh AudioManager nodes +api.addEventListener("executed", (event) => { + // After any node executes, refresh all AudioManager nodes + if (event?.detail?.node) { + const nodes = app.graph._nodes.filter(n => n.type === "AudioManager"); + for (const node of nodes) { + if (node._managerContainer) { + refreshFileList(node); + } + } + } +}); + function buildContent(container, summary, files) { - // Header with summary + loop toggle const headerRow = document.createElement("div"); - headerRow.style.cssText = "display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:4px;background:#333;border-radius:4px;"; + headerRow.style.cssText = "display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:4px;background:#333;border-radius:4px;gap:4px;"; const headerText = document.createElement("span"); headerText.textContent = summary; - headerText.style.cssText = "font-weight:bold;color:#fff;font-size:13px;"; + headerText.style.cssText = "font-weight:bold;color:#fff;font-size:12px;white-space:nowrap;"; - const loopBtn = document.createElement("button"); - loopBtn.textContent = loopEnabled ? "\uD83D\uDD01 Loop AN" : "\uD83D\uDD01 Loop AUS"; - loopBtn.style.cssText = `background:${loopEnabled ? "#2a5a2a" : "#444"};border:none;color:${loopEnabled ? "#8f8" : "#888"};cursor:pointer;padding:3px 8px;border-radius:3px;font-size:11px;`; - loopBtn.onclick = () => { - loopEnabled = !loopEnabled; - loopBtn.textContent = loopEnabled ? "\uD83D\uDD01 Loop AN" : "\uD83D\uDD01 Loop AUS"; - loopBtn.style.background = loopEnabled ? "#2a5a2a" : "#444"; - loopBtn.style.color = loopEnabled ? "#8f8" : "#888"; - if (activeAudio) { - activeAudio.loop = loopEnabled; - } + const btnGroup = document.createElement("div"); + btnGroup.style.cssText = "display:flex;gap:4px;"; + + const refreshBtn = document.createElement("button"); + refreshBtn.textContent = "\u21BB Refresh"; + refreshBtn.style.cssText = "background:#2a3a5a;border:none;color:#8cf;cursor:pointer;padding:3px 8px;border-radius:3px;font-size:11px;white-space:nowrap;"; + refreshBtn.onclick = () => { + const node = app.graph._nodes.find(n => n._managerContainer === container.parentElement || n._managerContainer === container); + if (node) refreshFileList(node); }; + const loopBtn = document.createElement("button"); + loopBtn.textContent = loopEnabled ? "\uD83D\uDD01 Loop AN" : "\uD83D\uDD01 Loop"; + loopBtn.style.cssText = `background:${loopEnabled ? "#2a5a2a" : "#444"};border:none;color:${loopEnabled ? "#8f8" : "#888"};cursor:pointer;padding:3px 8px;border-radius:3px;font-size:11px;white-space:nowrap;`; + loopBtn.onclick = () => { + loopEnabled = !loopEnabled; + loopBtn.textContent = loopEnabled ? "\uD83D\uDD01 Loop AN" : "\uD83D\uDD01 Loop"; + loopBtn.style.background = loopEnabled ? "#2a5a2a" : "#444"; + loopBtn.style.color = loopEnabled ? "#8f8" : "#888"; + if (activeAudio) activeAudio.loop = loopEnabled; + }; + + btnGroup.appendChild(refreshBtn); + btnGroup.appendChild(loopBtn); headerRow.appendChild(headerText); - headerRow.appendChild(loopBtn); + headerRow.appendChild(btnGroup); container.appendChild(headerRow); if (files.length === 0) return; @@ -112,13 +150,34 @@ function buildContent(container, summary, files) { dlBtn.title = "Download"; dlBtn.style.cssText = "color:#8cf;text-decoration:none;font-size:13px;padding:0 4px;"; + const delBtn = document.createElement("button"); + delBtn.textContent = "\u2716"; + delBtn.title = "Loeschen"; + delBtn.style.cssText = "background:#533;border:none;color:#f66;cursor:pointer;padding:2px 6px;border-radius:3px;font-size:11px;margin-left:4px;"; + delBtn.onclick = async () => { + if (activeBtn === playBtn) stopActive(); + try { + const resp = await api.fetchApi("/audiocraft/delete", { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({filename: f.name}), + }); + if (resp.ok) { + row.style.opacity = "0.3"; + row.style.pointerEvents = "none"; + delBtn.textContent = "\u2714"; + delBtn.style.color = "#888"; + } + } catch (e) { + console.error("Delete failed:", e); + } + }; + playBtn.onclick = () => { - // If clicking the same button that's playing, stop it if (activeBtn === playBtn) { stopActive(); return; } - // Stop any other playing audio first stopActive(); const audio = new Audio(audioSrc); @@ -139,30 +198,6 @@ function buildContent(container, summary, files) { }; }; - const delBtn = document.createElement("button"); - delBtn.textContent = "\u2716"; - delBtn.title = "Loeschen"; - delBtn.style.cssText = "background:#533;border:none;color:#f66;cursor:pointer;padding:2px 6px;border-radius:3px;font-size:11px;margin-left:4px;"; - delBtn.onclick = async () => { - // Stop if this file is playing - if (activeBtn === playBtn) stopActive(); - try { - const resp = await api.fetchApi("/audiocraft/delete", { - method: "POST", - headers: {"Content-Type": "application/json"}, - body: JSON.stringify({filename: f.name}), - }); - if (resp.ok) { - row.style.opacity = "0.3"; - row.style.pointerEvents = "none"; - delBtn.textContent = "\u2714"; - delBtn.style.color = "#888"; - } - } catch (e) { - console.error("Delete failed:", e); - } - }; - row.appendChild(name); row.appendChild(size); row.appendChild(playBtn);