Merge pull request #3060 from erew123/release Support for AllTalk V1 and V2

85d25a8e1372d5b5c47d0c45138d81019923b622

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +1072 -840Ignore whitespace
public/scripts/extensions/tts/alltalk.js+1072 -840
@@ -1,840 +1,1072 @@
11import { doExtrasFetch } from '../../extensions.js';
22import { saveTtsProviderSettingsdebounce } from '../index../utils.js';
3-
3+import { saveTtsProviderSettings } from './index.js';
4-export { AllTalkTtsProvider };
4+
5-
5+export { AllTalkTtsProvider };
6-class AllTalkTtsProvider {
6+
7- //########//
7+class AllTalkTtsProvider {
8- // Config //
8+ //########//
9- //########//
9+ // Config //
10-
10+ //########//
11- settings = {};
11+
12- constructor() {
12+ settings = {};
13- // Initialize with default settings if they are not already set
13+ constructor() {
14- this.settings = {
14+ // Initialize with default settings if they are not already set
15- provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
15+ this.settings = {
1616 languageprovider_endpoint: this.settings.languageprovider_endpoint || 'enhttp://localhost:7851',
1717 voiceMapserver_version: this.settings.voiceMapserver_version || {}'v2',
1818 at_generation_methodlanguage: this.settings.at_generation_methodlanguage || 'standard_generationen',
1919 narrator_enabledvoiceMap: this.settings.narrator_enabledvoiceMap || 'false'{},
2020 at_narrator_text_not_insideat_generation_method: this.settings.at_narrator_text_not_insideat_generation_method || 'narratorstandard_generation',
2121 narrator_voice_gennarrator_enabled: this.settings.narrator_voice_gennarrator_enabled || 'female_01.wavfalse',
2222 finetuned_modelat_narrator_text_not_inside: this.settings.finetuned_modelat_narrator_text_not_inside || 'falsenarrator',
23- };
23+ narrator_voice_gen: this.settings.narrator_voice_gen || 'Please set a voice',
24- // Separate property for dynamically updated settings from the server
24+ rvc_character_voice: this.settings.rvc_character_voice || 'Disabled',
25- this.dynamicSettings = {
25+ rvc_character_pitch: this.settings.rvc_character_pitch || '0',
26- modelsAvailable: [],
26+ rvc_narrator_voice: this.settings.rvc_narrator_voice || 'Disabled',
27- currentModel: '',
27+ rvc_narrator_pitch: this.settings.rvc_narrator_pitch || '0',
28- deepspeed_available: false,
28+ finetuned_model: this.settings.finetuned_model || 'false',
29- deepSpeedEnabled: false,
29+ };
30- lowVramEnabled: false,
30+ // Separate property for dynamically updated settings from the server
31- };
31+ this.dynamicSettings = {
32- }
32+ modelsAvailable: [],
33- ready = false;
33+ currentModel: '',
34- voices = [];
34+ deepspeed_available: false,
35- separator = '. ';
35+ deepspeed_enabled: false,
36- audioElement = document.createElement('audio');
36+ lowvram_capable: false,
37-
37+ lowvram_enabled: false,
38- languageLabels = {
38+ };
39- 'Arabic': 'ar',
39+ this.rvcVoices = []; // Initialize rvcVoices as an empty array
40- 'Brazilian Portuguese': 'pt',
40+ }
41- 'Chinese': 'zh-cn',
41+ ready = false;
42- 'Czech': 'cs',
42+ voices = [];
43- 'Dutch': 'nl',
43+ separator = '. ';
44- 'English': 'en',
44+ audioElement = document.createElement('audio');
45- 'French': 'fr',
45+
46- 'German': 'de',
46+ languageLabels = {
4747 'ItalianArabic': 'itar',
4848 'PolishBrazilian Portuguese': 'plpt',
4949 'RussianChinese': 'ruzh-cn',
5050 'SpanishCzech': 'escs',
5151 'TurkishDutch': 'trnl',
5252 'JapaneseEnglish': 'jaen',
5353 'KoreanFrench': 'kofr',
5454 'HungarianGerman': 'hude',
5555 'HindiItalian': 'hiit',
56- };
56+ 'Polish': 'pl',
57-
57+ 'Russian': 'ru',
58- get settingsHtml() {
58+ 'Spanish': 'es',
59- let html = '<div class="at-settings-separator">AllTalk Settings</div>';
59+ 'Turkish': 'tr',
60-
60+ 'Japanese': 'ja',
61- html += `<div class="at-settings-row">
61+ 'Korean': 'ko',
62-
62+ 'Hungarian': 'hu',
63- <div class="at-settings-option">
63+ 'Hindi': 'hi',
64- <label for="at_generation_method">AllTalk TTS Generation Method</label>
64+ };
65- <select id="at_generation_method">
65+
66- <option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
66+ get settingsHtml() {
67- <option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>
67+ // HTML template literals can trigger ESLint quotes warnings when quotes are used in HTML attributes.
68- </select>
68+ // Disabling quotes rule for this one line as it's a false positive with HTML template literals.
69- </div>
69+ // eslint-disable-next-line quotes
70- </div>`;
70+ let html = `<div class="at-settings-separator">AllTalk V2 Settings</div>`;
71-
71+
7272 html += `<div class="'at-settings-row"'>
73-
73+ <div class='at-settings-option'>
74- <div class="at-settings-option">
74+ <label for='at_generation_method'>AllTalk TTS Generation Method</label>
75- <label for="at_narrator_enabled">AT Narrator</label>
75+ <select id='at_generation_method'>
76- <select id="at_narrator_enabled">
76+ <option value='standard_generation'>Standard Audio Generation (AT Narrator - Optional)</option>
77- <option value="true">Enabled</option>
77+ <option value='streaming_enabled'>Streaming Audio Generation (AT Narrator - Disabled)</option>
78- <option value="false">Disabled</option>
78+ </select>
7979 </selectdiv>
8080 </div>`;
81-
81+
8282 html += `<div class="'at-settings-option"row'>
83- <label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
83+ <div class='at-settings-option'>
84- <select id="at_narrator_text_not_inside">
84+ <label for='at_narrator_enabled'>AT Narrator</label>
85- <option value="narrator">Narrator</option>
85+ <select id='at_narrator_enabled'>
8686 <option value="character"'true'>CharacterEnabled</option>
87- </select>
87+ <option value='silent'>Enabled (Silenced)</option>
88- </div>
88+ <option value='false'>Disabled</option>
89-
89+ </select>
9090 </div>`;
91-
91+
9292 html += `<div class="'at-settings-row"option'>
93- <div class="at-settings-option">
93+ <label for='at_narrator_text_not_inside'>Text Not Inside * or " is</label>
94- <label for="narrator_voice">Narrator Voice</label>
94+ <select id='at_narrator_text_not_inside'>
95- <select id="narrator_voice">`;
95+ <option value='character'>Character</option>
96- if (this.voices) {
96+ <option value='narrator'>Narrator</option>
97- for (let voice of this.voices) {
97+ <option value='silent'>Silent</option>
98- html += `<option value="${voice.voice_id}">${voice.name}</option>`;
98+ </select>
99- }
99+ </div>
100- }
100+ </div>`;
101- html += `</select>
101+
102- </div>
102+ html += `<div class='at-settings-row'>
103103 <div class="'at-settings-option"'>
104104 <label for="language_options"'narrator_voice'>LanguageNarrator Voice</label>
105105 <select id="language_options"'narrator_voice'>`;
106106 forif (let language in this.languageLabelsvoices) {
107- html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
107+ for (let voice of this.voices) {
108- }
108+ html += `<option value='${voice.voice_id}'>${voice.name}</option>`;
109- html += `</select>
109+ }
110- </div>
110+ }
111- </div>`;
111+ html += `</select>
112-
112+ </div>
113-
113+ <div class='at-settings-option'>
114- html += `<div class="at-model-endpoint-row">
114+ <label for='language_options'>Language</label>
115- <div class="at-model-option">
115+ <select id='language_options'>`;
116- <label for="switch_model">Switch Model</label>
116+ for (let language in this.languageLabels) {
117- <select id="switch_model">
117+ html += `<option value='${this.languageLabels[language]}' ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
118- <option value="api_tts">API TTS</option>
118+ }
119- <option value="api_local">API Local</option>
119+ html += `</select>
120- <option value="xttsv2_local">XTTSv2 Local</option>
120+ </div>
121- </select>
121+ </div>`;
122- </div>
122+
123- </div>
123+ html += `<div class='at-settings-row'>
124-
124+ <div class='at-settings-option'>
125- <div class="at-model-endpoint-row">
125+ <label for='rvc_character_voice'>RVC Character</label>
126-
126+ <select id='rvc_character_voice'>`;
127- <div class="at-endpoint-option">
127+ if (this.rvcVoices) {
128- <label for="at_server">AllTalk Endpoint:</label>
128+ for (let rvccharvoice of this.rvcVoices) {
129- <input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>
129+ html += `<option value='${rvccharvoice.voice_id}'>${rvccharvoice.name}</option>`;
130- <i><b>Important:</b> Must match IP address & port in AllTalk settings.</i>
130+ }
131- </div>
131+ }
132- </div>`;
132+ html += `</select>
133-
133+ </div>
134-
134+ <div class='at-settings-option'>
135- html += `<div class="at-model-endpoint-row">
135+ <label for='rvc_narrator_voice'>RVC Narrator</label>
136- <div class="at-settings-option">
136+ <select id='rvc_narrator_voice'>`;
137- <label for="low_vram">Low VRAM</label>
137+ if (this.rvcVoices) {
138- <input id="low_vram" type="checkbox"/>
138+ for (let rvcnarrvoice of this.rvcVoices) {
139- </div>
139+ html += `<option value='${rvcnarrvoice.voice_id}'>${rvcnarrvoice.name}</option>`;
140- <div class="at-settings-option">
140+ }
141- <label for="deepspeed">DeepSpeed</label>
141+ }
142- <input id="deepspeed" type="checkbox"/>
142+ html += `</select>
143143 </div>
144- <div class="at-settings-option status-option">
144+</div>`;
145- <span>Status: <span id="status_info">Ready</span></span>
145+
146- </div>
146+ html += `<div class='at-settings-row'>
147147 <div class="'at-settings-option empty-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++) {
151-
151+ const selected = i === 0 ? 'selected="selected"' : '';
152-
152+ 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'>
157-
157+ <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>`;
162-
162+ }
163163 html += `<div class="at-website-row"/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'>
168-
168+ <div class='at-model-option'>
169- return html;
169+ <label for='switch_model'>Switch Model</label>
170- }
170+ <select id='switch_model'>
171-
171+ <!-- Options will be dynamically populated -->
172-
172+ </select>
173- //#################//
173+ </div>
174- // Startup ST & AT //
174+
175- //#################//
175+ <div class='at-endpoint-option'>
176-
176+ <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>
179-
179+ </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 settings
183+ <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'>
192-
192+ <div class='at-settings-option'>
193- // Update UI elements to reflect the loaded settings
193+ <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'>
201-
201+ <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 -->
205-
205+ </div>
206- async initEndpoint() {
206+</div>`;
207- try {
207+
208- // Check if TTS provider is ready
208+ html += `<div class='at-website-row'>
209- this.setupEventListeners();
209+ <div class='at-website-option'>
210- this.updateLanguageDropdown();
210+ <span>AllTalk V2<a target='_blank' href='${this.settings.provider_endpoint}'>Config & Docs</a>.</span>
211- await this.checkReady();
211+ </div>
212- await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
212+
213- await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
213+ <div class='at-website-option'>
214- this.updateNarratorVoicesDropdown();
214+ <span>AllTalk <a target='_blank' href='https://github.com/erew123/alltalk_tts/'>Website</a>.</span>
215- this.applySettingsToHTML();
215+ </div>
216- updateStatus('Ready');
216+</div>`;
217- } catch (error) {
217+
218- console.error('Error loading settings:', error);
218+ html += `<div class='at-website-row'>
219- updateStatus('Offline');
219+<div class='at-website-option'>
220- }
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>
221- }
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>
222-
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>
223- applySettingsToHTML() {
223+</div>
224- // Apply loaded settings or use defaults
224+</div>`;
225- const narratorVoiceSelect = document.getElementById('narrator_voice');
225+
226- const atNarratorSelect = document.getElementById('at_narrator_enabled');
226+ return html;
227- const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
227+ }
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 dropdown
230+ //#################//
231- if (narratorVoiceSelect && this.settings.narrator_voice) {
231+ // Startup ST & AT //
232- narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', '');
232+ //#################//
233- }
233+
234- // Apply settings to AT Narrator Enabled dropdown
234+ async loadSettings(settings) {
235- if (atNarratorSelect) {
235+ updateStatus('Offline');
236- // Sync the state with the checkbox in index.js
236+
237- const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
237+ if (Object.keys(settings).length === 0) {
238- const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
238+ console.info('Using default AllTalk TTS Provider settings');
239- const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
239+ } else {
240240 // Sync thePopulate statesettings with theprovided checkboxvalues, inignoring index.jsserver-provided settings
241241 iffor (this.const key in settings.narrator_enabled) {
242- ttsPassAsterisksCheckbox.checked = false;
242+ if (key in this.settings) {
243- $('#tts_pass_asterisks').click(); // Simulate a click event
243+ this.settings[key] = settings[key];
244- $('#tts_pass_asterisks').trigger('change');
244+ } else {
245- }
245+ console.debug(`Ignoring non-user-configurable setting: ${key}`);
246- if (!this.settings.narrator_enabled) {
246+ }
247- ttsPassAsterisksCheckbox.checked = true;
247+ }
248- $('#tts_pass_asterisks').click(); // Simulate a click event
248+ }
249- $('#tts_pass_asterisks').trigger('change');
249+
250- }
250+ // Update UI elements to reflect the loaded settings
251- // Uncheck and set tts_narrate_quoted to false if narrator is enabled
251+ $('#at_server').val(this.settings.provider_endpoint);
252- if (this.settings.narrator_enabledd) {
252+ $('#language_options').val(this.settings.language);
253- ttsNarrateQuotedCheckbox.checked = true;
253+ $('#at_generation_method').val(this.settings.at_generation_method);
254- ttsNarrateDialoguesCheckbox.checked = true;
254+ $('#at_narrator_enabled').val(this.settings.narrator_enabled);
255- // Trigger click events instead of change events
255+ $('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);
256256 $('#tts_narrate_quotednarrator_voice').clickval(this.settings.narrator_voice_gen);
257257 $('#tts_narrate_quotedrvc_character_voice').triggerval('change'this.settings.rvc_character_voice);
258258 $('#tts_narrate_dialoguesrvc_narrator_voice').clickval(this.settings.rvc_narrator_voice);
259259 $('#tts_narrate_dialoguesrvc_character_pitch').triggerval('change'this.settings.rvc_character_pitch);
260- }
260+ $('#rvc_narrator_pitch').val(this.settings.rvc_narrator_pitch);
261- atNarratorSelect.value = this.settings.narrator_enabled.toString();
261+
262- this.settings.narrator_enabled = this.settings.narrator_enabled.toString();
262+ console.debug('AllTalkTTS: Settings loaded');
263- }
263+ try {
264264 // ApplyCheck settingsif toTTS theprovider Languageis dropdownready
265- const languageSelect = document.getElementById('language_options');
265+ await this.checkReady();
266- if (languageSelect && this.settings.language) {
266+ await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
267- languageSelect.value = this.settings.language;
267+ await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
268- }
268+ await this.fetchRvcVoiceObjects(); // Fetch RVC voices
269- // Apply settings to Text Not Inside dropdown
269+ this.updateNarratorVoicesDropdown();
270- if (textNotInsideSelect && this.settings.text_not_inside) {
270+ this.updateLanguageDropdown();
271- textNotInsideSelect.value = this.settings.text_not_inside;
271+ this.setupEventListeners();
272- this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
272+ this.applySettingsToHTML();
273- }
273+ updateStatus('Ready');
274- // Apply settings to Generation Method dropdown
274+ } catch (error) {
275- if (generationMethodSelect && this.settings.at_generation_method) {
275+ console.error('Error loading settings:', error);
276- generationMethodSelect.value = this.settings.at_generation_method;
276+ updateStatus('Offline');
277277 }
278- // Additional logic to disable/enable dropdowns based on the selected generation method
278+ }
279- const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
279+
280- if (isStreamingEnabled) {
280+
281- // Disable certain dropdowns when streaming is enabled
281+ applySettingsToHTML() {
282- if (atNarratorSelect) atNarratorSelect.disabled = true;
282+ const narratorVoiceSelect = document.getElementById('narrator_voice');
283- if (textNotInsideSelect) textNotInsideSelect.disabled = true;
283+ const atNarratorSelect = document.getElementById('at_narrator_enabled');
284- if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;
284+ const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
285- } else {
285+ const generationMethodSelect = document.getElementById('at_generation_method');
286- // Enable dropdowns for standard generation
286+ this.settings.narrator_voice = this.settings.narrator_voice_gen;
287- if (atNarratorSelect) atNarratorSelect.disabled = false;
287+ // Apply settings to Narrator Voice dropdown
288- if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;
288+ if (narratorVoiceSelect && this.settings.narrator_voice) {
289289 if (narratorVoiceSelect) narratorVoiceSelect.disabledvalue = !this.settings.narrator_enablednarrator_voice; // Remove the parentheses
290290 }
291- const modelSelect = document.getElementById('switch_model');
291+ // Apply settings to AT Narrator Enabled dropdown
292- if (this.settings.finetuned_model === 'true') {
292+ if (atNarratorSelect) {
293293 const ftOptionttsPassAsterisksCheckbox = document.createElementgetElementById('optiontts_pass_asterisks');
294- ftOption.value = 'XTTSv2 FT';
294+ const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted');
295- ftOption.textContent = 'XTTSv2 FT';
295+ const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues');
296- modelSelect.appendChild(ftOption);
296+ if (this.settings.narrator_enabled) {
297- }
297+ ttsPassAsterisksCheckbox.checked = false;
298- }
298+ $('#tts_pass_asterisks').click();
299-
299+ $('#tts_pass_asterisks').trigger('change');
300- //##############################//
300+ }
301- // Check AT Server is Available //
301+ if (!this.settings.narrator_enabled) {
302- //##############################//
302+ ttsPassAsterisksCheckbox.checked = true;
303-
303+ $('#tts_pass_asterisks').click();
304- async checkReady() {
304+ $('#tts_pass_asterisks').trigger('change');
305- try {
305+ }
306- const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);
306+ if (this.settings.narrator_enabled) {
307- // Check if the HTTP request was successful
307+ ttsNarrateQuotedCheckbox.checked = true;
308- if (!response.ok) {
308+ ttsNarrateDialoguesCheckbox.checked = true;
309- throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
309+ $('#tts_narrate_quoted').click();
310- }
310+ $('#tts_narrate_quoted').trigger('change');
311- const statusText = await response.text();
311+ $('#tts_narrate_dialogues').click();
312- // Check if the response is 'Ready'
312+ $('#tts_narrate_dialogues').trigger('change');
313- if (statusText === 'Ready') {
313+ }
314- this.ready = true; // Set the ready flag to true
314+ atNarratorSelect.value = this.settings.narrator_enabled.toString();
315- console.log('TTS service is ready.');
315+ this.settings.narrator_enabled = this.settings.narrator_enabled.toString();
316- } else {
316+ }
317- this.ready = false;
317+ const languageSelect = document.getElementById('language_options');
318- console.log('TTS service is not ready.');
318+ if (languageSelect && this.settings.language) {
319- }
319+ languageSelect.value = this.settings.language;
320- } catch (error) {
320+ }
321- console.error('Error checking TTS service readiness:', error);
321+ if (textNotInsideSelect && this.settings.text_not_inside) {
322- this.ready = false; // Ensure ready flag is set to false in case of error
322+ textNotInsideSelect.value = this.settings.text_not_inside;
323- throw error; // Rethrow the error for further handling
323+ this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
324324 }
325- }
325+ if (generationMethodSelect && this.settings.at_generation_method) {
326-
326+ generationMethodSelect.value = this.settings.at_generation_method;
327- //######################//
327+ }
328- // Get Available Voices //
328+ const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
329- //######################//
329+ if (isStreamingEnabled) {
330-
330+ if (atNarratorSelect) atNarratorSelect.disabled = true;
331- async fetchTtsVoiceObjects() {
331+ if (textNotInsideSelect) textNotInsideSelect.disabled = true;
332- const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);
332+ if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;
333- if (!response.ok) {
333+ } else {
334- const errorText = await response.text();
334+ if (atNarratorSelect) atNarratorSelect.disabled = false;
335- throw new Error(`HTTP ${response.status}: ${errorText}`);
335+ if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;
336- }
336+ if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;
337- const data = await response.json();
337+ }
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+ // Check AT Server is Available //
343- preview_url: null, // Preview URL will be dynamically generated
343+ //##############################//
344- lang: 'en', // Default language
344+
345- };
345+ async checkReady() {
346- });
346+ try {
347- this.voices = voices; // Assign to the class property
347+ const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);
348- return voices; // Also return this list
348+ // Check if the HTTP request was successful
349- }
349+ if (!response.ok) {
350-
350+ throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
351- //##########################################//
351+ }
352- // Get Current AT Server Config & Update ST //
352+ const statusText = await response.text();
353- //##########################################//
353+ // Check if the response is 'Ready'
354-
354+ if (statusText === 'Ready') {
355- async updateSettingsFromServer() {
355+ this.ready = true; // Set the ready flag to true
356- try {
356+ console.log('TTS service is ready.');
357- // Fetch current settings
357+ } else {
358- const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);
358+ this.ready = false;
359- if (!response.ok) {
359+ console.log('TTS service is not ready.');
360- throw new Error(`Failed to fetch current settings: ${response.statusText}`);
360+ }
361- }
361+ } catch (error) {
362- const currentSettings = await response.json();
362+ console.error('Error checking TTS service readiness:', error);
363- // Update internal settings
363+ this.ready = false; // Ensure ready flag is set to false in case of error
364- this.settings.modelsAvailable = currentSettings.models_available;
364+ }
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+ // Get Available Voices //
369- this.settings.finetuned_model = currentSettings.finetuned_model;
369+ //######################//
370- // Update HTML elements
370+
371- this.updateModelDropdown();
371+ async fetchTtsVoiceObjects() {
372- this.updateCheckboxes();
372+ const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);
373373 } catchif (error!response.ok) {
374- console.error(`Error updating settings from server: ${error}`);
374+ const errorText = await response.text();
375- }
375+ throw new Error(`HTTP ${response.status}: ${errorText}`);
376- }
376+ }
377-
377+ const data = await response.json();
378- //###################################################//
378+ const voices = data.voices.map(filename => {
379- // Get Current AT Server Config & Update ST (Models) //
379+ return {
380- //###################################################//
380+ name: filename,
381-
381+ voice_id: filename,
382- updateModelDropdown() {
382+ preview_url: null, // Preview URL will be dynamically generated
383- const modelSelect = document.getElementById('switch_model');
383+ lang: 'en', // Default language
384- if (modelSelect) {
384+ };
385- modelSelect.innerHTML = ''; // Clear existing options
385+ });
386- this.settings.modelsAvailable.forEach(model => {
386+ this.voices = voices; // Assign to the class property
387- const option = document.createElement('option');
387+ return voices; // Also return this list
388- option.value = model.model_name;
388+ }
389- option.textContent = model.model_name; // Use model_name instead of name
389+
390- option.selected = model.model_name === this.settings.currentModel;
390+ async fetchRvcVoiceObjects() {
391- modelSelect.appendChild(option);
391+ if (this.settings.server_version !== 'v2') {
392- });
392+ console.log('Skipping RVC voices fetch for V1 server');
393- }
393+ return [];
394- }
394+ }
395-
395+
396- //#######################################################//
396+ console.log('Fetching RVC Voices');
397- // Get Current AT Server Config & Update ST (DS and LVR) //
397+ try {
398- //#######################################################//
398+ const response = await fetch(`${this.settings.provider_endpoint}/api/rvcvoices`);
399-
399+ if (!response.ok) {
400- updateCheckboxes() {
400+ const errorText = await response.text();
401- const deepspeedCheckbox = document.getElementById('deepspeed');
401+ console.error('Error text:', errorText);
402- const lowVramCheckbox = document.getElementById('low_vram');
402+ throw new Error(`HTTP ${response.status}: ${errorText}`);
403- if (lowVramCheckbox) lowVramCheckbox.checked = this.settings.lowVramEnabled;
403+ }
404- if (deepspeedCheckbox) {
404+
405- deepspeedCheckbox.checked = this.settings.deepSpeedEnabled;
405+ const data = await response.json();
406- deepspeedCheckbox.disabled = !this.settings.deepspeed_available; // Disable checkbox if deepspeed is not available
406+ if (!data || !data.rvcvoices) {
407- }
407+ console.error('Invalid data format:', data);
408- }
408+ throw new Error('Invalid data format received from /api/rvcvoices');
409-
409+ }
410- //###############################################################//
410+
411- // Get Current AT Server Config & Update ST (AT Narrator Voices) //
411+ const voices = data.rvcvoices.map(filename => {
412- //###############################################################//
412+ return {
413-
413+ name: filename,
414- updateNarratorVoicesDropdown() {
414+ voice_id: filename,
415- const narratorVoiceSelect = document.getElementById('narrator_voice');
415+ };
416- if (narratorVoiceSelect && this.voices) {
416+ });
417- // Clear existing options
417+
418- narratorVoiceSelect.innerHTML = '';
418+ console.log('RVC voices:', voices);
419- // Add new options
419+ this.rvcVoices = voices; // Assign to the class property
420- for (let voice of this.voices) {
420+ this.updateRvcVoiceDropdowns(); // Update UI after fetching voices
421- const option = document.createElement('option');
421+ return voices; // Also return this list
422- option.value = voice.voice_id;
422+ } catch (error) {
423- option.textContent = voice.name;
423+ console.error('Error fetching RVC voices:', error);
424- narratorVoiceSelect.appendChild(option);
424+ this.rvcVoices = [{ name: 'Disabled', voice_id: 'Disabled' }]; // Set default on error
425- }
425+ throw error;
426- }
426+ } finally {
427- }
427+ // Ensure dropdowns are updated even if there was an error
428-
428+ this.updateRvcVoiceDropdowns();
429- //######################################################//
429+ }
430- // Get Current AT Server Config & Update ST (Languages) //
430+ }
431- //######################################################//
431+
432-
432+ //##########################################//
433- updateLanguageDropdown() {
433+ // Get Current AT Server Config & Update ST //
434- const languageSelect = document.getElementById('language_options');
434+ //##########################################//
435- if (languageSelect) {
435+
436- // Ensure default language is set (??? whatever that means)
436+ async updateSettingsFromServer() {
437- // this.settings.language = this.settings.language;
437+ try {
438-
438+ const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);
439- languageSelect.innerHTML = '';
439+ if (!response.ok) {
440- for (let language in this.languageLabels) {
440+ throw new Error(`Failed to fetch current settings: ${response.statusText}`);
441- const option = document.createElement('option');
441+ }
442- option.value = this.languageLabels[language];
442+ const currentSettings = await response.json();
443- option.textContent = language;
443+ currentSettings.models_available.sort((a, b) => a.name.localeCompare(b.name));
444- if (this.languageLabels[language] === this.settings.language) {
444+
445- option.selected = true;
445+ this.settings.enginesAvailable = currentSettings.engines_available;
446- }
446+ this.settings.currentEngineLoaded = currentSettings.current_engine_loaded;
447- languageSelect.appendChild(option);
447+ this.settings.modelsAvailable = currentSettings.models_available;
448- }
448+ this.settings.currentModel = currentSettings.current_model_loaded;
449- }
449+ this.settings.deepspeed_capable = currentSettings.deepspeed_capable;
450- }
450+ this.settings.deepspeed_available = currentSettings.deepspeed_available;
451-
451+ this.settings.deepspeed_enabled = currentSettings.deepspeed_enabled;
452- //########################################//
452+ this.settings.lowvram_capable = currentSettings.lowvram_capable;
453- // Start AT TTS extenstion page listeners //
453+ this.settings.lowvram_enabled = currentSettings.lowvram_enabled;
454- //########################################//
454+
455-
455+ await this.fetchRvcVoiceObjects(); // Fetch RVC voices
456- setupEventListeners() {
456+
457-
457+ this.updateModelDropdown();
458- let debounceTimeout;
458+ this.updateCheckboxes();
459- const debounceDelay = 500; // Milliseconds
459+ this.updateRvcVoiceDropdowns(); // Update the RVC voice dropdowns
460-
460+ } catch (error) {
461- // Define the event handler function
461+ console.error(`Error updating settings from server: ${error}`);
462- const onModelSelectChange = async (event) => {
462+ }
463- console.log('Model select change event triggered'); // Debugging statement
463+ }
464- const selectedModel = event.target.value;
464+
465- console.log(`Selected model: ${selectedModel}`); // Debugging statement
465+ updateRvcVoiceDropdowns() {
466- // Set status to Processing
466+ // Handle all RVC-related elements
467- updateStatus('Processing');
467+ const rvcElements = document.querySelectorAll('.rvc-setting');
468- try {
468+ const isV2 = this.settings.server_version === 'v2';
469- const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
469+
470- method: 'POST',
470+ rvcElements.forEach(element => {
471- });
471+ element.style.display = isV2 ? 'block' : 'none';
472- if (!response.ok) {
472+ });
473- throw new Error(`HTTP Error: ${response.status}`);
473+
474- }
474+ // Update and disable/enable character voice dropdown
475475 const datarvcCharacterVoiceSelect = await responsedocument.jsongetElementById('rvc_character_voice');
476- console.log('POST response data:', data); // Debugging statement
476+ if (rvcCharacterVoiceSelect) {
477- // Set status to Ready if successful
477+ rvcCharacterVoiceSelect.disabled = !isV2;
478- updateStatus('Ready');
478+ if (this.rvcVoices) {
479- } catch (error) {
479+ rvcCharacterVoiceSelect.innerHTML = '';
480- console.error('POST request error:', error); // Debugging statement
480+ for (let voice of this.rvcVoices) {
481- // Set status to Error in case of failure
481+ const option = document.createElement('option');
482- updateStatus('Error');
482+ option.value = voice.voice_id;
483- }
483+ option.textContent = voice.name;
484-
484+ if (voice.voice_id === this.settings.rvc_character_voice) {
485- // Handle response or error
485+ option.selected = true;
486- };
486+ }
487-
487+ rvcCharacterVoiceSelect.appendChild(option);
488- const debouncedModelSelectChange = (event) => {
488+ }
489- clearTimeout(debounceTimeout);
489+ }
490- debounceTimeout = setTimeout(() => {
490+ }
491- onModelSelectChange(event);
491+
492- }, debounceDelay);
492+ // Update and disable/enable narrator voice dropdown
493- };
493+ const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice');
494-
494+ if (rvcNarratorVoiceSelect) {
495- // Switch Model Listener
495+ rvcNarratorVoiceSelect.disabled = !isV2;
496- const modelSelect = document.getElementById('switch_model');
496+ if (this.rvcVoices) {
497- if (modelSelect) {
497+ rvcNarratorVoiceSelect.innerHTML = '';
498- // Remove the event listener if it was previously added
498+ for (let voice of this.rvcVoices) {
499- // Add the debounced event listener
499+ const option = document.createElement('option');
500- $(modelSelect).off('change').on('change', debouncedModelSelectChange);
500+ option.value = voice.voice_id;
501- }
501+ option.textContent = voice.name;
502-
502+ if (voice.voice_id === this.settings.rvc_narrator_voice) {
503- // DeepSpeed Listener
503+ option.selected = true;
504- const deepspeedCheckbox = document.getElementById('deepspeed');
504+ }
505- if (deepspeedCheckbox) {
505+ rvcNarratorVoiceSelect.appendChild(option);
506- $(deepspeedCheckbox).off('change').on('change', async (event) => {
506+ }
507- const deepSpeedValue = event.target.checked ? 'True' : 'False';
507+ }
508- // Set status to Processing
508+ }
509- updateStatus('Processing');
509+
510- try {
510+ // Update pitch inputs
511- const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
511+ const characterPitch = document.getElementById('rvc_character_pitch');
512- method: 'POST',
512+ if (characterPitch) {
513- });
513+ characterPitch.disabled = !isV2;
514- if (!response.ok) {
514+ }
515- throw new Error(`HTTP Error: ${response.status}`);
515+
516- }
516+ const narratorPitch = document.getElementById('rvc_narrator_pitch');
517- const data = await response.json();
517+ if (narratorPitch) {
518- console.log('POST response data:', data); // Debugging statement
518+ narratorPitch.disabled = !isV2;
519- // Set status to Ready if successful
519+ }
520- updateStatus('Ready');
520+ }
521- } catch (error) {
521+
522- console.error('POST request error:', error); // Debugging statement
522+ //###################################################//
523- // Set status to Error in case of failure
523+ // Get Current AT Server Config & Update ST (Models) //
524- updateStatus('Error');
524+ //###################################################//
525- }
525+
526- });
526+ updateModelDropdown() {
527- }
527+ const modelSelect = document.getElementById('switch_model');
528-
528+ if (modelSelect) {
529- // Low VRAM Listener
529+ modelSelect.innerHTML = ''; // Clear existing options
530- const lowVramCheckbox = document.getElementById('low_vram');
530+ this.settings.modelsAvailable.forEach(model => {
531- if (lowVramCheckbox) {
531+ const option = document.createElement('option');
532- $(lowVramCheckbox).off('change').on('change', async (event) => {
532+ option.value = model.name;
533- const lowVramValue = event.target.checked ? 'True' : 'False';
533+ option.textContent = model.name; // Use model name directly
534- // Set status to Processing
534+ option.selected = model.name === this.settings.currentModel;
535- updateStatus('Processing');
535+ modelSelect.appendChild(option);
536- try {
536+ });
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+ // Get Current AT Server Config & Update ST (DS and LVR) //
542- }
542+ //#######################################################//
543- const data = await response.json();
543+
544- console.log('POST response data:', data); // Debugging statement
544+ updateCheckboxes() {
545- // Set status to Ready if successful
545+ const deepspeedCheckbox = document.getElementById('deepspeed');
546- updateStatus('Ready');
546+ const lowVramCheckbox = document.getElementById('low_vram');
547- } catch (error) {
547+
548- console.error('POST request error:', error); // Debugging statement
548+ // Handle DeepSpeed checkbox
549- // Set status to Error in case of failure
549+ if (deepspeedCheckbox) {
550- updateStatus('Error');
550+ if (this.settings.deepspeed_capable) {
551- }
551+ // If TTS engine is capable of using DeepSpeed
552- });
552+ deepspeedCheckbox.disabled = !this.settings.deepspeed_available;
553- }
553+ this.settings.deepspeed_enabled = this.settings.deepspeed_available && this.settings.deepspeed_enabled;
554-
554+ } else {
555- // Narrator Voice Dropdown Listener
555+ // If TTS engine is NOT capable of using DeepSpeed
556- const narratorVoiceSelect = document.getElementById('narrator_voice');
556+ deepspeedCheckbox.disabled = true;
557- if (narratorVoiceSelect) {
557+ this.settings.deepspeed_enabled = false;
558- $(narratorVoiceSelect).off('change').on('change', (event) => {
558+ }
559- this.settings.narrator_voice_gen = `${event.target.value}.wav`;
559+ deepspeedCheckbox.checked = this.settings.deepspeed_enabled;
560- this.onSettingsChange(); // Save the settings after change
560+ }
561- });
561+
562- }
562+ // Handle Low VRAM checkbox
563-
563+ if (lowVramCheckbox) {
564- const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
564+ if (this.settings.lowvram_capable) {
565- if (textNotInsideSelect) {
565+ // If TTS engine is capable of low VRAM
566- $(textNotInsideSelect).off('change').on('change', (event) => {
566+ lowVramCheckbox.disabled = false;
567- this.settings.text_not_inside = event.target.value;
567+ } else {
568- this.onSettingsChange(); // Save the settings after change
568+ // If TTS engine is NOT capable of low VRAM
569- });
569+ lowVramCheckbox.disabled = true;
570- }
570+ this.settings.lowvram_enabled = false;
571-
571+ }
572- // AT Narrator Dropdown Listener
572+ lowVramCheckbox.checked = this.settings.lowvram_enabled;
573- const atNarratorSelect = document.getElementById('at_narrator_enabled');
573+ }
574- const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
574+ }
575- const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
575+
576- const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
576+
577-
577+ //###############################################################//
578- if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {
578+ // Get Current AT Server Config & Update ST (AT Narrator Voices) //
579- $(atNarratorSelect).off('change').on('change', (event) => {
579+ //###############################################################//
580- const isNarratorEnabled = event.target.value === 'true';
580+
581- this.settings.narrator_enabled = isNarratorEnabled; // Update the setting here
581+ updateNarratorVoicesDropdown() {
582- textNotInsideSelect.disabled = !isNarratorEnabled;
582+ const narratorVoiceSelect = document.getElementById('narrator_voice');
583- narratorVoiceSelect.disabled = !isNarratorEnabled;
583+ if (narratorVoiceSelect && this.voices) {
584-
584+ // Clear existing options
585- // Sync the state with the checkbox in index.js
585+ narratorVoiceSelect.innerHTML = '';
586- if (isNarratorEnabled) {
586+ // Add new options
587- ttsPassAsterisksCheckbox.checked = false;
587+ for (let voice of this.voices) {
588- $('#tts_pass_asterisks').click(); // Simulate a click event
588+ const option = document.createElement('option');
589- $('#tts_pass_asterisks').trigger('change');
589+ option.value = voice.voice_id;
590- }
590+ option.textContent = voice.name;
591- if (!isNarratorEnabled) {
591+ narratorVoiceSelect.appendChild(option);
592- ttsPassAsterisksCheckbox.checked = true;
592+ }
593- $('#tts_pass_asterisks').click(); // Simulate a click event
593+ }
594- $('#tts_pass_asterisks').trigger('change');
594+ }
595- }
595+
596- // Uncheck and set tts_narrate_quoted to false if narrator is enabled
596+ //######################################################//
597- if (isNarratorEnabled) {
597+ // Get Current AT Server Config & Update ST (Languages) //
598- ttsNarrateQuotedCheckbox.checked = true;
598+ //######################################################//
599- ttsNarrateDialoguesCheckbox.checked = true;
599+
600- // Trigger click events instead of change events
600+ updateLanguageDropdown() {
601- $('#tts_narrate_quoted').click();
601+ const languageSelect = document.getElementById('language_options');
602- $('#tts_narrate_quoted').trigger('change');
602+ if (languageSelect) {
603- $('#tts_narrate_dialogues').click();
603+ // Ensure default language is set
604- $('#tts_narrate_dialogues').trigger('change');
604+ this.settings.language = this.settings.language || 'en';
605- }
605+
606- this.onSettingsChange(); // Save the settings after change
606+ languageSelect.innerHTML = '';
607- });
607+ for (let language in this.languageLabels) {
608- }
608+ const option = document.createElement('option');
609-
609+ option.value = this.languageLabels[language];
610-
610+ option.textContent = language;
611- // Event Listener for AT Generation Method Dropdown
611+ if (this.languageLabels[language] === this.settings.language) {
612- const atGenerationMethodSelect = document.getElementById('at_generation_method');
612+ option.selected = true;
613- const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
613+ }
614- if (atGenerationMethodSelect) {
614+ languageSelect.appendChild(option);
615- $(atGenerationMethodSelect).off('change').on('change', (event) => {
615+ }
616- const selectedMethod = event.target.value;
616+ }
617-
617+ }
618- if (selectedMethod === 'streaming_enabled') {
618+
619- // Disable and unselect AT Narrator
619+ //########################################//
620- atNarratorEnabledSelect.disabled = true;
620+ // Start AT TTS extenstion page listeners //
621- atNarratorEnabledSelect.value = 'false';
621+ //########################################//
622- textNotInsideSelect.disabled = true;
622+
623- narratorVoiceSelect.disabled = true;
623+ setupEventListeners() {
624- } else if (selectedMethod === 'standard_generation') {
624+ // Define the event handler function
625- // Enable AT Narrator
625+ const onModelSelectChange = async (event) => {
626- atNarratorEnabledSelect.disabled = false;
626+ console.log('Model select change event triggered');
627- }
627+ const selectedModel = event.target.value;
628- this.settings.at_generation_method = selectedMethod; // Update the setting here
628+ console.log(`Selected model: ${selectedModel}`);
629- this.onSettingsChange(); // Save the settings after change
629+ updateStatus('Processing');
630- });
630+ try {
631- }
631+ const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
632-
632+ method: 'POST',
633- // Listener for Language Dropdown
633+ });
634- const languageSelect = document.getElementById('language_options');
634+ if (!response.ok) {
635- if (languageSelect) {
635+ throw new Error(`HTTP Error: ${response.status}`);
636- $(languageSelect).off('change').on('change', (event) => {
636+ }
637- this.settings.language = event.target.value;
637+ const data = await response.json();
638- this.onSettingsChange(); // Save the settings after change
638+ console.log('POST response data:', data);
639- });
639+ updateStatus('Ready');
640- }
640+ } catch (error) {
641-
641+ console.error('POST request error:', error);
642- // Listener for AllTalk Endpoint Input
642+ updateStatus('Error');
643- const atServerInput = document.getElementById('at_server');
643+ }
644- if (atServerInput) {
644+ };
645- $(atServerInput).off('input').on('input', (event) => {
645+
646- this.settings.provider_endpoint = event.target.value;
646+ // Switch Model Listener with debounce
647- this.onSettingsChange(); // Save the settings after change
647+ const modelSelect = document.getElementById('switch_model');
648- });
648+ if (modelSelect) {
649- }
649+ const debouncedModelSelectChange = debounce(onModelSelectChange, 1400);
650-
650+ modelSelect.addEventListener('change', debouncedModelSelectChange);
651- }
651+ }
652-
652+
653- //#############################//
653+ // AllTalk Server version change listener
654- // Store ST interface settings //
654+ const serverVersionSelect = document.getElementById('server_version');
655- //#############################//
655+ if (serverVersionSelect) {
656-
656+ serverVersionSelect.addEventListener('change', async (event) => {
657- onSettingsChange() {
657+ this.settings.server_version = event.target.value;
658- // Update settings based on the UI elements
658+ if (event.target.value === 'v2') {
659- //this.settings.provider_endpoint = $('#at_server').val();
659+ await this.fetchRvcVoiceObjects();
660- this.settings.language = $('#language_options').val();
660+ }
661- //this.settings.voiceMap = $('#voicemap').val();
661+ this.updateRvcVoiceDropdowns();
662- this.settings.at_generation_method = $('#at_generation_method').val();
662+ this.onSettingsChange();
663- this.settings.narrator_enabled = $('#at_narrator_enabled').val();
663+ });
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+
666666 // SaveRVC theVoice updatedand settingsPitch listeners
667- saveTtsProviderSettings();
667+ const rvcCharacterVoiceSelect = document.getElementById('rvc_character_voice');
668- }
668+ if (rvcCharacterVoiceSelect) {
669-
669+ rvcCharacterVoiceSelect.addEventListener('change', (event) => {
670- //#########################//
670+ this.settings.rvccharacter_voice_gen = event.target.value;
671- // ST Handle Reload button //
671+ this.onSettingsChange();
672- //#########################//
672+ });
673-
673+ }
674- async onRefreshClick() {
674+
675- await this.initEndpoint();
675+ const rvcNarratorVoiceSelect = document.getElementById('rvc_narrator_voice');
676- // Additional actions as needed
676+ if (rvcNarratorVoiceSelect) {
677- }
677+ rvcNarratorVoiceSelect.addEventListener('change', (event) => {
678-
678+ this.settings.rvcnarrator_voice_gen = event.target.value;
679- //##################//
679+ this.onSettingsChange();
680- // Preview AT Voice //
680+ });
681- //##################//
681+ }
682-
682+
683- async previewTtsVoice(voiceName) {
683+ const rvcCharacterPitchSelect = document.getElementById('rvc_character_pitch');
684- try {
684+ if (rvcCharacterPitchSelect) {
685- // Prepare data for POST request
685+ rvcCharacterPitchSelect.addEventListener('change', (event) => {
686- const postData = new URLSearchParams();
686+ this.settings.rvc_character_pitch = event.target.value;
687- postData.append('voice', `${voiceName}.wav`);
687+ this.onSettingsChange();
688- // Making the POST request
688+ });
689- const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
689+ }
690- method: 'POST',
690+
691- headers: {
691+ const rvcNarratorPitchSelect = document.getElementById('rvc_narrator_pitch');
692- 'Content-Type': 'application/x-www-form-urlencoded',
692+ if (rvcNarratorPitchSelect) {
693- },
693+ rvcNarratorPitchSelect.addEventListener('change', (event) => {
694- body: postData,
694+ this.settings.rvc_narrator_pitch = event.target.value;
695- });
695+ this.onSettingsChange();
696- if (!response.ok) {
696+ });
697- const errorText = await response.text();
697+ }
698- console.error('[previewTtsVoice] Error Response Text:', errorText);
698+
699- throw new Error(`HTTP ${response.status}: ${errorText}`);
699+ // DeepSpeed Listener
700- }
700+ const deepspeedCheckbox = document.getElementById('deepspeed');
701- // Assuming the server returns a URL to the .wav file
701+ if (deepspeedCheckbox) {
702702 const datahandleDeepSpeedChange = awaitasync response.json(event); => {
703- if (data.output_file_url) {
703+ const deepSpeedValue = event.target.checked ? 'True' : 'False';
704- // Use an audio element to play the .wav file
704+ updateStatus('Processing');
705- const audioElement = new Audio(data.output_file_url);
705+ try {
706- audioElement.play().catch(e => console.error('Error playing audio:', e));
706+ const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
707- } else {
707+ method: 'POST',
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+ if (!response.ok) {
710- }
710+ throw new Error(`HTTP Error: ${response.status}`);
711-
711+ }
712- } catch (error) {
712+ const data = await response.json();
713713 console.errorlog('[previewTtsVoice] Exception caught duringPOST previewresponse generationdata:', errordata);
714- throw error;
714+ updateStatus('Ready');
715- }
715+ } catch (error) {
716- }
716+ console.error('POST request error:', error);
717-
717+ updateStatus('Error');
718- //#####################//
718+ }
719- // Populate ST voices //
719+ };
720- //#####################//
720+
721-
721+ const debouncedHandleDeepSpeedChange = debounce(handleDeepSpeedChange, 300);
722- async getVoice(voiceName, generatePreview = false) {
722+ deepspeedCheckbox.addEventListener('change', debouncedHandleDeepSpeedChange);
723- // Ensure this.voices is populated
723+ }
724- if (this.voices.length === 0) {
724+
725- // Fetch voice objects logic
725+ // Low VRAM Listener
726- }
726+ const lowVramCheckbox = document.getElementById('low_vram');
727- // Find the object where the name matches voiceName
727+ if (lowVramCheckbox) {
728- const match = this.voices.find(voice => voice.name === voiceName);
728+ const handleLowVramChange = async (event) => {
729- if (!match) {
729+ const lowVramValue = event.target.checked ? 'True' : 'False';
730- // Error handling
730+ updateStatus('Processing');
731- }
731+ try {
732- // Generate preview URL only if requested
732+ const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {
733- if (!match.preview_url && generatePreview) {
733+ method: 'POST',
734- // Generate preview logic
734+ });
735- }
735+ if (!response.ok) {
736- return match; // Return the found voice object
736+ throw new Error(`HTTP Error: ${response.status}`);
737- }
737+ }
738-
738+ const data = await response.json();
739- //##########################################//
739+ console.log('POST response data:', data);
740- // Generate TTS Streaming or call Standard //
740+ updateStatus('Ready');
741- //##########################################//
741+ } catch (error) {
742-
742+ console.error('POST request error:', error);
743- async generateTts(inputText, voiceId) {
743+ updateStatus('Error');
744- try {
744+ }
745- if (this.settings.at_generation_method === 'streaming_enabled') {
745+ };
746- // Construct the streaming URL
746+
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 debouncedHandleLowVramChange = debounce(handleLowVramChange, 300);
748748 console lowVramCheckbox.logaddEventListener('Streaming URL:change', streamingUrldebouncedHandleLowVramChange);
749-
749+ }
750- // Return the streaming URL directly
750+
751- return streamingUrl;
751+ // Other listeners without debounce since they don't need it
752- } else {
752+ const narratorVoiceSelect = document.getElementById('narrator_voice');
753- // For standard method
753+ if (narratorVoiceSelect) {
754- const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
754+ narratorVoiceSelect.addEventListener('change', (event) => {
755- const audioResponse = await fetch(outputUrl);
755+ this.settings.narrator_voice_gen = `${event.target.value}`;
756- if (!audioResponse.ok) {
756+ this.onSettingsChange();
757- throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
757+ });
758- }
758+ }
759- return audioResponse; // Return the fetch response directly
759+
760- }
760+ const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
761761 } catchif (errortextNotInsideSelect) {
762- console.error('Error in generateTts:', error);
762+ textNotInsideSelect.addEventListener('change', (event) => {
763- throw error;
763+ this.settings.text_not_inside = event.target.value;
764- }
764+ this.onSettingsChange();
765- }
765+ });
766-
766+ }
767-
767+
768- //####################//
768+ // AT Narrator Dropdown Listener
769- // Generate Standard //
769+ const atNarratorSelect = document.getElementById('at_narrator_enabled');
770- //####################//
770+ const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks');
771-
771+ const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted');
772- async fetchTtsGeneration(inputText, voiceId) {
772+ const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues');
773- // Prepare the request payload
773+
774- const requestBody = new URLSearchParams({
774+ if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {
775- 'text_input': inputText,
775+ atNarratorSelect.addEventListener('change', (event) => {
776- 'text_filtering': 'standard',
776+ const narratorOption = event.target.value;
777- 'character_voice_gen': voiceId + '.wav',
777+ this.settings.narrator_enabled = narratorOption;
778- 'narrator_enabled': this.settings.narrator_enabled,
778+
779- 'narrator_voice_gen': this.settings.narrator_voice_gen + '.wav',
779+ const isNarratorDisabled = narratorOption === 'false';
780- 'text_not_inside': this.settings.at_narrator_text_not_inside,
780+ textNotInsideSelect.disabled = isNarratorDisabled;
781- 'language': this.settings.language,
781+ narratorVoiceSelect.disabled = isNarratorDisabled;
782- 'output_file_name': 'st_output',
782+
783- 'output_file_timestamp': 'true',
783+ if (narratorOption === 'true') {
784- 'autoplay': 'false',
784+ ttsPassAsterisksCheckbox.checked = false;
785- 'autoplay_volume': '0.8',
785+ $('#tts_pass_asterisks').click();
786- }).toString();
786+ $('#tts_pass_asterisks').trigger('change');
787-
787+ ttsNarrateQuotedCheckbox.checked = true;
788- try {
788+ ttsNarrateDialoguesCheckbox.checked = true;
789- const response = await doExtrasFetch(
789+ $('#tts_narrate_quoted').click();
790- `${this.settings.provider_endpoint}/api/tts-generate`,
790+ $('#tts_narrate_quoted').trigger('change');
791- {
791+ $('#tts_narrate_dialogues').click();
792- method: 'POST',
792+ $('#tts_narrate_dialogues').trigger('change');
793- headers: {
793+ } else if (narratorOption === 'silent') {
794- 'Content-Type': 'application/x-www-form-urlencoded',
794+ ttsPassAsterisksCheckbox.checked = false;
795- 'Cache-Control': 'no-cache',
795+ $('#tts_pass_asterisks').click();
796- },
796+ $('#tts_pass_asterisks').trigger('change');
797- body: requestBody,
797+ } else {
798- },
798+ ttsPassAsterisksCheckbox.checked = true;
799- );
799+ $('#tts_pass_asterisks').click();
800-
800+ $('#tts_pass_asterisks').trigger('change');
801- if (!response.ok) {
801+ }
802- const errorText = await response.text();
802+
803- console.error('[fetchTtsGeneration] Error Response Text:', errorText);
803+ this.onSettingsChange();
804- // toastr.error(response.statusText, 'TTS Generation Failed');
804+ });
805- throw new Error(`HTTP ${response.status}: ${errorText}`);
805+ }
806- }
806+
807- const data = await response.json();
807+ // Event Listener for AT Generation Method Dropdown
808808 const outputUrlatGenerationMethodSelect = datadocument.output_file_urlgetElementById('at_generation_method');
809- return outputUrl; // Return only the output_file_url
809+ if (atGenerationMethodSelect) {
810- } catch (error) {
810+ atGenerationMethodSelect.addEventListener('change', (event) => {
811- console.error('[fetchTtsGeneration] Exception caught:', error);
811+ const selectedMethod = event.target.value;
812- throw error; // Rethrow the error for further handling
812+
813- }
813+ if (selectedMethod === 'streaming_enabled') {
814- }
814+ // Disable and unselect AT Narrator
815-}
815+ atNarratorSelect.disabled = true;
816-
816+ atNarratorSelect.value = 'false';
817-//#########################//
817+ textNotInsideSelect.disabled = true;
818-// Update Status Messages //
818+ narratorVoiceSelect.disabled = true;
819-//#########################//
819+ } else if (selectedMethod === 'standard_generation') {
820-
820+ // Enable AT Narrator
821-function updateStatus(message) {
821+ atNarratorSelect.disabled = false;
822- const statusElement = document.getElementById('status_info');
822+ }
823- if (statusElement) {
823+ this.settings.at_generation_method = selectedMethod;
824- statusElement.textContent = message;
824+ this.onSettingsChange();
825- switch (message) {
825+ });
826- case 'Offline':
826+ }
827- statusElement.style.color = 'red';
827+
828- break;
828+ // Language Dropdown Listener
829- case 'Ready':
829+ const languageSelect = document.getElementById('language_options');
830- statusElement.style.color = 'lightgreen';
830+ if (languageSelect) {
831- break;
831+ languageSelect.addEventListener('change', (event) => {
832- case 'Processing':
832+ this.settings.language = event.target.value;
833- statusElement.style.color = 'blue';
833+ this.onSettingsChange();
834- break;
834+ });
835- case 'Error':
835+ }
836- statusElement.style.color = 'red';
836+
837- break;
837+ // AllTalk Endpoint Input Listener
838- }
838+ const atServerInput = document.getElementById('at_server');
839- }
839+ if (atServerInput) {
840-}
840+ atServerInput.addEventListener('input', (event) => {
841+ this.settings.provider_endpoint = event.target.value;
842+ this.onSettingsChange();
843+ });
844+ }
845+ }
846+
847+ //#############################//
848+ // Store ST interface settings //
849+ //#############################//
850+
851+ onSettingsChange() {
852+ // Update settings based on the UI elements
853+ //this.settings.provider_endpoint = $('#at_server').val();
854+ this.settings.language = $('#language_options').val();
855+ //this.settings.voiceMap = $('#voicemap').val();
856+ this.settings.at_generation_method = $('#at_generation_method').val();
857+ this.settings.narrator_enabled = $('#at_narrator_enabled').val();
858+ this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();
859+ this.settings.rvc_character_voice = $('#rvc_character_voice').val();
860+ this.settings.rvc_narrator_voice = $('#rvc_narrator_voice').val();
861+ this.settings.rvc_character_pitch = $('#rvc_character_pitch').val();
862+ this.settings.rvc_narrator_pitch = $('#rvc_narrator_pitch').val();
863+ this.settings.narrator_voice_gen = $('#narrator_voice').val();
864+ // Save the updated settings
865+ saveTtsProviderSettings();
866+ }
867+
868+ //#########################//
869+ // ST Handle Reload button //
870+ //#########################//
871+
872+ async onRefreshClick() {
873+ try {
874+ updateStatus('Processing'); // Set status to Processing while refreshing
875+ await this.checkReady(); // Check if the TTS provider is ready
876+ await this.loadSettings(this.settings); // Reload the settings
877+ await this.checkReady(); // Check if the TTS provider is ready
878+ updateStatus(this.ready ? 'Ready' : 'Offline'); // Update the status based on readiness
879+ } catch (error) {
880+ console.error('Error during refresh:', error);
881+ updateStatus('Error'); // Set status to Error in case of failure
882+ }
883+ }
884+
885+ //##################//
886+ // Preview AT Voice //
887+ //##################//
888+
889+ async previewTtsVoice(voiceName) {
890+ try {
891+ // Prepare data for POST request
892+ const postData = new URLSearchParams();
893+ postData.append('voice', `${voiceName}`);
894+
895+ // Add RVC parameters for V2 if applicable
896+ if (this.settings.server_version === 'v2' && this.settings.rvc_character_voice !== 'Disabled') {
897+ postData.append('rvccharacter_voice_gen', this.settings.rvc_character_voice);
898+ postData.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0');
899+ }
900+
901+ // Making the POST request
902+ const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
903+ method: 'POST',
904+ headers: {
905+ 'Content-Type': 'application/x-www-form-urlencoded',
906+ },
907+ body: postData,
908+ });
909+
910+ if (!response.ok) {
911+ const errorText = await response.text();
912+ console.error('previewTtsVoice Error Response Text:', errorText);
913+ throw new Error(`HTTP ${response.status}: ${errorText}`);
914+ }
915+
916+ const data = await response.json();
917+ if (data.output_file_url) {
918+ // Handle V1/V2 URL differences
919+ const fullUrl = this.settings.server_version === 'v1'
920+ ? data.output_file_url
921+ : `${this.settings.provider_endpoint}${data.output_file_url}`;
922+
923+ const audioElement = new Audio(fullUrl);
924+ audioElement.play().catch(e => console.error('Error playing audio:', e));
925+ } else {
926+ console.warn('previewTtsVoice No output file URL received in the response');
927+ throw new Error('No output file URL received in the response');
928+ }
929+ } catch (error) {
930+ console.error('previewTtsVoice Exception caught during preview generation:', error);
931+ throw error;
932+ }
933+ }
934+
935+ //#####################//
936+ // Populate ST voices //
937+ //#####################//
938+
939+ async getVoice(voiceName, generatePreview = false) {
940+ // Ensure this.voices is populated
941+ if (this.voices.length === 0) {
942+ // Fetch voice objects logic
943+ }
944+ // Find the object where the name matches voiceName
945+ const match = this.voices.find(voice => voice.name === voiceName);
946+ if (!match) {
947+ // Error handling
948+ }
949+ // Generate preview URL only if requested
950+ if (!match.preview_url && generatePreview) {
951+ // Generate preview logic
952+ }
953+ return match; // Return the found voice object
954+ }
955+
956+ //##########################################//
957+ // Generate TTS Streaming or call Standard //
958+ //##########################################//
959+
960+ async generateTts(inputText, voiceId) {
961+ try {
962+ if (this.settings.at_generation_method === 'streaming_enabled') {
963+ // Construct the streaming URL
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`;
965+ console.log('Streaming URL:', streamingUrl);
966+
967+ // Return the streaming URL directly
968+ return streamingUrl;
969+ } else {
970+ // For standard method
971+ const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
972+ const audioResponse = await fetch(outputUrl);
973+ if (!audioResponse.ok) {
974+ throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
975+ }
976+ return audioResponse; // Return the fetch response directly
977+ }
978+ } catch (error) {
979+ console.error('Error in generateTts:', error);
980+ throw error;
981+ }
982+ }
983+
984+
985+ //####################//
986+ // Generate Standard //
987+ //####################//
988+
989+ async fetchTtsGeneration(inputText, voiceId) {
990+ const requestBody = new URLSearchParams({
991+ 'text_input': inputText,
992+ 'text_filtering': 'standard',
993+ 'character_voice_gen': voiceId,
994+ 'narrator_enabled': this.settings.narrator_enabled,
995+ 'narrator_voice_gen': this.settings.narrator_voice_gen,
996+ 'text_not_inside': this.settings.at_narrator_text_not_inside,
997+ 'language': this.settings.language,
998+ 'output_file_name': 'st_output',
999+ 'output_file_timestamp': 'true',
1000+ 'autoplay': 'false',
1001+ 'autoplay_volume': '0.8',
1002+ });
1003+
1004+ // Add RVC parameters only for V2
1005+ if (this.settings.server_version === 'v2') {
1006+ if (this.settings.rvc_character_voice !== 'Disabled') {
1007+ requestBody.append('rvccharacter_voice_gen', this.settings.rvc_character_voice);
1008+ requestBody.append('rvccharacter_pitch', this.settings.rvc_character_pitch || '0');
1009+ }
1010+ if (this.settings.rvc_narrator_voice !== 'Disabled') {
1011+ requestBody.append('rvcnarrator_voice_gen', this.settings.rvc_narrator_voice);
1012+ requestBody.append('rvcnarrator_pitch', this.settings.rvc_narrator_pitch || '0');
1013+ }
1014+ }
1015+
1016+ try {
1017+ const response = await doExtrasFetch(
1018+ `${this.settings.provider_endpoint}/api/tts-generate`,
1019+ {
1020+ method: 'POST',
1021+ headers: {
1022+ 'Content-Type': 'application/x-www-form-urlencoded',
1023+ 'Cache-Control': 'no-cache',
1024+ },
1025+ body: requestBody,
1026+ },
1027+ );
1028+
1029+ if (!response.ok) {
1030+ const errorText = await response.text();
1031+ console.error('fetchTtsGeneration Error Response Text:', errorText);
1032+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1033+ }
1034+ const data = await response.json();
1035+
1036+ // Handle V1/V2 URL differences
1037+ const outputUrl = this.settings.server_version === 'v1'
1038+ ? data.output_file_url // V1 returns full URL
1039+ : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path
1040+
1041+ return outputUrl;
1042+ } catch (error) {
1043+ console.error('[fetchTtsGeneration] Exception caught:', error);
1044+ throw error;
1045+ }
1046+ }
1047+}
1048+
1049+//#########################//
1050+// Update Status Messages //
1051+//#########################//
1052+
1053+function updateStatus(message) {
1054+ const statusElement = document.getElementById('status_info');
1055+ if (statusElement) {
1056+ statusElement.textContent = message;
1057+ switch (message) {
1058+ case 'Offline':
1059+ statusElement.style.color = 'red';
1060+ break;
1061+ case 'Ready':
1062+ statusElement.style.color = 'lightgreen';
1063+ break;
1064+ case 'Processing':
1065+ statusElement.style.color = 'blue';
1066+ break;
1067+ case 'Error':
1068+ statusElement.style.color = 'red';
1069+ break;
1070+ }
1071+ }
1072+}