mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2024-12-13 18:07:20 +01:00
774 lines
29 KiB
JavaScript
774 lines
29 KiB
JavaScript
/*
|
|
TODO:
|
|
- Hide voice map its just confusing
|
|
- Delete useless call
|
|
*/
|
|
|
|
import { doExtrasFetch, extension_settings, getApiUrl, getContext, modules, ModuleWorkerWrapper } from "../../extensions.js"
|
|
import { callPopup } from "../../../script.js"
|
|
import { initVoiceMap } from "./index.js"
|
|
|
|
export { CoquiTtsProvider }
|
|
|
|
const DEBUG_PREFIX = "<Coqui TTS module> ";
|
|
const UPDATE_INTERVAL = 1000;
|
|
|
|
let inApiCall = false;
|
|
let voiceIdList = []; // Updated with module worker
|
|
let coquiApiModels = {}; // Initialized only once
|
|
let coquiApiModelsFull = {}; // Initialized only once
|
|
let coquiLocalModels = []; // Initialized only once
|
|
let coquiLocalModelsReceived = false;
|
|
/*
|
|
coquiApiModels format [language][dataset][name]:coqui-api-model-id, example:
|
|
{
|
|
"en": {
|
|
"vctk": {
|
|
"vits": "tts_models/en/vctk/vits"
|
|
}
|
|
},
|
|
"ja": {
|
|
"kokoro": {
|
|
"tacotron2-DDC": "tts_models/ja/kokoro/tacotron2-DDC"
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
const languageLabels = {
|
|
"multilingual": "Multilingual",
|
|
"en": "English",
|
|
"fr": "French",
|
|
"es": "Spanish",
|
|
"ja": "Japanese"
|
|
}
|
|
|
|
function throwIfModuleMissing() {
|
|
if (!modules.includes('coqui-tts')) {
|
|
const message = `Coqui TTS module not loaded. Add coqui-tts to enable-modules and restart the Extras API.`
|
|
// toastr.error(message, { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
throw new Error(DEBUG_PREFIX, message);
|
|
}
|
|
}
|
|
|
|
function resetModelSettings() {
|
|
$("#coqui_api_model_settings_language").val("none");
|
|
$("#coqui_api_model_settings_speaker").val("none");
|
|
}
|
|
|
|
class CoquiTtsProvider {
|
|
//#############################//
|
|
// Extension UI and Settings //
|
|
//#############################//
|
|
|
|
settings
|
|
|
|
defaultSettings = {
|
|
voiceMap: {},
|
|
customVoices: {},
|
|
voiceIds: [],
|
|
voiceMapDict: {}
|
|
}
|
|
|
|
get settingsHtml() {
|
|
let html = `
|
|
<div class="flex wide100p flexGap10 alignitemscenter">
|
|
<div>
|
|
<div style="flex: 50%;">
|
|
<small>To use CoquiTTS, select the origin, language, and model, then click Add Voice. The voice will then be available to add to a character. Voices are saved globally. </small><br>
|
|
<label for="coqui_voicename_select">Select Saved Voice:</label>
|
|
<select id="coqui_voicename_select">
|
|
<!-- Populated by JS -->
|
|
</select>
|
|
<div class="tts_block">
|
|
<input id="coqui_remove_voiceId_mapping" class="menu_button" type="button" value="Remove Voice" />
|
|
<input id="coqui_add_voiceId_mapping" class="menu_button" type="button" value="Add Voice" />
|
|
</div>
|
|
<label for="coqui_model_origin">Models:</label>
|
|
<select id="coqui_model_origin">gpu_mode
|
|
<option value="none">Select Origin</option>
|
|
<option value="coqui-api">Coqui API (Tested)</option>
|
|
<option value="coqui-api-full">Coqui API (Experimental)</option>
|
|
<option value="local">My Models</option>
|
|
</select>
|
|
|
|
<div id="coqui_api_model_div">
|
|
<select id="coqui_api_language">
|
|
<!-- Populated by JS and request -->
|
|
</select>
|
|
|
|
<select id="coqui_api_model_name">
|
|
<!-- Populated by JS and request -->
|
|
</select>
|
|
|
|
<div id="coqui_api_model_settings">
|
|
<select id="coqui_api_model_settings_language">
|
|
<!-- Populated by JS and request -->
|
|
</select>
|
|
<select id="coqui_api_model_settings_speaker">
|
|
<!-- Populated by JS and request -->
|
|
</select>
|
|
</div>
|
|
<span id="coqui_api_model_install_status">Model installed on extras server</span>
|
|
<input id="coqui_api_model_install_button" class="menu_button" type="button" value="Install" />
|
|
</div>
|
|
|
|
<div id="coqui_local_model_div">
|
|
<select id="coqui_local_model_name">
|
|
<!-- Populated by JS and request -->
|
|
</select>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
return html
|
|
}
|
|
|
|
async loadSettings(settings) {
|
|
// Only accept keys defined in defaultSettings
|
|
this.settings = this.defaultSettings
|
|
|
|
for (const key in settings) {
|
|
if (key in this.settings) {
|
|
this.settings[key] = settings[key]
|
|
} else {
|
|
throw DEBUG_PREFIX + `Invalid setting passed to extension: ${key}`
|
|
}
|
|
}
|
|
|
|
await initLocalModels();
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
|
|
$("#coqui_api_model_div").hide();
|
|
$("#coqui_local_model_div").hide();
|
|
|
|
$("#coqui_api_language").show();
|
|
$("#coqui_api_model_name").hide();
|
|
$("#coqui_api_model_settings").hide();
|
|
$("#coqui_api_model_install_status").hide();
|
|
$("#coqui_api_model_install_button").hide();
|
|
|
|
let that = this
|
|
$("#coqui_model_origin").on("change", function () { that.onModelOriginChange() });
|
|
$("#coqui_api_language").on("change", function () { that.onModelLanguageChange() });
|
|
$("#coqui_api_model_name").on("change", function () { that.onModelNameChange() });
|
|
|
|
$("#coqui_remove_voiceId_mapping").on("click", function () { that.onRemoveClick() });
|
|
$("#coqui_add_voiceId_mapping").on("click", function () { that.onAddClick() });
|
|
|
|
// Load coqui-api settings from json file
|
|
await fetch("/scripts/extensions/tts/coqui_api_models_settings.json")
|
|
.then(response => response.json())
|
|
.then(json => {
|
|
coquiApiModels = json;
|
|
console.debug(DEBUG_PREFIX,"initialized coqui-api model list to", coquiApiModels);
|
|
/*
|
|
$('#coqui_api_language')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model language</option>')
|
|
.val('none');
|
|
|
|
for(let language in coquiApiModels) {
|
|
$("#coqui_api_language").append(new Option(languageLabels[language],language));
|
|
console.log(DEBUG_PREFIX,"added language",language);
|
|
}*/
|
|
});
|
|
|
|
// Load coqui-api FULL settings from json file
|
|
await fetch("/scripts/extensions/tts/coqui_api_models_settings_full.json")
|
|
.then(response => response.json())
|
|
.then(json => {
|
|
coquiApiModelsFull = json;
|
|
console.debug(DEBUG_PREFIX,"initialized coqui-api full model list to", coquiApiModelsFull);
|
|
/*
|
|
$('#coqui_api_full_language')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model language</option>')
|
|
.val('none');
|
|
|
|
for(let language in coquiApiModelsFull) {
|
|
$("#coqui_api_full_language").append(new Option(languageLabels[language],language));
|
|
console.log(DEBUG_PREFIX,"added language",language);
|
|
}*/
|
|
});
|
|
}
|
|
|
|
// Perform a simple readiness check by trying to fetch voiceIds
|
|
async checkReady(){
|
|
throwIfModuleMissing()
|
|
await this.fetchTtsVoiceObjects()
|
|
}
|
|
|
|
updateCustomVoices() {
|
|
// Takes voiceMapDict and converts it to a string to save to voiceMap
|
|
this.settings.customVoices = {};
|
|
for (let voiceName in this.settings.voiceMapDict) {
|
|
const voiceId = this.settings.voiceMapDict[voiceName];
|
|
this.settings.customVoices[voiceName] = voiceId["model_id"];
|
|
|
|
if (voiceId["model_language"] != null)
|
|
this.settings.customVoices[voiceName] += "[" + voiceId["model_language"] + "]";
|
|
|
|
if (voiceId["model_speaker"] != null)
|
|
this.settings.customVoices[voiceName] += "[" + voiceId["model_speaker"] + "]";
|
|
}
|
|
|
|
// Update UI select list with voices
|
|
$("#coqui_voicename_select").empty()
|
|
$('#coqui_voicename_select')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select Voice</option>')
|
|
.val('none')
|
|
for (const voiceName in this.settings.voiceMapDict) {
|
|
$("#coqui_voicename_select").append(new Option(voiceName, voiceName));
|
|
}
|
|
|
|
this.onSettingsChange()
|
|
}
|
|
|
|
onSettingsChange() {
|
|
console.debug(DEBUG_PREFIX, "Settings changes", this.settings);
|
|
extension_settings.tts.Coqui = this.settings;
|
|
}
|
|
|
|
async onRefreshClick() {
|
|
this.checkReady()
|
|
}
|
|
|
|
async onAddClick() {
|
|
if (inApiCall) {
|
|
return; //TODO: block dropdown
|
|
}
|
|
|
|
// Ask user for voiceId name to save voice
|
|
const voiceName = await callPopup('<h3>Name of Coqui voice to add to voice select dropdown:</h3>', 'input')
|
|
|
|
const model_origin = $("#coqui_model_origin").val();
|
|
const model_language = $("#coqui_api_language").val();
|
|
const model_name = $("#coqui_api_model_name").val();
|
|
let model_setting_language = $("#coqui_api_model_settings_language").val();
|
|
let model_setting_speaker = $("#coqui_api_model_settings_speaker").val();
|
|
|
|
|
|
if (!voiceName) {
|
|
toastr.error(`Voice name empty, please enter one.`, DEBUG_PREFIX + " voice mapping voice name", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
if (model_origin == "none") {
|
|
toastr.error(`Origin not selected, please select one.`, DEBUG_PREFIX + " voice mapping origin", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
if (model_origin == "local") {
|
|
const model_id = $("#coqui_local_model_name").val();
|
|
|
|
if (model_name == "none") {
|
|
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX + " voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
this.settings.voiceMapDict[voiceName] = { model_type: "local", model_id: "local/" + model_id };
|
|
console.debug(DEBUG_PREFIX, "Registered new voice map: ", voiceName, ":", this.settings.voiceMapDict[voiceName]);
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
if (model_language == "none") {
|
|
toastr.error(`Language not selected, please select one.`, DEBUG_PREFIX + " voice mapping language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
if (model_name == "none") {
|
|
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX + " voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
this.updateCustomVoices(); // Overide any manual modification
|
|
return;
|
|
}
|
|
|
|
if (model_setting_language == "none")
|
|
model_setting_language = null;
|
|
|
|
if (model_setting_speaker == "none")
|
|
model_setting_speaker = null;
|
|
|
|
const tokens = $('#coqui_api_model_name').val().split("/");
|
|
const model_dataset = tokens[0];
|
|
const model_label = tokens[1];
|
|
const model_id = "tts_models/" + model_language + "/" + model_dataset + "/" + model_label
|
|
|
|
let modelDict = coquiApiModels
|
|
if (model_origin == "coqui-api-full")
|
|
modelDict = coquiApiModelsFull
|
|
|
|
if (model_setting_language == null & "languages" in modelDict[model_language][model_dataset][model_label]) {
|
|
toastr.error(`Model language not selected, please select one.`, DEBUG_PREFIX+" voice mapping model language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
return;
|
|
}
|
|
|
|
if (model_setting_speaker == null & "speakers" in modelDict[model_language][model_dataset][model_label]) {
|
|
toastr.error(`Model speaker not selected, please select one.`, DEBUG_PREFIX+" voice mapping model speaker", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
return;
|
|
}
|
|
|
|
console.debug(DEBUG_PREFIX, "Current custom voices: ", this.settings.customVoices);
|
|
|
|
this.settings.voiceMapDict[voiceName] = { model_type: "coqui-api", model_id: model_id, model_language: model_setting_language, model_speaker: model_setting_speaker };
|
|
|
|
console.debug(DEBUG_PREFIX, "Registered new voice map: ", voiceName, ":", this.settings.voiceMapDict[voiceName]);
|
|
|
|
this.updateCustomVoices();
|
|
initVoiceMap() // Update TTS extension voiceMap
|
|
|
|
let successMsg = voiceName + ":" + model_id;
|
|
if (model_setting_language != null)
|
|
successMsg += "[" + model_setting_language + "]";
|
|
if (model_setting_speaker != null)
|
|
successMsg += "[" + model_setting_speaker + "]";
|
|
toastr.info(successMsg, DEBUG_PREFIX + " voice map updated", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
|
|
return
|
|
}
|
|
|
|
async getVoice(voiceName) {
|
|
let match = await this.fetchTtsVoiceObjects()
|
|
match = match.filter(
|
|
voice => voice.name == voiceName
|
|
)[0]
|
|
if (!match) {
|
|
throw `TTS Voice name ${voiceName} not found in CoquiTTS Provider voice list`
|
|
}
|
|
return match;
|
|
}
|
|
|
|
async onRemoveClick() {
|
|
const voiceName = $("#coqui_voicename_select").val();
|
|
|
|
if (voiceName === "none") {
|
|
toastr.error(`Voice not selected, please select one.`, DEBUG_PREFIX + " voice mapping voiceId", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
return;
|
|
}
|
|
|
|
// Todo erase from voicemap
|
|
delete (this.settings.voiceMapDict[voiceName]);
|
|
this.updateCustomVoices();
|
|
initVoiceMap() // Update TTS extension voiceMap
|
|
}
|
|
|
|
async onModelOriginChange() {
|
|
throwIfModuleMissing()
|
|
resetModelSettings();
|
|
const model_origin = $('#coqui_model_origin').val();
|
|
|
|
if (model_origin == "none") {
|
|
$("#coqui_local_model_div").hide();
|
|
$("#coqui_api_model_div").hide();
|
|
}
|
|
|
|
// show coqui model selected list (SAFE)
|
|
if (model_origin == "coqui-api") {
|
|
$("#coqui_local_model_div").hide();
|
|
|
|
$('#coqui_api_language')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model language</option>')
|
|
.val('none');
|
|
|
|
for(let language in coquiApiModels) {
|
|
let languageLabel = language
|
|
if (language in languageLabels)
|
|
languageLabel = languageLabels[language]
|
|
$("#coqui_api_language").append(new Option(languageLabel,language));
|
|
console.log(DEBUG_PREFIX,"added language",languageLabel,"(",language,")");
|
|
}
|
|
|
|
$("#coqui_api_model_div").show();
|
|
}
|
|
|
|
// show coqui model full list (UNSAFE)
|
|
if (model_origin == "coqui-api-full") {
|
|
$("#coqui_local_model_div").hide();
|
|
|
|
$('#coqui_api_language')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model language</option>')
|
|
.val('none');
|
|
|
|
for(let language in coquiApiModelsFull) {
|
|
let languageLabel = language
|
|
if (language in languageLabels)
|
|
languageLabel = languageLabels[language]
|
|
$("#coqui_api_language").append(new Option(languageLabel,language));
|
|
console.log(DEBUG_PREFIX,"added language",languageLabel,"(",language,")");
|
|
}
|
|
|
|
$("#coqui_api_model_div").show();
|
|
}
|
|
|
|
|
|
// show local model list
|
|
if (model_origin == "local") {
|
|
$("#coqui_api_model_div").hide();
|
|
$("#coqui_local_model_div").show();
|
|
}
|
|
}
|
|
|
|
async onModelLanguageChange() {
|
|
throwIfModuleMissing();
|
|
resetModelSettings();
|
|
$("#coqui_api_model_settings").hide();
|
|
const model_origin = $('#coqui_model_origin').val();
|
|
const model_language = $('#coqui_api_language').val();
|
|
console.debug(model_language);
|
|
|
|
if (model_language == "none") {
|
|
$("#coqui_api_model_name").hide();
|
|
return;
|
|
}
|
|
|
|
$("#coqui_api_model_name").show();
|
|
$('#coqui_api_model_name')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model</option>')
|
|
.val('none');
|
|
|
|
let modelDict = coquiApiModels
|
|
if (model_origin == "coqui-api-full")
|
|
modelDict = coquiApiModelsFull
|
|
|
|
for(let model_dataset in modelDict[model_language])
|
|
for(let model_name in modelDict[model_language][model_dataset]) {
|
|
const model_id = model_dataset + "/" + model_name
|
|
const model_label = model_name + " (" + model_dataset + " dataset)"
|
|
$("#coqui_api_model_name").append(new Option(model_label, model_id));
|
|
}
|
|
}
|
|
|
|
async onModelNameChange() {
|
|
throwIfModuleMissing();
|
|
resetModelSettings();
|
|
$("#coqui_api_model_settings").hide();
|
|
const model_origin = $('#coqui_model_origin').val();
|
|
|
|
// No model selected
|
|
if ($('#coqui_api_model_name').val() == "none") {
|
|
$("#coqui_api_model_install_button").off('click');
|
|
$("#coqui_api_model_install_button").hide();
|
|
return;
|
|
}
|
|
|
|
// Get languages and speakers options
|
|
const model_language = $('#coqui_api_language').val();
|
|
const tokens = $('#coqui_api_model_name').val().split("/");
|
|
const model_dataset = tokens[0];
|
|
const model_name = tokens[1];
|
|
|
|
let modelDict = coquiApiModels
|
|
if (model_origin == "coqui-api-full")
|
|
modelDict = coquiApiModelsFull
|
|
|
|
const model_settings = modelDict[model_language][model_dataset][model_name]
|
|
|
|
if ("languages" in model_settings) {
|
|
$("#coqui_api_model_settings").show();
|
|
$("#coqui_api_model_settings_language").show();
|
|
$('#coqui_api_model_settings_language')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select language</option>')
|
|
.val('none');
|
|
|
|
for (var i = 0; i < model_settings["languages"].length; i++) {
|
|
const language_label = JSON.stringify(model_settings["languages"][i]).replaceAll("\"", "");
|
|
$("#coqui_api_model_settings_language").append(new Option(language_label, i));
|
|
}
|
|
}
|
|
else {
|
|
$("#coqui_api_model_settings_language").hide();
|
|
}
|
|
|
|
if ("speakers" in model_settings) {
|
|
$("#coqui_api_model_settings").show();
|
|
$("#coqui_api_model_settings_speaker").show();
|
|
$('#coqui_api_model_settings_speaker')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select speaker</option>')
|
|
.val('none');
|
|
|
|
for (var i = 0; i < model_settings["speakers"].length; i++) {
|
|
const speaker_label = JSON.stringify(model_settings["speakers"][i]).replaceAll("\"", "");
|
|
$("#coqui_api_model_settings_speaker").append(new Option(speaker_label, i));
|
|
}
|
|
}
|
|
else {
|
|
$("#coqui_api_model_settings_speaker").hide();
|
|
}
|
|
|
|
$("#coqui_api_model_install_status").text("Requesting model to extras server...");
|
|
$("#coqui_api_model_install_status").show();
|
|
|
|
// Check if already installed and propose to do it otherwise
|
|
const model_id = modelDict[model_language][model_dataset][model_name]["id"]
|
|
console.debug(DEBUG_PREFIX,"Check if model is already installed",model_id);
|
|
let result = await CoquiTtsProvider.checkmodel_state(model_id);
|
|
result = await result.json();
|
|
const model_state = result["model_state"];
|
|
|
|
console.debug(DEBUG_PREFIX, " Model state:", model_state)
|
|
|
|
if (model_state == "installed") {
|
|
$("#coqui_api_model_install_status").text("Model already installed on extras server");
|
|
$("#coqui_api_model_install_button").hide();
|
|
}
|
|
else {
|
|
let action = "download"
|
|
if (model_state == "corrupted") {
|
|
action = "repare"
|
|
//toastr.error("Click install button to reinstall the model "+$("#coqui_api_model_name").find(":selected").text(), DEBUG_PREFIX+" corrupted model install", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
$("#coqui_api_model_install_status").text("Model found but incomplete try install again (maybe still downloading)"); // (remove and download again)
|
|
}
|
|
else {
|
|
toastr.info("Click download button to install the model " + $("#coqui_api_model_name").find(":selected").text(), DEBUG_PREFIX + " model not installed", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
$("#coqui_api_model_install_status").text("Model not found on extras server");
|
|
}
|
|
|
|
const onModelNameChange_pointer = this.onModelNameChange;
|
|
|
|
$("#coqui_api_model_install_button").off("click").on("click", async function () {
|
|
try {
|
|
$("#coqui_api_model_install_status").text("Downloading model...");
|
|
$("#coqui_api_model_install_button").hide();
|
|
//toastr.info("For model "+model_id, DEBUG_PREFIX+" Started "+action, { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
let apiResult = await CoquiTtsProvider.installModel(model_id, action);
|
|
apiResult = await apiResult.json();
|
|
|
|
console.debug(DEBUG_PREFIX, "Response:", apiResult);
|
|
|
|
if (apiResult["status"] == "done") {
|
|
$("#coqui_api_model_install_status").text("Model installed and ready to use!");
|
|
$("#coqui_api_model_install_button").hide();
|
|
onModelNameChange_pointer();
|
|
}
|
|
|
|
if (apiResult["status"] == "downloading") {
|
|
toastr.error("Check extras console for progress", DEBUG_PREFIX + " already downloading", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
$("#coqui_api_model_install_status").text("Already downloading a model, check extras console!");
|
|
$("#coqui_api_model_install_button").show();
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
toastr.error(error, DEBUG_PREFIX + " error with model download", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
|
onModelNameChange_pointer();
|
|
}
|
|
// will refresh model status
|
|
});
|
|
|
|
$("#coqui_api_model_install_button").show();
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//#############################//
|
|
// API Calls //
|
|
//#############################//
|
|
|
|
/*
|
|
Check model installation state, return one of ["installed", "corrupted", "absent"]
|
|
*/
|
|
static async checkmodel_state(model_id) {
|
|
throwIfModuleMissing()
|
|
const url = new URL(getApiUrl());
|
|
url.pathname = '/api/text-to-speech/coqui/coqui-api/check-model-state';
|
|
|
|
const apiResult = await doExtrasFetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cache-Control': 'no-cache'
|
|
},
|
|
body: JSON.stringify({
|
|
"model_id": model_id,
|
|
})
|
|
});
|
|
|
|
if (!apiResult.ok) {
|
|
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Check model state request failed');
|
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
}
|
|
|
|
return apiResult
|
|
}
|
|
|
|
static async installModel(model_id, action) {
|
|
throwIfModuleMissing()
|
|
const url = new URL(getApiUrl());
|
|
url.pathname = '/api/text-to-speech/coqui/coqui-api/install-model';
|
|
|
|
const apiResult = await doExtrasFetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cache-Control': 'no-cache'
|
|
},
|
|
body: JSON.stringify({
|
|
"model_id": model_id,
|
|
"action": action
|
|
})
|
|
});
|
|
|
|
if (!apiResult.ok) {
|
|
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Install model ' + model_id + ' request failed');
|
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
}
|
|
|
|
return apiResult
|
|
}
|
|
|
|
/*
|
|
Retrieve user custom models
|
|
*/
|
|
static async getLocalModelList() {
|
|
throwIfModuleMissing()
|
|
const url = new URL(getApiUrl());
|
|
url.pathname = '/api/text-to-speech/coqui/local/get-models';
|
|
|
|
const apiResult = await doExtrasFetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cache-Control': 'no-cache'
|
|
},
|
|
body: JSON.stringify({
|
|
"model_id": "model_id",
|
|
"action": "action"
|
|
})
|
|
})
|
|
|
|
if (!apiResult.ok) {
|
|
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Get local model list request failed');
|
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
}
|
|
|
|
return apiResult
|
|
}
|
|
|
|
|
|
// Expect voiceId format to be like:
|
|
// tts_models/multilingual/multi-dataset/your_tts[2][1]
|
|
// tts_models/en/ljspeech/glow-tts
|
|
// ts_models/ja/kokoro/tacotron2-DDC
|
|
async generateTts(text, voiceId) {
|
|
throwIfModuleMissing()
|
|
voiceId = this.settings.customVoices[voiceId]
|
|
|
|
const url = new URL(getApiUrl());
|
|
url.pathname = '/api/text-to-speech/coqui/generate-tts';
|
|
|
|
let language = "none"
|
|
let speaker = "none"
|
|
const tokens = voiceId.replaceAll("]", "").replaceAll("\"", "").split("[");
|
|
const model_id = tokens[0]
|
|
|
|
console.debug(DEBUG_PREFIX, "Preparing TTS request for", tokens)
|
|
|
|
// First option
|
|
if (tokens.length > 1) {
|
|
const option1 = tokens[1]
|
|
|
|
if (model_id.includes("multilingual"))
|
|
language = option1
|
|
else
|
|
speaker = option1
|
|
}
|
|
|
|
// Second option
|
|
if (tokens.length > 2)
|
|
speaker = tokens[2];
|
|
|
|
const apiResult = await doExtrasFetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cache-Control': 'no-cache'
|
|
},
|
|
body: JSON.stringify({
|
|
"text": text,
|
|
"model_id": model_id,
|
|
"language_id": parseInt(language),
|
|
"speaker_id": parseInt(speaker)
|
|
})
|
|
});
|
|
|
|
if (!apiResult.ok) {
|
|
toastr.error(apiResult.statusText, 'TTS Generation Failed');
|
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
}
|
|
|
|
return apiResult
|
|
}
|
|
|
|
// Dirty hack to say not implemented
|
|
async fetchTtsVoiceObjects() {
|
|
const voiceIds = Object
|
|
.keys(this.settings.voiceMapDict)
|
|
.map(voice => ({ name: voice, voice_id: voice, preview_url: false }));
|
|
return voiceIds
|
|
}
|
|
|
|
// Do nothing
|
|
previewTtsVoice(id) {
|
|
return
|
|
}
|
|
|
|
async fetchTtsFromHistory(history_item_id) {
|
|
return Promise.resolve(history_item_id);
|
|
}
|
|
}
|
|
|
|
async function initLocalModels() {
|
|
if (!modules.includes('coqui-tts'))
|
|
return
|
|
|
|
// Initialized local model once
|
|
if (!coquiLocalModelsReceived) {
|
|
let result = await CoquiTtsProvider.getLocalModelList();
|
|
result = await result.json();
|
|
|
|
coquiLocalModels = result["models_list"];
|
|
|
|
$("#coqui_local_model_name").show();
|
|
$('#coqui_local_model_name')
|
|
.find('option')
|
|
.remove()
|
|
.end()
|
|
.append('<option value="none">Select model</option>')
|
|
.val('none');
|
|
|
|
for (const model_dataset of coquiLocalModels)
|
|
$("#coqui_local_model_name").append(new Option(model_dataset, model_dataset));
|
|
|
|
coquiLocalModelsReceived = true;
|
|
}
|
|
}
|