2023-08-12 06:05:39 +02:00
/ *
TODO :
2023-08-14 04:03:28 +02:00
- Hide voice map its just confusing
- Delete useless call
2023-08-12 06:05:39 +02:00
* /
2023-08-14 04:03:28 +02:00
import { saveSettingsDebounced } from "../../../script.js"
2023-08-13 02:18:46 +02:00
import { doExtrasFetch , extension _settings , getApiUrl , getContext , modules , ModuleWorkerWrapper } from "../../extensions.js"
2023-08-12 06:05:39 +02:00
export { CoquiTtsProvider }
2023-08-17 01:16:57 +02:00
const DEBUG _PREFIX = "<Coqui TTS module> " ;
const UPDATE _INTERVAL = 1000 ;
let inApiCall = false ;
let charactersList = [ ] ; // Updated with module worker
let coquiApiModels = { } ; // Initialized only once
2023-08-17 21:33:06 +02:00
let coquiApiModelsFull = { } ; // Initialized only once
2023-08-17 01:16:57 +02:00
let coquiLocalModels = [ ] ; // Initialized only once
let coquiLocalModelsReceived = false ;
2023-08-13 02:18:46 +02:00
/ *
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"
}
2023-08-12 06:05:39 +02:00
function throwIfModuleMissing ( ) {
if ( ! modules . includes ( 'coqui-tts' ) ) {
2023-08-13 02:18:46 +02:00
toastr . error ( ` Add coqui-tts to enable-modules and restart the Extras API. ` , "Coqui TTS module not loaded." , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
throw new Error ( DEBUG _PREFIX , ` Coqui TTS module not loaded. ` ) ;
2023-08-12 06:05:39 +02:00
}
}
2023-08-13 02:18:46 +02:00
function resetModelSettings ( ) {
$ ( "#coqui_api_model_settings_language" ) . val ( "none" ) ;
$ ( "#coqui_api_model_settings_speaker" ) . val ( "none" ) ;
}
2023-08-14 04:03:28 +02:00
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 ) ;
}
}
2023-08-12 06:05:39 +02:00
class CoquiTtsProvider {
//#############################//
// Extension UI and Settings //
//#############################//
settings
defaultSettings = {
2023-08-13 02:18:46 +02:00
voiceMap : "" ,
voiceMapDict : { }
2023-08-12 06:05:39 +02:00
}
get settingsHtml ( ) {
let html = `
< div class = "flex wide100p flexGap10 alignitemscenter" >
< div >
< div style = "flex: 50%;" >
< label for = "coqui_character_select" > Character : < / l a b e l >
< select id = "coqui_character_select" >
<!-- Populated by JS -- >
< / s e l e c t >
2023-08-17 01:16:57 +02:00
< input id = "coqui_remove_char_mapping" class = "menu_button" type = "button" value = "Remove from Voice Map" / >
2023-08-12 06:05:39 +02:00
< label for = "coqui_model_origin" > Models : < / l a b e l >
2023-08-14 04:03:28 +02:00
< select id = "coqui_model_origin" > gpu _mode
2023-08-12 06:05:39 +02:00
< option value = "none" > Select Origin < / o p t i o n >
2023-08-17 21:33:06 +02:00
< option value = "coqui-api" > Coqui API ( Tested ) < / o p t i o n >
< option value = "coqui-api-full" > Coqui API ( Experimental ) < / o p t i o n >
2023-08-16 19:19:13 +02:00
< option value = "local" > My Models < / o p t i o n >
2023-08-12 06:05:39 +02:00
< / s e l e c t >
< div id = "coqui_api_model_div" >
< select id = "coqui_api_language" >
<!-- Populated by JS and request -- >
< / s e l e c t >
< select id = "coqui_api_model_name" >
<!-- Populated by JS and request -- >
< / s e l e c t >
2023-08-13 02:18:46 +02:00
< div id = "coqui_api_model_settings" >
< select id = "coqui_api_model_settings_language" >
<!-- Populated by JS and request -- >
< / s e l e c t >
< select id = "coqui_api_model_settings_speaker" >
<!-- Populated by JS and request -- >
< / s e l e c t >
< / d i v >
2023-08-14 04:03:28 +02:00
< span id = "coqui_api_model_install_status" > Model installed on extras server < / s p a n >
< input id = "coqui_api_model_install_button" class = "menu_button" type = "button" value = "Install" / >
2023-08-12 06:05:39 +02:00
< / d i v >
2023-08-16 19:19:13 +02:00
< div id = "coqui_local_model_div" >
< select id = "coqui_local_model_name" >
<!-- Populated by JS and request -- >
< / s e l e c t >
< / d i v >
2023-08-12 06:05:39 +02:00
< / d i v >
< / d i v >
< / d i v >
`
return html
}
loadSettings ( settings ) {
2023-08-13 02:18:46 +02:00
// 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 } `
}
}
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-12 06:05:39 +02:00
$ ( "#coqui_api_model_div" ) . hide ( ) ;
2023-08-16 19:19:13 +02:00
$ ( "#coqui_local_model_div" ) . hide ( ) ;
$ ( "#coqui_api_language" ) . show ( ) ;
2023-08-12 06:05:39 +02:00
$ ( "#coqui_api_model_name" ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
$ ( "#coqui_api_model_settings" ) . hide ( ) ;
2023-08-14 04:03:28 +02:00
$ ( "#coqui_api_model_install_status" ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
$ ( "#coqui_api_model_install_button" ) . hide ( ) ;
2023-08-14 04:03:28 +02:00
2023-08-17 01:16:57 +02:00
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_char_mapping" ) . on ( "click" , function ( ) { that . onRemoveClick ( ) } ) ;
2023-08-14 04:03:28 +02:00
// 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 ) ;
2023-08-17 21:33:06 +02:00
/ *
2023-08-14 04:03:28 +02:00
$ ( '#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 ) ;
2023-08-17 21:33:06 +02:00
} * /
} ) ;
// Load coqui-api FULL settings from json file
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 ) ;
} * /
2023-08-14 04:03:28 +02:00
} ) ;
}
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 ;
2023-08-12 06:05:39 +02:00
}
onSettingsChange ( ) {
2023-08-14 04:03:28 +02:00
console . debug ( DEBUG _PREFIX , "Settings changes" , this . settings ) ;
extension _settings . tts . Coqui = this . settings ;
2023-08-12 06:05:39 +02:00
}
async onApplyClick ( ) {
2023-08-13 02:18:46 +02:00
if ( inApiCall ) {
return ; // TOdo block dropdown
}
const character = $ ( "#coqui_character_select" ) . val ( ) ;
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 ( character === "none" ) {
toastr . error ( ` Character not selected, please select one. ` , DEBUG _PREFIX + " voice mapping character" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
if ( model _origin == "none" ) {
toastr . error ( ` Origin not selected, please select one. ` , DEBUG _PREFIX + " voice mapping origin" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
if ( model _origin == "local" ) {
2023-08-16 19:19:13 +02:00
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 . updateVoiceMap ( ) ; // Overide any manual modification
return ;
}
this . settings . voiceMapDict [ character ] = { model _type : "local" , model _id : "local/" + model _id } ;
console . debug ( DEBUG _PREFIX , "Registered new voice map: " , character , ":" , this . settings . voiceMapDict [ character ] ) ;
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
if ( model _language == "none" ) {
toastr . error ( ` Language not selected, please select one. ` , DEBUG _PREFIX + " voice mapping language" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
if ( model _name == "none" ) {
toastr . error ( ` Model not selected, please select one. ` , DEBUG _PREFIX + " voice mapping model" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
if ( model _setting _language == "none" )
model _setting _language = null ;
if ( model _setting _speaker == "none" )
model _setting _speaker = null ;
2023-08-14 04:03:28 +02:00
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
2023-08-13 02:18:46 +02:00
2023-08-17 21:33:06 +02:00
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 ] ) {
2023-08-14 04:03:28 +02:00
toastr . error ( ` Model language not selected, please select one. ` , DEBUG _PREFIX + " voice mapping model language" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
return ;
}
2023-08-13 02:18:46 +02:00
2023-08-17 21:33:06 +02:00
if ( model _setting _speaker == null & "speakers" in modelDict [ model _language ] [ model _dataset ] [ model _label ] ) {
2023-08-14 04:03:28 +02:00
toastr . error ( ` Model speaker not selected, please select one. ` , DEBUG _PREFIX + " voice mapping model speaker" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
return ;
}
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
console . debug ( DEBUG _PREFIX , "Current voice map: " , this . settings . voiceMap ) ;
2023-08-13 02:18:46 +02:00
2023-08-16 19:19:13 +02:00
this . settings . voiceMapDict [ character ] = { model _type : "coqui-api" , model _id : model _id , model _language : model _setting _language , model _speaker : model _setting _speaker } ;
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
console . debug ( DEBUG _PREFIX , "Registered new voice map: " , character , ":" , this . settings . voiceMapDict [ character ] ) ;
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
this . updateVoiceMap ( ) ;
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
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 } ) ;
2023-08-12 06:05:39 +02:00
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 ;
}
2023-08-17 01:16:57 +02:00
async onRemoveClick ( ) {
const character = $ ( "#coqui_character_select" ) . val ( ) ;
if ( character === "none" ) {
toastr . error ( ` Character not selected, please select one. ` , DEBUG _PREFIX + " voice mapping character" , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
return ;
}
// Todo erase from voicemap
delete ( this . settings . voiceMapDict [ character ] ) ;
this . updateVoiceMap ( ) ; // TODO
}
2023-08-12 06:05:39 +02:00
async onModelOriginChange ( ) {
2023-08-13 02:18:46 +02:00
throwIfModuleMissing ( )
resetModelSettings ( ) ;
2023-08-12 06:05:39 +02:00
const model _origin = $ ( '#coqui_model_origin' ) . val ( ) ;
2023-08-16 21:31:51 +02:00
if ( model _origin == "none" ) {
$ ( "#coqui_local_model_div" ) . hide ( ) ;
$ ( "#coqui_api_model_div" ) . hide ( ) ;
}
2023-08-12 06:05:39 +02:00
2023-08-17 21:33:06 +02:00
// show coqui model selected list (SAFE)
2023-08-13 02:18:46 +02:00
if ( model _origin == "coqui-api" ) {
2023-08-16 19:19:13 +02:00
$ ( "#coqui_local_model_div" ) . hide ( ) ;
2023-08-17 21:33:06 +02:00
$ ( '#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 , ")" ) ;
}
2023-08-12 06:05:39 +02:00
$ ( "#coqui_api_model_div" ) . show ( ) ;
}
2023-08-16 19:19:13 +02:00
2023-08-12 06:05:39 +02:00
2023-08-16 19:19:13 +02:00
// show local model list
2023-08-12 06:05:39 +02:00
if ( model _origin == "local" ) {
2023-08-16 19:19:13 +02:00
$ ( "#coqui_api_model_div" ) . hide ( ) ;
$ ( "#coqui_local_model_div" ) . show ( ) ;
2023-08-12 06:05:39 +02:00
}
}
async onModelLanguageChange ( ) {
2023-08-13 02:18:46 +02:00
throwIfModuleMissing ( ) ;
resetModelSettings ( ) ;
$ ( "#coqui_api_model_settings" ) . hide ( ) ;
const model _origin = $ ( '#coqui_model_origin' ) . val ( ) ;
2023-08-12 06:05:39 +02:00
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>' )
2023-08-13 02:18:46 +02:00
. val ( 'none' ) ;
2023-08-12 06:05:39 +02:00
2023-08-17 21:33:06 +02:00
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 ] ) {
2023-08-14 04:03:28 +02:00
const model _id = model _dataset + "/" + model _name
2023-08-13 02:18:46 +02:00
const model _label = model _name + " (" + model _dataset + " dataset)"
$ ( "#coqui_api_model_name" ) . append ( new Option ( model _label , model _id ) ) ;
2023-08-12 06:05:39 +02:00
}
}
2023-08-13 02:18:46 +02:00
async onModelNameChange ( ) {
throwIfModuleMissing ( ) ;
resetModelSettings ( ) ;
$ ( "#coqui_api_model_settings" ) . hide ( ) ;
2023-08-17 21:33:06 +02:00
const model _origin = $ ( '#coqui_model_origin' ) . val ( ) ;
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
// No model selected
if ( $ ( '#coqui_api_model_name' ) . val ( ) == "none" ) {
2023-08-13 02:18:46 +02:00
$ ( "#coqui_api_model_install_button" ) . off ( 'click' ) ;
2023-08-14 04:03:28 +02:00
$ ( "#coqui_api_model_install_button" ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
return ;
}
2023-08-14 04:03:28 +02:00
// 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 ] ;
2023-08-13 02:18:46 +02:00
2023-08-17 21:33:06 +02:00
let modelDict = coquiApiModels
if ( model _origin == "coqui-api-full" )
modelDict = coquiApiModelsFull
const model _settings = modelDict [ model _language ] [ model _dataset ] [ model _name ]
2023-08-13 02:18:46 +02:00
2023-08-14 04:03:28 +02:00
if ( "languages" in model _settings ) {
2023-08-13 02:18:46 +02:00
$ ( "#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' ) ;
2023-08-14 04:03:28 +02:00
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 ) ) ;
2023-08-13 02:18:46 +02:00
}
}
else {
$ ( "#coqui_api_model_settings_language" ) . hide ( ) ;
}
2023-08-14 04:03:28 +02:00
if ( "speakers" in model _settings ) {
2023-08-13 02:18:46 +02:00
$ ( "#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' ) ;
2023-08-14 04:03:28 +02:00
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 ) ) ;
2023-08-13 02:18:46 +02:00
}
}
else {
$ ( "#coqui_api_model_settings_speaker" ) . hide ( ) ;
}
2023-08-14 04:03:28 +02:00
$ ( "#coqui_api_model_install_status" ) . text ( "Requesting model to extras server..." ) ;
$ ( "#coqui_api_model_install_status" ) . show ( ) ;
2023-08-12 06:05:39 +02:00
2023-08-14 04:03:28 +02:00
// Check if already installed and propose to do it otherwise
2023-08-17 21:33:06 +02:00
const model _id = modelDict [ model _language ] [ model _dataset ] [ model _name ] [ "id" ]
2023-08-14 04:03:28 +02:00
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" ] ;
2023-08-12 06:05:39 +02:00
2023-08-14 04:03:28 +02:00
console . debug ( DEBUG _PREFIX , " Model state:" , model _state )
2023-08-12 06:05:39 +02:00
2023-08-14 04:03:28 +02:00
if ( model _state == "installed" ) {
$ ( "#coqui_api_model_install_status" ) . text ( "Model already installed on extras server" ) ;
$ ( "#coqui_api_model_install_button" ) . hide ( ) ;
2023-08-12 06:05:39 +02:00
}
2023-08-14 04:03:28 +02:00
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" ) ;
}
2023-08-12 06:05:39 +02:00
2023-08-14 04:03:28 +02:00
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 ;
}
2023-08-12 06:05:39 +02:00
}
2023-08-14 04:03:28 +02:00
//#############################//
// API Calls //
//#############################//
2023-08-12 06:05:39 +02:00
2023-08-13 02:18:46 +02:00
/ *
Check model installation state , return one of [ "installed" , "corrupted" , "absent" ]
* /
2023-08-14 04:03:28 +02:00
static async checkmodel _state ( model _id ) {
2023-08-13 02:18:46 +02:00
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 ( {
2023-08-14 04:03:28 +02:00
"model_id" : model _id ,
2023-08-13 02:18:46 +02:00
} )
} ) ;
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
}
2023-08-14 04:03:28 +02:00
static async installModel ( model _id , action ) {
2023-08-13 02:18:46 +02:00
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 ( {
2023-08-14 04:03:28 +02:00
"model_id" : model _id ,
"action" : action
2023-08-13 02:18:46 +02:00
} )
} ) ;
if ( ! apiResult . ok ) {
2023-08-14 04:03:28 +02:00
toastr . error ( apiResult . statusText , DEBUG _PREFIX + ' Install model ' + model _id + ' request failed' ) ;
2023-08-13 02:18:46 +02:00
throw new Error ( ` HTTP ${ apiResult . status } : ${ await apiResult . text ( ) } ` ) ;
}
return apiResult
}
2023-08-16 19:19:13 +02:00
/ *
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
}
2023-08-13 02:18:46 +02:00
2023-08-12 06:05:39 +02:00
// 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 ) {
2023-08-13 02:18:46 +02:00
throwIfModuleMissing ( )
2023-08-12 06:05:39 +02:00
const url = new URL ( getApiUrl ( ) ) ;
2023-08-14 04:03:28 +02:00
url . pathname = '/api/text-to-speech/coqui/generate-tts' ;
2023-08-12 06:05:39 +02:00
2023-08-13 02:18:46 +02:00
let language = "none"
let speaker = "none"
2023-08-14 04:03:28 +02:00
const tokens = voiceId . replaceAll ( "]" , "" ) . replaceAll ( "\"" , "" ) . split ( "[" ) ;
const model _id = tokens [ 0 ]
2023-08-13 02:18:46 +02:00
console . debug ( DEBUG _PREFIX , "Preparing TTS request for" , tokens )
// First option
if ( tokens . length > 1 ) {
const option1 = tokens [ 1 ]
2023-08-14 04:03:28 +02:00
if ( model _id . includes ( "multilingual" ) )
2023-08-13 02:18:46 +02:00
language = option1
else
speaker = option1
}
// Second option
if ( tokens . length > 2 )
speaker = tokens [ 2 ] ;
2023-08-12 06:05:39 +02:00
const apiResult = await doExtrasFetch ( url , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
'Cache-Control' : 'no-cache'
} ,
body : JSON . stringify ( {
"text" : text ,
2023-08-14 04:03:28 +02:00
"model_id" : model _id ,
"language_id" : parseInt ( language ) ,
"speaker_id" : parseInt ( speaker )
2023-08-12 06:05:39 +02:00
} )
2023-08-13 02:18:46 +02:00
} ) ;
2023-08-12 06:05:39 +02:00
if ( ! apiResult . ok ) {
toastr . error ( apiResult . statusText , 'TTS Generation Failed' ) ;
throw new Error ( ` HTTP ${ apiResult . status } : ${ await apiResult . text ( ) } ` ) ;
}
return apiResult
}
2023-08-14 04:03:28 +02:00
// 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
}
2023-08-12 06:05:39 +02:00
async fetchTtsFromHistory ( history _item _id ) {
return Promise . resolve ( history _item _id ) ;
}
}
2023-08-13 02:18:46 +02:00
//#############################//
// Module Worker //
//#############################//
async function moduleWorker ( ) {
2023-08-14 04:03:28 +02:00
updateCharactersList ( ) ;
2023-08-16 19:19:13 +02:00
if ( ! modules . includes ( 'coqui-tts' ) )
return
// Initialized local model once
2023-08-17 01:16:57 +02:00
if ( ! coquiLocalModelsReceived ) {
2023-08-16 19:19:13 +02:00
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 ) ) ;
2023-08-17 01:16:57 +02:00
coquiLocalModelsReceived = true ;
2023-08-16 19:19:13 +02:00
}
2023-08-13 02:18:46 +02:00
}
$ ( document ) . ready ( function ( ) {
const wrapper = new ModuleWorkerWrapper ( moduleWorker ) ;
setInterval ( wrapper . update . bind ( wrapper ) , UPDATE _INTERVAL ) ;
moduleWorker ( ) ;
} )