mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Finished new version of coqui TTS UI.
This commit is contained in:
@@ -1,11 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
TODO:
|
TODO:
|
||||||
- load/download models
|
- Hide voice map its just confusing
|
||||||
- load assign voice ids
|
- Delete useless call
|
||||||
- handle apply/available voices button
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { eventSource, event_types } from "../../../script.js"
|
import { saveSettingsDebounced } from "../../../script.js"
|
||||||
import { doExtrasFetch, extension_settings, getApiUrl, getContext, modules, ModuleWorkerWrapper } from "../../extensions.js"
|
import { doExtrasFetch, extension_settings, getApiUrl, getContext, modules, ModuleWorkerWrapper } from "../../extensions.js"
|
||||||
|
|
||||||
export { CoquiTtsProvider }
|
export { CoquiTtsProvider }
|
||||||
@@ -56,6 +55,32 @@ function resetModelSettings() {
|
|||||||
$("#coqui_api_model_settings_speaker").val("none");
|
$("#coqui_api_model_settings_speaker").val("none");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCharactersList() {
|
||||||
|
let currentcharacters = new Set();
|
||||||
|
for (const i of getContext().characters) {
|
||||||
|
currentcharacters.add(i.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentcharacters = Array.from(currentcharacters)
|
||||||
|
|
||||||
|
if (JSON.stringify(charactersList) !== JSON.stringify(currentcharacters)) {
|
||||||
|
charactersList = currentcharacters
|
||||||
|
|
||||||
|
$('#coqui_character_select')
|
||||||
|
.find('option')
|
||||||
|
.remove()
|
||||||
|
.end()
|
||||||
|
.append('<option value="none">Select Character</option>')
|
||||||
|
.val('none')
|
||||||
|
|
||||||
|
for(const charName of charactersList) {
|
||||||
|
$("#coqui_character_select").append(new Option(charName,charName));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX,"Updated character list to:", charactersList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CoquiTtsProvider {
|
class CoquiTtsProvider {
|
||||||
//#############################//
|
//#############################//
|
||||||
// Extension UI and Settings //
|
// Extension UI and Settings //
|
||||||
@@ -78,7 +103,7 @@ class CoquiTtsProvider {
|
|||||||
<!-- Populated by JS -->
|
<!-- Populated by JS -->
|
||||||
</select>
|
</select>
|
||||||
<label for="coqui_model_origin">Models:</label>
|
<label for="coqui_model_origin">Models:</label>
|
||||||
<select id="coqui_model_origin">
|
<select id="coqui_model_origin">gpu_mode
|
||||||
<option value="none">Select Origin</option>
|
<option value="none">Select Origin</option>
|
||||||
<option value="coqui-api">Coqui TTS</option>
|
<option value="coqui-api">Coqui TTS</option>
|
||||||
<option value="local">My models</option>
|
<option value="local">My models</option>
|
||||||
@@ -93,10 +118,6 @@ class CoquiTtsProvider {
|
|||||||
<!-- Populated by JS and request -->
|
<!-- Populated by JS and request -->
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<input id="coqui_api_model_install_button" class="menu_button" type="submit" value="Install" />
|
|
||||||
|
|
||||||
<span id="coqui_api_model_loading">Loading...</span>
|
|
||||||
|
|
||||||
<div id="coqui_api_model_settings">
|
<div id="coqui_api_model_settings">
|
||||||
<select id="coqui_api_model_settings_language">
|
<select id="coqui_api_model_settings_language">
|
||||||
<!-- Populated by JS and request -->
|
<!-- Populated by JS and request -->
|
||||||
@@ -105,6 +126,8 @@ class CoquiTtsProvider {
|
|||||||
<!-- Populated by JS and request -->
|
<!-- Populated by JS and request -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -125,17 +148,72 @@ class CoquiTtsProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
|
|
||||||
$("#coqui_api_model_div").hide();
|
$("#coqui_api_model_div").hide();
|
||||||
$("#coqui_api_model_name").hide();
|
$("#coqui_api_model_name").hide();
|
||||||
$("#coqui_api_model_settings").hide();
|
$("#coqui_api_model_settings").hide();
|
||||||
$("#coqui_api_model_loading").hide();
|
$("#coqui_api_model_install_status").hide();
|
||||||
$("#coqui_api_model_install_button").hide();
|
$("#coqui_api_model_install_button").hide();
|
||||||
|
|
||||||
$("#coqui_model_origin").on("change",this.onModelOriginChange);
|
$("#coqui_model_origin").on("change",this.onModelOriginChange);
|
||||||
$("#coqui_api_language").on("change",this.onModelLanguageChange);
|
$("#coqui_api_language").on("change",this.onModelLanguageChange);
|
||||||
$("#coqui_api_model_name").on("change",this.onModelNameChange);
|
$("#coqui_api_model_name").on("change",this.onModelNameChange);
|
||||||
|
|
||||||
|
// Load characters list
|
||||||
|
$('#coqui_character_select')
|
||||||
|
.find('option')
|
||||||
|
.remove()
|
||||||
|
.end()
|
||||||
|
.append('<option value="none">Select Character</option>')
|
||||||
|
.val('none')
|
||||||
|
|
||||||
|
for(const charName of charactersList) {
|
||||||
|
$("#coqui_character_select").append(new Option(charName,charName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load coqui-api settings from json file
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVoiceMap(){
|
||||||
|
this.settings.voiceMap = "";
|
||||||
|
for(let i in this.settings.voiceMapDict) {
|
||||||
|
const voice_settings = this.settings.voiceMapDict[i];
|
||||||
|
this.settings.voiceMap += i + ":" + voice_settings["model_id"];
|
||||||
|
|
||||||
|
if (voice_settings["model_language"] != null)
|
||||||
|
this.settings.voiceMap += "[" + voice_settings["model_language"] + "]";
|
||||||
|
|
||||||
|
if (voice_settings["model_speaker"] != null)
|
||||||
|
this.settings.voiceMap += "[" + voice_settings["model_speaker"] + "]";
|
||||||
|
|
||||||
|
this.settings.voiceMap += ",";
|
||||||
|
}
|
||||||
|
$("#tts_voice_map").val(this.settings.voiceMap);
|
||||||
|
extension_settings.tts.Coqui = this.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSettingsChange() {
|
onSettingsChange() {
|
||||||
|
console.debug(DEBUG_PREFIX,"Settings changes",this.settings);
|
||||||
|
extension_settings.tts.Coqui = this.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onApplyClick() {
|
async onApplyClick() {
|
||||||
@@ -153,26 +231,31 @@ class CoquiTtsProvider {
|
|||||||
|
|
||||||
if (character === "none") {
|
if (character === "none") {
|
||||||
toastr.error(`Character not selected, please select one.`, DEBUG_PREFIX+" voice mapping character", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
toastr.error(`Character not selected, please select one.`, DEBUG_PREFIX+" voice mapping character", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model_origin == "none") {
|
if (model_origin == "none") {
|
||||||
toastr.error(`Origin not selected, please select one.`, DEBUG_PREFIX+" voice mapping origin", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
toastr.error(`Origin not selected, please select one.`, DEBUG_PREFIX+" voice mapping origin", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model_origin == "local") {
|
if (model_origin == "local") {
|
||||||
throwLocalOrigin();
|
throwLocalOrigin();
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model_language == "none") {
|
if (model_language == "none") {
|
||||||
toastr.error(`Language not selected, please select one.`, DEBUG_PREFIX+" voice mapping language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
toastr.error(`Language not selected, please select one.`, DEBUG_PREFIX+" voice mapping language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model_name == "none") {
|
if (model_name == "none") {
|
||||||
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX+" voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX+" voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
this.updateVoiceMap(); // Overide any manual modification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,28 +264,36 @@ class CoquiTtsProvider {
|
|||||||
|
|
||||||
if (model_setting_speaker == "none")
|
if (model_setting_speaker == "none")
|
||||||
model_setting_speaker = null;
|
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
|
||||||
|
|
||||||
|
if (model_setting_language == null & "languages" in coquiApiModels[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 coquiApiModels[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 voice map: ",this.settings.voiceMap);
|
console.debug(DEBUG_PREFIX,"Current voice map: ",this.settings.voiceMap);
|
||||||
|
|
||||||
this.settings.voiceMapDict[character] = {model_id: model_name, model_language:model_setting_language, model_speaker:model_setting_speaker};
|
this.settings.voiceMapDict[character] = {model_id: model_id, model_language:model_setting_language, model_speaker:model_setting_speaker};
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX,"Registered new voice map: ",character,":",this.settings.voiceMapDict[character]);
|
console.debug(DEBUG_PREFIX,"Registered new voice map: ",character,":",this.settings.voiceMapDict[character]);
|
||||||
|
|
||||||
this.settings.voiceMap = "";
|
this.updateVoiceMap();
|
||||||
for(let i in this.settings.voiceMapDict) {
|
|
||||||
const voice_settings = this.settings.voiceMapDict[i];
|
|
||||||
this.settings.voiceMap += i + ":" + voice_settings["model_id"];
|
|
||||||
|
|
||||||
if (model_setting_language != null)
|
|
||||||
this.settings.voiceMap += "[" + voice_settings["model_language"] + "]";
|
|
||||||
|
|
||||||
if (model_setting_speaker != null)
|
|
||||||
this.settings.voiceMap += "[" + voice_settings["model_speaker"] + "]";
|
|
||||||
|
|
||||||
this.settings.voiceMap += ",";
|
|
||||||
}
|
|
||||||
$("#tts_voice_map").val(this.settings.voiceMap);
|
|
||||||
|
|
||||||
|
let successMsg = character+":"+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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,13 +347,10 @@ class CoquiTtsProvider {
|
|||||||
|
|
||||||
for(let model_dataset in coquiApiModels[model_language])
|
for(let model_dataset in coquiApiModels[model_language])
|
||||||
for(let model_name in coquiApiModels[model_language][model_dataset]) {
|
for(let model_name in coquiApiModels[model_language][model_dataset]) {
|
||||||
const model_id = coquiApiModels[model_language][model_dataset][model_name]
|
const model_id = model_dataset + "/" + model_name
|
||||||
const model_label = model_name + " ("+model_dataset+" dataset)"
|
const model_label = model_name + " ("+model_dataset+" dataset)"
|
||||||
$("#coqui_api_model_name").append(new Option(model_label,model_id));
|
$("#coqui_api_model_name").append(new Option(model_label,model_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: populate with corresponding dataset/model pairs got from initialization request
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModelNameChange() {
|
async onModelNameChange() {
|
||||||
@@ -270,51 +358,22 @@ class CoquiTtsProvider {
|
|||||||
resetModelSettings();
|
resetModelSettings();
|
||||||
$("#coqui_api_model_settings").hide();
|
$("#coqui_api_model_settings").hide();
|
||||||
|
|
||||||
const modelId = $("#coqui_api_model_name").val();
|
// No model selected
|
||||||
|
if ($('#coqui_api_model_name').val() == "none") {
|
||||||
if (modelId == "none") {
|
|
||||||
$("#coqui_api_model_install_button").off('click');
|
$("#coqui_api_model_install_button").off('click');
|
||||||
$("#coqui_api_model_install_button").show();
|
$("#coqui_api_model_install_button").hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#coqui_api_model_loading").show();
|
// Get languages and speakers options
|
||||||
|
const model_language = $('#coqui_api_language').val();
|
||||||
// Check if already download and propose to do it
|
const tokens = $('#coqui_api_model_name').val().split("/");
|
||||||
console.debug(DEBUG_PREFIX,"Check if model is already installed",modelId);
|
const model_dataset = tokens[0];
|
||||||
let result = await CoquiTtsProvider.checkModelState(modelId);
|
const model_name = tokens[1];
|
||||||
result = await result.json();
|
|
||||||
const modelState = result["model_state"];
|
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX," Model state:", modelState)
|
const model_settings = coquiApiModels[model_language][model_dataset][model_name]
|
||||||
|
|
||||||
if (modelState != "installed") {
|
if ("languages" in model_settings) {
|
||||||
$("#coqui_api_model_install_button").show();
|
|
||||||
$("#coqui_api_model_install_button").on("click", function (){
|
|
||||||
CoquiTtsProvider.installModel(modelId);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (modelState == "corrupted") {
|
|
||||||
toastr.error("Click repare 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_button").val("Repare");
|
|
||||||
}
|
|
||||||
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_button").val("Download");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX,"Request model settings for",modelId);
|
|
||||||
|
|
||||||
result = await CoquiTtsProvider.getModelSettings(modelId);
|
|
||||||
const modelSettings = await result.json();
|
|
||||||
$("#coqui_api_model_loading").hide();
|
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX,modelSettings)
|
|
||||||
|
|
||||||
|
|
||||||
if (modelSettings["languages"].length > 0) {
|
|
||||||
$("#coqui_api_model_settings").show();
|
$("#coqui_api_model_settings").show();
|
||||||
$("#coqui_api_model_settings_language").show();
|
$("#coqui_api_model_settings_language").show();
|
||||||
$('#coqui_api_model_settings_language')
|
$('#coqui_api_model_settings_language')
|
||||||
@@ -324,15 +383,16 @@ class CoquiTtsProvider {
|
|||||||
.append('<option value="none">Select language</option>')
|
.append('<option value="none">Select language</option>')
|
||||||
.val('none');
|
.val('none');
|
||||||
|
|
||||||
for(const language of modelSettings["languages"]) {
|
for(var i = 0; i < model_settings["languages"].length; i++) {
|
||||||
$("#coqui_api_model_settings_language").append(new Option(language,language));
|
const language_label = JSON.stringify(model_settings["languages"][i]).replaceAll("\"","");
|
||||||
|
$("#coqui_api_model_settings_language").append(new Option(language_label,i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("#coqui_api_model_settings_language").hide();
|
$("#coqui_api_model_settings_language").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelSettings["speakers"].length > 0) {
|
if ("speakers" in model_settings) {
|
||||||
$("#coqui_api_model_settings").show();
|
$("#coqui_api_model_settings").show();
|
||||||
$("#coqui_api_model_settings_speaker").show();
|
$("#coqui_api_model_settings_speaker").show();
|
||||||
$('#coqui_api_model_settings_speaker')
|
$('#coqui_api_model_settings_speaker')
|
||||||
@@ -342,14 +402,78 @@ class CoquiTtsProvider {
|
|||||||
.append('<option value="none">Select speaker</option>')
|
.append('<option value="none">Select speaker</option>')
|
||||||
.val('none');
|
.val('none');
|
||||||
|
|
||||||
for(const speaker of modelSettings["speakers"]) {
|
for(var i = 0; i < model_settings["speakers"].length;i++) {
|
||||||
$("#coqui_api_model_settings_speaker").append(new Option(speaker,speaker));
|
const speaker_label = JSON.stringify(model_settings["speakers"][i]).replaceAll("\"","");
|
||||||
|
$("#coqui_api_model_settings_speaker").append(new Option(speaker_label,i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("#coqui_api_model_settings_speaker").hide();
|
$("#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 = coquiApiModels[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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -357,29 +481,10 @@ class CoquiTtsProvider {
|
|||||||
// API Calls //
|
// API Calls //
|
||||||
//#############################//
|
//#############################//
|
||||||
|
|
||||||
static async getModelList(origin) {
|
|
||||||
throwIfModuleMissing()
|
|
||||||
const url = new URL(getApiUrl());
|
|
||||||
|
|
||||||
if (origin == "coqui-api")
|
|
||||||
url.pathname = '/api/text-to-speech/coqui/coqui-api/get-models';
|
|
||||||
else
|
|
||||||
url.pathname = '/api/text-to-speech/coqui/local/get-models';
|
|
||||||
|
|
||||||
const apiResult = await doExtrasFetch(url, {method: 'POST'})
|
|
||||||
|
|
||||||
if (!apiResult.ok) {
|
|
||||||
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Get models list request failed');
|
|
||||||
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiResult
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check model installation state, return one of ["installed", "corrupted", "absent"]
|
Check model installation state, return one of ["installed", "corrupted", "absent"]
|
||||||
*/
|
*/
|
||||||
static async checkModelState(modelId) {
|
static async checkmodel_state(model_id) {
|
||||||
throwIfModuleMissing()
|
throwIfModuleMissing()
|
||||||
const url = new URL(getApiUrl());
|
const url = new URL(getApiUrl());
|
||||||
url.pathname = '/api/text-to-speech/coqui/coqui-api/check-model-state';
|
url.pathname = '/api/text-to-speech/coqui/coqui-api/check-model-state';
|
||||||
@@ -391,7 +496,7 @@ class CoquiTtsProvider {
|
|||||||
'Cache-Control': 'no-cache'
|
'Cache-Control': 'no-cache'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"model_id": modelId,
|
"model_id": model_id,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -403,7 +508,7 @@ class CoquiTtsProvider {
|
|||||||
return apiResult
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
static async installModel(modelId) {
|
static async installModel(model_id, action) {
|
||||||
throwIfModuleMissing()
|
throwIfModuleMissing()
|
||||||
const url = new URL(getApiUrl());
|
const url = new URL(getApiUrl());
|
||||||
url.pathname = '/api/text-to-speech/coqui/coqui-api/install-model';
|
url.pathname = '/api/text-to-speech/coqui/coqui-api/install-model';
|
||||||
@@ -415,53 +520,19 @@ class CoquiTtsProvider {
|
|||||||
'Cache-Control': 'no-cache'
|
'Cache-Control': 'no-cache'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"model_id": modelId,
|
"model_id": model_id,
|
||||||
|
"action": action
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!apiResult.ok) {
|
if (!apiResult.ok) {
|
||||||
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Install model '+modelId+' request failed');
|
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Install model '+model_id+' request failed');
|
||||||
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiResult
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getModelSettings(modelId) {
|
|
||||||
throwIfModuleMissing()
|
|
||||||
// API is busy
|
|
||||||
inApiCall = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = new URL(getApiUrl());
|
|
||||||
url.pathname = '/api/text-to-speech/coqui/coqui-api/get-model-settings';
|
|
||||||
|
|
||||||
const apiResult = await doExtrasFetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Cache-Control': 'no-cache'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
"model_id": modelId,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!apiResult.ok) {
|
|
||||||
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Get model setting request failed for '+modelId);
|
|
||||||
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiResult
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.debug(error);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
inApiCall = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get speakers
|
// Get speakers
|
||||||
|
|
||||||
|
|
||||||
@@ -472,12 +543,12 @@ class CoquiTtsProvider {
|
|||||||
async generateTts(text, voiceId) {
|
async generateTts(text, voiceId) {
|
||||||
throwIfModuleMissing()
|
throwIfModuleMissing()
|
||||||
const url = new URL(getApiUrl());
|
const url = new URL(getApiUrl());
|
||||||
url.pathname = '/api/text-to-speech/coqui/process-text';
|
url.pathname = '/api/text-to-speech/coqui/generate-tts';
|
||||||
|
|
||||||
let language = "none"
|
let language = "none"
|
||||||
let speaker = "none"
|
let speaker = "none"
|
||||||
const tokens = voiceId.replaceAll("]","").split("[");
|
const tokens = voiceId.replaceAll("]","").replaceAll("\"","").split("[");
|
||||||
const modelId = tokens[0]
|
const model_id = tokens[0]
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX,"Preparing TTS request for",tokens)
|
console.debug(DEBUG_PREFIX,"Preparing TTS request for",tokens)
|
||||||
|
|
||||||
@@ -485,7 +556,7 @@ class CoquiTtsProvider {
|
|||||||
if (tokens.length > 1) {
|
if (tokens.length > 1) {
|
||||||
const option1 = tokens[1]
|
const option1 = tokens[1]
|
||||||
|
|
||||||
if (modelId.includes("multilingual"))
|
if (model_id.includes("multilingual"))
|
||||||
language = option1
|
language = option1
|
||||||
else
|
else
|
||||||
speaker = option1
|
speaker = option1
|
||||||
@@ -503,9 +574,9 @@ class CoquiTtsProvider {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"text": text,
|
"text": text,
|
||||||
"model_id": modelId,
|
"model_id": model_id,
|
||||||
"language": language,
|
"language_id": parseInt(language),
|
||||||
"speaker": speaker
|
"speaker_id": parseInt(speaker)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -517,7 +588,15 @@ class CoquiTtsProvider {
|
|||||||
return apiResult
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
// Dirty hack to say not implemented
|
||||||
|
async fetchTtsVoiceIds() {
|
||||||
|
return [{name:"Voice samples not implemented for coqui TTS yet, search for the model samples online", voice_id:"",lang:"",}]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing
|
||||||
|
previewTtsVoice(id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
async fetchTtsFromHistory(history_item_id) {
|
async fetchTtsFromHistory(history_item_id) {
|
||||||
return Promise.resolve(history_item_id);
|
return Promise.resolve(history_item_id);
|
||||||
@@ -532,52 +611,8 @@ async function moduleWorker() {
|
|||||||
// Skip if module not loaded
|
// Skip if module not loaded
|
||||||
if (!modules.includes('coqui-tts'))
|
if (!modules.includes('coqui-tts'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 1) Update characters list
|
|
||||||
//----------------------------
|
|
||||||
let currentcharacters = new Set();
|
|
||||||
for (const i of getContext().characters) {
|
|
||||||
currentcharacters.add(i.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentcharacters = Array.from(currentcharacters)
|
updateCharactersList();
|
||||||
|
|
||||||
if (JSON.stringify(charactersList) !== JSON.stringify(currentcharacters)) {
|
|
||||||
charactersList = currentcharacters
|
|
||||||
|
|
||||||
$('#coqui_character_select')
|
|
||||||
.find('option')
|
|
||||||
.remove()
|
|
||||||
.end()
|
|
||||||
.append('<option value="none">Select Character</option>')
|
|
||||||
.val('none')
|
|
||||||
|
|
||||||
for(const charName of charactersList) {
|
|
||||||
$("#coqui_character_select").append(new Option(charName,charName));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug(DEBUG_PREFIX,"Updated character list to:", charactersList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Initialize TTS models lists
|
|
||||||
//---------------------------------
|
|
||||||
if (Object.keys(coquiApiModels).length === 0) {
|
|
||||||
const result = await CoquiTtsProvider.getModelList("coqui-api");
|
|
||||||
coquiApiModels = await result.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
187
public/scripts/extensions/tts/coqui_api_models_settings.json
Normal file
187
public/scripts/extensions/tts/coqui_api_models_settings.json
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
{
|
||||||
|
"multilingual": {
|
||||||
|
"multi-dataset": {
|
||||||
|
"your_tts": {
|
||||||
|
"id": "tts_models/multilingual/multi-dataset/your_tts",
|
||||||
|
"languages": [
|
||||||
|
"en",
|
||||||
|
"fr-fr",
|
||||||
|
"pt-br"
|
||||||
|
],
|
||||||
|
"speakers": [
|
||||||
|
"female-en-5",
|
||||||
|
"female-en-5\n",
|
||||||
|
"female-pt-4\n",
|
||||||
|
"male-en-2",
|
||||||
|
"male-en-2\n",
|
||||||
|
"male-pt-3\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
"ljspeech": {
|
||||||
|
"tacotron2-DDC": {
|
||||||
|
"id": "tts_models/en/ljspeech/tacotron2-DDC"
|
||||||
|
},
|
||||||
|
"glow-tts": {
|
||||||
|
"id": "tts_models/en/ljspeech/glow-tts"
|
||||||
|
},
|
||||||
|
"vits": {
|
||||||
|
"id": "tts_models/en/ljspeech/vits"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vctk": {
|
||||||
|
"vits": {
|
||||||
|
"id": "tts_models/en/vctk/vits",
|
||||||
|
"speakers": [
|
||||||
|
"ED\n",
|
||||||
|
"p225",
|
||||||
|
"p226",
|
||||||
|
"p227",
|
||||||
|
"p228",
|
||||||
|
"p229",
|
||||||
|
"p230",
|
||||||
|
"p231",
|
||||||
|
"p232",
|
||||||
|
"p233",
|
||||||
|
"p234",
|
||||||
|
"p236",
|
||||||
|
"p237",
|
||||||
|
"p238",
|
||||||
|
"p239",
|
||||||
|
"p240",
|
||||||
|
"p241",
|
||||||
|
"p243",
|
||||||
|
"p244",
|
||||||
|
"p245",
|
||||||
|
"p246",
|
||||||
|
"p247",
|
||||||
|
"p248",
|
||||||
|
"p249",
|
||||||
|
"p250",
|
||||||
|
"p251",
|
||||||
|
"p252",
|
||||||
|
"p253",
|
||||||
|
"p254",
|
||||||
|
"p255",
|
||||||
|
"p256",
|
||||||
|
"p257",
|
||||||
|
"p258",
|
||||||
|
"p259",
|
||||||
|
"p260",
|
||||||
|
"p261",
|
||||||
|
"p262",
|
||||||
|
"p263",
|
||||||
|
"p264",
|
||||||
|
"p265",
|
||||||
|
"p266",
|
||||||
|
"p267",
|
||||||
|
"p268",
|
||||||
|
"p269",
|
||||||
|
"p270",
|
||||||
|
"p271",
|
||||||
|
"p272",
|
||||||
|
"p273",
|
||||||
|
"p274",
|
||||||
|
"p275",
|
||||||
|
"p276",
|
||||||
|
"p277",
|
||||||
|
"p278",
|
||||||
|
"p279",
|
||||||
|
"p280",
|
||||||
|
"p281",
|
||||||
|
"p282",
|
||||||
|
"p283",
|
||||||
|
"p284",
|
||||||
|
"p285",
|
||||||
|
"p286",
|
||||||
|
"p287",
|
||||||
|
"p288",
|
||||||
|
"p292",
|
||||||
|
"p293",
|
||||||
|
"p294",
|
||||||
|
"p295",
|
||||||
|
"p297",
|
||||||
|
"p298",
|
||||||
|
"p299",
|
||||||
|
"p300",
|
||||||
|
"p301",
|
||||||
|
"p302",
|
||||||
|
"p303",
|
||||||
|
"p304",
|
||||||
|
"p305",
|
||||||
|
"p306",
|
||||||
|
"p307",
|
||||||
|
"p308",
|
||||||
|
"p310",
|
||||||
|
"p311",
|
||||||
|
"p312",
|
||||||
|
"p313",
|
||||||
|
"p314",
|
||||||
|
"p316",
|
||||||
|
"p317",
|
||||||
|
"p318",
|
||||||
|
"p323",
|
||||||
|
"p326",
|
||||||
|
"p329",
|
||||||
|
"p330",
|
||||||
|
"p333",
|
||||||
|
"p334",
|
||||||
|
"p335",
|
||||||
|
"p336",
|
||||||
|
"p339",
|
||||||
|
"p340",
|
||||||
|
"p341",
|
||||||
|
"p343",
|
||||||
|
"p345",
|
||||||
|
"p347",
|
||||||
|
"p351",
|
||||||
|
"p360",
|
||||||
|
"p361",
|
||||||
|
"p362",
|
||||||
|
"p363",
|
||||||
|
"p364",
|
||||||
|
"p374",
|
||||||
|
"p376"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jenny": {
|
||||||
|
"jenny": {
|
||||||
|
"id": "tts_models/en/jenny/jenny"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es": {
|
||||||
|
"mai": {
|
||||||
|
"tacotron2-DDC": {
|
||||||
|
"id": "tts_models/es/mai/tacotron2-DDC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"css10": {
|
||||||
|
"vits": {
|
||||||
|
"id": "tts_models/es/css10/vits"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr": {
|
||||||
|
"mai": {
|
||||||
|
"tacotron2-DDC": {
|
||||||
|
"id": "tts_models/fr/mai/tacotron2-DDC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"css10": {
|
||||||
|
"vits": {
|
||||||
|
"id": "tts_models/fr/css10/vits"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"kokoro": {
|
||||||
|
"tacotron2-DDC": {
|
||||||
|
"id": "tts_models/ja/kokoro/tacotron2-DDC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,403 +0,0 @@
|
|||||||
import { eventSource, event_types } from "../../../script.js"
|
|
||||||
import { doExtrasFetch, getApiUrl, modules } from "../../extensions.js"
|
|
||||||
|
|
||||||
export { CoquiTtsProvider }
|
|
||||||
|
|
||||||
function throwIfModuleMissing() {
|
|
||||||
if (!modules.includes('coqui-tts')) {
|
|
||||||
toastr.error(`Coqui TTS module not loaded. Add coqui-tts to enable-modules and restart the Extras API.`)
|
|
||||||
throw new Error(`Coqui TTS module not loaded.`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoquiTtsProvider {
|
|
||||||
//########//
|
|
||||||
// Config //
|
|
||||||
//########//
|
|
||||||
|
|
||||||
settings
|
|
||||||
voices = []
|
|
||||||
separator = ' .. '
|
|
||||||
|
|
||||||
defaultSettings = {
|
|
||||||
voiceMap: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get settingsHtml() {
|
|
||||||
let html = `
|
|
||||||
<div class="flex wide100p flexGap10 alignitemscenter">
|
|
||||||
<div style="flex: 80%;">
|
|
||||||
<label for="coqui_model">Model:</label>
|
|
||||||
<select id="coqui_model">
|
|
||||||
<option value="none">Select Model</option>
|
|
||||||
<!-- Add more model options here -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex justifyCenter" style="flex: 20%;">
|
|
||||||
<button id="coqui_preview" class="menu_button menu_button_icon wide100p" type="button">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex wide100p flexGap10">
|
|
||||||
<div class="flex1">
|
|
||||||
<label for="coqui_speaker">Speaker:</label>
|
|
||||||
<select id="coqui_speaker">
|
|
||||||
<!-- Add more speaker options here -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex1">
|
|
||||||
<label for="coqui_language">Language:</label>
|
|
||||||
<select id="coqui_language">
|
|
||||||
<!-- Add more language options here -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
onSettingsChange() {
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSettings(settings) {
|
|
||||||
// Pupulate Provider UI given input settings
|
|
||||||
if (Object.keys(settings).length == 0) {
|
|
||||||
console.info("Using default TTS Provider settings")
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelSelect = document.getElementById('coqui_model');
|
|
||||||
const previewButton = document.getElementById('coqui_preview');
|
|
||||||
previewButton.addEventListener('click', () => {
|
|
||||||
const selectedModel = modelSelect.value;
|
|
||||||
this.sampleTtsVoice(selectedModel);
|
|
||||||
});//add event listener to button
|
|
||||||
|
|
||||||
previewButton.disabled = true;
|
|
||||||
previewButton.innerText = "Select Model";
|
|
||||||
|
|
||||||
// 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 `Invalid setting passed to TTS Provider: ${key}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const textexample = document.getElementById('tts_voice_map');
|
|
||||||
textexample.placeholder = 'Enter comma separated map of charName:ttsName[speakerID][langID]. Example: \nAqua:tts_models--en--ljspeech--glow-tts\model_file.pth,\nDarkness:tts_models--multilingual--multi-dataset--your_tts\model_file.pth[2][3]';
|
|
||||||
|
|
||||||
//Load models function
|
|
||||||
eventSource.on(event_types.EXTRAS_CONNECTED, () => {
|
|
||||||
this.getModels();
|
|
||||||
});
|
|
||||||
this.onttsCoquiHideButtons();
|
|
||||||
console.info("Settings loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
async onttsCoquiHideButtons() {
|
|
||||||
// Get references to the select element and the two input elements
|
|
||||||
const ttsProviderSelect = document.getElementById('tts_provider');
|
|
||||||
const ttsVoicesInput = document.getElementById('tts_voices');
|
|
||||||
const ttsPreviewInput = document.getElementById('tts_preview');
|
|
||||||
|
|
||||||
ttsProviderSelect.addEventListener('click', () => {
|
|
||||||
this.getModels();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add an event listener to the 'change' event of the tts_provider select element
|
|
||||||
ttsProviderSelect.addEventListener('change', () => {
|
|
||||||
// Check if the selected value is 'Coqui'
|
|
||||||
if (ttsProviderSelect.value === 'Coqui') {
|
|
||||||
ttsVoicesInput.style.display = 'none'; // Hide the tts_voices input
|
|
||||||
ttsPreviewInput.style.display = ''; // Show the tts_preview input
|
|
||||||
} else {
|
|
||||||
ttsVoicesInput.style.display = ''; // Show the tts_voices input
|
|
||||||
ttsPreviewInput.style.display = 'none'; // Hide the tts_preview input
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async onApplyClick() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLang() {
|
|
||||||
try {
|
|
||||||
const response = await doExtrasFetch(`${getApiUrl()}/api/coqui-tts/multlang`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const voiceData = await response.json();
|
|
||||||
|
|
||||||
const languageSelect = document.getElementById('coqui_language');
|
|
||||||
languageSelect.innerHTML = ''; // Clear existing options
|
|
||||||
|
|
||||||
if (Object.keys(voiceData).length === 0) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'None';
|
|
||||||
languageSelect.appendChild(option);
|
|
||||||
} else {
|
|
||||||
for (const [key, value] of Object.entries(voiceData)) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = key;
|
|
||||||
option.textContent = key + ": " + value;
|
|
||||||
languageSelect.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
//console.error('Error fetching voice data:', error);
|
|
||||||
|
|
||||||
// Remove all options except "None"
|
|
||||||
const languageSelect = document.getElementById('coqui_language');
|
|
||||||
languageSelect.innerHTML = '';
|
|
||||||
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'None';
|
|
||||||
languageSelect.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async getSpeakers() {
|
|
||||||
try {
|
|
||||||
const response = await doExtrasFetch(`${getApiUrl()}/api/coqui-tts/multspeaker`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const voiceData = await response.json();
|
|
||||||
|
|
||||||
const speakerSelect = document.getElementById('coqui_speaker');
|
|
||||||
speakerSelect.innerHTML = ''; // Clear existing options
|
|
||||||
|
|
||||||
if (Object.keys(voiceData).length === 0) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'None';
|
|
||||||
speakerSelect.appendChild(option);
|
|
||||||
} else {
|
|
||||||
for (const [index, name] of Object.entries(voiceData)) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = index;
|
|
||||||
option.textContent = index + ": " + name;
|
|
||||||
speakerSelect.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
//console.error('Error fetching voice data:', error);
|
|
||||||
|
|
||||||
// Remove all options except "None"
|
|
||||||
const speakerSelect = document.getElementById('coqui_speaker');
|
|
||||||
speakerSelect.innerHTML = '';
|
|
||||||
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'None';
|
|
||||||
speakerSelect.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getModels() {
|
|
||||||
try {
|
|
||||||
throwIfModuleMissing();
|
|
||||||
const response = await doExtrasFetch(`${getApiUrl()}/api/coqui-tts/list`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const voiceIds = await response.json();
|
|
||||||
|
|
||||||
const modelSelect = document.getElementById('coqui_model');
|
|
||||||
if (voiceIds.length === 0) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'Select Model';
|
|
||||||
modelSelect.appendChild(option);
|
|
||||||
} else {
|
|
||||||
voiceIds.forEach(voiceId => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = voiceId;
|
|
||||||
option.textContent = voiceId;
|
|
||||||
modelSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update provider endpoint on model selection change
|
|
||||||
modelSelect.addEventListener('change', () => {
|
|
||||||
const selectedModel = modelSelect.value;
|
|
||||||
this.LoadModel(selectedModel);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching voice IDs:', error);
|
|
||||||
|
|
||||||
// Add "None" option when the request fails or the response is empty
|
|
||||||
const modelSelect = document.getElementById('coqui_model');
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = 'none';
|
|
||||||
option.textContent = 'None';
|
|
||||||
modelSelect.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async LoadModel(selectedModel) {
|
|
||||||
const previewButton = document.getElementById('coqui_preview');
|
|
||||||
previewButton.disabled = true;
|
|
||||||
previewButton.innerText = "Loading";
|
|
||||||
try {
|
|
||||||
throwIfModuleMissing();
|
|
||||||
const response = await doExtrasFetch(`${getApiUrl()}/api/coqui-tts/load?_model=${selectedModel}`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
this.getSpeakers();
|
|
||||||
this.getLang();
|
|
||||||
|
|
||||||
const previewButton = document.getElementById('coqui_preview');
|
|
||||||
previewButton.disabled = false;
|
|
||||||
previewButton.innerText = "Play";
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating provider endpoint:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getVoice(voiceName) {
|
|
||||||
//tts_models--multilingual--multi-dataset--your_tts\model_file.pth[2][1]
|
|
||||||
//tts_models--en--ljspeech--glow-tts\model_file.pth
|
|
||||||
|
|
||||||
let _voiceNameOrg = voiceName; // Store the original voiceName in a variable _voiceNameOrg
|
|
||||||
voiceName = voiceName.replace(/(\[\d+\])+$/, ''); // For example, converts 'model[2][1]' to 'model'
|
|
||||||
|
|
||||||
this.voices = []; //reset for follow up runs
|
|
||||||
|
|
||||||
if (this.voices.length === 0) { this.voices = await this.fetchCheckMap(); }
|
|
||||||
|
|
||||||
// Search for a voice object in the 'this.voices' array where the 'name' property matches the provided 'voiceName'
|
|
||||||
|
|
||||||
//const match = this.voices.find((CoquiVoice) => CoquiVoice.name === voiceName);
|
|
||||||
const match = this.voices.find((CoquiVoice) => CoquiVoice.name === voiceName);
|
|
||||||
|
|
||||||
// If no match is found, throw an error indicating that the TTS Voice name was not found
|
|
||||||
if (!match) {
|
|
||||||
throw new Error(`TTS Voice name ${voiceName} not found`);
|
|
||||||
} else {
|
|
||||||
match.name = _voiceNameOrg;
|
|
||||||
match.voice_id = _voiceNameOrg;
|
|
||||||
}
|
|
||||||
// Return the matched voice object (with the 'name' property updated if a match was found)
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchCheckMap() {
|
|
||||||
const endpoint = `${getApiUrl()}/api/coqui-tts/checkmap`;
|
|
||||||
const response = await doExtrasFetch(endpoint);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
|
||||||
}
|
|
||||||
const voiceData = await response.json();
|
|
||||||
const voices = voiceData.map((voice) => ({
|
|
||||||
id: voice.name,
|
|
||||||
name: voice.id, // this is the issue!!!
|
|
||||||
voice_id: voice.id, // this is the issue!!!
|
|
||||||
//preview_url: false,
|
|
||||||
lang: voice.lang,
|
|
||||||
}));
|
|
||||||
return voices;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchTtsVoiceIds() {
|
|
||||||
throwIfModuleMissing();
|
|
||||||
const endpoint = `${getApiUrl()}/api/coqui-tts/speaker_id`;
|
|
||||||
const response = await doExtrasFetch(endpoint);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
|
||||||
}
|
|
||||||
const voiceData = await response.json();
|
|
||||||
const voices = voiceData.map((voice) => ({
|
|
||||||
id: voice.name,
|
|
||||||
name: voice.id, //add filename here
|
|
||||||
voice_id: voice.id,
|
|
||||||
//preview_url: false,
|
|
||||||
//preview_url: `${getApiUrl()}/api/coqui-tts/download?model=${voice.id}`,
|
|
||||||
//http://localhost:5100/api/coqui-tts/speaker_id/tts_models/en/ljspeech/speedy-speech
|
|
||||||
lang: voice.lang,
|
|
||||||
}));
|
|
||||||
return voices;
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleTtsVoice(voiceId) {
|
|
||||||
// Get the selected values of speaker and language
|
|
||||||
const speakerSelect = document.getElementById('coqui_speaker');
|
|
||||||
const languageSelect = document.getElementById('coqui_language');
|
|
||||||
const selectedSpeaker = speakerSelect.value;
|
|
||||||
const selectedLanguage = languageSelect.value;
|
|
||||||
|
|
||||||
// Construct the URL with the selected values
|
|
||||||
const url = `${getApiUrl()}/api/coqui-tts/tts?text=The%20Quick%20Brown%20Fox%20Jumps%20Over%20the%20Lazy%20Dog.&speaker_id=${voiceId}&style_wav=&language_id=${selectedLanguage}&mspker=${selectedSpeaker}`;
|
|
||||||
|
|
||||||
doExtrasFetch(url)
|
|
||||||
.then(response => response.blob())
|
|
||||||
.then(blob => {
|
|
||||||
const audioUrl = URL.createObjectURL(blob);
|
|
||||||
// Play the audio
|
|
||||||
const audio = new Audio(audioUrl);
|
|
||||||
audio.play();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error performing TTS request:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
previewTtsVoice(voiceId) { //button on avail voices
|
|
||||||
throwIfModuleMissing();
|
|
||||||
const url = `${getApiUrl()}/api/coqui-tts/download?model=${voiceId}`;
|
|
||||||
|
|
||||||
doExtrasFetch(url)
|
|
||||||
.then(response => response.text()) // Expecting a text response
|
|
||||||
.then(responseText => {
|
|
||||||
const isResponseTrue = responseText.trim().toLowerCase() === 'true';
|
|
||||||
|
|
||||||
if (isResponseTrue) {
|
|
||||||
console.log("Downloading Model") //if true
|
|
||||||
} else {
|
|
||||||
console.error('Already Installed'); //if false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error performing download:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async generateTts(text, voiceId) {
|
|
||||||
const response = await this.fetchTtsGeneration(text, voiceId)
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchTtsGeneration(inputText, voiceId) {
|
|
||||||
throwIfModuleMissing();
|
|
||||||
console.info(`Generating new TTS for voice_id ${voiceId}`);
|
|
||||||
const response = await doExtrasFetch(`${getApiUrl()}/api/coqui-tts/tts?text=${encodeURIComponent(inputText)}&speaker_id=${voiceId}`);
|
|
||||||
if (!response.ok) {
|
|
||||||
toastr.error(response.statusText, 'TTS Generation Failed');
|
|
||||||
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
|
||||||
}
|
|
||||||
if (!response.ok) {
|
|
||||||
toastr.error(response.statusText, 'TTS Generation Failed');
|
|
||||||
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchTtsFromHistory(history_item_id) {
|
|
||||||
return Promise.resolve(history_item_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -4,8 +4,7 @@ import { escapeRegex, getStringHash } from '../../utils.js'
|
|||||||
import { EdgeTtsProvider } from './edge.js'
|
import { EdgeTtsProvider } from './edge.js'
|
||||||
import { ElevenLabsTtsProvider } from './elevenlabs.js'
|
import { ElevenLabsTtsProvider } from './elevenlabs.js'
|
||||||
import { SileroTtsProvider } from './silerotts.js'
|
import { SileroTtsProvider } from './silerotts.js'
|
||||||
//import { CoquiTtsProvider } from './coquitts.js'
|
import { CoquiTtsProvider } from './coqui.js'
|
||||||
import { CoquiTtsProvider } from './coqui.js' // TODO: rename once done
|
|
||||||
import { SystemTtsProvider } from './system.js'
|
import { SystemTtsProvider } from './system.js'
|
||||||
import { NovelTtsProvider } from './novel.js'
|
import { NovelTtsProvider } from './novel.js'
|
||||||
import { power_user } from '../../power-user.js'
|
import { power_user } from '../../power-user.js'
|
||||||
|
Reference in New Issue
Block a user