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 = `
AllTalk V2 Settings
`; html += `
`; html += `
`; html += `
`; html += `
`; html += `
`; html += `
`; html += `
`; html += `
Status: Ready
`; html += `
AllTalk V2Config & Docs.
AllTalk Website.
`; html += `
- If you change your TTS engine in AllTalk, you will need to Reload (button above) and re-select your voices.

- Assuming the server is Status: Ready, most problems will be resolved by hitting Reload and selecting voices that match the loaded TTS engine.

- Text-generation-webui users - Uncheck Enable TTS in the TGWUI interface, or you will hear 2x voices and file names being generated.
`; 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); 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 == 'v2') { 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; if (event.target.value === 'v2') { await this.fetchRvcVoiceObjects(); } this.updateRvcVoiceDropdowns(); this.onSettingsChange(); }); } // 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(); // Handle V1/V2 URL differences const outputUrl = this.settings.server_version === 'v1' ? data.output_file_url // V1 returns full URL : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path return outputUrl; } 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; } } }