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-12-02 20:11:06 +01:00
import { doExtrasFetch , extension _settings , getApiUrl , modules } from '../../extensions.js' ;
import { callPopup } from '../../../script.js' ;
import { initVoiceMap } from './index.js' ;
2023-08-12 06:05:39 +02:00
2023-12-02 20:11:06 +01:00
export { CoquiTtsProvider } ;
2023-08-12 06:05:39 +02:00
2023-12-02 19:04:51 +01:00
const DEBUG _PREFIX = '<Coqui TTS module> ' ;
2023-08-17 01:16:57 +02:00
let inApiCall = false ;
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 = {
2023-12-02 19:04:51 +01:00
'multilingual' : 'Multilingual' ,
'en' : 'English' ,
'fr' : 'French' ,
'es' : 'Spanish' ,
2023-12-02 21:06:57 +01:00
'ja' : 'Japanese' ,
2023-12-02 20:11:06 +01:00
} ;
2023-08-12 06:05:39 +02:00
function throwIfModuleMissing ( ) {
if ( ! modules . includes ( 'coqui-tts' ) ) {
2023-12-02 20:11:06 +01:00
const message = 'Coqui TTS module not loaded. Add coqui-tts to enable-modules and restart the Extras API.' ;
2023-08-23 15:27:53 +02:00
// toastr.error(message, { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
throw new Error ( DEBUG _PREFIX , message ) ;
2023-08-12 06:05:39 +02:00
}
}
2023-08-13 02:18:46 +02:00
function resetModelSettings ( ) {
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_settings_language' ) . val ( 'none' ) ;
$ ( '#coqui_api_model_settings_speaker' ) . val ( 'none' ) ;
2023-08-13 02:18:46 +02:00
}
2023-08-28 20:58:46 +02:00
2023-08-12 06:05:39 +02:00
class CoquiTtsProvider {
//#############################//
// Extension UI and Settings //
//#############################//
2023-12-02 20:11:06 +01:00
settings ;
2023-08-12 06:05:39 +02:00
defaultSettings = {
2023-08-26 05:51:58 +02:00
voiceMap : { } ,
customVoices : { } ,
2023-08-25 15:27:43 +02:00
voiceIds : [ ] ,
2023-12-02 21:06:57 +01:00
voiceMapDict : { } ,
2023-12-02 20:11:06 +01:00
} ;
2023-08-12 06:05:39 +02:00
get settingsHtml ( ) {
let html = `
< div class = "flex wide100p flexGap10 alignitemscenter" >
< div >
< div style = "flex: 50%;" >
2023-08-25 15:27:43 +02:00
< 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 . < / s m a l l > < b r >
2023-08-26 05:51:58 +02:00
< label for = "coqui_voicename_select" > Select Saved Voice : < / l a b e l >
< select id = "coqui_voicename_select" >
2023-08-12 06:05:39 +02:00
<!-- Populated by JS -- >
< / s e l e c t >
2023-08-25 15:27:43 +02:00
< 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" / >
< / d i v >
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-28 20:58:46 +02:00
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 >
2023-12-02 20:11:06 +01:00
` ;
return html ;
2023-08-12 06:05:39 +02:00
}
2023-08-28 20:58:46 +02:00
async loadSettings ( settings ) {
2023-08-13 02:18:46 +02:00
// Only accept keys defined in defaultSettings
2023-12-02 20:11:06 +01:00
this . settings = this . defaultSettings ;
2023-08-13 02:18:46 +02:00
2023-08-17 11:05:17 +02:00
for ( const key in settings ) {
if ( key in this . settings ) {
2023-12-02 20:11:06 +01:00
this . settings [ key ] = settings [ key ] ;
2023-08-13 02:18:46 +02:00
} else {
2023-12-02 20:11:06 +01:00
throw DEBUG _PREFIX + ` Invalid setting passed to extension: ${ key } ` ;
2023-08-13 02:18:46 +02:00
}
}
2023-08-28 20:58:46 +02:00
await initLocalModels ( ) ;
2023-08-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-14 04:03:28 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_div' ) . hide ( ) ;
$ ( '#coqui_local_model_div' ) . hide ( ) ;
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#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 ( ) ;
2023-08-14 04:03:28 +02:00
2023-12-02 20:11:06 +01: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 ( ) ; } ) ;
2023-08-17 01:16:57 +02:00
2023-12-02 20:11:06 +01:00
$ ( '#coqui_remove_voiceId_mapping' ) . on ( 'click' , function ( ) { that . onRemoveClick ( ) ; } ) ;
$ ( '#coqui_add_voiceId_mapping' ) . on ( 'click' , function ( ) { that . onAddClick ( ) ; } ) ;
2023-08-14 04:03:28 +02:00
// Load coqui-api settings from json file
2023-12-02 19:04:51 +01:00
await fetch ( '/scripts/extensions/tts/coqui_api_models_settings.json' )
2023-12-02 20:56:16 +01:00
. 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
} * /
2023-12-02 20:56:16 +01:00
} ) ;
2023-08-17 21:33:06 +02:00
// Load coqui-api FULL settings from json file
2023-12-02 19:04:51 +01:00
await fetch ( '/scripts/extensions/tts/coqui_api_models_settings_full.json' )
2023-12-02 20:56:16 +01:00
. then ( response => response . json ( ) )
. then ( json => {
coquiApiModelsFull = json ;
console . debug ( DEBUG _PREFIX , 'initialized coqui-api full model list to' , coquiApiModelsFull ) ;
2023-08-17 21:33:06 +02:00
/ *
$ ( '#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-12-02 20:56:16 +01:00
} ) ;
2023-08-14 04:03:28 +02:00
}
2023-08-22 15:30:33 +02:00
// Perform a simple readiness check by trying to fetch voiceIds
async checkReady ( ) {
2023-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
await this . fetchTtsVoiceObjects ( ) ;
2023-08-22 15:30:33 +02:00
}
2023-08-26 05:51:58 +02:00
updateCustomVoices ( ) {
2023-08-25 15:27:43 +02:00
// Takes voiceMapDict and converts it to a string to save to voiceMap
2023-08-26 05:51:58 +02:00
this . settings . customVoices = { } ;
for ( let voiceName in this . settings . voiceMapDict ) {
const voiceId = this . settings . voiceMapDict [ voiceName ] ;
2023-12-02 19:04:51 +01:00
this . settings . customVoices [ voiceName ] = voiceId [ 'model_id' ] ;
2023-08-14 04:03:28 +02:00
2023-12-02 19:04:51 +01:00
if ( voiceId [ 'model_language' ] != null )
this . settings . customVoices [ voiceName ] += '[' + voiceId [ 'model_language' ] + ']' ;
2023-08-14 04:03:28 +02:00
2023-12-02 19:04:51 +01:00
if ( voiceId [ 'model_speaker' ] != null )
this . settings . customVoices [ voiceName ] += '[' + voiceId [ 'model_speaker' ] + ']' ;
2023-08-14 04:03:28 +02:00
}
2023-08-25 15:27:43 +02:00
// Update UI select list with voices
2023-12-02 20:11:06 +01:00
$ ( '#coqui_voicename_select' ) . empty ( ) ;
2023-08-26 05:51:58 +02:00
$ ( '#coqui_voicename_select' )
2023-08-25 15:27:43 +02:00
. find ( 'option' )
. remove ( )
. end ( )
2023-08-26 05:51:58 +02:00
. append ( '<option value="none">Select Voice</option>' )
2023-12-02 20:11:06 +01:00
. val ( 'none' ) ;
2023-08-26 05:51:58 +02:00
for ( const voiceName in this . settings . voiceMapDict ) {
2023-12-02 19:04:51 +01:00
$ ( '#coqui_voicename_select' ) . append ( new Option ( voiceName , voiceName ) ) ;
2023-08-25 15:27:43 +02:00
}
2023-08-28 20:58:46 +02:00
2023-12-02 20:11:06 +01:00
this . onSettingsChange ( ) ;
2023-08-12 06:05:39 +02:00
}
2023-08-17 11:05:17 +02:00
2023-08-12 06:05:39 +02:00
onSettingsChange ( ) {
2023-12-02 19:04:51 +01:00
console . debug ( DEBUG _PREFIX , 'Settings changes' , this . settings ) ;
2023-08-14 04:03:28 +02:00
extension _settings . tts . Coqui = this . settings ;
2023-08-12 06:05:39 +02:00
}
2023-08-26 05:51:58 +02:00
async onRefreshClick ( ) {
2023-12-02 20:11:06 +01:00
this . checkReady ( ) ;
2023-08-25 15:27:43 +02:00
}
async onAddClick ( ) {
2023-08-13 02:18:46 +02:00
if ( inApiCall ) {
2023-08-25 15:27:43 +02:00
return ; //TODO: block dropdown
2023-08-13 02:18:46 +02:00
}
2023-08-28 20:58:46 +02:00
2023-08-25 15:27:43 +02:00
// Ask user for voiceId name to save voice
2023-12-02 20:11:06 +01:00
const voiceName = await callPopup ( '<h3>Name of Coqui voice to add to voice select dropdown:</h3>' , 'input' ) ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
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 ( ) ;
2023-08-13 02:18:46 +02:00
2023-08-17 11:05:17 +02:00
2023-08-26 05:51:58 +02:00
if ( ! voiceName ) {
2023-12-02 19:04:51 +01:00
toastr . error ( 'Voice name empty, please enter one.' , DEBUG _PREFIX + ' voice mapping voice name' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
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-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
if ( model _origin == 'local' ) {
const model _id = $ ( '#coqui_local_model_name' ) . val ( ) ;
2023-08-16 19:19:13 +02:00
2023-12-02 19:04:51 +01:00
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-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-16 19:19:13 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
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 ] ) ;
2023-08-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
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-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
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-26 05:51:58 +02:00
this . updateCustomVoices ( ) ; // Overide any manual modification
2023-08-13 02:18:46 +02:00
return ;
}
2023-12-02 19:04:51 +01:00
if ( model _setting _language == 'none' )
2023-08-13 02:18:46 +02:00
model _setting _language = null ;
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
if ( model _setting _speaker == 'none' )
2023-08-13 02:18:46 +02:00
model _setting _speaker = null ;
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
const tokens = $ ( '#coqui_api_model_name' ) . val ( ) . split ( '/' ) ;
2023-08-14 04:03:28 +02:00
const model _dataset = tokens [ 0 ] ;
const model _label = tokens [ 1 ] ;
2023-12-02 20:11:06 +01:00
const model _id = 'tts_models/' + model _language + '/' + model _dataset + '/' + model _label ;
2023-08-13 02:18:46 +02:00
2023-12-02 20:11:06 +01:00
let modelDict = coquiApiModels ;
2023-12-02 19:04:51 +01:00
if ( model _origin == 'coqui-api-full' )
2023-12-02 20:11:06 +01:00
modelDict = coquiApiModelsFull ;
2023-08-17 21:33:06 +02:00
2023-12-02 19:04:51 +01:00
if ( model _setting _language == null & 'languages' in modelDict [ model _language ] [ model _dataset ] [ model _label ] ) {
2024-01-01 15:07:21 +01:00
toastr . error ( 'Model language not selected, please select one.' , DEBUG _PREFIX + ' voice mapping model language' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
return ;
}
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
if ( model _setting _speaker == null & 'speakers' in modelDict [ model _language ] [ model _dataset ] [ model _label ] ) {
2024-01-01 15:07:21 +01:00
toastr . error ( 'Model speaker not selected, please select one.' , DEBUG _PREFIX + ' voice mapping model speaker' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
return ;
}
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
console . debug ( DEBUG _PREFIX , 'Current custom voices: ' , this . settings . customVoices ) ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
this . settings . voiceMapDict [ voiceName ] = { 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-12-02 19:04:51 +01:00
console . debug ( DEBUG _PREFIX , 'Registered new voice map: ' , voiceName , ':' , this . settings . voiceMapDict [ voiceName ] ) ;
2023-08-13 02:18:46 +02:00
2023-08-26 05:51:58 +02:00
this . updateCustomVoices ( ) ;
2023-12-02 20:11:06 +01:00
initVoiceMap ( ) ; // Update TTS extension voiceMap
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
let successMsg = voiceName + ':' + model _id ;
2023-08-14 04:03:28 +02:00
if ( model _setting _language != null )
2023-12-02 19:04:51 +01:00
successMsg += '[' + model _setting _language + ']' ;
2023-08-14 04:03:28 +02:00
if ( model _setting _speaker != null )
2023-12-02 19:04:51 +01:00
successMsg += '[' + model _setting _speaker + ']' ;
toastr . info ( successMsg , DEBUG _PREFIX + ' voice map updated' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-26 05:51:58 +02:00
2023-12-02 20:11:06 +01:00
return ;
2023-08-12 06:05:39 +02:00
}
async getVoice ( voiceName ) {
2023-12-02 20:11:06 +01:00
let match = await this . fetchTtsVoiceObjects ( ) ;
2023-08-26 05:51:58 +02:00
match = match . filter (
2023-12-02 21:06:57 +01:00
voice => voice . name == voiceName ,
2023-12-02 20:11:06 +01:00
) [ 0 ] ;
2023-08-26 05:51:58 +02:00
if ( ! match ) {
2023-12-02 20:11:06 +01:00
throw ` TTS Voice name ${ voiceName } not found in CoquiTTS Provider voice list ` ;
2023-08-26 05:51:58 +02:00
}
return match ;
2023-08-12 06:05:39 +02:00
}
2023-08-17 01:16:57 +02:00
async onRemoveClick ( ) {
2023-12-02 19:04:51 +01:00
const voiceName = $ ( '#coqui_voicename_select' ) . val ( ) ;
2023-08-17 01:16:57 +02:00
2023-12-02 19:04:51 +01:00
if ( voiceName === 'none' ) {
toastr . error ( 'Voice not selected, please select one.' , DEBUG _PREFIX + ' voice mapping voiceId' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-17 01:16:57 +02:00
return ;
}
// Todo erase from voicemap
2023-08-26 05:51:58 +02:00
delete ( this . settings . voiceMapDict [ voiceName ] ) ;
this . updateCustomVoices ( ) ;
2023-12-02 20:11:06 +01:00
initVoiceMap ( ) ; // Update TTS extension voiceMap
2023-08-17 01:16:57 +02:00
}
2023-08-12 06:05:39 +02:00
async onModelOriginChange ( ) {
2023-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
2023-08-13 02:18:46 +02:00
resetModelSettings ( ) ;
2023-08-12 06:05:39 +02:00
const model _origin = $ ( '#coqui_model_origin' ) . val ( ) ;
2023-08-16 21:31:51 +02:00
2023-12-02 19:04:51 +01:00
if ( model _origin == 'none' ) {
$ ( '#coqui_local_model_div' ) . hide ( ) ;
$ ( '#coqui_api_model_div' ) . hide ( ) ;
2023-08-16 21:31:51 +02:00
}
2023-08-28 20:58:46 +02:00
2023-08-17 21:33:06 +02:00
// show coqui model selected list (SAFE)
2023-12-02 19:04:51 +01:00
if ( model _origin == 'coqui-api' ) {
$ ( '#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 ) {
2023-12-02 20:11:06 +01:00
let languageLabel = language ;
2023-08-17 21:33:06 +02:00
if ( language in languageLabels )
2023-12-02 20:11:06 +01:00
languageLabel = languageLabels [ language ] ;
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_language' ) . append ( new Option ( languageLabel , language ) ) ;
console . log ( DEBUG _PREFIX , 'added language' , languageLabel , '(' , language , ')' ) ;
2023-08-17 21:33:06 +02:00
}
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_div' ) . show ( ) ;
2023-08-17 21:33:06 +02:00
}
// show coqui model full list (UNSAFE)
2023-12-02 19:04:51 +01:00
if ( model _origin == 'coqui-api-full' ) {
$ ( '#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 coquiApiModelsFull ) {
2023-12-02 20:11:06 +01:00
let languageLabel = language ;
2023-08-17 21:33:06 +02:00
if ( language in languageLabels )
2023-12-02 20:11:06 +01:00
languageLabel = languageLabels [ language ] ;
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_language' ) . append ( new Option ( languageLabel , language ) ) ;
console . log ( DEBUG _PREFIX , 'added language' , languageLabel , '(' , language , ')' ) ;
2023-08-17 21:33:06 +02:00
}
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_div' ) . show ( ) ;
2023-08-12 06:05:39 +02:00
}
2023-08-17 11:05:17 +02:00
2023-08-12 06:05:39 +02:00
2023-08-16 19:19:13 +02:00
// show local model list
2023-12-02 19:04:51 +01:00
if ( model _origin == 'local' ) {
$ ( '#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 ( ) ;
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_settings' ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
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 ) ;
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
if ( model _language == 'none' ) {
$ ( '#coqui_api_model_name' ) . hide ( ) ;
2023-08-12 06:05:39 +02:00
return ;
}
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_name' ) . show ( ) ;
2023-08-12 06:05:39 +02:00
$ ( '#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-12-02 20:11:06 +01:00
let modelDict = coquiApiModels ;
2023-12-02 19:04:51 +01:00
if ( model _origin == 'coqui-api-full' )
2023-12-02 20:11:06 +01:00
modelDict = coquiApiModelsFull ;
2023-08-17 21:33:06 +02:00
for ( let model _dataset in modelDict [ model _language ] )
for ( let model _name in modelDict [ model _language ] [ model _dataset ] ) {
2023-12-02 20:11:06 +01:00
const model _id = model _dataset + '/' + model _name ;
const model _label = model _name + ' (' + model _dataset + ' dataset)' ;
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_name' ) . append ( new Option ( model _label , model _id ) ) ;
2023-08-17 11:05:17 +02:00
}
2023-08-12 06:05:39 +02:00
}
2023-08-13 02:18:46 +02:00
async onModelNameChange ( ) {
throwIfModuleMissing ( ) ;
resetModelSettings ( ) ;
2023-12-02 19:04:51 +01:00
$ ( '#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
2023-12-02 19:04:51 +01:00
if ( $ ( '#coqui_api_model_name' ) . val ( ) == 'none' ) {
$ ( '#coqui_api_model_install_button' ) . off ( 'click' ) ;
$ ( '#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 ( ) ;
2023-12-02 19:04:51 +01:00
const tokens = $ ( '#coqui_api_model_name' ) . val ( ) . split ( '/' ) ;
2023-08-14 04:03:28 +02:00
const model _dataset = tokens [ 0 ] ;
const model _name = tokens [ 1 ] ;
2023-08-13 02:18:46 +02:00
2023-12-02 20:11:06 +01:00
let modelDict = coquiApiModels ;
2023-12-02 19:04:51 +01:00
if ( model _origin == 'coqui-api-full' )
2023-12-02 20:11:06 +01:00
modelDict = coquiApiModelsFull ;
2023-08-17 21:33:06 +02:00
2023-12-02 20:11:06 +01:00
const model _settings = modelDict [ model _language ] [ model _dataset ] [ model _name ] ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
if ( 'languages' in model _settings ) {
$ ( '#coqui_api_model_settings' ) . show ( ) ;
$ ( '#coqui_api_model_settings_language' ) . show ( ) ;
2023-08-13 02:18:46 +02:00
$ ( '#coqui_api_model_settings_language' )
2023-08-17 11:05:17 +02:00
. find ( 'option' )
. remove ( )
. end ( )
. append ( '<option value="none">Select language</option>' )
. val ( 'none' ) ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
for ( let 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 {
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_settings_language' ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
}
2023-12-02 19:04:51 +01:00
if ( 'speakers' in model _settings ) {
$ ( '#coqui_api_model_settings' ) . show ( ) ;
$ ( '#coqui_api_model_settings_speaker' ) . show ( ) ;
2023-08-13 02:18:46 +02:00
$ ( '#coqui_api_model_settings_speaker' )
2023-08-17 11:05:17 +02:00
. find ( 'option' )
. remove ( )
. end ( )
. append ( '<option value="none">Select speaker</option>' )
. val ( 'none' ) ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
for ( let 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 {
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_settings_speaker' ) . hide ( ) ;
2023-08-13 02:18:46 +02:00
}
2023-12-02 19:04:51 +01: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-12-02 20:11:06 +01:00
const model _id = modelDict [ model _language ] [ model _dataset ] [ model _name ] [ 'id' ] ;
2023-12-02 19:04:51 +01:00
console . debug ( DEBUG _PREFIX , 'Check if model is already installed' , model _id ) ;
2023-08-14 04:03:28 +02:00
let result = await CoquiTtsProvider . checkmodel _state ( model _id ) ;
result = await result . json ( ) ;
2023-12-02 19:04:51 +01:00
const model _state = result [ 'model_state' ] ;
2023-08-12 06:05:39 +02:00
2023-12-02 20:11:06 +01:00
console . debug ( DEBUG _PREFIX , ' Model state:' , model _state ) ;
2023-08-12 06:05:39 +02:00
2023-12-02 19:04:51 +01: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 {
2023-12-02 20:11:06 +01:00
let action = 'download' ;
2023-12-02 19:04:51 +01:00
if ( model _state == 'corrupted' ) {
2023-12-02 20:11:06 +01:00
action = 'repare' ;
2023-08-14 04:03:28 +02:00
//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 });
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_install_status' ) . text ( 'Model found but incomplete try install again (maybe still downloading)' ) ; // (remove and download again)
2023-08-14 04:03:28 +02:00
}
else {
2023-12-02 19:04:51 +01:00
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-14 04:03:28 +02:00
}
2023-08-12 06:05:39 +02:00
2023-08-14 04:03:28 +02:00
const onModelNameChange _pointer = this . onModelNameChange ;
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_install_button' ) . off ( 'click' ) . on ( 'click' , async function ( ) {
2023-08-14 04:03:28 +02:00
try {
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_install_status' ) . text ( 'Downloading model...' ) ;
$ ( '#coqui_api_model_install_button' ) . hide ( ) ;
2023-08-14 04:03:28 +02:00
//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 ( ) ;
2023-12-02 19:04:51 +01:00
console . debug ( DEBUG _PREFIX , 'Response:' , apiResult ) ;
2023-08-14 04:03:28 +02:00
2023-12-02 19:04:51 +01:00
if ( apiResult [ 'status' ] == 'done' ) {
$ ( '#coqui_api_model_install_status' ) . text ( 'Model installed and ready to use!' ) ;
$ ( '#coqui_api_model_install_button' ) . hide ( ) ;
2023-08-14 04:03:28 +02:00
onModelNameChange _pointer ( ) ;
}
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
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 ( ) ;
2023-08-14 04:03:28 +02:00
}
} catch ( error ) {
2023-12-02 20:11:06 +01:00
console . error ( error ) ;
2023-12-02 19:04:51 +01:00
toastr . error ( error , DEBUG _PREFIX + ' error with model download' , { timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ) ;
2023-08-14 04:03:28 +02:00
onModelNameChange _pointer ( ) ;
}
2023-08-17 11:05:17 +02:00
// will refresh model status
2023-08-14 04:03:28 +02:00
} ) ;
2023-08-17 11:05:17 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#coqui_api_model_install_button' ) . show ( ) ;
2023-08-14 04:03:28 +02:00
return ;
}
2023-08-17 11:05:17 +02:00
2023-08-12 06:05:39 +02:00
}
2023-08-17 11:05:17 +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-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
2023-08-13 02:18:46 +02:00
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' ,
2023-12-02 21:06:57 +01:00
'Cache-Control' : 'no-cache' ,
2023-08-13 02:18:46 +02:00
} ,
body : JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'model_id' : model _id ,
2023-12-02 21:06:57 +01:00
} ) ,
2023-08-13 02:18:46 +02:00
} ) ;
if ( ! apiResult . ok ) {
2023-08-17 11:05:17 +02:00
toastr . error ( apiResult . statusText , DEBUG _PREFIX + ' Check model state request failed' ) ;
2023-08-13 02:18:46 +02:00
throw new Error ( ` HTTP ${ apiResult . status } : ${ await apiResult . text ( ) } ` ) ;
}
2023-12-02 20:11:06 +01:00
return apiResult ;
2023-08-13 02:18:46 +02:00
}
2023-08-14 04:03:28 +02:00
static async installModel ( model _id , action ) {
2023-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
2023-08-13 02:18:46 +02:00
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' ,
2023-12-02 21:06:57 +01:00
'Cache-Control' : 'no-cache' ,
2023-08-13 02:18:46 +02:00
} ,
body : JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'model_id' : model _id ,
2023-12-02 21:06:57 +01:00
'action' : action ,
} ) ,
2023-08-13 02:18:46 +02:00
} ) ;
if ( ! apiResult . ok ) {
2023-08-17 11:05:17 +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 ( ) } ` ) ;
}
2023-12-02 20:11:06 +01:00
return apiResult ;
2023-08-13 02:18:46 +02:00
}
2023-08-16 19:19:13 +02:00
/ *
Retrieve user custom models
* /
static async getLocalModelList ( ) {
2023-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
2023-08-16 19:19:13 +02:00
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' ,
2023-12-02 21:06:57 +01:00
'Cache-Control' : 'no-cache' ,
2023-08-16 19:19:13 +02:00
} ,
body : JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'model_id' : 'model_id' ,
2023-12-02 21:06:57 +01:00
'action' : 'action' ,
} ) ,
2023-12-02 20:11:06 +01:00
} ) ;
2023-08-16 19:19:13 +02:00
if ( ! apiResult . ok ) {
2023-08-17 11:05:17 +02:00
toastr . error ( apiResult . statusText , DEBUG _PREFIX + ' Get local model list request failed' ) ;
2023-08-16 19:19:13 +02:00
throw new Error ( ` HTTP ${ apiResult . status } : ${ await apiResult . text ( ) } ` ) ;
}
2023-12-02 20:11:06 +01:00
return apiResult ;
2023-08-16 19:19:13 +02:00
}
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-12-02 20:11:06 +01:00
throwIfModuleMissing ( ) ;
voiceId = this . settings . customVoices [ voiceId ] ;
2023-08-26 05:51:58 +02:00
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-12-02 20:11:06 +01:00
let language = 'none' ;
let speaker = 'none' ;
2023-12-02 19:04:51 +01:00
const tokens = voiceId . replaceAll ( ']' , '' ) . replaceAll ( '"' , '' ) . split ( '[' ) ;
2023-12-02 20:11:06 +01:00
const model _id = tokens [ 0 ] ;
2023-08-13 02:18:46 +02:00
2023-12-02 20:11:06 +01:00
console . debug ( DEBUG _PREFIX , 'Preparing TTS request for' , tokens ) ;
2023-08-13 02:18:46 +02:00
// First option
if ( tokens . length > 1 ) {
2023-12-02 20:11:06 +01:00
const option1 = tokens [ 1 ] ;
2023-08-13 02:18:46 +02:00
2023-12-02 19:04:51 +01:00
if ( model _id . includes ( 'multilingual' ) )
2023-12-02 20:11:06 +01:00
language = option1 ;
2023-08-13 02:18:46 +02:00
else
2023-12-02 20:11:06 +01:00
speaker = option1 ;
2023-08-13 02:18:46 +02:00
}
// 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' ,
2023-12-02 21:06:57 +01:00
'Cache-Control' : 'no-cache' ,
2023-08-12 06:05:39 +02:00
} ,
body : JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'text' : text ,
'model_id' : model _id ,
'language_id' : parseInt ( language ) ,
2023-12-02 21:06:57 +01:00
'speaker_id' : parseInt ( speaker ) ,
} ) ,
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 ( ) } ` ) ;
}
2023-12-02 20:11:06 +01:00
return apiResult ;
2023-08-12 06:05:39 +02:00
}
2023-08-14 04:03:28 +02:00
// Dirty hack to say not implemented
2023-08-26 05:51:58 +02:00
async fetchTtsVoiceObjects ( ) {
const voiceIds = Object
. keys ( this . settings . voiceMapDict )
. map ( voice => ( { name : voice , voice _id : voice , preview _url : false } ) ) ;
2023-12-02 20:11:06 +01:00
return voiceIds ;
2023-08-14 04:03:28 +02:00
}
// Do nothing
previewTtsVoice ( id ) {
2023-12-02 20:11:06 +01:00
return ;
2023-08-14 04:03:28 +02:00
}
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
2023-08-25 15:27:43 +02:00
async function initLocalModels ( ) {
2023-08-16 19:19:13 +02:00
if ( ! modules . includes ( 'coqui-tts' ) )
2023-12-02 20:11:06 +01:00
return ;
2023-08-16 19:19:13 +02:00
// Initialized local model once
2023-08-17 11:05:17 +02:00
if ( ! coquiLocalModelsReceived ) {
2023-08-16 19:19:13 +02:00
let result = await CoquiTtsProvider . getLocalModelList ( ) ;
result = await result . json ( ) ;
2023-12-02 19:04:51 +01:00
coquiLocalModels = result [ 'models_list' ] ;
2023-08-16 19:19:13 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#coqui_local_model_name' ) . show ( ) ;
2023-08-16 19:19:13 +02:00
$ ( '#coqui_local_model_name' )
. find ( 'option' )
. remove ( )
. end ( )
. append ( '<option value="none">Select model</option>' )
. val ( 'none' ) ;
2023-08-17 11:05:17 +02:00
for ( const model _dataset of coquiLocalModels )
2023-12-02 19:04:51 +01:00
$ ( '#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-28 20:58:46 +02:00
}