diff --git a/public/scripts/extensions/tts/gsvi.js b/public/scripts/extensions/tts/gsvi.js
new file mode 100644
index 000000000..fc39a4c41
--- /dev/null
+++ b/public/scripts/extensions/tts/gsvi.js
@@ -0,0 +1,268 @@
+import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js';
+import { saveTtsProviderSettings } from './index.js';
+
+export { GSVITtsProvider };
+
+class GSVITtsProvider {
+ //########//
+ // Config //
+ //########//
+
+ settings;
+ ready = false;
+ separator = '. ';
+
+ characterList = {};
+ voices = [];
+ /**
+ * Perform any text processing before passing to TTS engine.
+ * @param {string} text Input text
+ * @returns {string} Processed text
+ */
+ processText(text) {
+ text = text.replace('
', '\n'); // Replace
with newline
+ return text;
+ }
+
+ languageLabels = {
+ '多语种混合': '多语种混合',
+ '中文': '中文',
+ '英文': '英文',
+ '日文': '日文',
+ '中英混合': '中英混合',
+ '日英混合': '日英混合',
+ };
+ defaultSettings = {
+ provider_endpoint: 'http://127.0.0.1:5000',
+
+ language: '多语种混合',
+
+ cha_name: '',
+ character_emotion: 'default',
+
+ speed: 1,
+
+ top_k: 6,
+ top_p: 0.85,
+ temperature: 0.75,
+ batch_size: 10,
+
+ stream: false,
+ stream_chunk_size: 100,
+ };
+
+
+ // 新增获取角色和情绪的方法
+ async fetchCharacterList() {
+ const response = await fetch(this.settings.provider_endpoint + '/character_list');
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
+ }
+ const characterList = await response.json();
+ this.characterList = characterList;
+ this.voices = Object.keys(characterList);
+
+ }
+
+
+
+ get settingsHtml() {
+ let html = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ return html;
+ }
+
+ onSettingsChange() {
+ // Update provider settings based on input fields
+ this.settings.provider_endpoint = $('#gsvi_tts_endpoint').val();
+ this.settings.language = $('#gsvi_api_language').val();
+
+
+ // Update the rest of TTS settings based on input fields
+ this.settings.speed = parseFloat($('#gsvi_speed').val());
+ this.settings.temperature = parseFloat($('#gsvi_temperature').val());
+ this.settings.top_k = parseInt($('#gsvi_top_k').val(), 10);
+ this.settings.top_p = parseFloat($('#gsvi_top_p').val());
+ this.settings.batch_size = parseInt($('#gsvi_batch_size').val(), 10);
+ this.settings.stream = $('#gsvi_tts_streaming').is(':checked');
+ this.settings.stream_chunk_size = parseInt($('#gsvi_stream_chunk_size').val(), 10);
+
+ // Update UI to reflect changes
+ $('#gsvi_tts_speed_output').text(this.settings.speed);
+ $('#gsvi_tts_temperature_output').text(this.settings.temperature);
+ $('#gsvi_top_k_output').text(this.settings.top_k);
+ $('#gsvi_top_p_output').text(this.settings.top_p);
+ $('#gsvi_stream_chunk_size_output').text(this.settings.stream_chunk_size);
+ $('#gsvi_batch_size_output').text(this.settings.batch_size);
+
+
+
+
+ // Persist settings changes
+ saveTtsProviderSettings();
+
+ }
+
+ async loadSettings(settings) {
+ // Populate Provider UI given input settings
+ if (Object.keys(settings).length === 0) {
+ console.info('Using default TTS Provider settings');
+ }
+
+ // Only accept keys defined in defaultSettings
+ this.settings = { ...this.defaultSettings, ...settings };
+
+ // Fetch character and emotion list
+ // Set initial values from the settings
+ $('#gsvi_tts_endpoint').val(this.settings.provider_endpoint);
+ $('#gsvi_api_language').val(this.settings.language);
+
+ $('#gsvi_speed').val(this.settings.speed);
+ $('#gsvi_temperature').val(this.settings.temperature);
+ $('#gsvi_top_k').val(this.settings.top_k);
+ $('#gsvi_top_p').val(this.settings.top_p);
+ $('#gsvi_batch_size').val(this.settings.batch_size);
+ $('#gsvi_tts_streaming').prop('checked', this.settings.stream);
+ $('#gsvi_stream_chunk_size').val(this.settings.stream_chunk_size);
+
+ // Update UI to reflect initial settings
+ $('#gsvi_tts_speed_output').text(this.settings.speed);
+ $('#gsvi_tts_temperature_output').text(this.settings.temperature);
+ $('#gsvi_top_k_output').text(this.settings.top_k);
+ $('#gsvi_top_p_output').text(this.settings.top_p);
+ $('#gsvi_stream_chunk_size_output').text(this.settings.stream_chunk_size);
+
+ // Register event listeners to update settings on user interaction
+ // (Similar to before, ensure event listeners for character and emotion selection are included)
+ // Register input/change event listeners to update settings on user interaction
+ $('#xtts_tts_endpoint').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_api_language').on('change', () => { this.onSettingsChange(); });
+
+ $('#gsvi_speed').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_temperature').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_top_k').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_top_p').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_batch_size').on('input', () => { this.onSettingsChange(); });
+ $('#gsvi_tts_streaming').on('change', () => { this.onSettingsChange(); });
+ $('#gsvi_stream_chunk_size').on('input', () => { this.onSettingsChange(); });
+
+
+ await this.checkReady();
+ console.debug('GSVI: Settings loaded');
+ }
+
+
+
+ // Perform a simple readiness check by trying to fetch voiceIds
+ async checkReady() {
+ await Promise.allSettled([this.fetchCharacterList()]);
+ }
+
+ async onRefreshClick() {
+ return;
+ }
+
+ //#################//
+ // TTS Interfaces //
+ //#################//
+
+ async getVoice(voiceName) {
+ if (this.voices.length == 0) {
+ this.fetchCharacterList();
+ }
+ if (!this.voices.includes(voiceName)) {
+ throw `TTS Voice name ${voiceName} not found`;
+ }
+ return { name: voiceName, voice_id: voiceName, preview_url: false, lang: 'zh-CN' };
+ }
+
+ async generateTts(text, voiceId) {
+ const response = await this.fetchTtsGeneration(text, voiceId);
+ return response;
+ }
+
+ //###########//
+ // API CALLS //
+ //###########//
+ async fetchTtsVoiceObjects() {
+ if (this.voices.length == 0) {
+ await this.fetchCharacterList();
+ }
+ console.log(this.voices);
+ const voices = this.voices.map(x => ({ name: x, voice_id: x, preview_url: false, lang: 'zh-CN' }));
+ return voices;
+ }
+
+
+ async fetchTtsGeneration(inputText, voiceId) {
+ console.info(`Generating new TTS for voice_id ${voiceId}`);
+
+
+ const params = new URLSearchParams();
+ params.append('text', inputText);
+ params.append('cha_name', voiceId);
+ params.append('text_language', this.settings.language);
+ params.append('batch_size', this.settings.batch_size.toString());
+ params.append('speed', this.settings.speed.toString());
+ params.append('top_k', this.settings.top_k.toString());
+ params.append('top_p', this.settings.top_p.toString());
+ params.append('temperature', this.settings.temperature.toString());
+ params.append('stream', this.settings.stream.toString());
+
+
+ return `${this.settings.provider_endpoint}/tts?${params.toString()}`;
+
+ }
+
+ // Interface not used by GSVI TTS
+ async fetchTtsFromHistory(history_item_id) {
+ return Promise.resolve(history_item_id);
+ }
+
+}
diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js
index b86e42ede..a1f2382d6 100644
--- a/public/scripts/extensions/tts/index.js
+++ b/public/scripts/extensions/tts/index.js
@@ -11,6 +11,7 @@ import { power_user } from '../../power-user.js';
import { registerSlashCommand } from '../../slash-commands.js';
import { OpenAITtsProvider } from './openai.js';
import { XTTSTtsProvider } from './xtts.js';
+import { GSVITtsProvider } from './gsvi.js';
import { AllTalkTtsProvider } from './alltalk.js';
import { SpeechT5TtsProvider } from './speecht5.js';
export { talkingAnimation };
@@ -71,6 +72,7 @@ let ttsProviders = {
ElevenLabs: ElevenLabsTtsProvider,
Silero: SileroTtsProvider,
XTTSv2: XTTSTtsProvider,
+ GSVI: GSVITtsProvider,
System: SystemTtsProvider,
Coqui: CoquiTtsProvider,
Edge: EdgeTtsProvider,