DungeonCrawler/comfyui-audio/comfyui_audiocraft/web/js/audioManager.js
Andre 8743133c43 AudioManager: Auto-Refresh nach jeder Generierung
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 <noreply@anthropic.com>
2026-03-21 20:26:34 +01:00

208 lines
8.1 KiB
JavaScript

import { app } from "../../../scripts/app.js";
import { api } from "../../../scripts/api.js";
let loopEnabled = false;
let activeAudio = null;
let activeBtn = null;
function stopActive() {
if (activeAudio) {
activeAudio.pause();
activeAudio = null;
}
if (activeBtn) {
activeBtn.textContent = "\u25B6";
activeBtn.style.color = "#8f8";
activeBtn = null;
}
}
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) {
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;gap:4px;";
const headerText = document.createElement("span");
headerText.textContent = summary;
headerText.style.cssText = "font-weight:bold;color:#fff;font-size:12px;white-space:nowrap;";
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(btnGroup);
container.appendChild(headerRow);
if (files.length === 0) return;
for (const f of files) {
const row = document.createElement("div");
row.style.cssText = "display:flex;align-items:center;padding:3px 2px;font-size:11px;color:#ccc;border-bottom:1px solid #333;min-height:22px;";
const name = document.createElement("span");
name.textContent = f.name;
name.style.cssText = "overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;margin-right:6px;";
const size = document.createElement("span");
size.textContent = f.size_mb + " MB";
size.style.cssText = "color:#8cf;min-width:55px;text-align:right;margin-right:6px;font-size:10px;";
const playBtn = document.createElement("button");
playBtn.textContent = "\u25B6";
playBtn.title = "Abspielen";
playBtn.style.cssText = "background:#444;border:none;color:#8f8;cursor:pointer;padding:2px 8px;border-radius:3px;font-size:11px;margin-right:4px;";
const audioSrc = api.apiURL(
`/view?filename=${encodeURIComponent(f.name)}&subfolder=audio&type=output`
);
const dlBtn = document.createElement("a");
dlBtn.href = audioSrc;
dlBtn.download = f.name;
dlBtn.textContent = "\u2B07";
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 (activeBtn === playBtn) {
stopActive();
return;
}
stopActive();
const audio = new Audio(audioSrc);
audio.loop = loopEnabled;
audio.play();
activeAudio = audio;
activeBtn = playBtn;
playBtn.textContent = "\u25A0";
playBtn.style.color = "#f88";
audio.onended = () => {
if (!audio.loop) {
playBtn.textContent = "\u25B6";
playBtn.style.color = "#8f8";
activeAudio = null;
activeBtn = null;
}
};
};
row.appendChild(name);
row.appendChild(size);
row.appendChild(playBtn);
row.appendChild(dlBtn);
row.appendChild(delBtn);
container.appendChild(row);
}
}