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>
41 lines
1.7 KiB
JavaScript
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 };
|
|
}
|