From 743f23240eff17d96e5d0b8945c835ad55dba313 Mon Sep 17 00:00:00 2001 From: erew123 <35898566+erew123@users.noreply.github.com> Date: Thu, 28 Mar 2024 21:36:51 +0000 Subject: [PATCH] Delete templates directory --- templates/STfiles/alltalk.js | 833 ------- templates/STfiles/index.js | 1046 --------- templates/STfiles/style.css | 159 -- templates/admin.html | 1643 -------------- templates/apilocal.wav | Bin 169040 -> 0 bytes templates/apitts.wav | Bin 218192 -> 0 bytes templates/changingmodel.wav | Bin 182352 -> 0 bytes templates/deepspeeddisabled.wav | Bin 119888 -> 0 bytes templates/deepspeedenabled.wav | Bin 102480 -> 0 bytes templates/deepspeedexample.jpg | Bin 487855 -> 0 bytes templates/favicon.ico | Bin 1150 -> 0 bytes templates/generate_form.html | 260 --- templates/lowvramdisabled.wav | Bin 151632 -> 0 bytes templates/lowvramenabled.wav | Bin 106576 -> 0 bytes templates/lowvrammode.png | Bin 560458 -> 0 bytes templates/textnotinside.jpg | Bin 159512 -> 0 bytes templates/tokenizer/tokenizer.py | 869 ------- templates/tts_chunk_player/crunker.d.ts | 179 -- templates/tts_chunk_player/crunker.esm.js | 2 - templates/tts_chunk_player/crunker.esm.js.map | 1 - templates/tts_chunk_player/favicon.ico | Bin 1150 -> 0 bytes templates/tts_chunk_player/howler.min.js | 4 - .../tts_chunk_player/tts_chunk_player.html | 1999 ----------------- templates/ttsgencomp/READEME.txt | 33 - templates/ttsgencomp/ttsgencomp.py | 191 -- templates/word_addin/1995fcd6d39cade7d871.js | 2 - .../1995fcd6d39cade7d871.js.LICENSE.txt | 1 - templates/word_addin/1fda685b81e1123773f6.css | 80 - templates/word_addin/AllTalk Word Add-in.pdf | Bin 498303 -> 0 bytes templates/word_addin/assets/icon-128.png | Bin 30741 -> 0 bytes templates/word_addin/assets/icon-16.png | Bin 839 -> 0 bytes templates/word_addin/assets/icon-32.png | Bin 2673 -> 0 bytes templates/word_addin/assets/icon-64.png | Bin 8984 -> 0 bytes templates/word_addin/assets/icon-80.png | Bin 13284 -> 0 bytes templates/word_addin/assets/logo-filled.png | Bin 101296 -> 0 bytes templates/word_addin/commands.html | 1 - templates/word_addin/commands.js | 2 - templates/word_addin/commands.js.map | 1 - templates/word_addin/manifest.xml | 79 - templates/word_addin/polyfill.js | 2 - templates/word_addin/polyfill.js.map | 1 - templates/word_addin/taskpane.html | 106 - templates/word_addin/taskpane.js | 3 - templates/word_addin/taskpane.js.LICENSE.txt | 1 - templates/word_addin/taskpane.js.map | 1 - templates/xttsfinetuned.wav | Bin 204880 -> 0 bytes templates/xttslocal.wav | Bin 204880 -> 0 bytes 47 files changed, 7499 deletions(-) delete mode 100644 templates/STfiles/alltalk.js delete mode 100644 templates/STfiles/index.js delete mode 100644 templates/STfiles/style.css delete mode 100644 templates/admin.html delete mode 100644 templates/apilocal.wav delete mode 100644 templates/apitts.wav delete mode 100644 templates/changingmodel.wav delete mode 100644 templates/deepspeeddisabled.wav delete mode 100644 templates/deepspeedenabled.wav delete mode 100644 templates/deepspeedexample.jpg delete mode 100644 templates/favicon.ico delete mode 100644 templates/generate_form.html delete mode 100644 templates/lowvramdisabled.wav delete mode 100644 templates/lowvramenabled.wav delete mode 100644 templates/lowvrammode.png delete mode 100644 templates/textnotinside.jpg delete mode 100644 templates/tokenizer/tokenizer.py delete mode 100644 templates/tts_chunk_player/crunker.d.ts delete mode 100644 templates/tts_chunk_player/crunker.esm.js delete mode 100644 templates/tts_chunk_player/crunker.esm.js.map delete mode 100644 templates/tts_chunk_player/favicon.ico delete mode 100644 templates/tts_chunk_player/howler.min.js delete mode 100644 templates/tts_chunk_player/tts_chunk_player.html delete mode 100644 templates/ttsgencomp/READEME.txt delete mode 100644 templates/ttsgencomp/ttsgencomp.py delete mode 100644 templates/word_addin/1995fcd6d39cade7d871.js delete mode 100644 templates/word_addin/1995fcd6d39cade7d871.js.LICENSE.txt delete mode 100644 templates/word_addin/1fda685b81e1123773f6.css delete mode 100644 templates/word_addin/AllTalk Word Add-in.pdf delete mode 100644 templates/word_addin/assets/icon-128.png delete mode 100644 templates/word_addin/assets/icon-16.png delete mode 100644 templates/word_addin/assets/icon-32.png delete mode 100644 templates/word_addin/assets/icon-64.png delete mode 100644 templates/word_addin/assets/icon-80.png delete mode 100644 templates/word_addin/assets/logo-filled.png delete mode 100644 templates/word_addin/commands.html delete mode 100644 templates/word_addin/commands.js delete mode 100644 templates/word_addin/commands.js.map delete mode 100644 templates/word_addin/manifest.xml delete mode 100644 templates/word_addin/polyfill.js delete mode 100644 templates/word_addin/polyfill.js.map delete mode 100644 templates/word_addin/taskpane.html delete mode 100644 templates/word_addin/taskpane.js delete mode 100644 templates/word_addin/taskpane.js.LICENSE.txt delete mode 100644 templates/word_addin/taskpane.js.map delete mode 100644 templates/xttsfinetuned.wav delete mode 100644 templates/xttslocal.wav diff --git a/templates/STfiles/alltalk.js b/templates/STfiles/alltalk.js deleted file mode 100644 index f70daf5..0000000 --- a/templates/STfiles/alltalk.js +++ /dev/null @@ -1,833 +0,0 @@ -import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js'; -import { saveTtsProviderSettings } from './index.js'; - -export { AllTalkTtsProvider }; - -class AllTalkTtsProvider { - //########// - // Config // - //########// - - settings = {}; - constructor() { - // Initialize with default settings if they are not already set - this.settings = { - provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851', - language: this.settings.language || 'en', - voiceMap: this.settings.voiceMap || {}, - at_generation_method: this.settings.at_generation_method || 'standard_generation', - narrator_enabled: this.settings.narrator_enabled || 'false', - at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator', - narrator_voice_gen: this.settings.narrator_voice_gen || 'female_01.wav', - finetuned_model: this.settings.finetuned_model || 'false' - }; - // Separate property for dynamically updated settings from the server - this.dynamicSettings = { - modelsAvailable: [], - currentModel: '', - deepspeed_available: false, - deepSpeedEnabled: false, - lowVramEnabled: false, - }; - } - ready = false; - voices = []; - separator = '. '; - audioElement = document.createElement('audio'); - - languageLabels = { - 'Arabic': 'ar', - 'Brazilian Portuguese': 'pt', - 'Chinese': 'zh-cn', - 'Czech': 'cs', - 'Dutch': 'nl', - 'English': 'en', - 'French': 'fr', - 'German': 'de', - 'Italian': 'it', - 'Polish': 'pl', - 'Russian': 'ru', - 'Spanish': 'es', - 'Turkish': 'tr', - 'Japanese': 'ja', - 'Korean': 'ko', - 'Hungarian': 'hu', - 'Hindi': 'hi', - }; - - get settingsHtml() { - let html = `
AllTalk Settings
`; - - html += `
- -
- - -
-
`; - - html += `
- -
- - -
- -
- - -
- -
`; - - html += `
-
- - -
-
- - -
-
`; - - - html += `
-
- - -
- -
- - -
-
`; - - - html += `
-
- - -
-
- - -
-
- Status: Ready -
-
- -
-
`; - - - html += `
-
- AllTalk Config & Docs. -
- -
- AllTalk Website. -
-
`; - - html += `
-
-Text-generation-webui users - Uncheck Enable TTS in Text-generation-webui. -
-
`; - - return html; - } - - - //#################// - // Startup ST & AT // - //#################// - - async loadSettings(settings) { - updateStatus('Offline'); - - if (Object.keys(settings).length === 0) { - console.info('Using default AllTalk TTS Provider settings'); - } else { - // Populate settings with provided values, ignoring server-provided settings - for (const key in settings) { - if (key in this.settings) { - this.settings[key] = settings[key]; - } else { - console.debug(`Ignoring non-user-configurable setting: ${key}`); - } - } - } - - // Update UI elements to reflect the loaded settings - $('#at_server').val(this.settings.provider_endpoint); - $('#language_options').val(this.settings.language); - //$('#voicemap').val(this.settings.voiceMap); - $('#at_generation_method').val(this.settings.at_generation_method); - $('#at_narrator_enabled').val(this.settings.narrator_enabled); - $('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside); - $('#narrator_voice').val(this.settings.narrator_voice_gen); - - console.debug('AllTalkTTS: Settings loaded'); - try { - // Check if TTS provider is ready - await this.checkReady(); - await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server - await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready - this.updateNarratorVoicesDropdown(); - this.updateLanguageDropdown(); - this.setupEventListeners(); - this.applySettingsToHTML(); - updateStatus('Ready'); - } catch (error) { - console.error("Error loading settings:", error); - updateStatus('Offline'); - } - } - - applySettingsToHTML() { - // Apply loaded settings or use defaults - const narratorVoiceSelect = document.getElementById('narrator_voice'); - const atNarratorSelect = document.getElementById('at_narrator_enabled'); - const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside'); - const generationMethodSelect = document.getElementById('at_generation_method'); - this.settings.narrator_voice = this.settings.narrator_voice_gen; - // Apply settings to Narrator Voice dropdown - if (narratorVoiceSelect && this.settings.narrator_voice) { - narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', ''); - } - // Apply settings to AT Narrator Enabled dropdown - if (atNarratorSelect) { - // Sync the state with the checkbox in index.js - const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js - const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js - const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js - // Sync the state with the checkbox in index.js - if (this.settings.narrator_enabled) { - ttsPassAsterisksCheckbox.checked = false; - $('#tts_pass_asterisks').click(); // Simulate a click event - $('#tts_pass_asterisks').trigger('change'); - } - if (!this.settings.narrator_enabled) { - ttsPassAsterisksCheckbox.checked = true; - $('#tts_pass_asterisks').click(); // Simulate a click event - $('#tts_pass_asterisks').trigger('change'); - } - // Uncheck and set tts_narrate_quoted to false if narrator is enabled - if (this.settings.narrator_enabledd) { - ttsNarrateQuotedCheckbox.checked = true; - ttsNarrateDialoguesCheckbox.checked = true; - // Trigger click events instead of change events - $('#tts_narrate_quoted').click(); - $('#tts_narrate_quoted').trigger('change'); - $('#tts_narrate_dialogues').click(); - $('#tts_narrate_dialogues').trigger('change'); - } - atNarratorSelect.value = this.settings.narrator_enabled.toString(); - this.settings.narrator_enabled = this.settings.narrator_enabled.toString(); - } - // Apply settings to the Language dropdown - const languageSelect = document.getElementById('language_options'); - if (languageSelect && this.settings.language) { - languageSelect.value = this.settings.language; - } - // Apply settings to Text Not Inside dropdown - if (textNotInsideSelect && this.settings.text_not_inside) { - textNotInsideSelect.value = this.settings.text_not_inside; - this.settings.at_narrator_text_not_inside = this.settings.text_not_inside; - } - // Apply settings to Generation Method dropdown - if (generationMethodSelect && this.settings.at_generation_method) { - generationMethodSelect.value = this.settings.at_generation_method; - } - // Additional logic to disable/enable dropdowns based on the selected generation method - const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled'; - if (isStreamingEnabled) { - // Disable certain dropdowns when streaming is enabled - if (atNarratorSelect) atNarratorSelect.disabled = true; - if (textNotInsideSelect) textNotInsideSelect.disabled = true; - if (narratorVoiceSelect) narratorVoiceSelect.disabled = true; - } else { - // Enable dropdowns for standard generation - if (atNarratorSelect) atNarratorSelect.disabled = false; - if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled; - if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled; - } - const modelSelect = document.getElementById('switch_model'); - if (this.settings.finetuned_model === 'true') { - const ftOption = document.createElement('option'); - ftOption.value = 'XTTSv2 FT'; - ftOption.textContent = 'XTTSv2 FT'; - modelSelect.appendChild(ftOption); - } - } - - //##############################// - // Check AT Server is Available // - //##############################// - - async checkReady() { - try { - const response = await fetch(`${this.settings.provider_endpoint}/api/ready`); - // Check if the HTTP request was successful - if (!response.ok) { - throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`); - } - const statusText = await response.text(); - // Check if the response is 'Ready' - if (statusText === 'Ready') { - this.ready = true; // Set the ready flag to true - console.log('TTS service is ready.'); - } else { - this.ready = false; - console.log('TTS service is not ready.'); - } - } catch (error) { - console.error('Error checking TTS service readiness:', error); - this.ready = false; // Ensure ready flag is set to false in case of error - } - } - - //######################// - // Get Available Voices // - //######################// - - async fetchTtsVoiceObjects() { - const response = await fetch(`${this.settings.provider_endpoint}/api/voices`); - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`HTTP ${response.status}: ${errorText}`); - } - const data = await response.json(); - const voices = data.voices.map(filename => { - const voiceName = filename.replace('.wav', ''); - return { - name: voiceName, - voice_id: voiceName, - preview_url: null, // Preview URL will be dynamically generated - lang: 'en' // Default language - }; - }); - this.voices = voices; // Assign to the class property - return voices; // Also return this list - } - - //##########################################// - // Get Current AT Server Config & Update ST // - //##########################################// - - async updateSettingsFromServer() { - try { - // Fetch current settings - const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`); - if (!response.ok) { - throw new Error(`Failed to fetch current settings: ${response.statusText}`); - } - const currentSettings = await response.json(); - // Update internal settings - this.settings.modelsAvailable = currentSettings.models_available; - this.settings.currentModel = currentSettings.current_model_loaded; - this.settings.deepspeed_available = currentSettings.deepspeed_available; - this.settings.deepSpeedEnabled = currentSettings.deepspeed_status; - this.settings.lowVramEnabled = currentSettings.low_vram_status; - this.settings.finetuned_model = currentSettings.finetuned_model - // Update HTML elements - this.updateModelDropdown(); - this.updateCheckboxes(); - } catch (error) { - console.error(`Error updating settings from server: ${error}`); - } - } - - //###################################################// - // Get Current AT Server Config & Update ST (Models) // - //###################################################// - - updateModelDropdown() { - const modelSelect = document.getElementById('switch_model'); - if (modelSelect) { - modelSelect.innerHTML = ''; // Clear existing options - this.settings.modelsAvailable.forEach(model => { - const option = document.createElement('option'); - option.value = model.model_name; - option.textContent = model.model_name; // Use model_name instead of name - option.selected = model.model_name === this.settings.currentModel; - modelSelect.appendChild(option); - }); - } - } - - //#######################################################// - // Get Current AT Server Config & Update ST (DS and LVR) // - //#######################################################// - - updateCheckboxes() { - const deepspeedCheckbox = document.getElementById('deepspeed'); - const lowVramCheckbox = document.getElementById('low_vram'); - if (lowVramCheckbox) lowVramCheckbox.checked = this.settings.lowVramEnabled; - if (deepspeedCheckbox) { - deepspeedCheckbox.checked = this.settings.deepSpeedEnabled; - deepspeedCheckbox.disabled = !this.settings.deepspeed_available; // Disable checkbox if deepspeed is not available - } - } - - //###############################################################// - // Get Current AT Server Config & Update ST (AT Narrator Voices) // - //###############################################################// - - updateNarratorVoicesDropdown() { - const narratorVoiceSelect = document.getElementById('narrator_voice'); - if (narratorVoiceSelect && this.voices) { - // Clear existing options - narratorVoiceSelect.innerHTML = ''; - // Add new options - for (let voice of this.voices) { - const option = document.createElement('option'); - option.value = voice.voice_id; - option.textContent = voice.name; - narratorVoiceSelect.appendChild(option); - } - } - } - - //######################################################// - // Get Current AT Server Config & Update ST (Languages) // - //######################################################// - - updateLanguageDropdown() { - const languageSelect = document.getElementById('language_options'); - if (languageSelect) { - // Ensure default language is set - this.settings.language = this.settings.language; - - languageSelect.innerHTML = ''; - for (let language in this.languageLabels) { - const option = document.createElement('option'); - option.value = this.languageLabels[language]; - option.textContent = language; - if (this.languageLabels[language] === this.settings.language) { - option.selected = true; - } - languageSelect.appendChild(option); - } - } - } - - //########################################// - // Start AT TTS extenstion page listeners // - //########################################// - - setupEventListeners() { - - let debounceTimeout; - const debounceDelay = 500; // Milliseconds - - // Define the event handler function - const onModelSelectChange = async (event) => { - console.log("Model select change event triggered"); // Debugging statement - const selectedModel = event.target.value; - console.log(`Selected model: ${selectedModel}`); // Debugging statement - // Set status to Processing - updateStatus('Processing'); - try { - const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, { - method: 'POST' - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status}`); - } - const data = await response.json(); - console.log("POST response data:", data); // Debugging statement - // Set status to Ready if successful - updateStatus('Ready'); - } catch (error) { - console.error("POST request error:", error); // Debugging statement - // Set status to Error in case of failure - updateStatus('Error'); - } - - // Handle response or error - }; - - const debouncedModelSelectChange = (event) => { - clearTimeout(debounceTimeout); - debounceTimeout = setTimeout(() => { - onModelSelectChange(event); - }, debounceDelay); - }; - - // Switch Model Listener - const modelSelect = document.getElementById('switch_model'); - if (modelSelect) { - // Remove the event listener if it was previously added - modelSelect.removeEventListener('change', debouncedModelSelectChange); - // Add the debounced event listener - modelSelect.addEventListener('change', debouncedModelSelectChange); - } - - // DeepSpeed Listener - const deepspeedCheckbox = document.getElementById('deepspeed'); - if (deepspeedCheckbox) { - deepspeedCheckbox.addEventListener('change', async (event) => { - const deepSpeedValue = event.target.checked ? 'True' : 'False'; - // Set status to Processing - updateStatus('Processing'); - try { - const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, { - method: 'POST' - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status}`); - } - const data = await response.json(); - console.log("POST response data:", data); // Debugging statement - // Set status to Ready if successful - updateStatus('Ready'); - } catch (error) { - console.error("POST request error:", error); // Debugging statement - // Set status to Error in case of failure - updateStatus('Error'); - } - }); - } - - // Low VRAM Listener - const lowVramCheckbox = document.getElementById('low_vram'); - if (lowVramCheckbox) { - lowVramCheckbox.addEventListener('change', async (event) => { - const lowVramValue = event.target.checked ? 'True' : 'False'; - // Set status to Processing - updateStatus('Processing'); - try { - const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, { - method: 'POST' - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status}`); - } - const data = await response.json(); - console.log("POST response data:", data); // Debugging statement - // Set status to Ready if successful - updateStatus('Ready'); - } catch (error) { - console.error("POST request error:", error); // Debugging statement - // Set status to Error in case of failure - updateStatus('Error'); - } - }); - } - - // Narrator Voice Dropdown Listener - const narratorVoiceSelect = document.getElementById('narrator_voice'); - if (narratorVoiceSelect) { - narratorVoiceSelect.addEventListener('change', (event) => { - this.settings.narrator_voice_gen = `${event.target.value}.wav`; - this.onSettingsChange(); // Save the settings after change - }); - } - - const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside'); - if (textNotInsideSelect) { - textNotInsideSelect.addEventListener('change', (event) => { - this.settings.text_not_inside = event.target.value; - this.onSettingsChange(); // Save the settings after change - }); - } - - // AT Narrator Dropdown Listener - const atNarratorSelect = document.getElementById('at_narrator_enabled'); - const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js - const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js - const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js - - if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) { - atNarratorSelect.addEventListener('change', (event) => { - const isNarratorEnabled = event.target.value === 'true'; - this.settings.narrator_enabled = isNarratorEnabled; // Update the setting here - textNotInsideSelect.disabled = !isNarratorEnabled; - narratorVoiceSelect.disabled = !isNarratorEnabled; - - // Sync the state with the checkbox in index.js - if (isNarratorEnabled) { - ttsPassAsterisksCheckbox.checked = false; - $('#tts_pass_asterisks').click(); // Simulate a click event - $('#tts_pass_asterisks').trigger('change'); - } - if (!isNarratorEnabled) { - ttsPassAsterisksCheckbox.checked = true; - $('#tts_pass_asterisks').click(); // Simulate a click event - $('#tts_pass_asterisks').trigger('change'); - } - // Uncheck and set tts_narrate_quoted to false if narrator is enabled - if (isNarratorEnabled) { - ttsNarrateQuotedCheckbox.checked = true; - ttsNarrateDialoguesCheckbox.checked = true; - // Trigger click events instead of change events - $('#tts_narrate_quoted').click(); - $('#tts_narrate_quoted').trigger('change'); - $('#tts_narrate_dialogues').click(); - $('#tts_narrate_dialogues').trigger('change'); - } - this.onSettingsChange(); // Save the settings after change - }); - } - - - // Event Listener for AT Generation Method Dropdown - const atGenerationMethodSelect = document.getElementById('at_generation_method'); - const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled'); - if (atGenerationMethodSelect) { - atGenerationMethodSelect.addEventListener('change', (event) => { - const selectedMethod = event.target.value; - - if (selectedMethod === 'streaming_enabled') { - // Disable and unselect AT Narrator - atNarratorEnabledSelect.disabled = true; - atNarratorEnabledSelect.value = 'false'; - textNotInsideSelect.disabled = true; - narratorVoiceSelect.disabled = true; - } else if (selectedMethod === 'standard_generation') { - // Enable AT Narrator - atNarratorEnabledSelect.disabled = false; - } - this.settings.at_generation_method = selectedMethod; // Update the setting here - this.onSettingsChange(); // Save the settings after change - }); - } - - // Listener for Language Dropdown - const languageSelect = document.getElementById('language_options'); - if (languageSelect) { - languageSelect.addEventListener('change', (event) => { - this.settings.language = event.target.value; - this.onSettingsChange(); // Save the settings after change - }); - } - - // Listener for AllTalk Endpoint Input - const atServerInput = document.getElementById('at_server'); - if (atServerInput) { - atServerInput.addEventListener('input', (event) => { - this.settings.provider_endpoint = event.target.value; - this.onSettingsChange(); // Save the settings after change - }); - } - - } - - //#############################// - // Store ST interface settings // - //#############################// - - onSettingsChange() { - // Update settings based on the UI elements - //this.settings.provider_endpoint = $('#at_server').val(); - this.settings.language = $('#language_options').val(); - //this.settings.voiceMap = $('#voicemap').val(); - this.settings.at_generation_method = $('#at_generation_method').val(); - this.settings.narrator_enabled = $('#at_narrator_enabled').val(); - this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val(); - this.settings.narrator_voice_gen = $('#narrator_voice').val(); - // Save the updated settings - saveTtsProviderSettings(); - } - - //#########################// - // ST Handle Reload button // - //#########################// - - async onRefreshClick() { - await this.checkReady(); // Check if the TTS provider is ready - await this.loadSettings(this.settings); // Reload the settings - // Additional actions as needed - } - - //##################// - // Preview AT Voice // - //##################// - - async previewTtsVoice(voiceName) { - try { - // Prepare data for POST request - const postData = new URLSearchParams(); - postData.append("voice", `${voiceName}.wav`); - // Making the POST request - const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, { - method: "POST", - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: postData, - }); - if (!response.ok) { - const errorText = await response.text(); - console.error(`[previewTtsVoice] Error Response Text:`, errorText); - throw new Error(`HTTP ${response.status}: ${errorText}`); - } - // Assuming the server returns a URL to the .wav file - const data = await response.json(); - if (data.output_file_url) { - // Use an audio element to play the .wav file - const audioElement = new Audio(data.output_file_url); - audioElement.play().catch(e => console.error("Error playing audio:", e)); - } else { - console.warn("[previewTtsVoice] No output file URL received in the response"); - throw new Error("No output file URL received in the response"); - } - - } catch (error) { - console.error("[previewTtsVoice] Exception caught during preview generation:", error); - throw error; - } - } - - //#####################// - // Populate ST voices // - //#####################// - - async getVoice(voiceName, generatePreview = false) { - // Ensure this.voices is populated - if (this.voices.length === 0) { - // Fetch voice objects logic - } - // Find the object where the name matches voiceName - const match = this.voices.find(voice => voice.name === voiceName); - if (!match) { - // Error handling - } - // Generate preview URL only if requested - if (!match.preview_url && generatePreview) { - // Generate preview logic - } - return match; // Return the found voice object - } - - //##########################################// - // Generate TTS Streaming or call Standard // - //##########################################// - - async generateTts(inputText, voiceId) { - try { - if (this.settings.at_generation_method === 'streaming_enabled') { - // Construct the streaming URL - const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`; - console.log("Streaming URL:", streamingUrl); - - // Return the streaming URL directly - return streamingUrl; - } else { - // For standard method - const outputUrl = await this.fetchTtsGeneration(inputText, voiceId); - const audioResponse = await fetch(outputUrl); - if (!audioResponse.ok) { - throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`); - } - return audioResponse; // Return the fetch response directly - } - } catch (error) { - console.error("Error in generateTts:", error); - throw error; - } - } - - - //####################// - // Generate Standard // - //####################// - - async fetchTtsGeneration(inputText, voiceId) { - // Prepare the request payload - const requestBody = new URLSearchParams({ - 'text_input': inputText, - 'text_filtering': "standard", - 'character_voice_gen': voiceId + ".wav", - 'narrator_enabled': this.settings.narrator_enabled, - 'narrator_voice_gen': this.settings.narrator_voice_gen + ".wav", - 'text_not_inside': this.settings.at_narrator_text_not_inside, - 'language': this.settings.language, - 'output_file_name': "st_output", - 'output_file_timestamp': "true", - 'autoplay': "false", - 'autoplay_volume': "0.8" - }).toString(); - - try { - const response = await doExtrasFetch( - `${this.settings.provider_endpoint}/api/tts-generate`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Cache-Control': 'no-cache', - }, - body: requestBody - } - ); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`[fetchTtsGeneration] Error Response Text:`, errorText); - // toastr.error(response.statusText, 'TTS Generation Failed'); - throw new Error(`HTTP ${response.status}: ${errorText}`); - } - const data = await response.json(); - const outputUrl = data.output_file_url; - return outputUrl; // Return only the output_file_url - } catch (error) { - console.error("[fetchTtsGeneration] Exception caught:", error); - throw error; // Rethrow the error for further handling - } - } -} - -//#########################// -// Update Status Messages // -//#########################// - -function updateStatus(message) { - const statusElement = document.getElementById('status_info'); - if (statusElement) { - statusElement.textContent = message; - switch (message) { - case 'Offline': - statusElement.style.color = 'red'; - break; - case 'Ready': - statusElement.style.color = 'lightgreen'; - break; - case 'Processing': - statusElement.style.color = 'blue'; - break; - case 'Error': - statusElement.style.color = 'red'; - break; - } - } -} \ No newline at end of file diff --git a/templates/STfiles/index.js b/templates/STfiles/index.js deleted file mode 100644 index dd84a22..0000000 --- a/templates/STfiles/index.js +++ /dev/null @@ -1,1046 +0,0 @@ -import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.js'; -import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules } from '../../extensions.js'; -import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique } from '../../utils.js'; -import { EdgeTtsProvider } from './edge.js'; -import { ElevenLabsTtsProvider } from './elevenlabs.js'; -import { SileroTtsProvider } from './silerotts.js'; -import { CoquiTtsProvider } from './coqui.js'; -import { SystemTtsProvider } from './system.js'; -import { NovelTtsProvider } from './novel.js'; -import { power_user } from '../../power-user.js'; -import { registerSlashCommand } from '../../slash-commands.js'; -import { OpenAITtsProvider } from './openai.js'; -import { XTTSTtsProvider } from './xtts.js'; -import { AllTalkTtsProvider } from './alltalk.js'; -export { talkingAnimation }; - -const UPDATE_INTERVAL = 1000; - -let voiceMapEntries = []; -let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} -let storedvalue = false; -let lastChatId = null; -let lastMessageHash = null; - -const DEFAULT_VOICE_MARKER = '[Default Voice]'; -const DISABLED_VOICE_MARKER = 'disabled'; - -export function getPreviewString(lang) { - const previewStrings = { - 'en-US': 'The quick brown fox jumps over the lazy dog', - 'en-GB': 'Sphinx of black quartz, judge my vow', - 'fr-FR': 'Portez ce vieux whisky au juge blond qui fume', - 'de-DE': 'Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich', - 'it-IT': 'Pranzo d\'acqua fa volti sghembi', - 'es-ES': 'Quiere la boca exhausta vid, kiwi, piña y fugaz jamón', - 'es-MX': 'Fabio me exige, sin tapujos, que añada cerveza al whisky', - 'ru-RU': 'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!', - 'pt-BR': 'Vejo xá gritando que fez show sem playback.', - 'pt-PR': 'Todo pajé vulgar faz boquinha sexy com kiwi.', - 'uk-UA': 'Фабрикуймо гідність, лящім їжею, ґав хапаймо, з\'єднавці чаш!', - 'pl-PL': 'Pchnąć w tę łódź jeża lub ośm skrzyń fig', - 'cs-CZ': 'Příliš žluťoučký kůň úpěl ďábelské ódy', - 'sk-SK': 'Vyhŕňme si rukávy a vyprážajme čínske ryžové cestoviny', - 'hu-HU': 'Árvíztűrő tükörfúrógép', - 'tr-TR': 'Pijamalı hasta yağız şoföre çabucak güvendi', - 'nl-NL': 'De waard heeft een kalfje en een pinkje opgegeten', - 'sv-SE': 'Yxskaftbud, ge vårbygd, zinkqvarn', - 'da-DK': 'Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon', - 'ja-JP': 'いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす', - 'ko-KR': '가나다라마바사아자차카타파하', - 'zh-CN': '我能吞下玻璃而不伤身体', - 'ro-RO': 'Muzicologă în bej vând whisky și tequila, preț fix', - 'bg-BG': 'Щъркелите се разпръснаха по цялото небе', - 'el-GR': 'Ταχίστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός', - 'fi-FI': 'Voi veljet, miksi juuri teille myin nämä vehkeet?', - 'he-IL': 'הקצינים צעקו: "כל הכבוד לצבא הצבאות!"', - 'id-ID': 'Jangkrik itu memang enak, apalagi kalau digoreng', - 'ms-MY': 'Muzik penyanyi wanita itu menggambarkan kehidupan yang penuh dengan duka nestapa', - 'th-TH': 'เป็นไงบ้างครับ ผมชอบกินข้าวผัดกระเพราหมูกรอบ', - 'vi-VN': 'Cô bé quàng khăn đỏ đang ngồi trên bãi cỏ xanh', - 'ar-SA': 'أَبْجَدِيَّة عَرَبِيَّة', - 'hi-IN': 'श्वेता ने श्वेता के श्वेते हाथों में श्वेता का श्वेता चावल पकड़ा', - }; - const fallbackPreview = 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet'; - - return previewStrings[lang] ?? fallbackPreview; -} - -let ttsProviders = { - ElevenLabs: ElevenLabsTtsProvider, - Silero: SileroTtsProvider, - XTTSv2: XTTSTtsProvider, - System: SystemTtsProvider, - Coqui: CoquiTtsProvider, - Edge: EdgeTtsProvider, - Novel: NovelTtsProvider, - OpenAI: OpenAITtsProvider, - AllTalk: AllTalkTtsProvider, -}; -let ttsProvider; -let ttsProviderName; - -let ttsLastMessage = null; - -async function onNarrateOneMessage() { - audioElement.src = '/sounds/silence.mp3'; - const context = getContext(); - const id = $(this).closest('.mes').attr('mesid'); - const message = context.chat[id]; - - if (!message) { - return; - } - - resetTtsPlayback(); - ttsJobQueue.push(message); - moduleWorker(); -} - -async function onNarrateText(args, text) { - if (!text) { - return; - } - - audioElement.src = '/sounds/silence.mp3'; - - // To load all characters in the voice map, set unrestricted to true - await initVoiceMap(true); - - const baseName = args?.voice || name2; - const name = (baseName === 'SillyTavern System' ? DEFAULT_VOICE_MARKER : baseName) || DEFAULT_VOICE_MARKER; - - const voiceMapEntry = voiceMap[name] === DEFAULT_VOICE_MARKER - ? voiceMap[DEFAULT_VOICE_MARKER] - : voiceMap[name]; - - if (!voiceMapEntry || voiceMapEntry === DISABLED_VOICE_MARKER) { - toastr.info(`Specified voice for ${name} was not found. Check the TTS extension settings.`); - return; - } - - resetTtsPlayback(); - ttsJobQueue.push({ mes: text, name: name }); - await moduleWorker(); - - // Return back to the chat voices - await initVoiceMap(false); -} - -async function moduleWorker() { - // Primarily determining when to add new chat to the TTS queue - const enabled = $('#tts_enabled').is(':checked'); - $('body').toggleClass('tts', enabled); - if (!enabled) { - return; - } - - const context = getContext(); - const chat = context.chat; - - processTtsQueue(); - processAudioJobQueue(); - updateUiAudioPlayState(); - - // Auto generation is disabled - if (extension_settings.tts.auto_generation == false) { - return; - } - - // no characters or group selected - if (!context.groupId && context.characterId === undefined) { - return; - } - - // Chat changed - if ( - context.chatId !== lastChatId - ) { - currentMessageNumber = context.chat.length ? context.chat.length : 0; - saveLastValues(); - - // Force to speak on the first message in the new chat - if (context.chat.length === 1) { - lastMessageHash = -1; - } - - return; - } - - // take the count of messages - let lastMessageNumber = context.chat.length ? context.chat.length : 0; - - // There's no new messages - let diff = lastMessageNumber - currentMessageNumber; - let hashNew = getStringHash((chat.length && chat[chat.length - 1].mes) ?? ''); - - // if messages got deleted, diff will be < 0 - if (diff < 0) { - // necessary actions will be taken by the onChatDeleted() handler - return; - } - - // if no new messages, or same message, or same message hash, do nothing - if (diff == 0 && hashNew === lastMessageHash) { - return; - } - - // If streaming, wait for streaming to finish before processing new messages - if (context.streamingProcessor && !context.streamingProcessor.isFinished) { - return; - } - - // clone message object, as things go haywire if message object is altered below (it's passed by reference) - const message = structuredClone(chat[chat.length - 1]); - - // if last message within current message, message got extended. only send diff to TTS. - if (ttsLastMessage !== null && message.mes.indexOf(ttsLastMessage) !== -1) { - let tmp = message.mes; - message.mes = message.mes.replace(ttsLastMessage, ''); - ttsLastMessage = tmp; - } else { - ttsLastMessage = message.mes; - } - - // We're currently swiping. Don't generate voice - if (!message || message.mes === '...' || message.mes === '') { - return; - } - - // Don't generate if message doesn't have a display text - if (extension_settings.tts.narrate_translated_only && !(message?.extra?.display_text)) { - return; - } - - // Don't generate if message is a user message and user message narration is disabled - if (message.is_user && !extension_settings.tts.narrate_user) { - return; - } - - // New messages, add new chat to history - lastMessageHash = hashNew; - currentMessageNumber = lastMessageNumber; - - console.debug( - `Adding message from ${message.name} for TTS processing: "${message.mes}"`, - ); - ttsJobQueue.push(message); -} - -function talkingAnimation(switchValue) { - if (!modules.includes('talkinghead')) { - console.debug('Talking Animation module not loaded'); - return; - } - - const apiUrl = getApiUrl(); - const animationType = switchValue ? 'start' : 'stop'; - - if (switchValue !== storedvalue) { - try { - console.log(animationType + ' Talking Animation'); - doExtrasFetch(`${apiUrl}/api/talkinghead/${animationType}_talking`); - storedvalue = switchValue; // Update the storedvalue to the current switchValue - } catch (error) { - // Handle the error here or simply ignore it to prevent logging - } - } - updateUiAudioPlayState(); -} - -function resetTtsPlayback() { - // Stop system TTS utterance - cancelTtsPlay(); - - // Clear currently processing jobs - currentTtsJob = null; - currentAudioJob = null; - - // Reset audio element - audioElement.currentTime = 0; - audioElement.src = ''; - - // Clear any queue items - ttsJobQueue.splice(0, ttsJobQueue.length); - audioJobQueue.splice(0, audioJobQueue.length); - - // Set audio ready to process again - audioQueueProcessorReady = true; -} - -function isTtsProcessing() { - let processing = false; - - // Check job queues - if (ttsJobQueue.length > 0 || audioJobQueue.length > 0) { - processing = true; - } - // Check current jobs - if (currentTtsJob != null || currentAudioJob != null) { - processing = true; - } - return processing; -} - -function debugTtsPlayback() { - console.log(JSON.stringify( - { - 'ttsProviderName': ttsProviderName, - 'voiceMap': voiceMap, - 'currentMessageNumber': currentMessageNumber, - 'audioPaused': audioPaused, - 'audioJobQueue': audioJobQueue, - 'currentAudioJob': currentAudioJob, - 'audioQueueProcessorReady': audioQueueProcessorReady, - 'ttsJobQueue': ttsJobQueue, - 'currentTtsJob': currentTtsJob, - 'ttsConfig': extension_settings.tts, - }, - )); -} -window.debugTtsPlayback = debugTtsPlayback; - -//##################// -// Audio Control // -//##################// - -let audioElement = new Audio(); -audioElement.id = 'tts_audio'; -audioElement.autoplay = true; - -let audioJobQueue = []; -let currentAudioJob; -let audioPaused = false; -let audioQueueProcessorReady = true; - -async function playAudioData(audioBlob) { - // Since current audio job can be cancelled, don't playback if it is null - if (currentAudioJob == null) { - console.log('Cancelled TTS playback because currentAudioJob was null'); - } - if (audioBlob instanceof Blob) { - const srcUrl = await getBase64Async(audioBlob); - audioElement.src = srcUrl; - } else if (typeof audioBlob === 'string') { - audioElement.src = audioBlob; - } else { - throw `TTS received invalid audio data type ${typeof audioBlob}`; - } - audioElement.addEventListener('ended', completeCurrentAudioJob); - audioElement.addEventListener('canplay', () => { - console.debug('Starting TTS playback'); - audioElement.play(); - }); -} - -window['tts_preview'] = function (id) { - const audio = document.getElementById(id); - - if (audio && !$(audio).data('disabled')) { - audio.play(); - } - else { - ttsProvider.previewTtsVoice(id); - } -}; - -async function onTtsVoicesClick() { - let popupText = ''; - - try { - const voiceIds = await ttsProvider.fetchTtsVoiceObjects(); - - for (const voice of voiceIds) { - popupText += ` -
- ${voice.lang || ''} - ${voice.name} - -
`; - if (voice.preview_url) { - popupText += ``; - } - } - } catch { - popupText = 'Could not load voices list. Check your API key.'; - } - - callPopup(popupText, 'text'); -} - -function updateUiAudioPlayState() { - if (extension_settings.tts.enabled == true) { - $('#ttsExtensionMenuItem').show(); - let img; - // Give user feedback that TTS is active by setting the stop icon if processing or playing - if (!audioElement.paused || isTtsProcessing()) { - img = 'fa-solid fa-stop-circle extensionsMenuExtensionButton'; - } else { - img = 'fa-solid fa-circle-play extensionsMenuExtensionButton'; - } - $('#tts_media_control').attr('class', img); - } else { - $('#ttsExtensionMenuItem').hide(); - } -} - -function onAudioControlClicked() { - audioElement.src = '/sounds/silence.mp3'; - let context = getContext(); - // Not pausing, doing a full stop to anything TTS is doing. Better UX as pause is not as useful - if (!audioElement.paused || isTtsProcessing()) { - resetTtsPlayback(); - talkingAnimation(false); - } else { - // Default play behavior if not processing or playing is to play the last message. - ttsJobQueue.push(context.chat[context.chat.length - 1]); - } - updateUiAudioPlayState(); -} - -function addAudioControl() { - - $('#extensionsMenu').prepend(` -
-
- TTS Playback -
`); - $('#ttsExtensionMenuItem').attr('title', 'TTS play/pause').on('click', onAudioControlClicked); - updateUiAudioPlayState(); -} - -function completeCurrentAudioJob() { - audioQueueProcessorReady = true; - currentAudioJob = null; - talkingAnimation(false); //stop lip animation - // updateUiPlayState(); -} - -/** - * Accepts an HTTP response containing audio/mpeg data, and puts the data as a Blob() on the queue for playback - * @param {Response} response - */ -async function addAudioJob(response) { - if (typeof response === 'string') { - audioJobQueue.push(response); - } else { - const audioData = await response.blob(); - if (!audioData.type.startsWith('audio/')) { - throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`; - } - audioJobQueue.push(audioData); - } - console.debug('Pushed audio job to queue.'); -} - -async function processAudioJobQueue() { - // Nothing to do, audio not completed, or audio paused - stop processing. - if (audioJobQueue.length == 0 || !audioQueueProcessorReady || audioPaused) { - return; - } - try { - audioQueueProcessorReady = false; - currentAudioJob = audioJobQueue.shift(); - playAudioData(currentAudioJob); - talkingAnimation(true); - } catch (error) { - console.error(error); - audioQueueProcessorReady = true; - } -} - -//################// -// TTS Control // -//################// - -let ttsJobQueue = []; -let currentTtsJob; // Null if nothing is currently being processed -let currentMessageNumber = 0; - -function completeTtsJob() { - console.info(`Current TTS job for ${currentTtsJob?.name} completed.`); - currentTtsJob = null; -} - -function saveLastValues() { - const context = getContext(); - lastChatId = context.chatId; - lastMessageHash = getStringHash( - (context.chat.length && context.chat[context.chat.length - 1].mes) ?? '', - ); -} - -async function tts(text, voiceId, char) { - async function processResponse(response) { - // RVC injection - if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function') - response = await window['rvcVoiceConversion'](response, char, text); - - await addAudioJob(response); - } - - let response = await ttsProvider.generateTts(text, voiceId); - - // If async generator, process every chunk as it comes in - if (typeof response[Symbol.asyncIterator] === 'function') { - for await (const chunk of response) { - await processResponse(chunk); - } - } else { - await processResponse(response); - } - - completeTtsJob(); -} - -async function processTtsQueue() { - // Called each moduleWorker iteration to pull chat messages from queue - if (currentTtsJob || ttsJobQueue.length <= 0 || audioPaused) { - return; - } - - console.debug('New message found, running TTS'); - currentTtsJob = ttsJobQueue.shift(); - let text = extension_settings.tts.narrate_translated_only ? (currentTtsJob?.extra?.display_text || currentTtsJob.mes) : currentTtsJob.mes; - - if (extension_settings.tts.skip_codeblocks) { - text = text.replace(/^\s{4}.*$/gm, '').trim(); - text = text.replace(/```.*?```/gs, '').trim(); - } - - if (!extension_settings.tts.pass_asterisks) { - text = extension_settings.tts.narrate_dialogues_only - ? text.replace(/\*[^*]*?(\*|$)/g, '').trim() // remove asterisks content - : text.replaceAll('*', '').trim(); // remove just the asterisks - } - - if (extension_settings.tts.narrate_quoted_only) { - const special_quotes = /[“”]/g; // Extend this regex to include other special quotes - text = text.replace(special_quotes, '"'); - const matches = text.match(/".*?"/g); // Matches text inside double quotes, non-greedily - const partJoiner = (ttsProvider?.separator || ' ... '); - text = matches ? matches.join(partJoiner) : text; - } - - if (typeof ttsProvider?.processText === 'function') { - text = await ttsProvider.processText(text); - } - - // Collapse newlines and spaces into single space - text = text.replace(/\s+/g, ' ').trim(); - - console.log(`TTS: ${text}`); - const char = currentTtsJob.name; - - // Remove character name from start of the line if power user setting is disabled - if (char && !power_user.allow_name2_display) { - const escapedChar = escapeRegex(char); - text = text.replace(new RegExp(`^${escapedChar}:`, 'gm'), ''); - } - - try { - if (!text) { - console.warn('Got empty text in TTS queue job.'); - completeTtsJob(); - return; - } - - const voiceMapEntry = voiceMap[char] === DEFAULT_VOICE_MARKER ? voiceMap[DEFAULT_VOICE_MARKER] : voiceMap[char]; - - if (!voiceMapEntry || voiceMapEntry === DISABLED_VOICE_MARKER) { - throw `${char} not in voicemap. Configure character in extension settings voice map`; - } - const voice = await ttsProvider.getVoice(voiceMapEntry); - const voiceId = voice.voice_id; - if (voiceId == null) { - toastr.error(`Specified voice for ${char} was not found. Check the TTS extension settings.`); - throw `Unable to attain voiceId for ${char}`; - } - tts(text, voiceId, char); - } catch (error) { - console.error(error); - currentTtsJob = null; - } -} - -// Secret function for now -async function playFullConversation() { - const context = getContext(); - const chat = context.chat; - ttsJobQueue = chat; -} -window.playFullConversation = playFullConversation; - -//#############################// -// Extension UI and Settings // -//#############################// - -function loadSettings() { - if (Object.keys(extension_settings.tts).length === 0) { - Object.assign(extension_settings.tts, defaultSettings); - } - for (const key in defaultSettings) { - if (!(key in extension_settings.tts)) { - extension_settings.tts[key] = defaultSettings[key]; - } - } - $('#tts_provider').val(extension_settings.tts.currentProvider); - $('#tts_enabled').prop( - 'checked', - extension_settings.tts.enabled, - ); - $('#tts_narrate_dialogues').prop('checked', extension_settings.tts.narrate_dialogues_only); - $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only); - $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation); - $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only); - $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user); - $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks); - $('body').toggleClass('tts', extension_settings.tts.enabled); -} - -const defaultSettings = { - voiceMap: '', - ttsEnabled: false, - currentProvider: 'ElevenLabs', - auto_generation: true, - narrate_user: false, -}; - -function setTtsStatus(status, success) { - $('#tts_status').text(status); - if (success) { - $('#tts_status').removeAttr('style'); - } else { - $('#tts_status').css('color', 'red'); - } -} - -function onRefreshClick() { - Promise.all([ - ttsProvider.onRefreshClick(), - // updateVoiceMap() - ]).then(() => { - extension_settings.tts[ttsProviderName] = ttsProvider.settings; - saveSettingsDebounced(); - setTtsStatus('Successfully applied settings', true); - console.info(`Saved settings ${ttsProviderName} ${JSON.stringify(ttsProvider.settings)}`); - initVoiceMap(); - updateVoiceMap(); - }).catch(error => { - console.error(error); - setTtsStatus(error, false); - }); -} - -function onEnableClick() { - extension_settings.tts.enabled = $('#tts_enabled').is( - ':checked', - ); - updateUiAudioPlayState(); - saveSettingsDebounced(); -} - - -function onAutoGenerationClick() { - extension_settings.tts.auto_generation = !!$('#tts_auto_generation').prop('checked'); - saveSettingsDebounced(); -} - - -function onNarrateDialoguesClick() { - extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked'); - saveSettingsDebounced(); -} - -function onNarrateUserClick() { - extension_settings.tts.narrate_user = !!$('#tts_narrate_user').prop('checked'); - saveSettingsDebounced(); -} - -function onNarrateQuotedClick() { - extension_settings.tts.narrate_quoted_only = !!$('#tts_narrate_quoted').prop('checked'); - saveSettingsDebounced(); -} - - -function onNarrateTranslatedOnlyClick() { - extension_settings.tts.narrate_translated_only = !!$('#tts_narrate_translated_only').prop('checked'); - saveSettingsDebounced(); -} - -function onSkipCodeblocksClick() { - extension_settings.tts.skip_codeblocks = !!$('#tts_skip_codeblocks').prop('checked'); - saveSettingsDebounced(); -} - -function onPassAsterisksClick() { - extension_settings.tts.pass_asterisks = !!$('#tts_pass_asterisks').prop('checked'); - saveSettingsDebounced(); - console.log("setting pass asterisks", extension_settings.tts.pass_asterisks) -} - -//##############// -// TTS Provider // -//##############// - -async function loadTtsProvider(provider) { - //Clear the current config and add new config - $('#tts_provider_settings').html(''); - - if (!provider) { - return; - } - - // Init provider references - extension_settings.tts.currentProvider = provider; - ttsProviderName = provider; - ttsProvider = new ttsProviders[provider]; - - // Init provider settings - $('#tts_provider_settings').append(ttsProvider.settingsHtml); - if (!(ttsProviderName in extension_settings.tts)) { - console.warn(`Provider ${ttsProviderName} not in Extension Settings, initiatilizing provider in settings`); - extension_settings.tts[ttsProviderName] = {}; - } - await ttsProvider.loadSettings(extension_settings.tts[ttsProviderName]); - await initVoiceMap(); -} - -function onTtsProviderChange() { - const ttsProviderSelection = $('#tts_provider').val(); - extension_settings.tts.currentProvider = ttsProviderSelection; - loadTtsProvider(ttsProviderSelection); -} - -// Ensure that TTS provider settings are saved to extension settings. -export function saveTtsProviderSettings() { - extension_settings.tts[ttsProviderName] = ttsProvider.settings; - updateVoiceMap(); - saveSettingsDebounced(); - console.info(`Saved settings ${ttsProviderName} ${JSON.stringify(ttsProvider.settings)}`); -} - - -//###################// -// voiceMap Handling // -//###################// - -async function onChatChanged() { - await resetTtsPlayback(); - const voiceMapInit = initVoiceMap(); - await Promise.race([voiceMapInit, delay(1000)]); - ttsLastMessage = null; -} - -async function onChatDeleted() { - const context = getContext(); - - // update internal references to new last message - lastChatId = context.chatId; - currentMessageNumber = context.chat.length ? context.chat.length : 0; - - // compare against lastMessageHash. If it's the same, we did not delete the last chat item, so no need to reset tts queue - let messageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1].mes) ?? ''); - if (messageHash === lastMessageHash) { - return; - } - lastMessageHash = messageHash; - ttsLastMessage = (context.chat.length && context.chat[context.chat.length - 1].mes) ?? ''; - - // stop any tts playback since message might not exist anymore - await resetTtsPlayback(); -} - -/** - * Get characters in current chat - * @param {boolean} unrestricted - If true, will include all characters in voiceMapEntries, even if they are not in the current chat. - * @returns {string[]} - Array of character names - */ -function getCharacters(unrestricted) { - const context = getContext(); - - if (unrestricted) { - const names = context.characters.map(char => char.name); - names.unshift(DEFAULT_VOICE_MARKER); - return names.filter(onlyUnique); - } - - let characters = []; - if (context.groupId === null) { - // Single char chat - characters.push(DEFAULT_VOICE_MARKER); - characters.push(context.name1); - characters.push(context.name2); - } else { - // Group chat - characters.push(DEFAULT_VOICE_MARKER); - characters.push(context.name1); - const group = context.groups.find(group => context.groupId == group.id); - for (let member of group.members) { - const character = context.characters.find(char => char.avatar == member); - if (character) { - characters.push(character.name); - } - } - } - return characters.filter(onlyUnique); -} - -function sanitizeId(input) { - // Remove any non-alphanumeric characters except underscore (_) and hyphen (-) - let sanitized = input.replace(/[^a-zA-Z0-9-_]/g, ''); - - // Ensure first character is always a letter - if (!/^[a-zA-Z]/.test(sanitized)) { - sanitized = 'element_' + sanitized; - } - - return sanitized; -} - -function parseVoiceMap(voiceMapString) { - let parsedVoiceMap = {}; - for (const [charName, voiceId] of voiceMapString - .split(',') - .map(s => s.split(':'))) { - if (charName && voiceId) { - parsedVoiceMap[charName.trim()] = voiceId.trim(); - } - } - return parsedVoiceMap; -} - - - -/** - * Apply voiceMap based on current voiceMapEntries - */ -function updateVoiceMap() { - const tempVoiceMap = {}; - for (const voice of voiceMapEntries) { - if (voice.voiceId === null) { - continue; - } - tempVoiceMap[voice.name] = voice.voiceId; - } - if (Object.keys(tempVoiceMap).length !== 0) { - voiceMap = tempVoiceMap; - console.log(`Voicemap updated to ${JSON.stringify(voiceMap)}`); - } - if (!extension_settings.tts[ttsProviderName].voiceMap) { - extension_settings.tts[ttsProviderName].voiceMap = {}; - } - Object.assign(extension_settings.tts[ttsProviderName].voiceMap, voiceMap); - saveSettingsDebounced(); -} - -class VoiceMapEntry { - name; - voiceId; - selectElement; - constructor(name, voiceId = DEFAULT_VOICE_MARKER) { - this.name = name; - this.voiceId = voiceId; - this.selectElement = null; - } - - addUI(voiceIds) { - let sanitizedName = sanitizeId(this.name); - let defaultOption = this.name === DEFAULT_VOICE_MARKER ? - `` : - ``; - let template = ` -
- ${this.name} - -
- `; - $('#tts_voicemap_block').append(template); - - // Populate voice ID select list - for (const voiceId of voiceIds) { - const option = document.createElement('option'); - option.innerText = voiceId.name; - option.value = voiceId.name; - $(`#tts_voicemap_char_${sanitizedName}_voice`).append(option); - } - - this.selectElement = $(`#tts_voicemap_char_${sanitizedName}_voice`); - this.selectElement.on('change', args => this.onSelectChange(args)); - this.selectElement.val(this.voiceId); - } - - onSelectChange(args) { - this.voiceId = this.selectElement.find(':selected').val(); - updateVoiceMap(); - } -} - -/** - * Init voiceMapEntries for character select list. - * @param {boolean} unrestricted - If true, will include all characters in voiceMapEntries, even if they are not in the current chat. - */ -export async function initVoiceMap(unrestricted = false) { - // Gate initialization if not enabled or TTS Provider not ready. Prevents error popups. - const enabled = $('#tts_enabled').is(':checked'); - if (!enabled) { - return; - } - - // Keep errors inside extension UI rather than toastr. Toastr errors for TTS are annoying. - try { - await ttsProvider.checkReady(); - } catch (error) { - const message = `TTS Provider not ready. ${error}`; - setTtsStatus(message, false); - return; - } - - setTtsStatus('TTS Provider Loaded', true); - - // Clear existing voiceMap state - $('#tts_voicemap_block').empty(); - voiceMapEntries = []; - - // Get characters in current chat - const characters = getCharacters(unrestricted); - - // Get saved voicemap from provider settings, handling new and old representations - let voiceMapFromSettings = {}; - if ('voiceMap' in extension_settings.tts[ttsProviderName]) { - // Handle previous representation - if (typeof extension_settings.tts[ttsProviderName].voiceMap === 'string') { - voiceMapFromSettings = parseVoiceMap(extension_settings.tts[ttsProviderName].voiceMap); - // Handle new representation - } else if (typeof extension_settings.tts[ttsProviderName].voiceMap === 'object') { - voiceMapFromSettings = extension_settings.tts[ttsProviderName].voiceMap; - } - } - - // Get voiceIds from provider - let voiceIdsFromProvider; - try { - voiceIdsFromProvider = await ttsProvider.fetchTtsVoiceObjects(); - } - catch { - toastr.error('TTS Provider failed to return voice ids.'); - } - - // Build UI using VoiceMapEntry objects - for (const character of characters) { - if (character === 'SillyTavern System') { - continue; - } - // Check provider settings for voiceIds - let voiceId; - if (character in voiceMapFromSettings) { - voiceId = voiceMapFromSettings[character]; - } else if (character === DEFAULT_VOICE_MARKER) { - voiceId = DISABLED_VOICE_MARKER; - } else { - voiceId = DEFAULT_VOICE_MARKER; - } - const voiceMapEntry = new VoiceMapEntry(character, voiceId); - voiceMapEntry.addUI(voiceIdsFromProvider); - voiceMapEntries.push(voiceMapEntry); - } - updateVoiceMap(); -} - -$(document).ready(function () { - function addExtensionControls() { - const settingsHtml = ` -
-
-
- TTS -
-
-
-
-
- Select TTS Provider
-
- - -
-
- - - - - - - - -
-
-
-
-
-
-
- -
-
-
-
- - `; - $('#extensions_settings').append(settingsHtml); - $('#tts_refresh').on('click', onRefreshClick); - $('#tts_enabled').on('click', onEnableClick); - $('#tts_narrate_dialogues').on('click', onNarrateDialoguesClick); - $('#tts_narrate_quoted').on('click', onNarrateQuotedClick); - $('#tts_narrate_translated_only').on('click', onNarrateTranslatedOnlyClick); - $('#tts_skip_codeblocks').on('click', onSkipCodeblocksClick); - $('#tts_pass_asterisks').on('click', onPassAsterisksClick); - $('#tts_auto_generation').on('click', onAutoGenerationClick); - $('#tts_narrate_user').on('click', onNarrateUserClick); - $('#tts_voices').on('click', onTtsVoicesClick); - for (const provider in ttsProviders) { - $('#tts_provider').append($('