Updated AllTalk Extension to support AllTalk V2 1. Core Feature Addition: - Added AllTalk V1/V2 server version selection - Added RVC (Realistic Voice Conversion) support for V2 - RVC features are automatically disabled when V1 is selected 2. Code Improvements: - Replaced custom debounce implementation with shared utils.js debounce utility - Fixed linting issues: - Converted HTML attribute quotes in template literals to single quotes - Added trailing commas where required - Fixed console.error message formatting 3. New Settings/Properties Added: ```javascript server_version: 'v2' (default) rvc_character_voice: 'Disabled' rvc_character_pitch: '0' rvc_narrator_voice: 'Disabled' rvc_narrator_pitch: '0' ``` 4. Bug Fixes/Improvements: - Better error handling for RVC voice fetching - Improved URL handling for V1/V2 differences in API responses - Enhanced settings initialization and validation 5. Structural Changes: - Added RVC-specific UI elements and controls - Added version-specific logic for API endpoints - Improved settings synchronization between UI and backend **NOTE** On line 70 there is an eslint bypass: ```javascript // 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>`; ``` The reason is: 1. ESLint's quotes rule wants all strings to use single quotes 2. However, this is a template literal containing HTML, where double quotes are standard for attributes 3. I tried various solutions: - Using single quotes: `<div class='at-settings-separator'>` - Using double quotes: `<div class="at-settings-separator">` - Even tried escaping quotes But ESLint just kept flagging it as an error

e857db40fb49bcd9b2f6d784276988c32d893ec2

erew123 <35898566+erew123@users.noreply.github.com>

Signed
1 files changed, +171 -212Ignore whitespace
public/scripts/extensions/tts/alltalk.js+171 -212
@@ -1,4 +1,5 @@
1import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js'; 1import { doExtrasFetch } from '../../extensions.js';
2import { debounce } from '../../utils.js';
2import { saveTtsProviderSettings } from './index.js'; 3import { saveTtsProviderSettings } from './index.js';
3 4
4export { AllTalkTtsProvider }; 5export { AllTalkTtsProvider };
@@ -13,7 +14,7 @@ class AllTalkTtsProvider {
13 // Initialize with default settings if they are not already set 14 // Initialize with default settings if they are not already set
14 this.settings = { 15 this.settings = {
15 provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851', 16 provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
16 server_version: this.settings.server_version || 'v2', 17 server_version: this.settings.server_version || 'v2',
17 language: this.settings.language || 'en', 18 language: this.settings.language || 'en',
18 voiceMap: this.settings.voiceMap || {}, 19 voiceMap: this.settings.voiceMap || {},
19 at_generation_method: this.settings.at_generation_method || 'standard_generation', 20 at_generation_method: this.settings.at_generation_method || 'standard_generation',
@@ -24,7 +25,7 @@ class AllTalkTtsProvider {
24 rvc_character_pitch: this.settings.rvc_character_pitch || '0', 25 rvc_character_pitch: this.settings.rvc_character_pitch || '0',
25 rvc_narrator_voice: this.settings.rvc_narrator_voice || 'Disabled', 26 rvc_narrator_voice: this.settings.rvc_narrator_voice || 'Disabled',
26 rvc_narrator_pitch: this.settings.rvc_narrator_pitch || '0', 27 rvc_narrator_pitch: this.settings.rvc_narrator_pitch || '0',
27 finetuned_model: this.settings.finetuned_model || 'false' 28 finetuned_model: this.settings.finetuned_model || 'false',
28 }; 29 };
29 // Separate property for dynamically updated settings from the server 30 // Separate property for dynamically updated settings from the server
30 this.dynamicSettings = { 31 this.dynamicSettings = {
@@ -63,161 +64,159 @@ class AllTalkTtsProvider {
63 }; 64 };
64 65
65 get settingsHtml() { 66 get settingsHtml() {
67 // HTML template literals can trigger ESLint quotes warnings when quotes are used in HTML attributes.
68 // Disabling quotes rule for this one line as it's a false positive with HTML template literals.
69 // eslint-disable-next-line quotes
66 let html = `<div class="at-settings-separator">AllTalk V2 Settings</div>`; 70 let html = `<div class="at-settings-separator">AllTalk V2 Settings</div>`;
67 71
68 html += `<div class="at-settings-row"> 72 html += `<div class='at-settings-row'>
69 73 <div class='at-settings-option'>
70 <div class="at-settings-option"> 74 <label for='at_generation_method'>AllTalk TTS Generation Method</label>
71 <label for="at_generation_method">AllTalk TTS Generation Method</label> 75 <select id='at_generation_method'>
72 <select id="at_generation_method"> 76 <option value='standard_generation'>Standard Audio Generation (AT Narrator - Optional)</option>
73 <option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option> 77 <option value='streaming_enabled'>Streaming Audio Generation (AT Narrator - Disabled)</option>
74 <option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>
75 </select> 78 </select>
76 </div> 79 </div>
77 </div>`; 80 </div>`;
78 81
79 html += `<div class="at-settings-row"> 82 html += `<div class='at-settings-row'>
80 83 <div class='at-settings-option'>
81 <div class="at-settings-option"> 84 <label for='at_narrator_enabled'>AT Narrator</label>
82 <label for="at_narrator_enabled">AT Narrator</label> 85 <select id='at_narrator_enabled'>
83 <select id="at_narrator_enabled"> 86 <option value='true'>Enabled</option>
84 <option value="true">Enabled</option> 87 <option value='silent'>Enabled (Silenced)</option>
85 <option value="silent">Enabled (Silenced)</option> 88 <option value='false'>Disabled</option>
86 <option value="false">Disabled</option>
87 </select> 89 </select>
88 </div> 90 </div>
89 91
90 <div class="at-settings-option"> 92 <div class='at-settings-option'>
91 <label for="at_narrator_text_not_inside">Text Not Inside * or " is</label> 93 <label for='at_narrator_text_not_inside'>Text Not Inside * or " is</label>
92 <select id="at_narrator_text_not_inside"> 94 <select id='at_narrator_text_not_inside'>
93 <option value="character">Character</option> 95 <option value='character'>Character</option>
94 <option value="narrator">Narrator</option> 96 <option value='narrator'>Narrator</option>
95 <option value="silent">Silent</option> 97 <option value='silent'>Silent</option>
96 </select> 98 </select>
97 </div> 99 </div>
98
99 </div>`; 100 </div>`;
100 101
101 html += `<div class="at-settings-row"> 102 html += `<div class='at-settings-row'>
102 <div class="at-settings-option"> 103 <div class='at-settings-option'>
103 <label for="narrator_voice">Narrator Voice</label> 104 <label for='narrator_voice'>Narrator Voice</label>
104 <select id="narrator_voice">`; 105 <select id='narrator_voice'>`;
105 if (this.voices) { 106 if (this.voices) {
106 for (let voice of this.voices) { 107 for (let voice of this.voices) {
107 html += `<option value="${voice.voice_id}">${voice.name}</option>`; 108 html += `<option value='${voice.voice_id}'>${voice.name}</option>`;
108 } 109 }
109 } 110 }
110 html += `</select> 111 html += `</select>
111 </div> 112 </div>
112 <div class="at-settings-option"> 113 <div class='at-settings-option'>
113 <label for="language_options">Language</label> 114 <label for='language_options'>Language</label>
114 <select id="language_options">`; 115 <select id='language_options'>`;
115 for (let language in this.languageLabels) { 116 for (let language in this.languageLabels) {
116 html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`; 117 html += `<option value='${this.languageLabels[language]}' ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
117 } 118 }
118 html += `</select> 119 html += `</select>
119 </div> 120 </div>
120 </div>`; 121 </div>`;
121 122
122 html += `<div class="at-settings-row"> 123 html += `<div class='at-settings-row'>
123 <div class="at-settings-option"> 124 <div class='at-settings-option'>
124 <label for="rvc_character_voice">RVC Character</label> 125 <label for='rvc_character_voice'>RVC Character</label>
125 <select id="rvc_character_voice">`; 126 <select id='rvc_character_voice'>`;
126 if (this.rvcVoices) { 127 if (this.rvcVoices) {
127 for (let rvccharvoice of this.rvcVoices) { 128 for (let rvccharvoice of this.rvcVoices) {
128 html += `<option value="${rvccharvoice.voice_id}">${rvccharvoice.name}</option>`; 129 html += `<option value='${rvccharvoice.voice_id}'>${rvccharvoice.name}</option>`;
129 } 130 }
130 } 131 }
131 html += `</select> 132 html += `</select>
132 </div> 133 </div>
133 <div class="at-settings-option"> 134 <div class='at-settings-option'>
134 <label for="rvc_narrator_voice">RVC Narrator</label> 135 <label for='rvc_narrator_voice'>RVC Narrator</label>
135 <select id="rvc_narrator_voice">`; 136 <select id='rvc_narrator_voice'>`;
136 if (this.rvcVoices) { 137 if (this.rvcVoices) {
137 for (let rvcnarrvoice of this.rvcVoices) { 138 for (let rvcnarrvoice of this.rvcVoices) {
138 html += `<option value="${rvcnarrvoice.voice_id}">${rvcnarrvoice.name}</option>`; 139 html += `<option value='${rvcnarrvoice.voice_id}'>${rvcnarrvoice.name}</option>`;
139 } 140 }
140 } 141 }
141 html += `</select> 142 html += `</select>
142 </div> 143 </div>
143</div>`; 144</div>`;
144 145
145 // Add the RVC pitch control row 146 html += `<div class='at-settings-row'>
146 html += `<div class="at-settings-row"> 147 <div class='at-settings-option'>
147 <div class="at-settings-option"> 148 <label for='rvc_character_pitch'>RVC Character Pitch</label>
148 <label for="rvc_character_pitch">RVC Character Pitch</label> 149 <select id='rvc_character_pitch'>`;
149 <select id="rvc_character_pitch">`;
150 for (let i = -24; i <= 24; i++) { 150 for (let i = -24; i <= 24; i++) {
151 const selected = i === 0 ? 'selected="selected"' : ''; 151 const selected = i === 0 ? 'selected="selected"' : '';
152 html += `<option value="${i}" ${selected}>${i}</option>`; 152 html += `<option value='${i}' ${selected}>${i}</option>`;
153 } 153 }
154 html += `</select> 154 html += `</select>
155 </div> 155 </div>
156 <div class="at-settings-option"> 156 <div class='at-settings-option'>
157 <label for="rvc_narrator_pitch">RVC Narrator Pitch</label> 157 <label for='rvc_narrator_pitch'>RVC Narrator Pitch</label>
158 <select id="rvc_narrator_pitch">`; 158 <select id='rvc_narrator_pitch'>`;
159 for (let i = -24; i <= 24; i++) { 159 for (let i = -24; i <= 24; i++) {
160 const selected = i === 0 ? 'selected="selected"' : ''; 160 const selected = i === 0 ? 'selected="selected"' : '';
161 html += `<option value="${i}" ${selected}>${i}</option>`; 161 html += `<option value='${i}' ${selected}>${i}</option>`;
162 } 162 }
163 html += `</select> 163 html += `</select>
164 </div> 164 </div>
165 </div>`; 165 </div>`;
166 166
167 html += `<div class="at-model-endpoint-row"> 167 html += `<div class='at-model-endpoint-row'>
168 <div class="at-model-option"> 168 <div class='at-model-option'>
169 <label for="switch_model">Switch Model</label> 169 <label for='switch_model'>Switch Model</label>
170 <select id="switch_model"> 170 <select id='switch_model'>
171 <!-- Options will be dynamically populated --> 171 <!-- Options will be dynamically populated -->
172 </select> 172 </select>
173 </div> 173 </div>
174 174
175 <div class="at-endpoint-option"> 175 <div class='at-endpoint-option'>
176 <label for="at_server">AllTalk Endpoint:</label> 176 <label for='at_server'>AllTalk Endpoint:</label>
177 <input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/> 177 <input id='at_server' type='text' class='text_pole' maxlength='80' value='${this.settings.provider_endpoint}'/>
178 </div> 178 </div>
179 </div>`; 179 </div>`;
180 180
181 html += `<div class="at-settings-row"> 181 html += `<div class='at-settings-row'>
182 <div class="at-settings-option"> 182 <div class='at-settings-option'>
183 <label for="server_version">AllTalk Server Version</label> 183 <label for='server_version'>AllTalk Server Version</label>
184 <select id="server_version"> 184 <select id='server_version'>
185 <option value="v1">AllTalk V1</option> 185 <option value='v1'>AllTalk V1</option>
186 <option value="v2">AllTalk V2</option> 186 <option value='v2'>AllTalk V2</option>
187 </select> 187 </select>
188 </div> 188 </div>
189 </div>`; 189 </div>`;
190 190
191 html += `<div class="at-model-endpoint-row"> 191 html += `<div class='at-model-endpoint-row'>
192 <div class="at-settings-option"> 192 <div class='at-settings-option'>
193 <label for="low_vram">Low VRAM</label> 193 <label for='low_vram'>Low VRAM</label>
194 <input id="low_vram" type="checkbox"/> 194 <input id='low_vram' type='checkbox'/>
195 </div> 195 </div>
196 <div class="at-settings-option"> 196 <div class='at-settings-option'>
197 <label for="deepspeed">DeepSpeed</label> 197 <label for='deepspeed'>DeepSpeed</label>
198 <input id="deepspeed" type="checkbox"/> 198 <input id='deepspeed' type='checkbox'/>
199 </div> 199 </div>
200 <div class="at-settings-option status-option"> 200 <div class='at-settings-option status-option'>
201 <span>Status: <span id="status_info">Ready</span></span> 201 <span>Status: <span id='status_info'>Ready</span></span>
202 </div> 202 </div>
203 <div class="at-settings-option empty-option"> 203 <div class='at-settings-option empty-option'>
204 <!-- This div remains empty for spacing --> 204 <!-- This div remains empty for spacing -->
205 </div> 205 </div>
206</div>`; 206</div>`;
207 207
208 208 html += `<div class='at-website-row'>
209 html += `<div class="at-website-row"> 209 <div class='at-website-option'>
210 <div class="at-website-option"> 210 <span>AllTalk V2<a target='_blank' href='${this.settings.provider_endpoint}'>Config & Docs</a>.</span>
211 <span>AllTalk V2<a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
212 </div> 211 </div>
213 212
214 <div class="at-website-option"> 213 <div class='at-website-option'>
215 <span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span> 214 <span>AllTalk <a target='_blank' href='https://github.com/erew123/alltalk_tts/'>Website</a>.</span>
216 </div> 215 </div>
217</div>`; 216</div>`;
218 217
219 html += `<div class="at-website-row"> 218 html += `<div class='at-website-row'>
220<div class="at-website-option"> 219<div class='at-website-option'>
221<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> 220<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>
222<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> 221<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>
223<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> 222<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>
@@ -258,7 +257,7 @@ class AllTalkTtsProvider {
258 $('#rvc_character_voice').val(this.settings.rvc_character_voice); 257 $('#rvc_character_voice').val(this.settings.rvc_character_voice);
259 $('#rvc_narrator_voice').val(this.settings.rvc_narrator_voice); 258 $('#rvc_narrator_voice').val(this.settings.rvc_narrator_voice);
260 $('#rvc_character_pitch').val(this.settings.rvc_character_pitch); 259 $('#rvc_character_pitch').val(this.settings.rvc_character_pitch);
261 $('#rvc_narrator_pitch').val(this.settings.rvc_narrator_pitch); 260 $('#rvc_narrator_pitch').val(this.settings.rvc_narrator_pitch);
262 261
263 console.debug('AllTalkTTS: Settings loaded'); 262 console.debug('AllTalkTTS: Settings loaded');
264 try { 263 try {
@@ -273,7 +272,7 @@ class AllTalkTtsProvider {
273 this.applySettingsToHTML(); 272 this.applySettingsToHTML();
274 updateStatus('Ready'); 273 updateStatus('Ready');
275 } catch (error) { 274 } catch (error) {
276 console.error("Error loading settings:", error); 275 console.error('Error loading settings:', error);
277 updateStatus('Offline'); 276 updateStatus('Offline');
278 } 277 }
279 } 278 }
@@ -381,7 +380,7 @@ class AllTalkTtsProvider {
381 name: filename, 380 name: filename,
382 voice_id: filename, 381 voice_id: filename,
383 preview_url: null, // Preview URL will be dynamically generated 382 preview_url: null, // Preview URL will be dynamically generated
384 lang: 'en' // Default language 383 lang: 'en', // Default language
385 }; 384 };
386 }); 385 });
387 this.voices = voices; // Assign to the class property 386 this.voices = voices; // Assign to the class property
@@ -393,7 +392,7 @@ class AllTalkTtsProvider {
393 console.log('Skipping RVC voices fetch for V1 server'); 392 console.log('Skipping RVC voices fetch for V1 server');
394 return []; 393 return [];
395 } 394 }
396 395
397 console.log('Fetching RVC Voices'); 396 console.log('Fetching RVC Voices');
398 try { 397 try {
399 const response = await fetch(`${this.settings.provider_endpoint}/api/rvcvoices`); 398 const response = await fetch(`${this.settings.provider_endpoint}/api/rvcvoices`);
@@ -402,20 +401,20 @@ class AllTalkTtsProvider {
402 console.error('Error text:', errorText); 401 console.error('Error text:', errorText);
403 throw new Error(`HTTP ${response.status}: ${errorText}`); 402 throw new Error(`HTTP ${response.status}: ${errorText}`);
404 } 403 }
405 404
406 const data = await response.json(); 405 const data = await response.json();
407 if (!data || !data.rvcvoices) { 406 if (!data || !data.rvcvoices) {
408 console.error('Invalid data format:', data); 407 console.error('Invalid data format:', data);
409 throw new Error('Invalid data format received from /api/rvcvoices'); 408 throw new Error('Invalid data format received from /api/rvcvoices');
410 } 409 }
411 410
412 const voices = data.rvcvoices.map(filename => { 411 const voices = data.rvcvoices.map(filename => {
413 return { 412 return {
414 name: filename, 413 name: filename,
415 voice_id: filename, 414 voice_id: filename,
416 }; 415 };
417 }); 416 });
418 417
419 console.log('RVC voices:', voices); 418 console.log('RVC voices:', voices);
420 this.rvcVoices = voices; // Assign to the class property 419 this.rvcVoices = voices; // Assign to the class property
421 this.updateRvcVoiceDropdowns(); // Update UI after fetching voices 420 this.updateRvcVoiceDropdowns(); // Update UI after fetching voices
@@ -467,11 +466,11 @@ class AllTalkTtsProvider {
467 // Handle all RVC-related elements 466 // Handle all RVC-related elements
468 const rvcElements = document.querySelectorAll('.rvc-setting'); 467 const rvcElements = document.querySelectorAll('.rvc-setting');
469 const isV2 = this.settings.server_version === 'v2'; 468 const isV2 = this.settings.server_version === 'v2';
470 469
471 rvcElements.forEach(element => { 470 rvcElements.forEach(element => {
472 element.style.display = isV2 ? 'block' : 'none'; 471 element.style.display = isV2 ? 'block' : 'none';
473 }); 472 });
474 473
475 // Update and disable/enable character voice dropdown 474 // Update and disable/enable character voice dropdown
476 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice'); 475 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice');
477 if (rvcCharacterVoiceSelect) { 476 if (rvcCharacterVoiceSelect) {
@@ -489,7 +488,7 @@ class AllTalkTtsProvider {
489 } 488 }
490 } 489 }
491 } 490 }
492 491
493 // Update and disable/enable narrator voice dropdown 492 // Update and disable/enable narrator voice dropdown
494 const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice'); 493 const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice');
495 if (rvcNarratorVoiceSelect) { 494 if (rvcNarratorVoiceSelect) {
@@ -507,13 +506,13 @@ class AllTalkTtsProvider {
507 } 506 }
508 } 507 }
509 } 508 }
510 509
511 // Update pitch inputs 510 // Update pitch inputs
512 const characterPitch = document.getElementById('rvc_character_pitch'); 511 const characterPitch = document.getElementById('rvc_character_pitch');
513 if (characterPitch) { 512 if (characterPitch) {
514 characterPitch.disabled = !isV2; 513 characterPitch.disabled = !isV2;
515 } 514 }
516 515
517 const narratorPitch = document.getElementById('rvc_narrator_pitch'); 516 const narratorPitch = document.getElementById('rvc_narrator_pitch');
518 if (narratorPitch) { 517 if (narratorPitch) {
519 narratorPitch.disabled = !isV2; 518 narratorPitch.disabled = !isV2;
@@ -602,7 +601,7 @@ class AllTalkTtsProvider {
602 const languageSelect = document.getElementById('language_options'); 601 const languageSelect = document.getElementById('language_options');
603 if (languageSelect) { 602 if (languageSelect) {
604 // Ensure default language is set 603 // Ensure default language is set
605 this.settings.language = this.settings.language; 604 this.settings.language = this.settings.language || 'en';
606 605
607 languageSelect.innerHTML = ''; 606 languageSelect.innerHTML = '';
608 for (let language in this.languageLabels) { 607 for (let language in this.languageLabels) {
@@ -622,56 +621,54 @@ class AllTalkTtsProvider {
622 //########################################// 621 //########################################//
623 622
624 setupEventListeners() { 623 setupEventListeners() {
625
626 let debounceTimeout;
627 const debounceDelay = 1400; // Milliseconds
628
629 // Define the event handler function 624 // Define the event handler function
630 const onModelSelectChange = async (event) => { 625 const onModelSelectChange = async (event) => {
631 console.log("Model select change event triggered"); // Debugging statement 626 console.log('Model select change event triggered');
632 const selectedModel = event.target.value; 627 const selectedModel = event.target.value;
633 console.log(`Selected model: ${selectedModel}`); // Debugging statement 628 console.log(`Selected model: ${selectedModel}`);
634 // Set status to Processing
635 updateStatus('Processing'); 629 updateStatus('Processing');
636 try { 630 try {
637 const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, { 631 const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
638 method: 'POST' 632 method: 'POST',
639 }); 633 });
640 if (!response.ok) { 634 if (!response.ok) {
641 throw new Error(`HTTP Error: ${response.status}`); 635 throw new Error(`HTTP Error: ${response.status}`);
642 } 636 }
643 const data = await response.json(); 637 const data = await response.json();
644 console.log("POST response data:", data); // Debugging statement 638 console.log('POST response data:', data);
645 // Set status to Ready if successful
646 updateStatus('Ready'); 639 updateStatus('Ready');
647 } catch (error) { 640 } catch (error) {
648 console.error("POST request error:", error); // Debugging statement 641 console.error('POST request error:', error);
649 // Set status to Error in case of failure
650 updateStatus('Error'); 642 updateStatus('Error');
651 } 643 }
652
653 // Handle response or error
654 }; 644 };
655 645
646 // Switch Model Listener with debounce
647 const modelSelect = document.getElementById('switch_model');
648 if (modelSelect) {
649 const debouncedModelSelectChange = debounce(onModelSelectChange, 1400);
650 modelSelect.addEventListener('change', debouncedModelSelectChange);
651 }
652
656 // AllTalk Server version change listener 653 // AllTalk Server version change listener
657 const serverVersionSelect = document.getElementById('server_version'); 654 const serverVersionSelect = document.getElementById('server_version');
658 if (serverVersionSelect) { 655 if (serverVersionSelect) {
659 serverVersionSelect.addEventListener('change', async (event) => { 656 serverVersionSelect.addEventListener('change', async (event) => {
660 this.settings.server_version = event.target.value; 657 this.settings.server_version = event.target.value;
661 // Clear and refetch voices if needed
662 if (event.target.value === 'v2') { 658 if (event.target.value === 'v2') {
663 await this.fetchRvcVoiceObjects(); 659 await this.fetchRvcVoiceObjects();
664 } 660 }
665 this.updateRvcVoiceDropdowns(); 661 this.updateRvcVoiceDropdowns();
666 this.onSettingsChange(); 662 this.onSettingsChange();
667 }); 663 });
668 } 664 }
669 665
666 // RVC Voice and Pitch listeners
670 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice'); 667 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice');
671 if (rvcCharacterVoiceSelect) { 668 if (rvcCharacterVoiceSelect) {
672 rvcCharacterVoiceSelect.addEventListener('change', (event) => { 669 rvcCharacterVoiceSelect.addEventListener('change', (event) => {
673 this.settings.rvccharacter_voice_gen = event.target.value; 670 this.settings.rvccharacter_voice_gen = event.target.value;
674 this.onSettingsChange(); // Save the settings after change 671 this.onSettingsChange();
675 }); 672 });
676 } 673 }
677 674
@@ -679,7 +676,7 @@ class AllTalkTtsProvider {
679 if (rvcNarratorVoiceSelect) { 676 if (rvcNarratorVoiceSelect) {
680 rvcNarratorVoiceSelect.addEventListener('change', (event) => { 677 rvcNarratorVoiceSelect.addEventListener('change', (event) => {
681 this.settings.rvcnarrator_voice_gen = event.target.value; 678 this.settings.rvcnarrator_voice_gen = event.target.value;
682 this.onSettingsChange(); // Save the settings after change 679 this.onSettingsChange();
683 }); 680 });
684 } 681 }
685 682
@@ -687,7 +684,7 @@ class AllTalkTtsProvider {
687 if (rvcCharacterPitchSelect) { 684 if (rvcCharacterPitchSelect) {
688 rvcCharacterPitchSelect.addEventListener('change', (event) => { 685 rvcCharacterPitchSelect.addEventListener('change', (event) => {
689 this.settings.rvc_character_pitch = event.target.value; 686 this.settings.rvc_character_pitch = event.target.value;
690 this.onSettingsChange(); // Save the settings after change 687 this.onSettingsChange();
691 }); 688 });
692 } 689 }
693 690
@@ -695,59 +692,34 @@ class AllTalkTtsProvider {
695 if (rvcNarratorPitchSelect) { 692 if (rvcNarratorPitchSelect) {
696 rvcNarratorPitchSelect.addEventListener('change', (event) => { 693 rvcNarratorPitchSelect.addEventListener('change', (event) => {
697 this.settings.rvc_narrator_pitch = event.target.value; 694 this.settings.rvc_narrator_pitch = event.target.value;
698 this.onSettingsChange(); // Save the settings after change 695 this.onSettingsChange();
699 }); 696 });
700 }
701
702 const debouncedModelSelectChange = (event) => {
703 clearTimeout(debounceTimeout);
704 debounceTimeout = setTimeout(() => {
705 onModelSelectChange(event);
706 }, debounceDelay);
707 };
708
709 // Switch Model Listener
710 const modelSelect = document.getElementById('switch_model');
711 if (modelSelect) {
712 // Remove the event listener if it was previously added
713 modelSelect.removeEventListener('change', debouncedModelSelectChange);
714 // Add the debounced event listener
715 modelSelect.addEventListener('change', debouncedModelSelectChange);
716 } 697 }
717 698
718 // DeepSpeed Listener 699 // DeepSpeed Listener
719 const deepspeedCheckbox = document.getElementById('deepspeed'); 700 const deepspeedCheckbox = document.getElementById('deepspeed');
720 if (deepspeedCheckbox) { 701 if (deepspeedCheckbox) {
721 deepspeedCheckbox.addEventListener('change', async (event) => { 702 const handleDeepSpeedChange = async (event) => {
722 const deepSpeedValue = event.target.checked ? 'True' : 'False'; 703 const deepSpeedValue = event.target.checked ? 'True' : 'False';
723 // Set status to Processing
724 updateStatus('Processing'); 704 updateStatus('Processing');
725 try { 705 try {
726 const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, { 706 const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
727 method: 'POST' 707 method: 'POST',
728 }); 708 });
729 if (!response.ok) { 709 if (!response.ok) {
730 throw new Error(`HTTP Error: ${response.status}`); 710 throw new Error(`HTTP Error: ${response.status}`);
731 } 711 }
732 const data = await response.json(); 712 const data = await response.json();
733 console.log("POST response data:", data); // Debugging statement 713 console.log('POST response data:', data);
734 // Set status to Ready if successful
735 updateStatus('Ready'); 714 updateStatus('Ready');
736 } catch (error) { 715 } catch (error) {
737 console.error("POST request error:", error); // Debugging statement 716 console.error('POST request error:', error);
738 // Set status to Error in case of failure
739 updateStatus('Error'); 717 updateStatus('Error');
740 } 718 }
741 });
742 }
743
744 function lowvramdebounce(func, delay) {
745 let debounceTimer;
746 return function (...args) {
747 const context = this;
748 clearTimeout(debounceTimer);
749 debounceTimer = setTimeout(() => func.apply(context, args), delay);
750 }; 719 };
720
721 const debouncedHandleDeepSpeedChange = debounce(handleDeepSpeedChange, 300);
722 deepspeedCheckbox.addEventListener('change', debouncedHandleDeepSpeedChange);
751 } 723 }
752 724
753 // Low VRAM Listener 725 // Low VRAM Listener
@@ -755,38 +727,33 @@ class AllTalkTtsProvider {
755 if (lowVramCheckbox) { 727 if (lowVramCheckbox) {
756 const handleLowVramChange = async (event) => { 728 const handleLowVramChange = async (event) => {
757 const lowVramValue = event.target.checked ? 'True' : 'False'; 729 const lowVramValue = event.target.checked ? 'True' : 'False';
758 // Set status to Processing
759 updateStatus('Processing'); 730 updateStatus('Processing');
760 try { 731 try {
761 const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, { 732 const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {
762 method: 'POST' 733 method: 'POST',
763 }); 734 });
764 if (!response.ok) { 735 if (!response.ok) {
765 throw new Error(`HTTP Error: ${response.status}`); 736 throw new Error(`HTTP Error: ${response.status}`);
766 } 737 }
767 const data = await response.json(); 738 const data = await response.json();
768 console.log("POST response data:", data); // Debugging statement 739 console.log('POST response data:', data);
769 // Set status to Ready if successful
770 updateStatus('Ready'); 740 updateStatus('Ready');
771 } catch (error) { 741 } catch (error) {
772 console.error("POST request error:", error); // Debugging statement 742 console.error('POST request error:', error);
773 // Set status to Error in case of failure
774 updateStatus('Error'); 743 updateStatus('Error');
775 } 744 }
776 }; 745 };
777 746
778 const debouncedHandleLowVramChange = lowvramdebounce(handleLowVramChange, 300); // Adjust delay as needed 747 const debouncedHandleLowVramChange = debounce(handleLowVramChange, 300);
779
780 lowVramCheckbox.addEventListener('change', debouncedHandleLowVramChange); 748 lowVramCheckbox.addEventListener('change', debouncedHandleLowVramChange);
781 } 749 }
782 750
783 751 // Other listeners without debounce since they don't need it
784 // Narrator Voice Dropdown Listener
785 const narratorVoiceSelect = document.getElementById('narrator_voice'); 752 const narratorVoiceSelect = document.getElementById('narrator_voice');
786 if (narratorVoiceSelect) { 753 if (narratorVoiceSelect) {
787 narratorVoiceSelect.addEventListener('change', (event) => { 754 narratorVoiceSelect.addEventListener('change', (event) => {
788 this.settings.narrator_voice_gen = `${event.target.value}`; 755 this.settings.narrator_voice_gen = `${event.target.value}`;
789 this.onSettingsChange(); // Save the settings after change 756 this.onSettingsChange();
790 }); 757 });
791 } 758 }
792 759
@@ -794,7 +761,7 @@ class AllTalkTtsProvider {
794 if (textNotInsideSelect) { 761 if (textNotInsideSelect) {
795 textNotInsideSelect.addEventListener('change', (event) => { 762 textNotInsideSelect.addEventListener('change', (event) => {
796 this.settings.text_not_inside = event.target.value; 763 this.settings.text_not_inside = event.target.value;
797 this.onSettingsChange(); // Save the settings after change 764 this.onSettingsChange();
798 }); 765 });
799 } 766 }
800 767
@@ -809,15 +776,10 @@ class AllTalkTtsProvider {
809 const narratorOption = event.target.value; 776 const narratorOption = event.target.value;
810 this.settings.narrator_enabled = narratorOption; 777 this.settings.narrator_enabled = narratorOption;
811 778
812 // Check if narrator is disabled
813 const isNarratorDisabled = narratorOption === 'false'; 779 const isNarratorDisabled = narratorOption === 'false';
814 textNotInsideSelect.disabled = isNarratorDisabled; 780 textNotInsideSelect.disabled = isNarratorDisabled;
815 narratorVoiceSelect.disabled = isNarratorDisabled; 781 narratorVoiceSelect.disabled = isNarratorDisabled;
816 782
817 console.log(`Narrator option: ${narratorOption}`);
818 console.log(`textNotInsideSelect disabled: ${textNotInsideSelect.disabled}`);
819 console.log(`narratorVoiceSelect disabled: ${narratorVoiceSelect.disabled}`);
820
821 if (narratorOption === 'true') { 783 if (narratorOption === 'true') {
822 ttsPassAsterisksCheckbox.checked = false; 784 ttsPassAsterisksCheckbox.checked = false;
823 $('#tts_pass_asterisks').click(); 785 $('#tts_pass_asterisks').click();
@@ -842,47 +804,44 @@ class AllTalkTtsProvider {
842 }); 804 });
843 } 805 }
844 806
845
846 // Event Listener for AT Generation Method Dropdown 807 // Event Listener for AT Generation Method Dropdown
847 const atGenerationMethodSelect = document.getElementById('at_generation_method'); 808 const atGenerationMethodSelect = document.getElementById('at_generation_method');
848 const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
849 if (atGenerationMethodSelect) { 809 if (atGenerationMethodSelect) {
850 atGenerationMethodSelect.addEventListener('change', (event) => { 810 atGenerationMethodSelect.addEventListener('change', (event) => {
851 const selectedMethod = event.target.value; 811 const selectedMethod = event.target.value;
852 812
853 if (selectedMethod === 'streaming_enabled') { 813 if (selectedMethod === 'streaming_enabled') {
854 // Disable and unselect AT Narrator 814 // Disable and unselect AT Narrator
855 atNarratorEnabledSelect.disabled = true; 815 atNarratorSelect.disabled = true;
856 atNarratorEnabledSelect.value = 'false'; 816 atNarratorSelect.value = 'false';
857 textNotInsideSelect.disabled = true; 817 textNotInsideSelect.disabled = true;
858 narratorVoiceSelect.disabled = true; 818 narratorVoiceSelect.disabled = true;
859 } else if (selectedMethod === 'standard_generation') { 819 } else if (selectedMethod === 'standard_generation') {
860 // Enable AT Narrator 820 // Enable AT Narrator
861 atNarratorEnabledSelect.disabled = false; 821 atNarratorSelect.disabled = false;
862 } 822 }
863 this.settings.at_generation_method = selectedMethod; // Update the setting here 823 this.settings.at_generation_method = selectedMethod;
864 this.onSettingsChange(); // Save the settings after change 824 this.onSettingsChange();
865 }); 825 });
866 } 826 }
867 827
868 // Listener for Language Dropdown 828 // Language Dropdown Listener
869 const languageSelect = document.getElementById('language_options'); 829 const languageSelect = document.getElementById('language_options');
870 if (languageSelect) { 830 if (languageSelect) {
871 languageSelect.addEventListener('change', (event) => { 831 languageSelect.addEventListener('change', (event) => {
872 this.settings.language = event.target.value; 832 this.settings.language = event.target.value;
873 this.onSettingsChange(); // Save the settings after change 833 this.onSettingsChange();
874 }); 834 });
875 } 835 }
876 836
877 // Listener for AllTalk Endpoint Input 837 // AllTalk Endpoint Input Listener
878 const atServerInput = document.getElementById('at_server'); 838 const atServerInput = document.getElementById('at_server');
879 if (atServerInput) { 839 if (atServerInput) {
880 atServerInput.addEventListener('input', (event) => { 840 atServerInput.addEventListener('input', (event) => {
881 this.settings.provider_endpoint = event.target.value; 841 this.settings.provider_endpoint = event.target.value;
882 this.onSettingsChange(); // Save the settings after change 842 this.onSettingsChange();
883 }); 843 });
884 } 844 }
885
886 } 845 }
887 846
888 //#############################// 847 //#############################//
@@ -900,7 +859,7 @@ class AllTalkTtsProvider {
900 this.settings.rvc_character_voice = $('#rvc_character_voice').val(); 859 this.settings.rvc_character_voice = $('#rvc_character_voice').val();
901 this.settings.rvc_narrator_voice = $('#rvc_narrator_voice').val(); 860 this.settings.rvc_narrator_voice = $('#rvc_narrator_voice').val();
902 this.settings.rvc_character_pitch = $('#rvc_character_pitch').val(); 861 this.settings.rvc_character_pitch = $('#rvc_character_pitch').val();
903 this.settings.rvc_narrator_pitch = $('#rvc_narrator_pitch').val(); 862 this.settings.rvc_narrator_pitch = $('#rvc_narrator_pitch').val();
904 this.settings.narrator_voice_gen = $('#narrator_voice').val(); 863 this.settings.narrator_voice_gen = $('#narrator_voice').val();
905 // Save the updated settings 864 // Save the updated settings
906 saveTtsProviderSettings(); 865 saveTtsProviderSettings();
@@ -931,44 +890,44 @@ class AllTalkTtsProvider {
931 try { 890 try {
932 // Prepare data for POST request 891 // Prepare data for POST request
933 const postData = new URLSearchParams(); 892 const postData = new URLSearchParams();
934 postData.append("voice", `${voiceName}`); 893 postData.append('voice', `${voiceName}`);
935 894
936 // Add RVC parameters for V2 if applicable 895 // Add RVC parameters for V2 if applicable
937 if (this.settings.server_version === 'v2' && this.settings.rvc_character_voice !== 'Disabled') { 896 if (this.settings.server_version === 'v2' && this.settings.rvc_character_voice !== 'Disabled') {
938 postData.append("rvccharacter_voice_gen", this.settings.rvc_character_voice); 897 postData.append('rvccharacter_voice_gen', this.settings.rvc_character_voice);
939 postData.append("rvccharacter_pitch", this.settings.rvc_character_pitch || "0"); 898 postData.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0');
940 } 899 }
941 900
942 // Making the POST request 901 // Making the POST request
943 const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, { 902 const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
944 method: "POST", 903 method: 'POST',
945 headers: { 904 headers: {
946 'Content-Type': 'application/x-www-form-urlencoded' 905 'Content-Type': 'application/x-www-form-urlencoded',
947 }, 906 },
948 body: postData, 907 body: postData,
949 }); 908 });
950 909
951 if (!response.ok) { 910 if (!response.ok) {
952 const errorText = await response.text(); 911 const errorText = await response.text();
953 console.error(`[previewTtsVoice] Error Response Text:`, errorText); 912 console.error('previewTtsVoice Error Response Text:', errorText);
954 throw new Error(`HTTP ${response.status}: ${errorText}`); 913 throw new Error(`HTTP ${response.status}: ${errorText}`);
955 } 914 }
956 915
957 const data = await response.json(); 916 const data = await response.json();
958 if (data.output_file_url) { 917 if (data.output_file_url) {
959 // Handle V1/V2 URL differences 918 // Handle V1/V2 URL differences
960 const fullUrl = this.settings.server_version === 'v1' 919 const fullUrl = this.settings.server_version === 'v1'
961 ? data.output_file_url 920 ? data.output_file_url
962 : `${this.settings.provider_endpoint}${data.output_file_url}`; 921 : `${this.settings.provider_endpoint}${data.output_file_url}`;
963 922
964 const audioElement = new Audio(fullUrl); 923 const audioElement = new Audio(fullUrl);
965 audioElement.play().catch(e => console.error("Error playing audio:", e)); 924 audioElement.play().catch(e => console.error('Error playing audio:', e));
966 } else { 925 } else {
967 console.warn("[previewTtsVoice] No output file URL received in the response"); 926 console.warn('previewTtsVoice No output file URL received in the response');
968 throw new Error("No output file URL received in the response"); 927 throw new Error('No output file URL received in the response');
969 } 928 }
970 } catch (error) { 929 } catch (error) {
971 console.error("[previewTtsVoice] Exception caught during preview generation:", error); 930 console.error('previewTtsVoice Exception caught during preview generation:', error);
972 throw error; 931 throw error;
973 } 932 }
974 } 933 }
@@ -1003,7 +962,7 @@ class AllTalkTtsProvider {
1003 if (this.settings.at_generation_method === 'streaming_enabled') { 962 if (this.settings.at_generation_method === 'streaming_enabled') {
1004 // Construct the streaming URL 963 // Construct the streaming URL
1005 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`; 964 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`;
1006 console.log("Streaming URL:", streamingUrl); 965 console.log('Streaming URL:', streamingUrl);
1007 966
1008 // Return the streaming URL directly 967 // Return the streaming URL directly
1009 return streamingUrl; 968 return streamingUrl;
@@ -1017,7 +976,7 @@ class AllTalkTtsProvider {
1017 return audioResponse; // Return the fetch response directly 976 return audioResponse; // Return the fetch response directly
1018 } 977 }
1019 } catch (error) { 978 } catch (error) {
1020 console.error("Error in generateTts:", error); 979 console.error('Error in generateTts:', error);
1021 throw error; 980 throw error;
1022 } 981 }
1023 } 982 }
@@ -1030,30 +989,30 @@ class AllTalkTtsProvider {
1030 async fetchTtsGeneration(inputText, voiceId) { 989 async fetchTtsGeneration(inputText, voiceId) {
1031 const requestBody = new URLSearchParams({ 990 const requestBody = new URLSearchParams({
1032 'text_input': inputText, 991 'text_input': inputText,
1033 'text_filtering': "standard", 992 'text_filtering': 'standard',
1034 'character_voice_gen': voiceId, 993 'character_voice_gen': voiceId,
1035 'narrator_enabled': this.settings.narrator_enabled, 994 'narrator_enabled': this.settings.narrator_enabled,
1036 'narrator_voice_gen': this.settings.narrator_voice_gen, 995 'narrator_voice_gen': this.settings.narrator_voice_gen,
1037 'text_not_inside': this.settings.at_narrator_text_not_inside, 996 'text_not_inside': this.settings.at_narrator_text_not_inside,
1038 'language': this.settings.language, 997 'language': this.settings.language,
1039 'output_file_name': "st_output", 998 'output_file_name': 'st_output',
1040 'output_file_timestamp': "true", 999 'output_file_timestamp': 'true',
1041 'autoplay': "false", 1000 'autoplay': 'false',
1042 'autoplay_volume': "0.8" 1001 'autoplay_volume': '0.8',
1043 }); 1002 });
1044 1003
1045 // Add RVC parameters only for V2 1004 // Add RVC parameters only for V2
1046 if (this.settings.server_version === 'v2') { 1005 if (this.settings.server_version === 'v2') {
1047 if (this.settings.rvc_character_voice !== 'Disabled') { 1006 if (this.settings.rvc_character_voice !== 'Disabled') {
1048 requestBody.append('rvccharacter_voice_gen', this.settings.rvc_character_voice); 1007 requestBody.append('rvccharacter_voice_gen', this.settings.rvc_character_voice);
1049 requestBody.append('rvccharacter_pitch', this.settings.rvc_character_pitch || "0"); 1008 requestBody.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0');
1050 } 1009 }
1051 if (this.settings.rvc_narrator_voice !== 'Disabled') { 1010 if (this.settings.rvc_narrator_voice !== 'Disabled') {
1052 requestBody.append('rvcnarrator_voice_gen', this.settings.rvc_narrator_voice); 1011 requestBody.append('rvcnarrator_voice_gen', this.settings.rvc_narrator_voice);
1053 requestBody.append('rvcnarrator_pitch', this.settings.rvc_narrator_pitch || "0"); 1012 requestBody.append('rvcnarrator_pitch', this.settings.rvc_narrator_pitch || '0');
1054 } 1013 }
1055 } 1014 }
1056 1015
1057 try { 1016 try {
1058 const response = await doExtrasFetch( 1017 const response = await doExtrasFetch(
1059 `${this.settings.provider_endpoint}/api/tts-generate`, 1018 `${this.settings.provider_endpoint}/api/tts-generate`,
@@ -1063,25 +1022,25 @@ class AllTalkTtsProvider {
1063 'Content-Type': 'application/x-www-form-urlencoded', 1022 'Content-Type': 'application/x-www-form-urlencoded',
1064 'Cache-Control': 'no-cache', 1023 'Cache-Control': 'no-cache',
1065 }, 1024 },
1066 body: requestBody 1025 body: requestBody,
1067 } 1026 },
1068 ); 1027 );
1069 1028
1070 if (!response.ok) { 1029 if (!response.ok) {
1071 const errorText = await response.text(); 1030 const errorText = await response.text();
1072 console.error(`[fetchTtsGeneration] Error Response Text:`, errorText); 1031 console.error('fetchTtsGeneration Error Response Text:', errorText);
1073 throw new Error(`HTTP ${response.status}: ${errorText}`); 1032 throw new Error(`HTTP ${response.status}: ${errorText}`);
1074 } 1033 }
1075 const data = await response.json(); 1034 const data = await response.json();
1076 1035
1077 // Handle V1/V2 URL differences 1036 // Handle V1/V2 URL differences
1078 const outputUrl = this.settings.server_version === 'v1' 1037 const outputUrl = this.settings.server_version === 'v1'
1079 ? data.output_file_url // V1 returns full URL 1038 ? data.output_file_url // V1 returns full URL
1080 : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path 1039 : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path
1081 1040
1082 return outputUrl; 1041 return outputUrl;
1083 } catch (error) { 1042 } catch (error) {
1084 console.error("[fetchTtsGeneration] Exception caught:", error); 1043 console.error('[fetchTtsGeneration] Exception caught:', error);
1085 throw error; 1044 throw error;
1086 } 1045 }
1087 } 1046 }