Blame Raw
Cohee · e3f41666 · · 175 lines (5.6 KB)
1 contributor
1import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js';
2import { saveTtsProviderSettings } from './index.js';
3
4export { SileroTtsProvider };
5
6class SileroTtsProvider {
7 //########//
8 // Config //
9 //########//
10
11 settings;
12 ready = false;
13 voices = [];
14 separator = ' ';
15
16 defaultSettings = {
17 provider_endpoint: 'http://localhost:8001/tts',
18 voiceMap: {},
19 };
20
21 get settingsHtml() {
22 let html = `
23 <label for="silero_tts_endpoint">Provider Endpoint:</label>
24 <input id="silero_tts_endpoint" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.provider_endpoint}"/>
25 <span>
26 <span>Use <a target="_blank" href="https://github.com/SillyTavern/SillyTavern-extras">SillyTavern Extras API</a> or <a target="_blank" href="https://github.com/ouoertheo/silero-api-server">Silero TTS Server</a>.</span>
27 `;
28 return html;
29 }
30
31 onSettingsChange() {
32 // Used when provider settings are updated from UI
33 this.settings.provider_endpoint = $('#silero_tts_endpoint').val();
34 saveTtsProviderSettings();
35 this.refreshSession();
36 }
37
38 async loadSettings(settings) {
39 // Pupulate Provider UI given input settings
40 if (Object.keys(settings).length == 0) {
41 console.info('Using default TTS Provider settings');
42 }
43
44 // Only accept keys defined in defaultSettings
45 this.settings = this.defaultSettings;
46
47 for (const key in settings) {
48 if (key in this.settings) {
49 this.settings[key] = settings[key];
50 } else {
51 throw `Invalid setting passed to TTS Provider: ${key}`;
52 }
53 }
54
55 const apiCheckInterval = setInterval(() => {
56 // Use Extras API if TTS support is enabled
57 if (modules.includes('tts') || modules.includes('silero-tts')) {
58 const baseUrl = new URL(getApiUrl());
59 baseUrl.pathname = '/api/tts';
60 this.settings.provider_endpoint = baseUrl.toString();
61 $('#silero_tts_endpoint').val(this.settings.provider_endpoint);
62 clearInterval(apiCheckInterval);
63 }
64 }, 2000);
65
66 $('#silero_tts_endpoint').val(this.settings.provider_endpoint);
67 $('#silero_tts_endpoint').on('input', () => { this.onSettingsChange(); });
68 this.refreshSession();
69
70 await this.checkReady();
71
72 console.debug('SileroTTS: Settings loaded');
73 }
74
75 // Perform a simple readiness check by trying to fetch voiceIds
76 async checkReady() {
77 await this.fetchTtsVoiceObjects();
78 }
79
80 async onRefreshClick() {
81 return;
82 }
83
84 async refreshSession() {
85 await this.initSession();
86 }
87
88 //#################//
89 // TTS Interfaces //
90 //#################//
91
92 async getVoice(voiceName) {
93 if (this.voices.length == 0) {
94 this.voices = await this.fetchTtsVoiceObjects();
95 }
96 const match = this.voices.filter(
97 sileroVoice => sileroVoice.name == voiceName,
98 )[0];
99 if (!match) {
100 throw `TTS Voice name ${voiceName} not found`;
101 }
102 return match;
103 }
104
105 async generateTts(text, voiceId) {
106 const response = await this.fetchTtsGeneration(text, voiceId);
107 return response;
108 }
109
110 //###########//
111 // API CALLS //
112 //###########//
113 async fetchTtsVoiceObjects() {
114 const response = await doExtrasFetch(`${this.settings.provider_endpoint}/speakers`);
115 if (!response.ok) {
116 throw new Error(`HTTP ${response.status}: ${await response.json()}`);
117 }
118 const responseJson = await response.json();
119 return responseJson;
120 }
121
122 async fetchTtsGeneration(inputText, voiceId) {
123 console.info(`Generating new TTS for voice_id ${voiceId}`);
124 const response = await doExtrasFetch(
125 `${this.settings.provider_endpoint}/generate`,
126 {
127 method: 'POST',
128 headers: {
129 'Content-Type': 'application/json',
130 'Cache-Control': 'no-cache', // Added this line to disable caching of file so new files are always played - Rolyat 7/7/23
131 },
132 body: JSON.stringify({
133 'text': inputText,
134 'speaker': voiceId,
135 'session': 'sillytavern',
136 }),
137 },
138 );
139 if (!response.ok) {
140 toastr.error(response.statusText, 'TTS Generation Failed');
141 throw new Error(`HTTP ${response.status}: ${await response.text()}`);
142 }
143 return response;
144 }
145
146 async initSession() {
147 console.info('Silero TTS: requesting new session');
148 try {
149 const response = await doExtrasFetch(
150 `${this.settings.provider_endpoint}/session`,
151 {
152 method: 'POST',
153 headers: {
154 'Content-Type': 'application/json',
155 'Cache-Control': 'no-cache',
156 },
157 body: JSON.stringify({
158 'path': 'sillytavern',
159 }),
160 },
161 );
162
163 if (!response.ok && response.status !== 404) {
164 throw new Error(`HTTP ${response.status}: ${await response.text()}`);
165 }
166 } catch (error) {
167 console.info('Silero TTS: endpoint not available', error);
168 }
169 }
170
171 // Interface not used by Silero TTS
172 async fetchTtsFromHistory(history_item_id) {
173 return Promise.resolve(history_item_id);
174 }
175}