Files
diodiogod-TTS-Audio-Suite/web/moss_tts_engine_widgets.js
diodiogod 08e8e8c884 Version 5.8.1
Add MOSS community full-checkpoint support

Implementation details:
- Add the LAION MOSS-TTS v1.5 Voice Acting 8B community model to the engine dropdown and downloader
- Discover compatible local MOSS full checkpoints and resolve architecture from config.json
- Enable the LAION Delay checkpoint in the existing LoRA training pipeline
- Reject unsupported local MOSS layouts instead of assuming Local Transformer
- Address issue #340
2026-08-11 23:42:44 -03:00

324 lines
11 KiB
JavaScript

import { app } from "../../scripts/app.js";
const MODEL_LABEL = "standard model";
const MODE_LABEL = "speaker mode";
const NATIVE_MODEL_LABEL = "native model (locked)";
const LOCAL_N_VQ_WIDGET = "n_vq_for_inference";
const SAMPLER_WIDGETS = ["temperature", "top_p", "top_k", "repetition_penalty"];
function findWidgetByName(node, name) {
return node.widgets ? node.widgets.find((widget) => widget.name === name) : null;
}
function hideWidget(widget) {
if (!widget) {
return;
}
if (!widget.__ttsOriginalType) {
widget.__ttsOriginalType = widget.type;
}
if (!widget.__ttsOriginalComputeSize) {
widget.__ttsOriginalComputeSize = widget.computeSize;
}
widget.type = "hidden";
widget.computeSize = () => [0, -4];
widget.disabled = true;
if (widget.element) {
widget.element.style.display = "none";
}
}
function showWidget(widget) {
if (!widget) {
return;
}
if (widget.__ttsOriginalType) {
widget.type = widget.__ttsOriginalType;
}
if (widget.__ttsOriginalComputeSize) {
widget.computeSize = widget.__ttsOriginalComputeSize;
}
widget.disabled = false;
if (widget.element) {
widget.element.style.display = "";
}
}
function setWidgetEnabled(widget, enabled) {
if (!widget) {
return;
}
widget.disabled = !enabled;
if (widget.element) {
widget.element.style.opacity = enabled ? "1" : "0.55";
widget.element.style.pointerEvents = enabled ? "auto" : "none";
}
}
function resizeNode(node) {
if (typeof node?.computeSize === "function" && typeof node?.setSize === "function") {
const computed = node.computeSize();
const current = Array.isArray(node.size) ? node.size : computed;
const nextSize = [
Math.max(current[0] || 0, computed[0] || 0),
Math.max(current[1] || 0, computed[1] || 0),
];
if (nextSize[0] !== current[0] || nextSize[1] !== current[1]) {
node.setSize(nextSize);
}
}
if (node?.graph?.setDirtyCanvas) {
node.graph.setDirtyCanvas(true, true);
} else if (app.graph?.setDirtyCanvas) {
app.graph.setDirtyCanvas(true, true);
}
}
function isNativeModelOption(value) {
return typeof value === "string" && value.includes("TTSD");
}
function getStoredStandardOptions(node, modelWidget) {
if (!node.__ttsMossStandardModelOptions) {
const values = Array.isArray(modelWidget?.options?.values) ? [...modelWidget.options.values] : [];
node.__ttsMossStandardModelOptions = values.filter((value) => !isNativeModelOption(value));
}
return node.__ttsMossStandardModelOptions || [];
}
function getStoredNativeOption(node, modelWidget) {
if (!node.__ttsMossNativeModelOption) {
const values = Array.isArray(modelWidget?.options?.values) ? [...modelWidget.options.values] : [];
node.__ttsMossNativeModelOption = values.find((value) => isNativeModelOption(value)) || "MOSS-TTSD-v1.0";
}
return node.__ttsMossNativeModelOption;
}
function toStandardModelValue(node, modelWidget, modelValue) {
const standardOptions = getStoredStandardOptions(node, modelWidget);
if (standardOptions.includes(modelValue)) {
return modelValue;
}
return standardOptions[0];
}
function isLocalSmallModel(value) {
return typeof value === "string" && value.includes("Local-Transformer");
}
function isSoundEffectModel(value) {
return typeof value === "string"
&& (value.includes("MOSS-SoundEffect") || value.includes("Sound Effects"));
}
function isVoiceDesignModel(value) {
return typeof value === "string"
&& (value.includes("MOSS-VoiceGenerator") || value.includes("Voice Design"));
}
function supportsLora(value) {
const text = String(value || "");
return text === "v1 8B"
|| text === "v1.5 8B"
|| text === "local:MOSS-TTS"
|| text === "local:MOSS-TTS-v1.5"
|| isSoundEffectModel(text);
}
function canonicalModelName(value) {
const text = String(value || "").replace(/^local:/, "");
if (text.includes("Voice Acting") || text.includes("voice-acting")) {
return "moss-tts-v1.5-8b-voice-acting";
}
if (text.includes("SoundEffect") || text.includes("Sound Effects")) return "MOSS-SoundEffect";
if (text.includes("VoiceGenerator") || text.includes("Voice Design")) return "MOSS-VoiceGenerator";
if (text.includes("TTSD") || text.includes("Native") && text.includes("Dialogue")) return "MOSS-TTSD-v1.0";
if (text.includes("Local-Transformer") || text === "1.7B" || text.includes("Small 1.7B")) {
return "MOSS-TTS-Local-Transformer";
}
if (text.includes("v1.5")) return "MOSS-TTS-v1.5";
return "MOSS-TTS";
}
function updateSamplerState(node, modelValue) {
const presetWidget = findWidgetByName(node, "sampler_preset");
if (!presetWidget) return;
const samplerWidgets = SAMPLER_WIDGETS.map((name) => findWidgetByName(node, name));
const useModelDefaults = presetWidget.value === "Model default";
const previousPreset = node.__ttsMossLastSamplerPreset;
if (useModelDefaults) {
if (previousPreset === "Custom") {
node.__ttsMossCustomSamplerValues = samplerWidgets.map((widget) => widget?.value);
}
const modelDefaults = presetWidget.options?.model_defaults || {};
const defaults = modelDefaults[canonicalModelName(modelValue)];
samplerWidgets.forEach((widget, index) => {
if (widget && defaults) widget.value = defaults[SAMPLER_WIDGETS[index]];
setWidgetEnabled(widget, false);
});
} else {
if (previousPreset === "Model default" && node.__ttsMossCustomSamplerValues) {
samplerWidgets.forEach((widget, index) => {
if (widget && node.__ttsMossCustomSamplerValues[index] !== undefined) {
widget.value = node.__ttsMossCustomSamplerValues[index];
}
});
}
samplerWidgets.forEach((widget) => setWidgetEnabled(widget, true));
}
node.__ttsMossLastSamplerPreset = presetWidget.value;
}
const SPEECH_ONLY_WIDGETS = [
"multi_speaker_mode", "language", "duration_tokens", "chunk_minutes",
"instruction", "quality", "sound_event", "ambient_sound",
"speaker2_voice", "speaker3_voice", "speaker4_voice", "speaker5_voice",
"local_lora_adapter", "lora_adapter_override",
];
function updateSoundEffectState(node, soundEffectSelected) {
for (const name of SPEECH_ONLY_WIDGETS) {
setWidgetEnabled(findWidgetByName(node, name), !soundEffectSelected);
}
}
function refreshMossWidgets(node) {
if (node.comfyClass !== "MossTTSEngineNode") {
return;
}
if (node.__ttsMossRefreshing) {
return;
}
node.__ttsMossRefreshing = true;
try {
const modelWidget = findWidgetByName(node, "model_variant");
const modeWidget = findWidgetByName(node, "multi_speaker_mode");
const nVqWidget = findWidgetByName(node, LOCAL_N_VQ_WIDGET);
if (!modelWidget || !modeWidget) {
return;
}
const standardOptions = getStoredStandardOptions(node, modelWidget);
const nativeOption = getStoredNativeOption(node, modelWidget);
if (!standardOptions.length) {
return;
}
modelWidget.label = MODEL_LABEL;
modeWidget.label = MODE_LABEL;
const modeValue = modeWidget.value;
const isNativeDialogue = modeValue === "Native Multi-Speaker Dialogue";
if (isNativeDialogue) {
const currentStandard = toStandardModelValue(node, modelWidget, modelWidget.value);
node.__ttsMossLastStandardModel = currentStandard;
modelWidget.options = modelWidget.options || {};
modelWidget.options.values = [nativeOption];
if (modelWidget.value !== nativeOption) {
modelWidget.value = nativeOption;
}
modelWidget.label = NATIVE_MODEL_LABEL;
showWidget(modelWidget);
setWidgetEnabled(modelWidget, false);
setWidgetEnabled(findWidgetByName(node, "local_lora_adapter"), false);
setWidgetEnabled(findWidgetByName(node, "lora_adapter_override"), false);
updateSamplerState(node, nativeOption);
hideWidget(nVqWidget);
resizeNode(node);
return;
}
showWidget(modelWidget);
setWidgetEnabled(modelWidget, true);
modelWidget.label = MODEL_LABEL;
modelWidget.options = modelWidget.options || {};
modelWidget.options.values = [...standardOptions];
const restoredStandard =
node.__ttsMossLastStandardModel
|| toStandardModelValue(node, modelWidget, modelWidget.value);
if (!standardOptions.includes(modelWidget.value)) {
modelWidget.value = restoredStandard;
}
const selectedStandard = toStandardModelValue(node, modelWidget, modelWidget.value);
node.__ttsMossLastStandardModel = selectedStandard;
updateSamplerState(node, selectedStandard);
const soundEffectSelected = isSoundEffectModel(selectedStandard);
updateSoundEffectState(node, soundEffectSelected);
if (!soundEffectSelected) {
setWidgetEnabled(
findWidgetByName(node, "instruction"),
!isVoiceDesignModel(selectedStandard),
);
}
const loraSupported = supportsLora(selectedStandard);
setWidgetEnabled(findWidgetByName(node, "local_lora_adapter"), loraSupported);
setWidgetEnabled(findWidgetByName(node, "lora_adapter_override"), loraSupported);
if (isLocalSmallModel(selectedStandard) || selectedStandard === "1.7B") {
showWidget(nVqWidget);
} else {
hideWidget(nVqWidget);
}
resizeNode(node);
} finally {
node.__ttsMossRefreshing = false;
}
}
function hookWidgetValue(node, widget, callback) {
if (!widget || widget.__ttsMossHooked) {
return;
}
widget.__ttsMossHooked = true;
let widgetValue = widget.value;
let originalDescriptor = Object.getOwnPropertyDescriptor(widget, "value")
|| Object.getOwnPropertyDescriptor(Object.getPrototypeOf(widget), "value");
if (!originalDescriptor) {
originalDescriptor = Object.getOwnPropertyDescriptor(widget.constructor?.prototype || {}, "value");
}
Object.defineProperty(widget, "value", {
get() {
if (originalDescriptor?.get) {
return originalDescriptor.get.call(widget);
}
return widgetValue;
},
set(newValue) {
if (originalDescriptor?.set) {
originalDescriptor.set.call(widget, newValue);
} else {
widgetValue = newValue;
}
if (!node.__ttsMossRefreshing) {
callback(node);
}
},
});
}
app.registerExtension({
name: "tts-audio-suite.moss-tts.widgets",
nodeCreated(node) {
if (node.comfyClass !== "MossTTSEngineNode") {
return;
}
refreshMossWidgets(node);
hookWidgetValue(node, findWidgetByName(node, "multi_speaker_mode"), refreshMossWidgets);
hookWidgetValue(node, findWidgetByName(node, "model_variant"), refreshMossWidgets);
hookWidgetValue(node, findWidgetByName(node, "sampler_preset"), refreshMossWidgets);
},
});