Spaces:
Running
Running
import { doExtrasFetch } from '../../extensions.js'; | |
import { debounce } from '../../utils.js'; | |
import { saveTtsProviderSettings } from './index.js'; | |
export { AllTalkTtsProvider }; | |
class AllTalkTtsProvider { | |
//########// | |
// Config // | |
//########// | |
settings = {}; | |
constructor() { | |
// Initialize with default settings if they are not already set | |
this.settings = { | |
provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851', | |
server_version: this.settings.server_version || 'v2', | |
language: this.settings.language || 'en', | |
voiceMap: this.settings.voiceMap || {}, | |
at_generation_method: this.settings.at_generation_method || 'standard_generation', | |
narrator_enabled: this.settings.narrator_enabled || 'false', | |
at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator', | |
narrator_voice_gen: this.settings.narrator_voice_gen || 'Please set a voice', | |
rvc_character_voice: this.settings.rvc_character_voice || 'Disabled', | |
rvc_character_pitch: this.settings.rvc_character_pitch || '0', | |
rvc_narrator_voice: this.settings.rvc_narrator_voice || 'Disabled', | |
rvc_narrator_pitch: this.settings.rvc_narrator_pitch || '0', | |
finetuned_model: this.settings.finetuned_model || 'false', | |
}; | |
// Separate property for dynamically updated settings from the server | |
this.dynamicSettings = { | |
modelsAvailable: [], | |
currentModel: '', | |
deepspeed_available: false, | |
deepspeed_enabled: false, | |
lowvram_capable: false, | |
lowvram_enabled: false, | |
}; | |
this.rvcVoices = []; // Initialize rvcVoices as an empty array | |
} | |
ready = false; | |
voices = []; | |
separator = '. '; | |
audioElement = document.createElement('audio'); | |
languageLabels = { | |
'Arabic': 'ar', | |
'Brazilian Portuguese': 'pt', | |
'Chinese': 'zh-cn', | |
'Czech': 'cs', | |
'Dutch': 'nl', | |
'English': 'en', | |
'French': 'fr', | |
'German': 'de', | |
'Italian': 'it', | |
'Polish': 'pl', | |
'Russian': 'ru', | |
'Spanish': 'es', | |
'Turkish': 'tr', | |
'Japanese': 'ja', | |
'Korean': 'ko', | |
'Hungarian': 'hu', | |
'Hindi': 'hi', | |
}; | |
get settingsHtml() { | |
// HTML template literals can trigger ESLint quotes warnings when quotes are used in HTML attributes. | |
// Disabling quotes rule for this one line as it's a false positive with HTML template literals. | |
// eslint-disable-next-line quotes | |
let html = `<div class="at-settings-separator">AllTalk V2 Settings</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='at_generation_method'>AllTalk TTS Generation Method</label> | |
<select id='at_generation_method'> | |
<option value='standard_generation'>Standard Audio Generation (AT Narrator - Optional)</option> | |
<option value='streaming_enabled'>Streaming Audio Generation (AT Narrator - Disabled)</option> | |
</select> | |
</div> | |
</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='at_narrator_enabled'>AT Narrator</label> | |
<select id='at_narrator_enabled'> | |
<option value='true'>Enabled</option> | |
<option value='silent'>Enabled (Silenced)</option> | |
<option value='false'>Disabled</option> | |
</select> | |
</div> | |
<div class='at-settings-option'> | |
<label for='at_narrator_text_not_inside'>Text Not Inside * or " is</label> | |
<select id='at_narrator_text_not_inside'> | |
<option value='character'>Character</option> | |
<option value='narrator'>Narrator</option> | |
<option value='silent'>Silent</option> | |
</select> | |
</div> | |
</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='narrator_voice'>Narrator Voice</label> | |
<select id='narrator_voice'>`; | |
if (this.voices) { | |
for (let voice of this.voices) { | |
html += `<option value='${voice.voice_id}'>${voice.name}</option>`; | |
} | |
} | |
html += `</select> | |
</div> | |
<div class='at-settings-option'> | |
<label for='language_options'>Language</label> | |
<select id='language_options'>`; | |
for (let language in this.languageLabels) { | |
html += `<option value='${this.languageLabels[language]}' ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`; | |
} | |
html += `</select> | |
</div> | |
</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='rvc_character_voice'>RVC Character</label> | |
<select id='rvc_character_voice'>`; | |
if (this.rvcVoices) { | |
for (let rvccharvoice of this.rvcVoices) { | |
html += `<option value='${rvccharvoice.voice_id}'>${rvccharvoice.name}</option>`; | |
} | |
} | |
html += `</select> | |
</div> | |
<div class='at-settings-option'> | |
<label for='rvc_narrator_voice'>RVC Narrator</label> | |
<select id='rvc_narrator_voice'>`; | |
if (this.rvcVoices) { | |
for (let rvcnarrvoice of this.rvcVoices) { | |
html += `<option value='${rvcnarrvoice.voice_id}'>${rvcnarrvoice.name}</option>`; | |
} | |
} | |
html += `</select> | |
</div> | |
</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='rvc_character_pitch'>RVC Character Pitch</label> | |
<select id='rvc_character_pitch'>`; | |
for (let i = -24; i <= 24; i++) { | |
const selected = i === 0 ? 'selected="selected"' : ''; | |
html += `<option value='${i}' ${selected}>${i}</option>`; | |
} | |
html += `</select> | |
</div> | |
<div class='at-settings-option'> | |
<label for='rvc_narrator_pitch'>RVC Narrator Pitch</label> | |
<select id='rvc_narrator_pitch'>`; | |
for (let i = -24; i <= 24; i++) { | |
const selected = i === 0 ? 'selected="selected"' : ''; | |
html += `<option value='${i}' ${selected}>${i}</option>`; | |
} | |
html += `</select> | |
</div> | |
</div>`; | |
html += `<div class='at-model-endpoint-row'> | |
<div class='at-model-option'> | |
<label for='switch_model'>Switch Model</label> | |
<select id='switch_model'> | |
<!-- Options will be dynamically populated --> | |
</select> | |
</div> | |
<div class='at-endpoint-option'> | |
<label for='at_server'>AllTalk Endpoint:</label> | |
<input id='at_server' type='text' class='text_pole' maxlength='80' value='${this.settings.provider_endpoint}'/> | |
</div> | |
</div>`; | |
html += `<div class='at-settings-row'> | |
<div class='at-settings-option'> | |
<label for='server_version'>AllTalk Server Version</label> | |
<select id='server_version'> | |
<option value='v1'>AllTalk V1</option> | |
<option value='v2'>AllTalk V2</option> | |
</select> | |
</div> | |
</div>`; | |
html += `<div class='at-model-endpoint-row'> | |
<div class='at-settings-option'> | |
<label for='low_vram'>Low VRAM</label> | |
<input id='low_vram' type='checkbox'/> | |
</div> | |
<div class='at-settings-option'> | |
<label for='deepspeed'>DeepSpeed</label> | |
<input id='deepspeed' type='checkbox'/> | |
</div> | |
<div class='at-settings-option status-option'> | |
<span>Status: <span id='status_info'>Ready</span></span> | |
</div> | |
<div class='at-settings-option empty-option'> | |
<!-- This div remains empty for spacing --> | |
</div> | |
</div>`; | |
html += `<div class='at-website-row'> | |
<div class='at-website-option'> | |
<span>AllTalk V2<a target='_blank' href='${this.settings.provider_endpoint}'>Config & Docs</a>.</span> | |
</div> | |
<div class='at-website-option'> | |
<span>AllTalk <a target='_blank' href='https://github.com/erew123/alltalk_tts/'>Website</a>.</span> | |
</div> | |
</div>`; | |
html += `<div class='at-website-row'> | |
<div class='at-website-option'> | |
<span>- If you <strong>change your TTS engine</strong> in AllTalk, you will need to <strong>Reload</strong> (button above) and re-select your voices.</span><br><br> | |
<span>- Assuming the server is <strong>Status: Ready</strong>, most problems will be resolved by hitting Reload and selecting voices that match the loaded TTS engine.</span><br><br> | |
<span>- <strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in the TGWUI interface, or you will hear 2x voices and file names being generated.</span> | |
</div> | |
</div>`; | |
return html; | |
} | |
//#################// | |
// Startup ST & AT // | |
//#################// | |
async loadSettings(settings) { | |
updateStatus('Offline'); | |
if (Object.keys(settings).length === 0) { | |
console.info('Using default AllTalk TTS Provider settings'); | |
} else { | |
// Populate settings with provided values, ignoring server-provided settings | |
for (const key in settings) { | |
if (key in this.settings) { | |
this.settings[key] = settings[key]; | |
} else { | |
console.debug(`Ignoring non-user-configurable setting: ${key}`); | |
} | |
} | |
} | |
// Update UI elements to reflect the loaded settings | |
$('#at_server').val(this.settings.provider_endpoint); | |
$('#language_options').val(this.settings.language); | |
$('#at_generation_method').val(this.settings.at_generation_method); | |
$('#at_narrator_enabled').val(this.settings.narrator_enabled); | |
$('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside); | |
$('#narrator_voice').val(this.settings.narrator_voice_gen); | |
$('#rvc_character_voice').val(this.settings.rvc_character_voice); | |
$('#rvc_narrator_voice').val(this.settings.rvc_narrator_voice); | |
$('#rvc_character_pitch').val(this.settings.rvc_character_pitch); | |
$('#rvc_narrator_pitch').val(this.settings.rvc_narrator_pitch); | |
$('#server_version').val(this.settings.server_version); | |
console.debug('AllTalkTTS: Settings loaded'); | |
try { | |
// Check if TTS provider is ready | |
this.setupEventListeners(); | |
this.updateLanguageDropdown(); | |
await this.checkReady(); | |
await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server | |
await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready | |
await this.fetchRvcVoiceObjects(); // Fetch RVC voices | |
this.updateNarratorVoicesDropdown(); | |
this.applySettingsToHTML(); | |
updateStatus('Ready'); | |
} catch (error) { | |
console.error('Error loading settings:', error); | |
updateStatus('Offline'); | |
} | |
} | |
applySettingsToHTML() { | |
const narratorVoiceSelect = document.getElementById('narrator_voice'); | |
const atNarratorSelect = document.getElementById('at_narrator_enabled'); | |
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside'); | |
const generationMethodSelect = document.getElementById('at_generation_method'); | |
this.settings.narrator_voice = this.settings.narrator_voice_gen; | |
// Apply settings to Narrator Voice dropdown | |
if (narratorVoiceSelect && this.settings.narrator_voice) { | |
narratorVoiceSelect.value = this.settings.narrator_voice; // Remove the parentheses | |
} | |
// Apply settings to AT Narrator Enabled dropdown | |
if (atNarratorSelect) { | |
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); | |
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); | |
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); | |
if (this.settings.narrator_enabled) { | |
ttsPassAsterisksCheckbox.checked = false; | |
$('#tts_pass_asterisks').click(); | |
$('#tts_pass_asterisks').trigger('change'); | |
} | |
if (!this.settings.narrator_enabled) { | |
ttsPassAsterisksCheckbox.checked = true; | |
$('#tts_pass_asterisks').click(); | |
$('#tts_pass_asterisks').trigger('change'); | |
} | |
if (this.settings.narrator_enabled) { | |
ttsNarrateQuotedCheckbox.checked = true; | |
ttsNarrateDialoguesCheckbox.checked = true; | |
$('#tts_narrate_quoted').click(); | |
$('#tts_narrate_quoted').trigger('change'); | |
$('#tts_narrate_dialogues').click(); | |
$('#tts_narrate_dialogues').trigger('change'); | |
} | |
atNarratorSelect.value = this.settings.narrator_enabled.toString(); | |
this.settings.narrator_enabled = this.settings.narrator_enabled.toString(); | |
} | |
const languageSelect = document.getElementById('language_options'); | |
if (languageSelect && this.settings.language) { | |
languageSelect.value = this.settings.language; | |
} | |
if (textNotInsideSelect && this.settings.text_not_inside) { | |
textNotInsideSelect.value = this.settings.text_not_inside; | |
this.settings.at_narrator_text_not_inside = this.settings.text_not_inside; | |
} | |
if (generationMethodSelect && this.settings.at_generation_method) { | |
generationMethodSelect.value = this.settings.at_generation_method; | |
} | |
const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled'; | |
if (isStreamingEnabled) { | |
if (atNarratorSelect) atNarratorSelect.disabled = true; | |
if (textNotInsideSelect) textNotInsideSelect.disabled = true; | |
if (narratorVoiceSelect) narratorVoiceSelect.disabled = true; | |
} else { | |
if (atNarratorSelect) atNarratorSelect.disabled = false; | |
if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled; | |
if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled; | |
} | |
} | |
//##############################// | |
// Check AT Server is Available // | |
//##############################// | |
async checkReady() { | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/ready`); | |
// Check if the HTTP request was successful | |
if (!response.ok) { | |
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`); | |
} | |
const statusText = await response.text(); | |
// Check if the response is 'Ready' | |
if (statusText === 'Ready') { | |
this.ready = true; // Set the ready flag to true | |
console.log('TTS service is ready.'); | |
} else { | |
this.ready = false; | |
console.log('TTS service is not ready.'); | |
} | |
} catch (error) { | |
console.error('Error checking TTS service readiness:', error); | |
this.ready = false; // Ensure ready flag is set to false in case of error | |
} | |
} | |
//######################// | |
// Get Available Voices // | |
//######################// | |
async fetchTtsVoiceObjects() { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/voices`); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
throw new Error(`HTTP ${response.status}: ${errorText}`); | |
} | |
const data = await response.json(); | |
const voices = data.voices.map(filename => { | |
return { | |
name: filename, | |
voice_id: filename, | |
preview_url: null, // Preview URL will be dynamically generated | |
lang: 'en', // Default language | |
}; | |
}); | |
this.voices = voices; // Assign to the class property | |
return voices; // Also return this list | |
} | |
async fetchRvcVoiceObjects() { | |
if (this.settings.server_version == 'v1') { | |
console.log('Skipping RVC voices fetch for V1 server'); | |
return []; | |
} | |
console.log('Fetching RVC Voices'); | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/rvcvoices`); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
console.error('Error text:', errorText); | |
throw new Error(`HTTP ${response.status}: ${errorText}`); | |
} | |
const data = await response.json(); | |
if (!data || !data.rvcvoices) { | |
console.error('Invalid data format:', data); | |
throw new Error('Invalid data format received from /api/rvcvoices'); | |
} | |
const voices = data.rvcvoices.map(filename => { | |
return { | |
name: filename, | |
voice_id: filename, | |
}; | |
}); | |
console.log('RVC voices:', voices); | |
this.rvcVoices = voices; // Assign to the class property | |
this.updateRvcVoiceDropdowns(); // Update UI after fetching voices | |
return voices; // Also return this list | |
} catch (error) { | |
console.error('Error fetching RVC voices:', error); | |
this.rvcVoices = [{ name: 'Disabled', voice_id: 'Disabled' }]; // Set default on error | |
throw error; | |
} finally { | |
// Ensure dropdowns are updated even if there was an error | |
this.updateRvcVoiceDropdowns(); | |
} | |
} | |
//##########################################// | |
// Get Current AT Server Config & Update ST // | |
//##########################################// | |
async updateSettingsFromServer() { | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`); | |
if (!response.ok) { | |
throw new Error(`Failed to fetch current settings: ${response.statusText}`); | |
} | |
const currentSettings = await response.json(); | |
currentSettings.models_available.sort((a, b) => a.name.localeCompare(b.name)); | |
this.settings.enginesAvailable = currentSettings.engines_available; | |
this.settings.currentEngineLoaded = currentSettings.current_engine_loaded; | |
this.settings.modelsAvailable = currentSettings.models_available; | |
this.settings.currentModel = currentSettings.current_model_loaded; | |
this.settings.deepspeed_capable = currentSettings.deepspeed_capable; | |
this.settings.deepspeed_available = currentSettings.deepspeed_available; | |
this.settings.deepspeed_enabled = currentSettings.deepspeed_enabled; | |
this.settings.lowvram_capable = currentSettings.lowvram_capable; | |
this.settings.lowvram_enabled = currentSettings.lowvram_enabled; | |
await this.fetchRvcVoiceObjects(); // Fetch RVC voices | |
this.updateModelDropdown(); | |
this.updateCheckboxes(); | |
this.updateRvcVoiceDropdowns(); // Update the RVC voice dropdowns | |
} catch (error) { | |
console.error(`Error updating settings from server: ${error}`); | |
} | |
} | |
updateRvcVoiceDropdowns() { | |
// Handle all RVC-related elements | |
const rvcElements = document.querySelectorAll('.rvc-setting'); | |
const isV2 = this.settings.server_version === 'v2'; | |
rvcElements.forEach(element => { | |
element.style.display = isV2 ? 'block' : 'none'; | |
}); | |
// Update and disable/enable character voice dropdown | |
const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice'); | |
if (rvcCharacterVoiceSelect) { | |
rvcCharacterVoiceSelect.disabled = !isV2; | |
if (this.rvcVoices) { | |
rvcCharacterVoiceSelect.innerHTML = ''; | |
for (let voice of this.rvcVoices) { | |
const option = document.createElement('option'); | |
option.value = voice.voice_id; | |
option.textContent = voice.name; | |
if (voice.voice_id === this.settings.rvc_character_voice) { | |
option.selected = true; | |
} | |
rvcCharacterVoiceSelect.appendChild(option); | |
} | |
} | |
} | |
// Update and disable/enable narrator voice dropdown | |
const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice'); | |
if (rvcNarratorVoiceSelect) { | |
rvcNarratorVoiceSelect.disabled = !isV2; | |
if (this.rvcVoices) { | |
rvcNarratorVoiceSelect.innerHTML = ''; | |
for (let voice of this.rvcVoices) { | |
const option = document.createElement('option'); | |
option.value = voice.voice_id; | |
option.textContent = voice.name; | |
if (voice.voice_id === this.settings.rvc_narrator_voice) { | |
option.selected = true; | |
} | |
rvcNarratorVoiceSelect.appendChild(option); | |
} | |
} | |
} | |
// Update pitch inputs | |
const characterPitch = document.getElementById('rvc_character_pitch'); | |
if (characterPitch) { | |
characterPitch.disabled = !isV2; | |
} | |
const narratorPitch = document.getElementById('rvc_narrator_pitch'); | |
if (narratorPitch) { | |
narratorPitch.disabled = !isV2; | |
} | |
} | |
//###################################################// | |
// Get Current AT Server Config & Update ST (Models) // | |
//###################################################// | |
updateModelDropdown() { | |
const modelSelect = document.getElementById('switch_model'); | |
if (modelSelect) { | |
modelSelect.innerHTML = ''; // Clear existing options | |
this.settings.modelsAvailable.forEach(model => { | |
const option = document.createElement('option'); | |
option.value = model.name; | |
option.textContent = model.name; // Use model name directly | |
option.selected = model.name === this.settings.currentModel; | |
modelSelect.appendChild(option); | |
}); | |
} | |
} | |
//#######################################################// | |
// Get Current AT Server Config & Update ST (DS and LVR) // | |
//#######################################################// | |
updateCheckboxes() { | |
const deepspeedCheckbox = document.getElementById('deepspeed'); | |
const lowVramCheckbox = document.getElementById('low_vram'); | |
// Handle DeepSpeed checkbox | |
if (deepspeedCheckbox) { | |
if (this.settings.deepspeed_capable) { | |
// If TTS engine is capable of using DeepSpeed | |
deepspeedCheckbox.disabled = !this.settings.deepspeed_available; | |
this.settings.deepspeed_enabled = this.settings.deepspeed_available && this.settings.deepspeed_enabled; | |
} else { | |
// If TTS engine is NOT capable of using DeepSpeed | |
deepspeedCheckbox.disabled = true; | |
this.settings.deepspeed_enabled = false; | |
} | |
deepspeedCheckbox.checked = this.settings.deepspeed_enabled; | |
} | |
// Handle Low VRAM checkbox | |
if (lowVramCheckbox) { | |
if (this.settings.lowvram_capable) { | |
// If TTS engine is capable of low VRAM | |
lowVramCheckbox.disabled = false; | |
} else { | |
// If TTS engine is NOT capable of low VRAM | |
lowVramCheckbox.disabled = true; | |
this.settings.lowvram_enabled = false; | |
} | |
lowVramCheckbox.checked = this.settings.lowvram_enabled; | |
} | |
} | |
//###############################################################// | |
// Get Current AT Server Config & Update ST (AT Narrator Voices) // | |
//###############################################################// | |
updateNarratorVoicesDropdown() { | |
const narratorVoiceSelect = document.getElementById('narrator_voice'); | |
if (narratorVoiceSelect && this.voices) { | |
// Clear existing options | |
narratorVoiceSelect.innerHTML = ''; | |
// Add new options | |
for (let voice of this.voices) { | |
const option = document.createElement('option'); | |
option.value = voice.voice_id; | |
option.textContent = voice.name; | |
narratorVoiceSelect.appendChild(option); | |
} | |
} | |
} | |
//######################################################// | |
// Get Current AT Server Config & Update ST (Languages) // | |
//######################################################// | |
updateLanguageDropdown() { | |
const languageSelect = document.getElementById('language_options'); | |
if (languageSelect) { | |
// Ensure default language is set | |
this.settings.language = this.settings.language || 'en'; | |
languageSelect.innerHTML = ''; | |
for (let language in this.languageLabels) { | |
const option = document.createElement('option'); | |
option.value = this.languageLabels[language]; | |
option.textContent = language; | |
if (this.languageLabels[language] === this.settings.language) { | |
option.selected = true; | |
} | |
languageSelect.appendChild(option); | |
} | |
} | |
} | |
//########################################// | |
// Start AT TTS extenstion page listeners // | |
//########################################// | |
setupEventListeners() { | |
// Define the event handler function | |
const onModelSelectChange = async (event) => { | |
console.log('Model select change event triggered'); | |
const selectedModel = event.target.value; | |
console.log(`Selected model: ${selectedModel}`); | |
updateStatus('Processing'); | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, { | |
method: 'POST', | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP Error: ${response.status}`); | |
} | |
const data = await response.json(); | |
console.log('POST response data:', data); | |
updateStatus('Ready'); | |
} catch (error) { | |
console.error('POST request error:', error); | |
updateStatus('Error'); | |
} | |
}; | |
// Switch Model Listener with debounce | |
const modelSelect = document.getElementById('switch_model'); | |
if (modelSelect) { | |
const debouncedModelSelectChange = debounce(onModelSelectChange, 1400); | |
modelSelect.addEventListener('change', debouncedModelSelectChange); | |
} | |
// AllTalk Server version change listener | |
const serverVersionSelect = document.getElementById('server_version'); | |
if (serverVersionSelect) { | |
serverVersionSelect.addEventListener('change', async (event) => { | |
this.settings.server_version = event.target.value; | |
this.onSettingsChange(); | |
if (event.target.value === 'v2') { | |
await this.fetchRvcVoiceObjects(); | |
} | |
this.updateRvcVoiceDropdowns(); | |
}); | |
} | |
// RVC Voice and Pitch listeners | |
const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice'); | |
if (rvcCharacterVoiceSelect) { | |
rvcCharacterVoiceSelect.addEventListener('change', (event) => { | |
this.settings.rvccharacter_voice_gen = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice'); | |
if (rvcNarratorVoiceSelect) { | |
rvcNarratorVoiceSelect.addEventListener('change', (event) => { | |
this.settings.rvcnarrator_voice_gen = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
const rvcCharacterPitchSelect = document.getElementById('rvc_character_pitch'); | |
if (rvcCharacterPitchSelect) { | |
rvcCharacterPitchSelect.addEventListener('change', (event) => { | |
this.settings.rvc_character_pitch = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
const rvcNarratorPitchSelect = document.getElementById('rvc_narrator_pitch'); | |
if (rvcNarratorPitchSelect) { | |
rvcNarratorPitchSelect.addEventListener('change', (event) => { | |
this.settings.rvc_narrator_pitch = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
// DeepSpeed Listener | |
const deepspeedCheckbox = document.getElementById('deepspeed'); | |
if (deepspeedCheckbox) { | |
const handleDeepSpeedChange = async (event) => { | |
const deepSpeedValue = event.target.checked ? 'True' : 'False'; | |
updateStatus('Processing'); | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, { | |
method: 'POST', | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP Error: ${response.status}`); | |
} | |
const data = await response.json(); | |
console.log('POST response data:', data); | |
updateStatus('Ready'); | |
} catch (error) { | |
console.error('POST request error:', error); | |
updateStatus('Error'); | |
} | |
}; | |
const debouncedHandleDeepSpeedChange = debounce(handleDeepSpeedChange, 300); | |
deepspeedCheckbox.addEventListener('change', debouncedHandleDeepSpeedChange); | |
} | |
// Low VRAM Listener | |
const lowVramCheckbox = document.getElementById('low_vram'); | |
if (lowVramCheckbox) { | |
const handleLowVramChange = async (event) => { | |
const lowVramValue = event.target.checked ? 'True' : 'False'; | |
updateStatus('Processing'); | |
try { | |
const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, { | |
method: 'POST', | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP Error: ${response.status}`); | |
} | |
const data = await response.json(); | |
console.log('POST response data:', data); | |
updateStatus('Ready'); | |
} catch (error) { | |
console.error('POST request error:', error); | |
updateStatus('Error'); | |
} | |
}; | |
const debouncedHandleLowVramChange = debounce(handleLowVramChange, 300); | |
lowVramCheckbox.addEventListener('change', debouncedHandleLowVramChange); | |
} | |
// Other listeners without debounce since they don't need it | |
const narratorVoiceSelect = document.getElementById('narrator_voice'); | |
if (narratorVoiceSelect) { | |
narratorVoiceSelect.addEventListener('change', (event) => { | |
this.settings.narrator_voice_gen = `${event.target.value}`; | |
this.onSettingsChange(); | |
}); | |
} | |
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside'); | |
if (textNotInsideSelect) { | |
textNotInsideSelect.addEventListener('change', (event) => { | |
this.settings.text_not_inside = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
// AT Narrator Dropdown Listener | |
const atNarratorSelect = document.getElementById('at_narrator_enabled'); | |
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); | |
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); | |
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); | |
if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) { | |
atNarratorSelect.addEventListener('change', (event) => { | |
const narratorOption = event.target.value; | |
this.settings.narrator_enabled = narratorOption; | |
const isNarratorDisabled = narratorOption === 'false'; | |
textNotInsideSelect.disabled = isNarratorDisabled; | |
narratorVoiceSelect.disabled = isNarratorDisabled; | |
if (narratorOption === 'true') { | |
ttsPassAsterisksCheckbox.checked = false; | |
$('#tts_pass_asterisks').click(); | |
$('#tts_pass_asterisks').trigger('change'); | |
ttsNarrateQuotedCheckbox.checked = true; | |
ttsNarrateDialoguesCheckbox.checked = true; | |
$('#tts_narrate_quoted').click(); | |
$('#tts_narrate_quoted').trigger('change'); | |
$('#tts_narrate_dialogues').click(); | |
$('#tts_narrate_dialogues').trigger('change'); | |
} else if (narratorOption === 'silent') { | |
ttsPassAsterisksCheckbox.checked = false; | |
$('#tts_pass_asterisks').click(); | |
$('#tts_pass_asterisks').trigger('change'); | |
} else { | |
ttsPassAsterisksCheckbox.checked = true; | |
$('#tts_pass_asterisks').click(); | |
$('#tts_pass_asterisks').trigger('change'); | |
} | |
this.onSettingsChange(); | |
}); | |
} | |
// Event Listener for AT Generation Method Dropdown | |
const atGenerationMethodSelect = document.getElementById('at_generation_method'); | |
if (atGenerationMethodSelect) { | |
atGenerationMethodSelect.addEventListener('change', (event) => { | |
const selectedMethod = event.target.value; | |
if (selectedMethod === 'streaming_enabled') { | |
// Disable and unselect AT Narrator | |
atNarratorSelect.disabled = true; | |
atNarratorSelect.value = 'false'; | |
textNotInsideSelect.disabled = true; | |
narratorVoiceSelect.disabled = true; | |
} else if (selectedMethod === 'standard_generation') { | |
// Enable AT Narrator | |
atNarratorSelect.disabled = false; | |
} | |
this.settings.at_generation_method = selectedMethod; | |
this.onSettingsChange(); | |
}); | |
} | |
// Language Dropdown Listener | |
const languageSelect = document.getElementById('language_options'); | |
if (languageSelect) { | |
languageSelect.addEventListener('change', (event) => { | |
this.settings.language = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
// AllTalk Endpoint Input Listener | |
const atServerInput = document.getElementById('at_server'); | |
if (atServerInput) { | |
atServerInput.addEventListener('input', (event) => { | |
this.settings.provider_endpoint = event.target.value; | |
this.onSettingsChange(); | |
}); | |
} | |
} | |
//#############################// | |
// Store ST interface settings // | |
//#############################// | |
onSettingsChange() { | |
// Update settings based on the UI elements | |
//this.settings.provider_endpoint = $('#at_server').val(); | |
this.settings.language = $('#language_options').val(); | |
//this.settings.voiceMap = $('#voicemap').val(); | |
this.settings.at_generation_method = $('#at_generation_method').val(); | |
this.settings.narrator_enabled = $('#at_narrator_enabled').val(); | |
this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val(); | |
this.settings.rvc_character_voice = $('#rvc_character_voice').val(); | |
this.settings.rvc_narrator_voice = $('#rvc_narrator_voice').val(); | |
this.settings.rvc_character_pitch = $('#rvc_character_pitch').val(); | |
this.settings.rvc_narrator_pitch = $('#rvc_narrator_pitch').val(); | |
this.settings.narrator_voice_gen = $('#narrator_voice').val(); | |
// Save the updated settings | |
saveTtsProviderSettings(); | |
} | |
//#########################// | |
// ST Handle Reload button // | |
//#########################// | |
async onRefreshClick() { | |
try { | |
updateStatus('Processing'); // Set status to Processing while refreshing | |
await this.checkReady(); // Check if the TTS provider is ready | |
await this.loadSettings(this.settings); // Reload the settings | |
await this.checkReady(); // Check if the TTS provider is ready | |
updateStatus(this.ready ? 'Ready' : 'Offline'); // Update the status based on readiness | |
} catch (error) { | |
console.error('Error during refresh:', error); | |
updateStatus('Error'); // Set status to Error in case of failure | |
} | |
} | |
//##################// | |
// Preview AT Voice // | |
//##################// | |
async previewTtsVoice(voiceName) { | |
try { | |
// Prepare data for POST request | |
const postData = new URLSearchParams(); | |
postData.append('voice', `${voiceName}`); | |
// Add RVC parameters for V2 if applicable | |
if (this.settings.server_version === 'v2' && this.settings.rvc_character_voice !== 'Disabled') { | |
postData.append('rvccharacter_voice_gen', this.settings.rvc_character_voice); | |
postData.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0'); | |
} | |
// Making the POST request | |
const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
}, | |
body: postData, | |
}); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
console.error('previewTtsVoice Error Response Text:', errorText); | |
throw new Error(`HTTP ${response.status}: ${errorText}`); | |
} | |
const data = await response.json(); | |
if (data.output_file_url) { | |
// Handle V1/V2 URL differences | |
const fullUrl = this.settings.server_version === 'v1' | |
? data.output_file_url | |
: `${this.settings.provider_endpoint}${data.output_file_url}`; | |
const audioElement = new Audio(fullUrl); | |
audioElement.play().catch(e => console.error('Error playing audio:', e)); | |
} else { | |
console.warn('previewTtsVoice No output file URL received in the response'); | |
throw new Error('No output file URL received in the response'); | |
} | |
} catch (error) { | |
console.error('previewTtsVoice Exception caught during preview generation:', error); | |
throw error; | |
} | |
} | |
//#####################// | |
// Populate ST voices // | |
//#####################// | |
async getVoice(voiceName, generatePreview = false) { | |
// Ensure this.voices is populated | |
if (this.voices.length === 0) { | |
// Fetch voice objects logic | |
} | |
// Find the object where the name matches voiceName | |
const match = this.voices.find(voice => voice.name === voiceName); | |
if (!match) { | |
// Error handling | |
} | |
// Generate preview URL only if requested | |
if (!match.preview_url && generatePreview) { | |
// Generate preview logic | |
} | |
return match; // Return the found voice object | |
} | |
//##########################################// | |
// Generate TTS Streaming or call Standard // | |
//##########################################// | |
async generateTts(inputText, voiceId) { | |
try { | |
if (this.settings.at_generation_method === 'streaming_enabled') { | |
// Construct the streaming URL | |
const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`; | |
console.log('Streaming URL:', streamingUrl); | |
// Return the streaming URL directly | |
return streamingUrl; | |
} else { | |
// For standard method | |
const outputUrl = await this.fetchTtsGeneration(inputText, voiceId); | |
const audioResponse = await fetch(outputUrl); | |
if (!audioResponse.ok) { | |
throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`); | |
} | |
return audioResponse; // Return the fetch response directly | |
} | |
} catch (error) { | |
console.error('Error in generateTts:', error); | |
throw error; | |
} | |
} | |
//####################// | |
// Generate Standard // | |
//####################// | |
async fetchTtsGeneration(inputText, voiceId) { | |
const requestBody = new URLSearchParams({ | |
'text_input': inputText, | |
'text_filtering': 'standard', | |
'character_voice_gen': voiceId, | |
'narrator_enabled': this.settings.narrator_enabled, | |
'narrator_voice_gen': this.settings.narrator_voice_gen, | |
'text_not_inside': this.settings.at_narrator_text_not_inside, | |
'language': this.settings.language, | |
'output_file_name': 'st_output', | |
'output_file_timestamp': 'true', | |
'autoplay': 'false', | |
'autoplay_volume': '0.8', | |
}); | |
// Add RVC parameters only for V2 | |
if (this.settings.server_version === 'v2') { | |
if (this.settings.rvc_character_voice !== 'Disabled') { | |
requestBody.append('rvccharacter_voice_gen', this.settings.rvc_character_voice); | |
requestBody.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0'); | |
} | |
if (this.settings.rvc_narrator_voice !== 'Disabled') { | |
requestBody.append('rvcnarrator_voice_gen', this.settings.rvc_narrator_voice); | |
requestBody.append('rvcnarrator_pitch', this.settings.rvc_narrator_pitch || '0'); | |
} | |
} | |
try { | |
const response = await doExtrasFetch( | |
`${this.settings.provider_endpoint}/api/tts-generate`, | |
{ | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
'Cache-Control': 'no-cache', | |
}, | |
body: requestBody, | |
}, | |
); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
console.error('fetchTtsGeneration Error Response Text:', errorText); | |
throw new Error(`HTTP ${response.status}: ${errorText}`); | |
} | |
const data = await response.json(); | |
// V1 returns a complete URL, V2 returns a relative path | |
if (this.settings.server_version === 'v1') { | |
// V1: Use the complete URL directly from the response | |
return data.output_file_url; | |
} else { | |
// V2: Combine the endpoint with the relative path | |
return `${this.settings.provider_endpoint}${data.output_file_url}`; | |
} | |
} catch (error) { | |
console.error('[fetchTtsGeneration] Exception caught:', error); | |
throw error; | |
} | |
} | |
} | |
//#########################// | |
// Update Status Messages // | |
//#########################// | |
function updateStatus(message) { | |
const statusElement = document.getElementById('status_info'); | |
if (statusElement) { | |
statusElement.textContent = message; | |
switch (message) { | |
case 'Offline': | |
statusElement.style.color = 'red'; | |
break; | |
case 'Ready': | |
statusElement.style.color = 'lightgreen'; | |
break; | |
case 'Processing': | |
statusElement.style.color = 'blue'; | |
break; | |
case 'Error': | |
statusElement.style.color = 'red'; | |
break; | |
} | |
} | |
} | |