Files
DemonGatanjieuandClaude Opus 5.5 721a006b16 refactor(audio): drop F5-TTS, the audio page manages GPT-SoVITS only
The F5-TTS voice library (input/F5-TTS scanning, voice upload with ffmpeg
conversion, romanization, Load Audio clip drags) and the engine switch are
removed. The audio page now works with one engine, Anomalous_TTS
(GPT-SoVITS): characters, drags onto its node, the Script Director with
direct generation, pronunciation and settings. The audio gallery and its
stream route stay (output and temp folders only). The root PATH hack that
existed for F5-TTS's ffmpeg lookup is gone. Recorded as AD-016.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:53:07 +08:00

41 lines
1.7 KiB
JavaScript

import { comboValueForPath } from './audio_script.js';
/**
* The only canvas nodes the audio studio writes into. No guessing by widget name:
* a node that is not listed here is refused. Each node picks a character in
* `voiceWidget` (only values its own dropdown offers, in its spelling) and
* switches emotions itself through `{emotion}` tags in `speechWidget`, where the
* Script Director writes the tagged script.
*
* To support a new node, add one entry and a test; nothing else changes.
*/
export const AUDIO_NODE_TARGETS = Object.freeze([
Object.freeze({
id: 'anomalous-tts',
label: 'GPT-SoVITS',
types: Object.freeze(['AnomalousTTS_CharacterSpeech']),
voiceWidget: 'character',
speechWidget: 'text',
}),
]);
export function targetForNode(node) {
const type = node?.type || node?.comfyClass;
return AUDIO_NODE_TARGETS.find(target => target.types.includes(type)) || null;
}
/**
* Decide what dropping the character `group` on `node` does, without touching the node.
* Returns { ok: true, target, widget, value } or { ok: false, reason, target?, path? }.
*/
export function planVoiceDrop(node, group) {
const target = targetForNode(node);
if (!target) return { ok: false, reason: 'unsupported' };
if (!group?.has_main) return { ok: false, reason: 'noMainVoice', target };
const widget = node.widgets?.find(item => item.name === target.voiceWidget);
if (!widget) return { ok: false, reason: 'unsupported', target };
const value = comboValueForPath(widget, group.node_value);
if (value == null) return { ok: false, reason: 'notListed', target, path: group.node_value };
return { ok: true, target, widget, value };
}