Support for AllTalk V1 and V2 Select the version in the interface. RVC is not supported on AllTalk V1

3138252928875c0aeeb5130188c62f49b6501c47

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

Signed
1 files changed, +1113 -840Ignore whitespace
public/scripts/extensions/tts/alltalk.js+1113 -840
@@ -1,840 +1,1113 @@
1import { doExtrasFetch } from '../../extensions.js';1import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js';
2import { saveTtsProviderSettings } from './index.js';2import { saveTtsProviderSettings } from './index.js';
33
4export { AllTalkTtsProvider };4export { AllTalkTtsProvider };
55
6class AllTalkTtsProvider {6class AllTalkTtsProvider {
7 //########//7 //########//
8 // Config //8 // Config //
9 //########//9 //########//
1010
11 settings = {};11 settings = {};
12 constructor() {12 constructor() {
13 // Initialize with default settings if they are not already set13 // Initialize with default settings if they are not already set
14 this.settings = {14 this.settings = {
15 provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',15 provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
16 language: this.settings.language || 'en',16 server_version: this.settings.server_version || 'v2',
17 voiceMap: this.settings.voiceMap || {},17 language: this.settings.language || 'en',
18 at_generation_method: this.settings.at_generation_method || 'standard_generation',18 voiceMap: this.settings.voiceMap || {},
19 narrator_enabled: this.settings.narrator_enabled || 'false',19 at_generation_method: this.settings.at_generation_method || 'standard_generation',
20 at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator',20 narrator_enabled: this.settings.narrator_enabled || 'false',
21 narrator_voice_gen: this.settings.narrator_voice_gen || 'female_01.wav',21 at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator',
22 finetuned_model: this.settings.finetuned_model || 'false',22 narrator_voice_gen: this.settings.narrator_voice_gen || 'Please set a voice',
23 };23 rvc_character_voice: this.settings.rvc_character_voice || 'Disabled',
24 // Separate property for dynamically updated settings from the server24 rvc_character_pitch: this.settings.rvc_character_pitch || '0',
25 this.dynamicSettings = {25 rvc_narrator_voice: this.settings.rvc_narrator_voice || 'Disabled',
26 modelsAvailable: [],26 rvc_narrator_pitch: this.settings.rvc_narrator_pitch || '0',
27 currentModel: '',27 finetuned_model: this.settings.finetuned_model || 'false'
28 deepspeed_available: false,28 };
29 deepSpeedEnabled: false,29 // Separate property for dynamically updated settings from the server
30 lowVramEnabled: false,30 this.dynamicSettings = {
31 };31 modelsAvailable: [],
32 }32 currentModel: '',
33 ready = false;33 deepspeed_available: false,
34 voices = [];34 deepspeed_enabled: false,
35 separator = '. ';35 lowvram_capable: false,
36 audioElement = document.createElement('audio');36 lowvram_enabled: false,
3737 };
38 languageLabels = {38 this.rvcVoices = []; // Initialize rvcVoices as an empty array
39 'Arabic': 'ar',39 }
40 'Brazilian Portuguese': 'pt',40 ready = false;
41 'Chinese': 'zh-cn',41 voices = [];
42 'Czech': 'cs',42 separator = '. ';
43 'Dutch': 'nl',43 audioElement = document.createElement('audio');
44 'English': 'en',44
45 'French': 'fr',45 languageLabels = {
46 'German': 'de',46 'Arabic': 'ar',
47 'Italian': 'it',47 'Brazilian Portuguese': 'pt',
48 'Polish': 'pl',48 'Chinese': 'zh-cn',
49 'Russian': 'ru',49 'Czech': 'cs',
50 'Spanish': 'es',50 'Dutch': 'nl',
51 'Turkish': 'tr',51 'English': 'en',
52 'Japanese': 'ja',52 'French': 'fr',
53 'Korean': 'ko',53 'German': 'de',
54 'Hungarian': 'hu',54 'Italian': 'it',
55 'Hindi': 'hi',55 'Polish': 'pl',
56 };56 'Russian': 'ru',
5757 'Spanish': 'es',
58 get settingsHtml() {58 'Turkish': 'tr',
59 let html = '<div class="at-settings-separator">AllTalk Settings</div>';59 'Japanese': 'ja',
6060 'Korean': 'ko',
61 html += `<div class="at-settings-row">61 'Hungarian': 'hu',
6262 'Hindi': 'hi',
63 <div class="at-settings-option">63 };
64 <label for="at_generation_method">AllTalk TTS Generation Method</label>64
65 <select id="at_generation_method">65 get settingsHtml() {
66 <option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>66 let html = `<div class="at-settings-separator">AllTalk V2 Settings</div>`;
67 <option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>67
68 </select>68 html += `<div class="at-settings-row">
69 </div>69
70 </div>`;70 <div class="at-settings-option">
7171 <label for="at_generation_method">AllTalk TTS Generation Method</label>
72 html += `<div class="at-settings-row">72 <select id="at_generation_method">
7373 <option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
74 <div class="at-settings-option">74 <option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>
75 <label for="at_narrator_enabled">AT Narrator</label>75 </select>
76 <select id="at_narrator_enabled">76 </div>
77 <option value="true">Enabled</option>77 </div>`;
78 <option value="false">Disabled</option>78
79 </select>79 html += `<div class="at-settings-row">
80 </div>80
8181 <div class="at-settings-option">
82 <div class="at-settings-option">82 <label for="at_narrator_enabled">AT Narrator</label>
83 <label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>83 <select id="at_narrator_enabled">
84 <select id="at_narrator_text_not_inside">84 <option value="true">Enabled</option>
85 <option value="narrator">Narrator</option>85 <option value="silent">Enabled (Silenced)</option>
86 <option value="character">Character</option>86 <option value="false">Disabled</option>
87 </select>87 </select>
88 </div>88 </div>
8989
90 </div>`;90 <div class="at-settings-option">
9191 <label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
92 html += `<div class="at-settings-row">92 <select id="at_narrator_text_not_inside">
93 <div class="at-settings-option">93 <option value="character">Character</option>
94 <label for="narrator_voice">Narrator Voice</label>94 <option value="narrator">Narrator</option>
95 <select id="narrator_voice">`;95 <option value="silent">Silent</option>
96 if (this.voices) {96 </select>
97 for (let voice of this.voices) {97 </div>
98 html += `<option value="${voice.voice_id}">${voice.name}</option>`;98
99 }99 </div>`;
100 }100
101 html += `</select>101 html += `<div class="at-settings-row">
102 </div>102 <div class="at-settings-option">
103 <div class="at-settings-option">103 <label for="narrator_voice">Narrator Voice</label>
104 <label for="language_options">Language</label>104 <select id="narrator_voice">`;
105 <select id="language_options">`;105 if (this.voices) {
106 for (let language in this.languageLabels) {106 for (let voice of this.voices) {
107 html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;107 html += `<option value="${voice.voice_id}">${voice.name}</option>`;
108 }108 }
109 html += `</select>109 }
110 </div>110 html += `</select>
111 </div>`;111 </div>
112112 <div class="at-settings-option">
113113 <label for="language_options">Language</label>
114 html += `<div class="at-model-endpoint-row">114 <select id="language_options">`;
115 <div class="at-model-option">115 for (let language in this.languageLabels) {
116 <label for="switch_model">Switch Model</label>116 html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
117 <select id="switch_model">117 }
118 <option value="api_tts">API TTS</option>118 html += `</select>
119 <option value="api_local">API Local</option>119 </div>
120 <option value="xttsv2_local">XTTSv2 Local</option>120 </div>`;
121 </select>121
122 </div>122 html += `<div class="at-settings-row">
123 </div>123 <div class="at-settings-option">
124124 <label for="rvc_character_voice">RVC Character</label>
125 <div class="at-model-endpoint-row">125 <select id="rvc_character_voice">`;
126126 if (this.rvcVoices) {
127 <div class="at-endpoint-option">127 for (let rvccharvoice of this.rvcVoices) {
128 <label for="at_server">AllTalk Endpoint:</label>128 html += `<option value="${rvccharvoice.voice_id}">${rvccharvoice.name}</option>`;
129 <input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>129 }
130 <i><b>Important:</b> Must match IP address & port in AllTalk settings.</i>130 }
131 </div>131 html += `</select>
132 </div>`;132 </div>
133133 <div class="at-settings-option">
134134 <label for="rvc_narrator_voice">RVC Narrator</label>
135 html += `<div class="at-model-endpoint-row">135 <select id="rvc_narrator_voice">`;
136 <div class="at-settings-option">136 if (this.rvcVoices) {
137 <label for="low_vram">Low VRAM</label>137 for (let rvcnarrvoice of this.rvcVoices) {
138 <input id="low_vram" type="checkbox"/>138 html += `<option value="${rvcnarrvoice.voice_id}">${rvcnarrvoice.name}</option>`;
139 </div>139 }
140 <div class="at-settings-option">140 }
141 <label for="deepspeed">DeepSpeed</label>141 html += `</select>
142 <input id="deepspeed" type="checkbox"/>142 </div>
143 </div>143</div>`;
144 <div class="at-settings-option status-option">144
145 <span>Status: <span id="status_info">Ready</span></span>145 // Add the RVC pitch control row
146 </div>146 html += `<div class="at-settings-row">
147 <div class="at-settings-option empty-option">147 <div class="at-settings-option">
148 <!-- This div remains empty for spacing -->148 <label for="rvc_character_pitch">RVC Character Pitch</label>
149 </div>149 <select id="rvc_character_pitch">`;
150</div>`;150 for (let i = -24; i <= 24; i++) {
151151 const selected = i === 0 ? 'selected="selected"' : '';
152152 html += `<option value="${i}" ${selected}>${i}</option>`;
153 html += `<div class="at-website-row">153 }
154 <div class="at-website-option">154 html += `</select>
155 <span>AllTalk <a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>155 </div>
156 </div>156 <div class="at-settings-option">
157157 <label for="rvc_narrator_pitch">RVC Narrator Pitch</label>
158 <div class="at-website-option">158 <select id="rvc_narrator_pitch">`;
159 <span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span>159 for (let i = -24; i <= 24; i++) {
160 </div>160 const selected = i === 0 ? 'selected="selected"' : '';
161</div>`;161 html += `<option value="${i}" ${selected}>${i}</option>`;
162162 }
163 html += `<div class="at-website-row">163 html += `</select>
164<div class="at-website-option">164 </div>
165<span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span>165 </div>`;
166</div>166
167</div>`;167 html += `<div class="at-model-endpoint-row">
168168 <div class="at-model-option">
169 return html;169 <label for="switch_model">Switch Model</label>
170 }170 <select id="switch_model">
171171 <!-- Options will be dynamically populated -->
172172 </select>
173 //#################//173 </div>
174 // Startup ST & AT //174
175 //#################//175 <div class="at-endpoint-option">
176176 <label for="at_server">AllTalk Endpoint:</label>
177 async loadSettings(settings) {177 <input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>
178 updateStatus('Offline');178 </div>
179179 </div>`;
180 if (Object.keys(settings).length === 0) {180
181 console.info('Using default AllTalk TTS Provider settings');181 html += `<div class="at-settings-row">
182 } else {182 <div class="at-settings-option">
183 // Populate settings with provided values, ignoring server-provided settings183 <label for="server_version">AllTalk Server Version</label>
184 for (const key in settings) {184 <select id="server_version">
185 if (key in this.settings) {185 <option value="v1">AllTalk V1</option>
186 this.settings[key] = settings[key];186 <option value="v2">AllTalk V2</option>
187 } else {187 </select>
188 console.debug(`Ignoring non-user-configurable setting: ${key}`);188 </div>
189 }189 </div>`;
190 }190
191 }191 html += `<div class="at-model-endpoint-row">
192192 <div class="at-settings-option">
193 // Update UI elements to reflect the loaded settings193 <label for="low_vram">Low VRAM</label>
194 $('#at_server').val(this.settings.provider_endpoint);194 <input id="low_vram" type="checkbox"/>
195 $('#language_options').val(this.settings.language);195 </div>
196 //$('#voicemap').val(this.settings.voiceMap);196 <div class="at-settings-option">
197 $('#at_generation_method').val(this.settings.at_generation_method);197 <label for="deepspeed">DeepSpeed</label>
198 $('#at_narrator_enabled').val(this.settings.narrator_enabled);198 <input id="deepspeed" type="checkbox"/>
199 $('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);199 </div>
200 $('#narrator_voice').val(this.settings.narrator_voice_gen);200 <div class="at-settings-option status-option">
201201 <span>Status: <span id="status_info">Ready</span></span>
202 console.debug('AllTalkTTS: Settings loaded');202 </div>
203 await this.initEndpoint();203 <div class="at-settings-option empty-option">
204 }204 <!-- This div remains empty for spacing -->
205205 </div>
206 async initEndpoint() {206</div>`;
207 try {207
208 // Check if TTS provider is ready208
209 this.setupEventListeners();209 html += `<div class="at-website-row">
210 this.updateLanguageDropdown();210 <div class="at-website-option">
211 await this.checkReady();211 <span>AllTalk V2<a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
212 await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server212 </div>
213 await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready213
214 this.updateNarratorVoicesDropdown();214 <div class="at-website-option">
215 this.applySettingsToHTML();215 <span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span>
216 updateStatus('Ready');216 </div>
217 } catch (error) {217</div>`;
218 console.error('Error loading settings:', error);218
219 updateStatus('Offline');219 html += `<div class="at-website-row">
220 }220<div class="at-website-option">
221 }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>
222222<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 applySettingsToHTML() {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>
224 // Apply loaded settings or use defaults224</div>
225 const narratorVoiceSelect = document.getElementById('narrator_voice');225</div>`;
226 const atNarratorSelect = document.getElementById('at_narrator_enabled');226
227 const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');227 return html;
228 const generationMethodSelect = document.getElementById('at_generation_method');228 }
229 this.settings.narrator_voice = this.settings.narrator_voice_gen;229
230 // Apply settings to Narrator Voice dropdown230
231 if (narratorVoiceSelect && this.settings.narrator_voice) {231 //#################//
232 narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', '');232 // Startup ST & AT //
233 }233 //#################//
234 // Apply settings to AT Narrator Enabled dropdown234
235 if (atNarratorSelect) {235 async loadSettings(settings) {
236 // Sync the state with the checkbox in index.js236 updateStatus('Offline');
237 const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js237
238 const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js238 if (Object.keys(settings).length === 0) {
239 const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js239 console.info('Using default AllTalk TTS Provider settings');
240 // Sync the state with the checkbox in index.js240 } else {
241 if (this.settings.narrator_enabled) {241 // Populate settings with provided values, ignoring server-provided settings
242 ttsPassAsterisksCheckbox.checked = false;242 for (const key in settings) {
243 $('#tts_pass_asterisks').click(); // Simulate a click event243 if (key in this.settings) {
244 $('#tts_pass_asterisks').trigger('change');244 this.settings[key] = settings[key];
245 }245 } else {
246 if (!this.settings.narrator_enabled) {246 console.debug(`Ignoring non-user-configurable setting: ${key}`);
247 ttsPassAsterisksCheckbox.checked = true;247 }
248 $('#tts_pass_asterisks').click(); // Simulate a click event248 }
249 $('#tts_pass_asterisks').trigger('change');249 }
250 }250
251 // Uncheck and set tts_narrate_quoted to false if narrator is enabled251 // Update UI elements to reflect the loaded settings
252 if (this.settings.narrator_enabledd) {252 $('#at_server').val(this.settings.provider_endpoint);
253 ttsNarrateQuotedCheckbox.checked = true;253 $('#language_options').val(this.settings.language);
254 ttsNarrateDialoguesCheckbox.checked = true;254 $('#at_generation_method').val(this.settings.at_generation_method);
255 // Trigger click events instead of change events255 $('#at_narrator_enabled').val(this.settings.narrator_enabled);
256 $('#tts_narrate_quoted').click();256 $('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);
257 $('#tts_narrate_quoted').trigger('change');257 $('#narrator_voice').val(this.settings.narrator_voice_gen);
258 $('#tts_narrate_dialogues').click();258 $('#rvc_character_voice').val(this.settings.rvc_character_voice);
259 $('#tts_narrate_dialogues').trigger('change');259 $('#rvc_narrator_voice').val(this.settings.rvc_narrator_voice);
260 }260 $('#rvc_character_pitch').val(this.settings.rvc_character_pitch);
261 atNarratorSelect.value = this.settings.narrator_enabled.toString();261 $('#rvc_narrator_pitch').val(this.settings.rvc_narrator_pitch);
262 this.settings.narrator_enabled = this.settings.narrator_enabled.toString();262
263 }263 console.debug('AllTalkTTS: Settings loaded');
264 // Apply settings to the Language dropdown264 try {
265 const languageSelect = document.getElementById('language_options');265 // Check if TTS provider is ready
266 if (languageSelect && this.settings.language) {266 await this.checkReady();
267 languageSelect.value = this.settings.language;267 await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
268 }268 await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
269 // Apply settings to Text Not Inside dropdown269 await this.fetchRvcVoiceObjects(); // Fetch RVC voices
270 if (textNotInsideSelect && this.settings.text_not_inside) {270 this.updateNarratorVoicesDropdown();
271 textNotInsideSelect.value = this.settings.text_not_inside;271 this.updateLanguageDropdown();
272 this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;272 this.setupEventListeners();
273 }273 this.applySettingsToHTML();
274 // Apply settings to Generation Method dropdown274 updateStatus('Ready');
275 if (generationMethodSelect && this.settings.at_generation_method) {275 } catch (error) {
276 generationMethodSelect.value = this.settings.at_generation_method;276 console.error("Error loading settings:", error);
277 }277 updateStatus('Offline');
278 // Additional logic to disable/enable dropdowns based on the selected generation method278 }
279 const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';279 }
280 if (isStreamingEnabled) {280
281 // Disable certain dropdowns when streaming is enabled281
282 if (atNarratorSelect) atNarratorSelect.disabled = true;282 applySettingsToHTML() {
283 if (textNotInsideSelect) textNotInsideSelect.disabled = true;283 const narratorVoiceSelect = document.getElementById('narrator_voice');
284 if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;284 const atNarratorSelect = document.getElementById('at_narrator_enabled');
285 } else {285 const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
286 // Enable dropdowns for standard generation286 const generationMethodSelect = document.getElementById('at_generation_method');
287 if (atNarratorSelect) atNarratorSelect.disabled = false;287 this.settings.narrator_voice = this.settings.narrator_voice_gen;
288 if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;288 // Apply settings to Narrator Voice dropdown
289 if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;289 if (narratorVoiceSelect && this.settings.narrator_voice) {
290 }290 narratorVoiceSelect.value = this.settings.narrator_voice; // Remove the parentheses
291 const modelSelect = document.getElementById('switch_model');291 }
292 if (this.settings.finetuned_model === 'true') {292 // Apply settings to AT Narrator Enabled dropdown
293 const ftOption = document.createElement('option');293 if (atNarratorSelect) {
294 ftOption.value = 'XTTSv2 FT';294 const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks');
295 ftOption.textContent = 'XTTSv2 FT';295 const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted');
296 modelSelect.appendChild(ftOption);296 const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues');
297 }297 if (this.settings.narrator_enabled) {
298 }298 ttsPassAsterisksCheckbox.checked = false;
299299 $('#tts_pass_asterisks').click();
300 //##############################//300 $('#tts_pass_asterisks').trigger('change');
301 // Check AT Server is Available //301 }
302 //##############################//302 if (!this.settings.narrator_enabled) {
303303 ttsPassAsterisksCheckbox.checked = true;
304 async checkReady() {304 $('#tts_pass_asterisks').click();
305 try {305 $('#tts_pass_asterisks').trigger('change');
306 const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);306 }
307 // Check if the HTTP request was successful307 if (this.settings.narrator_enabled) {
308 if (!response.ok) {308 ttsNarrateQuotedCheckbox.checked = true;
309 throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);309 ttsNarrateDialoguesCheckbox.checked = true;
310 }310 $('#tts_narrate_quoted').click();
311 const statusText = await response.text();311 $('#tts_narrate_quoted').trigger('change');
312 // Check if the response is 'Ready'312 $('#tts_narrate_dialogues').click();
313 if (statusText === 'Ready') {313 $('#tts_narrate_dialogues').trigger('change');
314 this.ready = true; // Set the ready flag to true314 }
315 console.log('TTS service is ready.');315 atNarratorSelect.value = this.settings.narrator_enabled.toString();
316 } else {316 this.settings.narrator_enabled = this.settings.narrator_enabled.toString();
317 this.ready = false;317 }
318 console.log('TTS service is not ready.');318 const languageSelect = document.getElementById('language_options');
319 }319 if (languageSelect && this.settings.language) {
320 } catch (error) {320 languageSelect.value = this.settings.language;
321 console.error('Error checking TTS service readiness:', error);321 }
322 this.ready = false; // Ensure ready flag is set to false in case of error322 if (textNotInsideSelect && this.settings.text_not_inside) {
323 throw error; // Rethrow the error for further handling323 textNotInsideSelect.value = this.settings.text_not_inside;
324 }324 this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
325 }325 }
326326 if (generationMethodSelect && this.settings.at_generation_method) {
327 //######################//327 generationMethodSelect.value = this.settings.at_generation_method;
328 // Get Available Voices //328 }
329 //######################//329 const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
330330 if (isStreamingEnabled) {
331 async fetchTtsVoiceObjects() {331 if (atNarratorSelect) atNarratorSelect.disabled = true;
332 const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);332 if (textNotInsideSelect) textNotInsideSelect.disabled = true;
333 if (!response.ok) {333 if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;
334 const errorText = await response.text();334 } else {
335 throw new Error(`HTTP ${response.status}: ${errorText}`);335 if (atNarratorSelect) atNarratorSelect.disabled = false;
336 }336 if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;
337 const data = await response.json();337 if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;
338 const voices = data.voices.map(filename => {338 }
339 const voiceName = filename.replace('.wav', '');339 }
340 return {340
341 name: voiceName,341
342 voice_id: voiceName,342 //##############################//
343 preview_url: null, // Preview URL will be dynamically generated343 // Check AT Server is Available //
344 lang: 'en', // Default language344 //##############################//
345 };345
346 });346 async checkReady() {
347 this.voices = voices; // Assign to the class property347 try {
348 return voices; // Also return this list348 const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);
349 }349 // Check if the HTTP request was successful
350350 if (!response.ok) {
351 //##########################################//351 throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
352 // Get Current AT Server Config & Update ST //352 }
353 //##########################################//353 const statusText = await response.text();
354354 // Check if the response is 'Ready'
355 async updateSettingsFromServer() {355 if (statusText === 'Ready') {
356 try {356 this.ready = true; // Set the ready flag to true
357 // Fetch current settings357 console.log('TTS service is ready.');
358 const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);358 } else {
359 if (!response.ok) {359 this.ready = false;
360 throw new Error(`Failed to fetch current settings: ${response.statusText}`);360 console.log('TTS service is not ready.');
361 }361 }
362 const currentSettings = await response.json();362 } catch (error) {
363 // Update internal settings363 console.error('Error checking TTS service readiness:', error);
364 this.settings.modelsAvailable = currentSettings.models_available;364 this.ready = false; // Ensure ready flag is set to false in case of error
365 this.settings.currentModel = currentSettings.current_model_loaded;365 }
366 this.settings.deepspeed_available = currentSettings.deepspeed_available;366 }
367 this.settings.deepSpeedEnabled = currentSettings.deepspeed_status;367
368 this.settings.lowVramEnabled = currentSettings.low_vram_status;368 //######################//
369 this.settings.finetuned_model = currentSettings.finetuned_model;369 // Get Available Voices //
370 // Update HTML elements370 //######################//
371 this.updateModelDropdown();371
372 this.updateCheckboxes();372 async fetchTtsVoiceObjects() {
373 } catch (error) {373 const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);
374 console.error(`Error updating settings from server: ${error}`);374 if (!response.ok) {
375 }375 const errorText = await response.text();
376 }376 throw new Error(`HTTP ${response.status}: ${errorText}`);
377377 }
378 //###################################################//378 const data = await response.json();
379 // Get Current AT Server Config & Update ST (Models) //379 const voices = data.voices.map(filename => {
380 //###################################################//380 return {
381381 name: filename,
382 updateModelDropdown() {382 voice_id: filename,
383 const modelSelect = document.getElementById('switch_model');383 preview_url: null, // Preview URL will be dynamically generated
384 if (modelSelect) {384 lang: 'en' // Default language
385 modelSelect.innerHTML = ''; // Clear existing options385 };
386 this.settings.modelsAvailable.forEach(model => {386 });
387 const option = document.createElement('option');387 this.voices = voices; // Assign to the class property
388 option.value = model.model_name;388 return voices; // Also return this list
389 option.textContent = model.model_name; // Use model_name instead of name389 }
390 option.selected = model.model_name === this.settings.currentModel;390
391 modelSelect.appendChild(option);391 async fetchRvcVoiceObjects() {
392 });392 if (this.settings.server_version !== 'v2') {
393 }393 console.log('Skipping RVC voices fetch for V1 server');
394 }394 return [];
395395 }
396 //#######################################################//396
397 // Get Current AT Server Config & Update ST (DS and LVR) //397 console.log('Fetching RVC Voices');
398 //#######################################################//398 try {
399399 const response = await fetch(`${this.settings.provider_endpoint}/api/rvcvoices`);
400 updateCheckboxes() {400 if (!response.ok) {
401 const deepspeedCheckbox = document.getElementById('deepspeed');401 const errorText = await response.text();
402 const lowVramCheckbox = document.getElementById('low_vram');402 console.error('Error text:', errorText);
403 if (lowVramCheckbox) lowVramCheckbox.checked = this.settings.lowVramEnabled;403 throw new Error(`HTTP ${response.status}: ${errorText}`);
404 if (deepspeedCheckbox) {404 }
405 deepspeedCheckbox.checked = this.settings.deepSpeedEnabled;405
406 deepspeedCheckbox.disabled = !this.settings.deepspeed_available; // Disable checkbox if deepspeed is not available406 const data = await response.json();
407 }407 if (!data || !data.rvcvoices) {
408 }408 console.error('Invalid data format:', data);
409409 throw new Error('Invalid data format received from /api/rvcvoices');
410 //###############################################################//410 }
411 // Get Current AT Server Config & Update ST (AT Narrator Voices) //411
412 //###############################################################//412 const voices = data.rvcvoices.map(filename => {
413413 return {
414 updateNarratorVoicesDropdown() {414 name: filename,
415 const narratorVoiceSelect = document.getElementById('narrator_voice');415 voice_id: filename,
416 if (narratorVoiceSelect && this.voices) {416 };
417 // Clear existing options417 });
418 narratorVoiceSelect.innerHTML = '';418
419 // Add new options419 console.log('RVC voices:', voices);
420 for (let voice of this.voices) {420 this.rvcVoices = voices; // Assign to the class property
421 const option = document.createElement('option');421 this.updateRvcVoiceDropdowns(); // Update UI after fetching voices
422 option.value = voice.voice_id;422 return voices; // Also return this list
423 option.textContent = voice.name;423 } catch (error) {
424 narratorVoiceSelect.appendChild(option);424 console.error('Error fetching RVC voices:', error);
425 }425 this.rvcVoices = [{ name: 'Disabled', voice_id: 'Disabled' }]; // Set default on error
426 }426 throw error;
427 }427 } finally {
428428 // Ensure dropdowns are updated even if there was an error
429 //######################################################//429 this.updateRvcVoiceDropdowns();
430 // Get Current AT Server Config & Update ST (Languages) //430 }
431 //######################################################//431 }
432432
433 updateLanguageDropdown() {433 //##########################################//
434 const languageSelect = document.getElementById('language_options');434 // Get Current AT Server Config & Update ST //
435 if (languageSelect) {435 //##########################################//
436 // Ensure default language is set (??? whatever that means)436
437 // this.settings.language = this.settings.language;437 async updateSettingsFromServer() {
438438 try {
439 languageSelect.innerHTML = '';439 const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);
440 for (let language in this.languageLabels) {440 if (!response.ok) {
441 const option = document.createElement('option');441 throw new Error(`Failed to fetch current settings: ${response.statusText}`);
442 option.value = this.languageLabels[language];442 }
443 option.textContent = language;443 const currentSettings = await response.json();
444 if (this.languageLabels[language] === this.settings.language) {444 currentSettings.models_available.sort((a, b) => a.name.localeCompare(b.name));
445 option.selected = true;445
446 }446 this.settings.enginesAvailable = currentSettings.engines_available;
447 languageSelect.appendChild(option);447 this.settings.currentEngineLoaded = currentSettings.current_engine_loaded;
448 }448 this.settings.modelsAvailable = currentSettings.models_available;
449 }449 this.settings.currentModel = currentSettings.current_model_loaded;
450 }450 this.settings.deepspeed_capable = currentSettings.deepspeed_capable;
451451 this.settings.deepspeed_available = currentSettings.deepspeed_available;
452 //########################################//452 this.settings.deepspeed_enabled = currentSettings.deepspeed_enabled;
453 // Start AT TTS extenstion page listeners //453 this.settings.lowvram_capable = currentSettings.lowvram_capable;
454 //########################################//454 this.settings.lowvram_enabled = currentSettings.lowvram_enabled;
455455
456 setupEventListeners() {456 await this.fetchRvcVoiceObjects(); // Fetch RVC voices
457457
458 let debounceTimeout;458 this.updateModelDropdown();
459 const debounceDelay = 500; // Milliseconds459 this.updateCheckboxes();
460460 this.updateRvcVoiceDropdowns(); // Update the RVC voice dropdowns
461 // Define the event handler function461 } catch (error) {
462 const onModelSelectChange = async (event) => {462 console.error(`Error updating settings from server: ${error}`);
463 console.log('Model select change event triggered'); // Debugging statement463 }
464 const selectedModel = event.target.value;464 }
465 console.log(`Selected model: ${selectedModel}`); // Debugging statement465
466 // Set status to Processing466 updateRvcVoiceDropdowns() {
467 updateStatus('Processing');467 // Handle all RVC-related elements
468 try {468 const rvcElements = document.querySelectorAll('.rvc-setting');
469 const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {469 const isV2 = this.settings.server_version === 'v2';
470 method: 'POST',470
471 });471 rvcElements.forEach(element => {
472 if (!response.ok) {472 element.style.display = isV2 ? 'block' : 'none';
473 throw new Error(`HTTP Error: ${response.status}`);473 });
474 }474
475 const data = await response.json();475 // Update and disable/enable character voice dropdown
476 console.log('POST response data:', data); // Debugging statement476 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice');
477 // Set status to Ready if successful477 if (rvcCharacterVoiceSelect) {
478 updateStatus('Ready');478 rvcCharacterVoiceSelect.disabled = !isV2;
479 } catch (error) {479 if (this.rvcVoices) {
480 console.error('POST request error:', error); // Debugging statement480 rvcCharacterVoiceSelect.innerHTML = '';
481 // Set status to Error in case of failure481 for (let voice of this.rvcVoices) {
482 updateStatus('Error');482 const option = document.createElement('option');
483 }483 option.value = voice.voice_id;
484484 option.textContent = voice.name;
485 // Handle response or error485 if (voice.voice_id === this.settings.rvc_character_voice) {
486 };486 option.selected = true;
487487 }
488 const debouncedModelSelectChange = (event) => {488 rvcCharacterVoiceSelect.appendChild(option);
489 clearTimeout(debounceTimeout);489 }
490 debounceTimeout = setTimeout(() => {490 }
491 onModelSelectChange(event);491 }
492 }, debounceDelay);492
493 };493 // Update and disable/enable narrator voice dropdown
494494 const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice');
495 // Switch Model Listener495 if (rvcNarratorVoiceSelect) {
496 const modelSelect = document.getElementById('switch_model');496 rvcNarratorVoiceSelect.disabled = !isV2;
497 if (modelSelect) {497 if (this.rvcVoices) {
498 // Remove the event listener if it was previously added498 rvcNarratorVoiceSelect.innerHTML = '';
499 // Add the debounced event listener499 for (let voice of this.rvcVoices) {
500 $(modelSelect).off('change').on('change', debouncedModelSelectChange);500 const option = document.createElement('option');
501 }501 option.value = voice.voice_id;
502502 option.textContent = voice.name;
503 // DeepSpeed Listener503 if (voice.voice_id === this.settings.rvc_narrator_voice) {
504 const deepspeedCheckbox = document.getElementById('deepspeed');504 option.selected = true;
505 if (deepspeedCheckbox) {505 }
506 $(deepspeedCheckbox).off('change').on('change', async (event) => {506 rvcNarratorVoiceSelect.appendChild(option);
507 const deepSpeedValue = event.target.checked ? 'True' : 'False';507 }
508 // Set status to Processing508 }
509 updateStatus('Processing');509 }
510 try {510
511 const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {511 // Update pitch inputs
512 method: 'POST',512 const characterPitch = document.getElementById('rvc_character_pitch');
513 });513 if (characterPitch) {
514 if (!response.ok) {514 characterPitch.disabled = !isV2;
515 throw new Error(`HTTP Error: ${response.status}`);515 }
516 }516
517 const data = await response.json();517 const narratorPitch = document.getElementById('rvc_narrator_pitch');
518 console.log('POST response data:', data); // Debugging statement518 if (narratorPitch) {
519 // Set status to Ready if successful519 narratorPitch.disabled = !isV2;
520 updateStatus('Ready');520 }
521 } catch (error) {521 }
522 console.error('POST request error:', error); // Debugging statement522
523 // Set status to Error in case of failure523 //###################################################//
524 updateStatus('Error');524 // Get Current AT Server Config & Update ST (Models) //
525 }525 //###################################################//
526 });526
527 }527 updateModelDropdown() {
528528 const modelSelect = document.getElementById('switch_model');
529 // Low VRAM Listener529 if (modelSelect) {
530 const lowVramCheckbox = document.getElementById('low_vram');530 modelSelect.innerHTML = ''; // Clear existing options
531 if (lowVramCheckbox) {531 this.settings.modelsAvailable.forEach(model => {
532 $(lowVramCheckbox).off('change').on('change', async (event) => {532 const option = document.createElement('option');
533 const lowVramValue = event.target.checked ? 'True' : 'False';533 option.value = model.name;
534 // Set status to Processing534 option.textContent = model.name; // Use model name directly
535 updateStatus('Processing');535 option.selected = model.name === this.settings.currentModel;
536 try {536 modelSelect.appendChild(option);
537 const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {537 });
538 method: 'POST',538 }
539 });539 }
540 if (!response.ok) {540
541 throw new Error(`HTTP Error: ${response.status}`);541 //#######################################################//
542 }542 // Get Current AT Server Config & Update ST (DS and LVR) //
543 const data = await response.json();543 //#######################################################//
544 console.log('POST response data:', data); // Debugging statement544
545 // Set status to Ready if successful545 updateCheckboxes() {
546 updateStatus('Ready');546 const deepspeedCheckbox = document.getElementById('deepspeed');
547 } catch (error) {547 const lowVramCheckbox = document.getElementById('low_vram');
548 console.error('POST request error:', error); // Debugging statement548
549 // Set status to Error in case of failure549 // Handle DeepSpeed checkbox
550 updateStatus('Error');550 if (deepspeedCheckbox) {
551 }551 if (this.settings.deepspeed_capable) {
552 });552 // If TTS engine is capable of using DeepSpeed
553 }553 deepspeedCheckbox.disabled = !this.settings.deepspeed_available;
554554 this.settings.deepspeed_enabled = this.settings.deepspeed_available && this.settings.deepspeed_enabled;
555 // Narrator Voice Dropdown Listener555 } else {
556 const narratorVoiceSelect = document.getElementById('narrator_voice');556 // If TTS engine is NOT capable of using DeepSpeed
557 if (narratorVoiceSelect) {557 deepspeedCheckbox.disabled = true;
558 $(narratorVoiceSelect).off('change').on('change', (event) => {558 this.settings.deepspeed_enabled = false;
559 this.settings.narrator_voice_gen = `${event.target.value}.wav`;559 }
560 this.onSettingsChange(); // Save the settings after change560 deepspeedCheckbox.checked = this.settings.deepspeed_enabled;
561 });561 }
562 }562
563563 // Handle Low VRAM checkbox
564 const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');564 if (lowVramCheckbox) {
565 if (textNotInsideSelect) {565 if (this.settings.lowvram_capable) {
566 $(textNotInsideSelect).off('change').on('change', (event) => {566 // If TTS engine is capable of low VRAM
567 this.settings.text_not_inside = event.target.value;567 lowVramCheckbox.disabled = false;
568 this.onSettingsChange(); // Save the settings after change568 } else {
569 });569 // If TTS engine is NOT capable of low VRAM
570 }570 lowVramCheckbox.disabled = true;
571571 this.settings.lowvram_enabled = false;
572 // AT Narrator Dropdown Listener572 }
573 const atNarratorSelect = document.getElementById('at_narrator_enabled');573 lowVramCheckbox.checked = this.settings.lowvram_enabled;
574 const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js574 }
575 const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js575 }
576 const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js576
577577
578 if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {578 //###############################################################//
579 $(atNarratorSelect).off('change').on('change', (event) => {579 // Get Current AT Server Config & Update ST (AT Narrator Voices) //
580 const isNarratorEnabled = event.target.value === 'true';580 //###############################################################//
581 this.settings.narrator_enabled = isNarratorEnabled; // Update the setting here581
582 textNotInsideSelect.disabled = !isNarratorEnabled;582 updateNarratorVoicesDropdown() {
583 narratorVoiceSelect.disabled = !isNarratorEnabled;583 const narratorVoiceSelect = document.getElementById('narrator_voice');
584584 if (narratorVoiceSelect && this.voices) {
585 // Sync the state with the checkbox in index.js585 // Clear existing options
586 if (isNarratorEnabled) {586 narratorVoiceSelect.innerHTML = '';
587 ttsPassAsterisksCheckbox.checked = false;587 // Add new options
588 $('#tts_pass_asterisks').click(); // Simulate a click event588 for (let voice of this.voices) {
589 $('#tts_pass_asterisks').trigger('change');589 const option = document.createElement('option');
590 }590 option.value = voice.voice_id;
591 if (!isNarratorEnabled) {591 option.textContent = voice.name;
592 ttsPassAsterisksCheckbox.checked = true;592 narratorVoiceSelect.appendChild(option);
593 $('#tts_pass_asterisks').click(); // Simulate a click event593 }
594 $('#tts_pass_asterisks').trigger('change');594 }
595 }595 }
596 // Uncheck and set tts_narrate_quoted to false if narrator is enabled596
597 if (isNarratorEnabled) {597 //######################################################//
598 ttsNarrateQuotedCheckbox.checked = true;598 // Get Current AT Server Config & Update ST (Languages) //
599 ttsNarrateDialoguesCheckbox.checked = true;599 //######################################################//
600 // Trigger click events instead of change events600
601 $('#tts_narrate_quoted').click();601 updateLanguageDropdown() {
602 $('#tts_narrate_quoted').trigger('change');602 const languageSelect = document.getElementById('language_options');
603 $('#tts_narrate_dialogues').click();603 if (languageSelect) {
604 $('#tts_narrate_dialogues').trigger('change');604 // Ensure default language is set
605 }605 this.settings.language = this.settings.language;
606 this.onSettingsChange(); // Save the settings after change606
607 });607 languageSelect.innerHTML = '';
608 }608 for (let language in this.languageLabels) {
609609 const option = document.createElement('option');
610610 option.value = this.languageLabels[language];
611 // Event Listener for AT Generation Method Dropdown611 option.textContent = language;
612 const atGenerationMethodSelect = document.getElementById('at_generation_method');612 if (this.languageLabels[language] === this.settings.language) {
613 const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');613 option.selected = true;
614 if (atGenerationMethodSelect) {614 }
615 $(atGenerationMethodSelect).off('change').on('change', (event) => {615 languageSelect.appendChild(option);
616 const selectedMethod = event.target.value;616 }
617617 }
618 if (selectedMethod === 'streaming_enabled') {618 }
619 // Disable and unselect AT Narrator619
620 atNarratorEnabledSelect.disabled = true;620 //########################################//
621 atNarratorEnabledSelect.value = 'false';621 // Start AT TTS extenstion page listeners //
622 textNotInsideSelect.disabled = true;622 //########################################//
623 narratorVoiceSelect.disabled = true;623
624 } else if (selectedMethod === 'standard_generation') {624 setupEventListeners() {
625 // Enable AT Narrator625
626 atNarratorEnabledSelect.disabled = false;626 let debounceTimeout;
627 }627 const debounceDelay = 1400; // Milliseconds
628 this.settings.at_generation_method = selectedMethod; // Update the setting here628
629 this.onSettingsChange(); // Save the settings after change629 // Define the event handler function
630 });630 const onModelSelectChange = async (event) => {
631 }631 console.log("Model select change event triggered"); // Debugging statement
632632 const selectedModel = event.target.value;
633 // Listener for Language Dropdown633 console.log(`Selected model: ${selectedModel}`); // Debugging statement
634 const languageSelect = document.getElementById('language_options');634 // Set status to Processing
635 if (languageSelect) {635 updateStatus('Processing');
636 $(languageSelect).off('change').on('change', (event) => {636 try {
637 this.settings.language = event.target.value;637 const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
638 this.onSettingsChange(); // Save the settings after change638 method: 'POST'
639 });639 });
640 }640 if (!response.ok) {
641641 throw new Error(`HTTP Error: ${response.status}`);
642 // Listener for AllTalk Endpoint Input642 }
643 const atServerInput = document.getElementById('at_server');643 const data = await response.json();
644 if (atServerInput) {644 console.log("POST response data:", data); // Debugging statement
645 $(atServerInput).off('input').on('input', (event) => {645 // Set status to Ready if successful
646 this.settings.provider_endpoint = event.target.value;646 updateStatus('Ready');
647 this.onSettingsChange(); // Save the settings after change647 } catch (error) {
648 });648 console.error("POST request error:", error); // Debugging statement
649 }649 // Set status to Error in case of failure
650650 updateStatus('Error');
651 }651 }
652652
653 //#############################//653 // Handle response or error
654 // Store ST interface settings //654 };
655 //#############################//655
656656 // AllTalk Server version change listener
657 onSettingsChange() {657 const serverVersionSelect = document.getElementById('server_version');
658 // Update settings based on the UI elements658 if (serverVersionSelect) {
659 //this.settings.provider_endpoint = $('#at_server').val();659 serverVersionSelect.addEventListener('change', async (event) => {
660 this.settings.language = $('#language_options').val();660 this.settings.server_version = event.target.value;
661 //this.settings.voiceMap = $('#voicemap').val();661 // Clear and refetch voices if needed
662 this.settings.at_generation_method = $('#at_generation_method').val();662 if (event.target.value === 'v2') {
663 this.settings.narrator_enabled = $('#at_narrator_enabled').val();663 await this.fetchRvcVoiceObjects();
664 this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();664 }
665 this.settings.narrator_voice_gen = $('#narrator_voice').val();665 this.updateRvcVoiceDropdowns();
666 // Save the updated settings666 this.onSettingsChange();
667 saveTtsProviderSettings();667 });
668 }668 }
669669
670 //#########################//670 const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice');
671 // ST Handle Reload button //671 if (rvcCharacterVoiceSelect) {
672 //#########################//672 rvcCharacterVoiceSelect.addEventListener('change', (event) => {
673673 this.settings.rvccharacter_voice_gen = event.target.value;
674 async onRefreshClick() {674 this.onSettingsChange(); // Save the settings after change
675 await this.initEndpoint();675 });
676 // Additional actions as needed676 }
677 }677
678678 const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice');
679 //##################//679 if (rvcNarratorVoiceSelect) {
680 // Preview AT Voice //680 rvcNarratorVoiceSelect.addEventListener('change', (event) => {
681 //##################//681 this.settings.rvcnarrator_voice_gen = event.target.value;
682682 this.onSettingsChange(); // Save the settings after change
683 async previewTtsVoice(voiceName) {683 });
684 try {684 }
685 // Prepare data for POST request685
686 const postData = new URLSearchParams();686 const rvcCharacterPitchSelect = document.getElementById('rvc_character_pitch');
687 postData.append('voice', `${voiceName}.wav`);687 if (rvcCharacterPitchSelect) {
688 // Making the POST request688 rvcCharacterPitchSelect.addEventListener('change', (event) => {
689 const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {689 this.settings.rvc_character_pitch = event.target.value;
690 method: 'POST',690 this.onSettingsChange(); // Save the settings after change
691 headers: {691 });
692 'Content-Type': 'application/x-www-form-urlencoded',692 }
693 },693
694 body: postData,694 const rvcNarratorPitchSelect = document.getElementById('rvc_narrator_pitch');
695 });695 if (rvcNarratorPitchSelect) {
696 if (!response.ok) {696 rvcNarratorPitchSelect.addEventListener('change', (event) => {
697 const errorText = await response.text();697 this.settings.rvc_narrator_pitch = event.target.value;
698 console.error('[previewTtsVoice] Error Response Text:', errorText);698 this.onSettingsChange(); // Save the settings after change
699 throw new Error(`HTTP ${response.status}: ${errorText}`);699 });
700 }700 }
701 // Assuming the server returns a URL to the .wav file701
702 const data = await response.json();702 const debouncedModelSelectChange = (event) => {
703 if (data.output_file_url) {703 clearTimeout(debounceTimeout);
704 // Use an audio element to play the .wav file704 debounceTimeout = setTimeout(() => {
705 const audioElement = new Audio(data.output_file_url);705 onModelSelectChange(event);
706 audioElement.play().catch(e => console.error('Error playing audio:', e));706 }, debounceDelay);
707 } else {707 };
708 console.warn('[previewTtsVoice] No output file URL received in the response');708
709 throw new Error('No output file URL received in the response');709 // Switch Model Listener
710 }710 const modelSelect = document.getElementById('switch_model');
711711 if (modelSelect) {
712 } catch (error) {712 // Remove the event listener if it was previously added
713 console.error('[previewTtsVoice] Exception caught during preview generation:', error);713 modelSelect.removeEventListener('change', debouncedModelSelectChange);
714 throw error;714 // Add the debounced event listener
715 }715 modelSelect.addEventListener('change', debouncedModelSelectChange);
716 }716 }
717717
718 //#####################//718 // DeepSpeed Listener
719 // Populate ST voices //719 const deepspeedCheckbox = document.getElementById('deepspeed');
720 //#####################//720 if (deepspeedCheckbox) {
721721 deepspeedCheckbox.addEventListener('change', async (event) => {
722 async getVoice(voiceName, generatePreview = false) {722 const deepSpeedValue = event.target.checked ? 'True' : 'False';
723 // Ensure this.voices is populated723 // Set status to Processing
724 if (this.voices.length === 0) {724 updateStatus('Processing');
725 // Fetch voice objects logic725 try {
726 }726 const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
727 // Find the object where the name matches voiceName727 method: 'POST'
728 const match = this.voices.find(voice => voice.name === voiceName);728 });
729 if (!match) {729 if (!response.ok) {
730 // Error handling730 throw new Error(`HTTP Error: ${response.status}`);
731 }731 }
732 // Generate preview URL only if requested732 const data = await response.json();
733 if (!match.preview_url && generatePreview) {733 console.log("POST response data:", data); // Debugging statement
734 // Generate preview logic734 // Set status to Ready if successful
735 }735 updateStatus('Ready');
736 return match; // Return the found voice object736 } catch (error) {
737 }737 console.error("POST request error:", error); // Debugging statement
738738 // Set status to Error in case of failure
739 //##########################################//739 updateStatus('Error');
740 // Generate TTS Streaming or call Standard //740 }
741 //##########################################//741 });
742742 }
743 async generateTts(inputText, voiceId) {743
744 try {744 function lowvramdebounce(func, delay) {
745 if (this.settings.at_generation_method === 'streaming_enabled') {745 let debounceTimer;
746 // Construct the streaming URL746 return function (...args) {
747 const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`;747 const context = this;
748 console.log('Streaming URL:', streamingUrl);748 clearTimeout(debounceTimer);
749749 debounceTimer = setTimeout(() => func.apply(context, args), delay);
750 // Return the streaming URL directly750 };
751 return streamingUrl;751 }
752 } else {752
753 // For standard method753 // Low VRAM Listener
754 const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);754 const lowVramCheckbox = document.getElementById('low_vram');
755 const audioResponse = await fetch(outputUrl);755 if (lowVramCheckbox) {
756 if (!audioResponse.ok) {756 const handleLowVramChange = async (event) => {
757 throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);757 const lowVramValue = event.target.checked ? 'True' : 'False';
758 }758 // Set status to Processing
759 return audioResponse; // Return the fetch response directly759 updateStatus('Processing');
760 }760 try {
761 } catch (error) {761 const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {
762 console.error('Error in generateTts:', error);762 method: 'POST'
763 throw error;763 });
764 }764 if (!response.ok) {
765 }765 throw new Error(`HTTP Error: ${response.status}`);
766766 }
767767 const data = await response.json();
768 //####################//768 console.log("POST response data:", data); // Debugging statement
769 // Generate Standard //769 // Set status to Ready if successful
770 //####################//770 updateStatus('Ready');
771771 } catch (error) {
772 async fetchTtsGeneration(inputText, voiceId) {772 console.error("POST request error:", error); // Debugging statement
773 // Prepare the request payload773 // Set status to Error in case of failure
774 const requestBody = new URLSearchParams({774 updateStatus('Error');
775 'text_input': inputText,775 }
776 'text_filtering': 'standard',776 };
777 'character_voice_gen': voiceId + '.wav',777
778 'narrator_enabled': this.settings.narrator_enabled,778 const debouncedHandleLowVramChange = lowvramdebounce(handleLowVramChange, 300); // Adjust delay as needed
779 'narrator_voice_gen': this.settings.narrator_voice_gen + '.wav',779
780 'text_not_inside': this.settings.at_narrator_text_not_inside,780 lowVramCheckbox.addEventListener('change', debouncedHandleLowVramChange);
781 'language': this.settings.language,781 }
782 'output_file_name': 'st_output',782
783 'output_file_timestamp': 'true',783
784 'autoplay': 'false',784 // Narrator Voice Dropdown Listener
785 'autoplay_volume': '0.8',785 const narratorVoiceSelect = document.getElementById('narrator_voice');
786 }).toString();786 if (narratorVoiceSelect) {
787787 narratorVoiceSelect.addEventListener('change', (event) => {
788 try {788 this.settings.narrator_voice_gen = `${event.target.value}`;
789 const response = await doExtrasFetch(789 this.onSettingsChange(); // Save the settings after change
790 `${this.settings.provider_endpoint}/api/tts-generate`,790 });
791 {791 }
792 method: 'POST',792
793 headers: {793 const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
794 'Content-Type': 'application/x-www-form-urlencoded',794 if (textNotInsideSelect) {
795 'Cache-Control': 'no-cache',795 textNotInsideSelect.addEventListener('change', (event) => {
796 },796 this.settings.text_not_inside = event.target.value;
797 body: requestBody,797 this.onSettingsChange(); // Save the settings after change
798 },798 });
799 );799 }
800800
801 if (!response.ok) {801 // AT Narrator Dropdown Listener
802 const errorText = await response.text();802 const atNarratorSelect = document.getElementById('at_narrator_enabled');
803 console.error('[fetchTtsGeneration] Error Response Text:', errorText);803 const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks');
804 // toastr.error(response.statusText, 'TTS Generation Failed');804 const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted');
805 throw new Error(`HTTP ${response.status}: ${errorText}`);805 const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues');
806 }806
807 const data = await response.json();807 if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {
808 const outputUrl = data.output_file_url;808 atNarratorSelect.addEventListener('change', (event) => {
809 return outputUrl; // Return only the output_file_url809 const narratorOption = event.target.value;
810 } catch (error) {810 this.settings.narrator_enabled = narratorOption;
811 console.error('[fetchTtsGeneration] Exception caught:', error);811
812 throw error; // Rethrow the error for further handling812 // Check if narrator is disabled
813 }813 const isNarratorDisabled = narratorOption === 'false';
814 }814 textNotInsideSelect.disabled = isNarratorDisabled;
815}815 narratorVoiceSelect.disabled = isNarratorDisabled;
816816
817//#########################//817 console.log(`Narrator option: ${narratorOption}`);
818// Update Status Messages //818 console.log(`textNotInsideSelect disabled: ${textNotInsideSelect.disabled}`);
819//#########################//819 console.log(`narratorVoiceSelect disabled: ${narratorVoiceSelect.disabled}`);
820820
821function updateStatus(message) {821 if (narratorOption === 'true') {
822 const statusElement = document.getElementById('status_info');822 ttsPassAsterisksCheckbox.checked = false;
823 if (statusElement) {823 $('#tts_pass_asterisks').click();
824 statusElement.textContent = message;824 $('#tts_pass_asterisks').trigger('change');
825 switch (message) {825 ttsNarrateQuotedCheckbox.checked = true;
826 case 'Offline':826 ttsNarrateDialoguesCheckbox.checked = true;
827 statusElement.style.color = 'red';827 $('#tts_narrate_quoted').click();
828 break;828 $('#tts_narrate_quoted').trigger('change');
829 case 'Ready':829 $('#tts_narrate_dialogues').click();
830 statusElement.style.color = 'lightgreen';830 $('#tts_narrate_dialogues').trigger('change');
831 break;831 } else if (narratorOption === 'silent') {
832 case 'Processing':832 ttsPassAsterisksCheckbox.checked = false;
833 statusElement.style.color = 'blue';833 $('#tts_pass_asterisks').click();
834 break;834 $('#tts_pass_asterisks').trigger('change');
835 case 'Error':835 } else {
836 statusElement.style.color = 'red';836 ttsPassAsterisksCheckbox.checked = true;
837 break;837 $('#tts_pass_asterisks').click();
838 }838 $('#tts_pass_asterisks').trigger('change');
839 }839 }
840}840
841 this.onSettingsChange();
842 });
843 }
844
845
846 // Event Listener for AT Generation Method Dropdown
847 const atGenerationMethodSelect = document.getElementById('at_generation_method');
848 const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
849 if (atGenerationMethodSelect) {
850 atGenerationMethodSelect.addEventListener('change', (event) => {
851 const selectedMethod = event.target.value;
852
853 if (selectedMethod === 'streaming_enabled') {
854 // Disable and unselect AT Narrator
855 atNarratorEnabledSelect.disabled = true;
856 atNarratorEnabledSelect.value = 'false';
857 textNotInsideSelect.disabled = true;
858 narratorVoiceSelect.disabled = true;
859 } else if (selectedMethod === 'standard_generation') {
860 // Enable AT Narrator
861 atNarratorEnabledSelect.disabled = false;
862 }
863 this.settings.at_generation_method = selectedMethod; // Update the setting here
864 this.onSettingsChange(); // Save the settings after change
865 });
866 }
867
868 // Listener for Language Dropdown
869 const languageSelect = document.getElementById('language_options');
870 if (languageSelect) {
871 languageSelect.addEventListener('change', (event) => {
872 this.settings.language = event.target.value;
873 this.onSettingsChange(); // Save the settings after change
874 });
875 }
876
877 // Listener for AllTalk Endpoint Input
878 const atServerInput = document.getElementById('at_server');
879 if (atServerInput) {
880 atServerInput.addEventListener('input', (event) => {
881 this.settings.provider_endpoint = event.target.value;
882 this.onSettingsChange(); // Save the settings after change
883 });
884 }
885
886 }
887
888 //#############################//
889 // Store ST interface settings //
890 //#############################//
891
892 onSettingsChange() {
893 // Update settings based on the UI elements
894 //this.settings.provider_endpoint = $('#at_server').val();
895 this.settings.language = $('#language_options').val();
896 //this.settings.voiceMap = $('#voicemap').val();
897 this.settings.at_generation_method = $('#at_generation_method').val();
898 this.settings.narrator_enabled = $('#at_narrator_enabled').val();
899 this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();
900 this.settings.rvc_character_voice = $('#rvc_character_voice').val();
901 this.settings.rvc_narrator_voice = $('#rvc_narrator_voice').val();
902 this.settings.rvc_character_pitch = $('#rvc_character_pitch').val();
903 this.settings.rvc_narrator_pitch = $('#rvc_narrator_pitch').val();
904 this.settings.narrator_voice_gen = $('#narrator_voice').val();
905 // Save the updated settings
906 saveTtsProviderSettings();
907 }
908
909 //#########################//
910 // ST Handle Reload button //
911 //#########################//
912
913 async onRefreshClick() {
914 try {
915 updateStatus('Processing'); // Set status to Processing while refreshing
916 await this.checkReady(); // Check if the TTS provider is ready
917 await this.loadSettings(this.settings); // Reload the settings
918 await this.checkReady(); // Check if the TTS provider is ready
919 updateStatus(this.ready ? 'Ready' : 'Offline'); // Update the status based on readiness
920 } catch (error) {
921 console.error('Error during refresh:', error);
922 updateStatus('Error'); // Set status to Error in case of failure
923 }
924 }
925
926 //##################//
927 // Preview AT Voice //
928 //##################//
929
930 async previewTtsVoice(voiceName) {
931 try {
932 // Prepare data for POST request
933 const postData = new URLSearchParams();
934 postData.append("voice", `${voiceName}`);
935
936 // Add RVC parameters for V2 if applicable
937 if (this.settings.server_version === 'v2' && this.settings.rvc_character_voice !== 'Disabled') {
938 postData.append("rvccharacter_voice_gen", this.settings.rvc_character_voice);
939 postData.append("rvccharacter_pitch", this.settings.rvc_character_pitch || "0");
940 }
941
942 // Making the POST request
943 const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
944 method: "POST",
945 headers: {
946 'Content-Type': 'application/x-www-form-urlencoded'
947 },
948 body: postData,
949 });
950
951 if (!response.ok) {
952 const errorText = await response.text();
953 console.error(`[previewTtsVoice] Error Response Text:`, errorText);
954 throw new Error(`HTTP ${response.status}: ${errorText}`);
955 }
956
957 const data = await response.json();
958 if (data.output_file_url) {
959 // Handle V1/V2 URL differences
960 const fullUrl = this.settings.server_version === 'v1'
961 ? data.output_file_url
962 : `${this.settings.provider_endpoint}${data.output_file_url}`;
963
964 const audioElement = new Audio(fullUrl);
965 audioElement.play().catch(e => console.error("Error playing audio:", e));
966 } else {
967 console.warn("[previewTtsVoice] No output file URL received in the response");
968 throw new Error("No output file URL received in the response");
969 }
970 } catch (error) {
971 console.error("[previewTtsVoice] Exception caught during preview generation:", error);
972 throw error;
973 }
974 }
975
976 //#####################//
977 // Populate ST voices //
978 //#####################//
979
980 async getVoice(voiceName, generatePreview = false) {
981 // Ensure this.voices is populated
982 if (this.voices.length === 0) {
983 // Fetch voice objects logic
984 }
985 // Find the object where the name matches voiceName
986 const match = this.voices.find(voice => voice.name === voiceName);
987 if (!match) {
988 // Error handling
989 }
990 // Generate preview URL only if requested
991 if (!match.preview_url && generatePreview) {
992 // Generate preview logic
993 }
994 return match; // Return the found voice object
995 }
996
997 //##########################################//
998 // Generate TTS Streaming or call Standard //
999 //##########################################//
1000
1001 async generateTts(inputText, voiceId) {
1002 try {
1003 if (this.settings.at_generation_method === 'streaming_enabled') {
1004 // 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`;
1006 console.log("Streaming URL:", streamingUrl);
1007
1008 // Return the streaming URL directly
1009 return streamingUrl;
1010 } else {
1011 // For standard method
1012 const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
1013 const audioResponse = await fetch(outputUrl);
1014 if (!audioResponse.ok) {
1015 throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
1016 }
1017 return audioResponse; // Return the fetch response directly
1018 }
1019 } catch (error) {
1020 console.error("Error in generateTts:", error);
1021 throw error;
1022 }
1023 }
1024
1025
1026 //####################//
1027 // Generate Standard //
1028 //####################//
1029
1030 async fetchTtsGeneration(inputText, voiceId) {
1031 const requestBody = new URLSearchParams({
1032 'text_input': inputText,
1033 'text_filtering': "standard",
1034 'character_voice_gen': voiceId,
1035 'narrator_enabled': this.settings.narrator_enabled,
1036 'narrator_voice_gen': this.settings.narrator_voice_gen,
1037 'text_not_inside': this.settings.at_narrator_text_not_inside,
1038 'language': this.settings.language,
1039 'output_file_name': "st_output",
1040 'output_file_timestamp': "true",
1041 'autoplay': "false",
1042 'autoplay_volume': "0.8"
1043 });
1044
1045 // Add RVC parameters only for V2
1046 if (this.settings.server_version === 'v2') {
1047 if (this.settings.rvc_character_voice !== 'Disabled') {
1048 requestBody.append('rvccharacter_voice_gen', this.settings.rvc_character_voice);
1049 requestBody.append('rvccharacter_pitch', this.settings.rvc_character_pitch || "0");
1050 }
1051 if (this.settings.rvc_narrator_voice !== 'Disabled') {
1052 requestBody.append('rvcnarrator_voice_gen', this.settings.rvc_narrator_voice);
1053 requestBody.append('rvcnarrator_pitch', this.settings.rvc_narrator_pitch || "0");
1054 }
1055 }
1056
1057 try {
1058 const response = await doExtrasFetch(
1059 `${this.settings.provider_endpoint}/api/tts-generate`,
1060 {
1061 method: 'POST',
1062 headers: {
1063 'Content-Type': 'application/x-www-form-urlencoded',
1064 'Cache-Control': 'no-cache',
1065 },
1066 body: requestBody
1067 }
1068 );
1069
1070 if (!response.ok) {
1071 const errorText = await response.text();
1072 console.error(`[fetchTtsGeneration] Error Response Text:`, errorText);
1073 throw new Error(`HTTP ${response.status}: ${errorText}`);
1074 }
1075 const data = await response.json();
1076
1077 // Handle V1/V2 URL differences
1078 const outputUrl = this.settings.server_version === 'v1'
1079 ? data.output_file_url // V1 returns full URL
1080 : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path
1081
1082 return outputUrl;
1083 } catch (error) {
1084 console.error("[fetchTtsGeneration] Exception caught:", error);
1085 throw error;
1086 }
1087 }
1088}
1089
1090//#########################//
1091// Update Status Messages //
1092//#########################//
1093
1094function updateStatus(message) {
1095 const statusElement = document.getElementById('status_info');
1096 if (statusElement) {
1097 statusElement.textContent = message;
1098 switch (message) {
1099 case 'Offline':
1100 statusElement.style.color = 'red';
1101 break;
1102 case 'Ready':
1103 statusElement.style.color = 'lightgreen';
1104 break;
1105 case 'Processing':
1106 statusElement.style.color = 'blue';
1107 break;
1108 case 'Error':
1109 statusElement.style.color = 'red';
1110 break;
1111 }
1112 }
1113}