From 395de0fab8d382a0502b3d3998ccefaa0b0f22dd Mon Sep 17 00:00:00 2001 From: Tony Ribeiro Date: Sat, 12 Aug 2023 06:05:39 +0200 Subject: [PATCH] Started refactoring of Coqui-tts extension. --- public/scripts/extensions/tts/coqui.js | 200 +++++++++++++++++++++++++ public/scripts/extensions/tts/index.js | 3 +- 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 public/scripts/extensions/tts/coqui.js diff --git a/public/scripts/extensions/tts/coqui.js b/public/scripts/extensions/tts/coqui.js new file mode 100644 index 000000000..46fe6bb47 --- /dev/null +++ b/public/scripts/extensions/tts/coqui.js @@ -0,0 +1,200 @@ +/* +TODO: + - load/download models + - load assign voice ids + - handle apply/available voices button +*/ + +import { eventSource, event_types } from "../../../script.js" +import { doExtrasFetch, getApiUrl, modules } from "../../extensions.js" + +export { CoquiTtsProvider } + +const DEBUG_PREFIX = " " + +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 { + //#############################// + // Extension UI and Settings // + //#############################// + + settings + voices = [] + separator = ' .. ' + + defaultSettings = { + voiceMap: {} + } + + get settingsHtml() { + let html = ` +
+
+
+ + + + + +
+ + + +
+
+
+
+ ` + return html + } + + loadSettings(settings) { + $("#coqui_api_model_div").hide(); + $("#coqui_api_model_name").hide(); + $("#coqui_model_origin").on("change",this.onModelOriginChange); + $("#coqui_api_language").on("change",this.onModelLanguageChange); + + } + + onSettingsChange() { + } + + async onApplyClick() { + return + } + + // DBG: assume voiceName is correct + // TODO: check voice is correct + async getVoice(voiceName) { + console.log(DEBUG_PREFIX,"getVoice",voiceName); + const output = {voice_id: voiceName}; + return output; + } + + async onModelOriginChange() { + const model_origin = $('#coqui_model_origin').val(); + console.debug(model_origin); + + // TODO: show coqui model list + if (model_origin == "coquiApi") { + $("#coqui_api_model_div").show(); + } + else + $("#coqui_api_model_div").hide(); + + // TODO show local model list + if (model_origin == "local") { + toastr.info("coming soon, ready when ready, etc", DEBUG_PREFIX+' Custom models not supported yet'); + } + + } + + async onModelLanguageChange() { + 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(); + + // TODO: if model list not already load, request it from extras + const result = await CoquiTtsProvider.getCoquiApiModels(); + const models = await result.json(); + console.debug(models,typeof(models)); + console.debug("models lists:", models[model_language]); + + $('#coqui_api_model_name') + .find('option') + .remove() + .end() + .append('') + .val('whatever') + + for(let k in models[model_language]) { + let model_name = k.split("/") + model_name = model_name[0] + " ("+model_name[1]+" dataset)" + $("#coqui_api_model_name").append(new Option(model_name,models[model_language][k])); + } + + // TODO: populate with corresponding dataset/model pairs got from initialization request + + } + + + //#############################// + // API Calls // + //#############################// + + static async getCoquiApiModels() { + const url = new URL(getApiUrl()); + url.pathname = '/api/text-to-speech/coqui/supported-models'; + + const apiResult = await doExtrasFetch(url, {method: 'POST'}) + + if (!apiResult.ok) { + toastr.error(apiResult.statusText, DEBUG_PREFIX+' Get models list 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) { + const url = new URL(getApiUrl()); + url.pathname = '/api/text-to-speech/coqui/process-text'; + + const apiResult = await doExtrasFetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache' + }, + body: JSON.stringify({ + "text": text, + "voiceId": voiceId, + }) + }) + + if (!apiResult.ok) { + toastr.error(apiResult.statusText, 'TTS Generation Failed'); + throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`); + } + + return apiResult + } + + //------------------------------------------------------------------------------------ + + async fetchTtsFromHistory(history_item_id) { + return Promise.resolve(history_item_id); + } +} diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index d1a4d8bef..bb4e6418c 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -4,7 +4,8 @@ import { escapeRegex, getStringHash } from '../../utils.js' import { EdgeTtsProvider } from './edge.js' import { ElevenLabsTtsProvider } from './elevenlabs.js' import { SileroTtsProvider } from './silerotts.js' -import { CoquiTtsProvider } from './coquitts.js' +//import { CoquiTtsProvider } from './coquitts.js' +import { CoquiTtsProvider } from './coqui.js' // TODO: rename once done import { SystemTtsProvider } from './system.js' import { NovelTtsProvider } from './novel.js' import { power_user } from '../../power-user.js'