AudioManager Node: Dateien auflisten, abspielen, löschen
Neuer Node zeigt alle generierten Audio-Dateien mit Größe und Play-Button. Drei Modi: - Dateien auflisten (mit Abspielen im Browser) - Alle löschen - Nur Originale löschen (48kHz behalten) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9e0e541d7e
commit
b5af85a890
3 changed files with 168 additions and 2 deletions
|
|
@ -226,12 +226,69 @@ class AudioPreviewNode:
|
||||||
return {"ui": {"audio": [{"filename": os.path.basename(audio_file), "subfolder": "audio", "type": "output"}]}}
|
return {"ui": {"audio": [{"filename": os.path.basename(audio_file), "subfolder": "audio", "type": "output"}]}}
|
||||||
|
|
||||||
|
|
||||||
|
class AudioManagerNode:
|
||||||
|
"""Zeigt alle generierten Audio-Dateien an und kann sie löschen"""
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"action": (["Dateien auflisten", "Alle loeschen", "Nur Original loeschen (behalte 48kHz)"],),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "manage"
|
||||||
|
CATEGORY = "AudioCraft"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
def manage(self, action):
|
||||||
|
audio_dir = "/app/ComfyUI/output/audio"
|
||||||
|
if not os.path.exists(audio_dir):
|
||||||
|
return {"ui": {"text": [{"content": "Kein Audio-Ordner vorhanden."}], "files": []}}
|
||||||
|
|
||||||
|
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), "path": fpath})
|
||||||
|
|
||||||
|
if action == "Alle loeschen":
|
||||||
|
deleted = 0
|
||||||
|
for f in files:
|
||||||
|
os.remove(f["path"])
|
||||||
|
deleted += 1
|
||||||
|
print(f"[AudioManager] {deleted} Dateien geloescht ({total_size / 1024 / 1024:.1f} MB)")
|
||||||
|
return {"ui": {"text": [{"content": f"{deleted} Dateien geloescht ({total_size / 1024 / 1024:.1f} MB frei)"}], "files": []}}
|
||||||
|
|
||||||
|
elif action == "Nur Original loeschen (behalte 48kHz)":
|
||||||
|
deleted = 0
|
||||||
|
freed = 0
|
||||||
|
keep = []
|
||||||
|
for f in files:
|
||||||
|
if "_48000hz" not in f["name"]:
|
||||||
|
os.remove(f["path"])
|
||||||
|
deleted += 1
|
||||||
|
freed += f["size_mb"]
|
||||||
|
else:
|
||||||
|
keep.append(f)
|
||||||
|
print(f"[AudioManager] {deleted} Originale geloescht ({freed:.1f} MB frei)")
|
||||||
|
return {"ui": {"text": [{"content": f"{deleted} Originale geloescht ({freed:.1f} MB frei), {len(keep)} 48kHz Dateien behalten"}], "files": keep}}
|
||||||
|
|
||||||
|
# Auflisten
|
||||||
|
print(f"[AudioManager] {len(files)} Dateien, {total_size / 1024 / 1024:.1f} MB")
|
||||||
|
return {"ui": {"text": [{"content": f"{len(files)} Dateien | {total_size / 1024 / 1024:.1f} MB"}], "files": files}}
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"MusicGen": MusicGenNode,
|
"MusicGen": MusicGenNode,
|
||||||
"MusicGenLong": MusicGenLongNode,
|
"MusicGenLong": MusicGenLongNode,
|
||||||
"AudioGen": AudioGenNode,
|
"AudioGen": AudioGenNode,
|
||||||
"AudioUpsample": AudioUpsampleNode,
|
"AudioUpsample": AudioUpsampleNode,
|
||||||
"AudioPreview": AudioPreviewNode,
|
"AudioPreview": AudioPreviewNode,
|
||||||
|
"AudioManager": AudioManagerNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
|
@ -240,4 +297,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
"AudioGen": "AudioGen (Sound Effects)",
|
"AudioGen": "AudioGen (Sound Effects)",
|
||||||
"AudioUpsample": "Audio Upsample (Qualität)",
|
"AudioUpsample": "Audio Upsample (Qualität)",
|
||||||
"AudioPreview": "Audio Preview",
|
"AudioPreview": "Audio Preview",
|
||||||
|
"AudioManager": "Audio Manager (Dateien)",
|
||||||
}
|
}
|
||||||
|
|
|
||||||
95
comfyui-audio/comfyui_audiocraft/web/js/audioManager.js
Normal file
95
comfyui-audio/comfyui_audiocraft/web/js/audioManager.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { app } from "../../../scripts/app.js";
|
||||||
|
import { api } from "../../../scripts/api.js";
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Update existing widget content
|
||||||
|
this._managerContainer.innerHTML = "";
|
||||||
|
buildContent(this._managerContainer, summary, files);
|
||||||
|
const h = Math.min(60 + files.length * 22, 500);
|
||||||
|
this._managerWidget.computeSize = () => [this.size[0], h];
|
||||||
|
this.setSize([this.size[0], h + 80]);
|
||||||
|
} else {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.style.cssText = "padding:4px;overflow-y:auto;max-height:450px;";
|
||||||
|
buildContent(container, summary, files);
|
||||||
|
|
||||||
|
this._managerContainer = container;
|
||||||
|
this._managerWidget = this.addDOMWidget("audio_manager", "dom", container, {
|
||||||
|
serialize: false,
|
||||||
|
hideOnZoom: false,
|
||||||
|
});
|
||||||
|
const h = Math.min(60 + files.length * 22, 500);
|
||||||
|
this._managerWidget.computeSize = () => [this.size[0], h];
|
||||||
|
this.setSize([this.size[0], h + 80]);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.graph.setDirtyCanvas(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildContent(container, summary, files) {
|
||||||
|
const header = document.createElement("div");
|
||||||
|
header.textContent = summary;
|
||||||
|
header.style.cssText = "font-weight:bold;margin-bottom:6px;color:#fff;font-size:12px;";
|
||||||
|
container.appendChild(header);
|
||||||
|
|
||||||
|
if (files.length === 0) return;
|
||||||
|
|
||||||
|
for (const f of files) {
|
||||||
|
const row = document.createElement("div");
|
||||||
|
row.style.cssText = "display:flex;justify-content:space-between;padding:2px 0;font-size:11px;color:#ccc;border-bottom:1px solid #333;align-items:center;";
|
||||||
|
|
||||||
|
const name = document.createElement("span");
|
||||||
|
name.textContent = f.name;
|
||||||
|
name.style.cssText = "overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;margin-right:8px;";
|
||||||
|
|
||||||
|
const size = document.createElement("span");
|
||||||
|
size.textContent = f.size_mb + " MB";
|
||||||
|
size.style.cssText = "color:#8cf;min-width:60px;text-align:right;margin-right:8px;";
|
||||||
|
|
||||||
|
const playBtn = document.createElement("button");
|
||||||
|
playBtn.textContent = "\u25B6";
|
||||||
|
playBtn.style.cssText = "background:#444;border:none;color:#8f8;cursor:pointer;padding:1px 6px;border-radius:3px;font-size:10px;";
|
||||||
|
|
||||||
|
const audioSrc = api.apiURL(
|
||||||
|
`/view?filename=${encodeURIComponent(f.name)}&subfolder=audio&type=output`
|
||||||
|
);
|
||||||
|
|
||||||
|
let currentAudio = null;
|
||||||
|
playBtn.onclick = () => {
|
||||||
|
if (currentAudio) {
|
||||||
|
currentAudio.pause();
|
||||||
|
currentAudio = null;
|
||||||
|
playBtn.textContent = "\u25B6";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentAudio = new Audio(audioSrc);
|
||||||
|
currentAudio.play();
|
||||||
|
playBtn.textContent = "\u25A0";
|
||||||
|
currentAudio.onended = () => {
|
||||||
|
playBtn.textContent = "\u25B6";
|
||||||
|
currentAudio = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
row.appendChild(name);
|
||||||
|
row.appendChild(size);
|
||||||
|
row.appendChild(playBtn);
|
||||||
|
container.appendChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"last_node_id": 13,
|
"last_node_id": 14,
|
||||||
"last_link_id": 9,
|
"last_link_id": 9,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
|
|
@ -178,7 +178,20 @@
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"properties": {},
|
"properties": {},
|
||||||
"widgets_values": ["=== AudioGen SFX Guide (Meta Doku) ===\n\nModell: facebook/audiogen-medium (1.5B)\nTrainiert auf: 10s Segmente, 16kHz\nTrainingsdaten: Sonniss Game Effects,\n BBC SFX, AudioSet, AudioCaps u.a.\n\n--- Parameter pro SFX-Typ ---\n\nAttacken / Hits (1-2s):\n temperature 0.7 - 0.8\n cfg_coef 4.0 - 5.0\n top_k 150 - 200\n → scharf, praezise, wiederholbar\n\nFaehigkeiten / Spells (2-3s):\n temperature 0.9 - 1.0\n cfg_coef 3.0 - 4.0\n top_k 200 - 250\n → etwas mehr Variation erlaubt\n\nAmbient / Umgebung (5-10s):\n temperature 1.0 - 1.2\n cfg_coef 3.0\n top_k 250\n → natuerlich, organisch\n\nUI Sounds (0.5-1s):\n temperature 0.7\n cfg_coef 5.0\n top_k 150\n → kurz, knackig, konsistent\n\n--- Allgemeine Parameter ---\n\ntemperature:\n < 0.8 = fokussiert, deterministisch\n 1.0 = Standard, ausgewogen\n > 1.2 = mehr Variation, organischer\n\ncfg_coef:\n 3.0 = Standard (gut getestet)\n 4-5 = staerkere Prompt-Treue\n > 7 = Artefakte moeglich\n\ntop_k:\n 150 = eingeschraenkt, praezise\n 250 = Standard, gute Balance\n > 300 = zu viele Moeglichkeiten\n\nduration:\n Max Qualitaet bei <= 10s (Training)\n > 10s nutzt Sliding Window (Naehte)\n\n--- Prompt Aufbau ---\n\nSchema: [Aktion] [Material] [Detail] [Ort]\n\nBeispiele:\n heavy metal sword striking stone shield\n wooden door creaking open slowly dungeon\n fire crackling campfire burning wood\n metal chains rattling on stone floor\n footsteps echoing in stone corridor\n glass shattering breaking impact\n\nTipps:\n - Physikalisch beschreiben\n - Material + Groesse + Geschwindigkeit\n - Umgebung/Akustik angeben\n - Englisch (Trainingsdaten)\n - Kurz: 5-10 Woerter\n - 'sound effect' am Ende hilft\n - KEINE Musik-Begriffe\n - KEINE Vocals (nicht trainiert)\n\n--- Workflow Tipps ---\n\n - 3-5x generieren, bestes waehlen\n - Immer Upsample auf 48kHz\n - Verschiedene Seeds probieren\n - Mute (M) fuer inaktive Pipelines"]
|
"widgets_values": ["=== AudioGen SFX Guide ===\n\nModell: facebook/audiogen-medium (1.5B)\nTrainiert auf: 10s Segmente, 16kHz\nTrainingsdaten: Sonniss Game Effects,\n BBC SFX, AudioSet, AudioCaps u.a.\n\n--- Parameter pro SFX-Typ ---\n\nAttacken / Hits (1-2s):\n temperature 0.7 - 0.8\n cfg_coef 4.0 - 5.0\n top_k 150 - 200\n → scharf, praezise, wiederholbar\n\nFaehigkeiten / Spells (2-3s):\n temperature 0.9 - 1.0\n cfg_coef 3.0 - 4.0\n top_k 200 - 250\n → etwas mehr Variation erlaubt\n\nAmbient / Umgebung (5-10s):\n temperature 1.0 - 1.2\n cfg_coef 3.0\n top_k 250\n → natuerlich, organisch\n\nUI Sounds (0.5-1s):\n temperature 0.7\n cfg_coef 5.0\n top_k 150\n → kurz, knackig, konsistent\n\n--- Allgemeine Parameter ---\n\ntemperature:\n < 0.8 = fokussiert, deterministisch\n 1.0 = Standard, ausgewogen\n > 1.2 = mehr Variation, organischer\n\ncfg_coef:\n 3.0 = Standard (gut getestet)\n 4-5 = staerkere Prompt-Treue\n > 7 = Artefakte moeglich\n\ntop_k:\n 150 = eingeschraenkt, praezise\n 250 = Standard, gute Balance\n > 300 = zu viele Moeglichkeiten\n\nduration:\n Max Qualitaet bei <= 10s (Training)\n > 10s nutzt Sliding Window (Naehte)\n\n--- Prompt Aufbau ---\n\nSchema: [Aktion] [Material] [Detail] [Ort]\n\nBeispiele:\n heavy metal sword striking stone shield\n wooden door creaking open slowly dungeon\n fire crackling campfire burning wood\n metal chains rattling on stone floor\n footsteps echoing in stone corridor\n glass shattering breaking impact\n\nTipps:\n - Physikalisch beschreiben\n - Material + Groesse + Geschwindigkeit\n - Umgebung/Akustik angeben\n - Englisch (Trainingsdaten)\n - Kurz: 5-10 Woerter\n - 'sound effect' am Ende hilft\n - KEINE Musik-Begriffe\n - KEINE Vocals (nicht trainiert)\n\n--- Workflow Tipps ---\n\n - 3-5x generieren, bestes waehlen\n - Immer Upsample auf 48kHz\n - Verschiedene Seeds probieren\n - Mute (M) fuer inaktive Pipelines"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"type": "AudioManager",
|
||||||
|
"pos": [1150, 940],
|
||||||
|
"size": {"0": 520, "1": 400},
|
||||||
|
"flags": {},
|
||||||
|
"order": 10,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"properties": {"Node name for S&R": "AudioManager"},
|
||||||
|
"widgets_values": ["Dateien auflisten"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [
|
"links": [
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue