@andresaya/edge-tts 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -199,13 +199,20 @@ Path:speech.config\r
199
199
  console.log("WebSocket URL:", url);
200
200
  this.ws = new WebSocket(url);
201
201
  const SSML_text = this.getSSML(text, voice, options);
202
- const timeout = setTimeout(() => {
203
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
204
- this.ws.close();
205
- }
206
- reject(new Error("Synthesis timeout"));
207
- }, 3e4);
202
+ let timedOut = false;
203
+ let inactivityTimeout;
204
+ const resetInactivityTimeout = () => {
205
+ clearTimeout(inactivityTimeout);
206
+ inactivityTimeout = window.setTimeout(() => {
207
+ timedOut = true;
208
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
209
+ this.ws.close();
210
+ }
211
+ reject(new Error("WebSocket inactivity timeout - no response from server"));
212
+ }, 3e4);
213
+ };
208
214
  this.ws.onopen = () => {
215
+ resetInactivityTimeout();
209
216
  const message = this.buildTTSConfigMessage();
210
217
  this.ws.send(message);
211
218
  const timestamp = this.nowRFC1123();
@@ -218,6 +225,7 @@ ${SSML_text}`;
218
225
  this.ws.send(speechMessage);
219
226
  };
220
227
  this.ws.onmessage = (event) => {
228
+ resetInactivityTimeout();
221
229
  if (typeof event.data === "string") {
222
230
  if (event.data.includes("Path:turn.end")) {
223
231
  this.ws?.close();
@@ -233,15 +241,17 @@ ${SSML_text}`;
233
241
  }
234
242
  };
235
243
  this.ws.onerror = (err) => {
236
- clearTimeout(timeout);
244
+ clearTimeout(inactivityTimeout);
237
245
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
238
246
  this.ws.close();
239
247
  }
240
248
  reject(new Error("WebSocket error"));
241
249
  };
242
250
  this.ws.onclose = () => {
243
- clearTimeout(timeout);
244
- resolve();
251
+ clearTimeout(inactivityTimeout);
252
+ if (!timedOut) {
253
+ resolve();
254
+ }
245
255
  };
246
256
  });
247
257
  }
@@ -263,12 +273,25 @@ ${SSML_text}`;
263
273
  notify = null;
264
274
  }
265
275
  };
266
- const timeout = setTimeout(() => {
267
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
268
- this.ws.close();
269
- }
270
- }, 3e4);
276
+ let timedOut = false;
277
+ let inactivityTimeout;
278
+ const resetInactivityTimeout = () => {
279
+ clearTimeout(inactivityTimeout);
280
+ inactivityTimeout = window.setTimeout(() => {
281
+ timedOut = true;
282
+ error = new Error("WebSocket inactivity timeout - no response from server");
283
+ done = true;
284
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
285
+ this.ws.close();
286
+ }
287
+ if (notify) {
288
+ notify();
289
+ notify = null;
290
+ }
291
+ }, 3e4);
292
+ };
271
293
  this.ws.onopen = () => {
294
+ resetInactivityTimeout();
272
295
  const message = this.buildTTSConfigMessage();
273
296
  this.ws.send(message);
274
297
  const timestamp = this.nowRFC1123();
@@ -281,6 +304,7 @@ ${SSML_text}`;
281
304
  this.ws.send(speechMessage);
282
305
  };
283
306
  this.ws.onmessage = (event) => {
307
+ resetInactivityTimeout();
284
308
  if (typeof event.data === "string") {
285
309
  if (event.data.includes("Path:turn.end")) {
286
310
  this.ws?.close();
@@ -302,6 +326,7 @@ ${SSML_text}`;
302
326
  }
303
327
  };
304
328
  this.ws.onerror = (err) => {
329
+ clearTimeout(inactivityTimeout);
305
330
  error = new Error("WebSocket error");
306
331
  done = true;
307
332
  if (notify) {
@@ -310,7 +335,7 @@ ${SSML_text}`;
310
335
  }
311
336
  };
312
337
  this.ws.onclose = () => {
313
- clearTimeout(timeout);
338
+ clearTimeout(inactivityTimeout);
314
339
  done = true;
315
340
  if (notify) {
316
341
  notify();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/config/constants.ts", "../../src/browser/EdgeTTS.browser.ts"],
4
- "sourcesContent": ["export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n", "import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"Synthesis timeout\"));\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(timeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n resolve();\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n", "import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"WebSocket inactivity timeout - no response from server\"));\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout \r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n if (!timedOut) {\r\n resolve();\r\n }\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n error = new Error(\"WebSocket inactivity timeout - no response from server\");\r\n done = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,24 +1,24 @@
1
- var T=Object.defineProperty;var v=(u,e,t)=>e in u?T(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var f=(u,e,t)=>v(u,typeof e!="symbol"?e+"":e,t);var a={TRUSTED_CLIENT_TOKEN:"6A5AA1D4EAFF4E9FB37E23D68491D6F4",BASE_URL:"https://api.msedgeservices.com/tts/cognitiveservices",WSS_URL:"wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",VOICES_URL:"https://api.msedgeservices.com/tts/cognitiveservices/voices/list",CHROMIUM_FULL_VERSION:"142.0.3595.0",CHROMIUM_MAJOR_VERSION:"142",VERSION_MS_GEC:"1-142.0.3595",token32(){let u=new Uint8Array(16);return crypto.getRandomValues(u),Array.from(u).map(e=>e.toString(16).padStart(2,"0")).join("").toUpperCase()},getBaseHeaders(){return{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4",Cookie:"MUID="+this.token32()}},WSS_HEADERS:{Pragma:"no-cache","Cache-Control":"no-cache",Origin:"chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold","Sec-WebSocket-Protocol":"synthesize","Sec-WebSocket-Version":"13","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"},VOICE_HEADERS:{"Sec-CH-UA":'" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',"Sec-CH-UA-Mobile":"?0",Accept:"*/*","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty"}};var C=class{constructor(){f(this,"audio_stream",[]);f(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${a.VOICES_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}`,{headers:a.getBaseHeaders()})).json()).map(s=>(s.FriendlyName=s.FriendlyName||s.LocalName,delete s.SampleRateHertz,delete s.Status,s))}async getVoicesByLanguage(e){return(await this.getVoices()).filter(n=>n.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(n=>n.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let t;return typeof e=="string"?t=parseFloat(e.replace("%","")):t=e,t>=0?`+${t}%`:`${t}%`}validateVolume(e){let t;return typeof e=="string"?t=parseInt(e.replace("%",""),10):t=e,`${t}%`}detectSSML(e){let t=e.trim();if(!/^<\?xml|^<speak/i.test(t))return{isValid:!0,isSSML:!1};let s=[],i=/<speak\b[^>]*>[\s\S]*<\/speak>/i.test(t),l=/<voice\b[^>]*>[\s\S]*<\/voice>/i.test(t);if(!i)throw new Error("Invalid SSML: Missing <speak> tag");if(!l)throw new Error("Invalid SSML: Missing <voice> tag");if(!/xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(t)&&i)throw new Error("Invalid SSML: Missing or incorrect namespace declaration");return{isValid:s.length===0,isSSML:i||l,errors:s.length>0?s:void 0}}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,t,n={}){let s=n.inputType||"auto",i=!1;if(s==="ssml")i=!0;else if(s==="text")i=!1;else{let r=this.detectSSML(e);i=r.isSSML,r.isSSML?(console.log("\u2192 Detected SSML input"),r.isValid||console.warn("\u26A0 SSML validation warnings:",r.errors)):console.log("\u2192 Detected plain text input")}if(i){let r=e.trim();return r.includes("xmlns=")||(r=r.replace(/<speak([^>]*)>/i,'<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">')),!/<voice\b[^>]*>/i.test(r)&&t&&(r=r.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i,`$1<voice name="${t}">$2</voice>$3`)),r}let l=this.validatePitch(n.pitch??0),p=this.validateRate(n.rate??0),d=this.validateVolume(n.volume??0),S=this.escapeXML(e);return`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
1
+ var L=Object.defineProperty;var A=(p,e,t)=>e in p?L(p,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):p[e]=t;var E=(p,e,t)=>A(p,typeof e!="symbol"?e+"":e,t);var a={TRUSTED_CLIENT_TOKEN:"6A5AA1D4EAFF4E9FB37E23D68491D6F4",BASE_URL:"https://api.msedgeservices.com/tts/cognitiveservices",WSS_URL:"wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",VOICES_URL:"https://api.msedgeservices.com/tts/cognitiveservices/voices/list",CHROMIUM_FULL_VERSION:"142.0.3595.0",CHROMIUM_MAJOR_VERSION:"142",VERSION_MS_GEC:"1-142.0.3595",token32(){let p=new Uint8Array(16);return crypto.getRandomValues(p),Array.from(p).map(e=>e.toString(16).padStart(2,"0")).join("").toUpperCase()},getBaseHeaders(){return{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4",Cookie:"MUID="+this.token32()}},WSS_HEADERS:{Pragma:"no-cache","Cache-Control":"no-cache",Origin:"chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold","Sec-WebSocket-Protocol":"synthesize","Sec-WebSocket-Version":"13","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"},VOICE_HEADERS:{"Sec-CH-UA":'" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',"Sec-CH-UA-Mobile":"?0",Accept:"*/*","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty"}};var C=class{constructor(){E(this,"audio_stream",[]);E(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${a.VOICES_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}`,{headers:a.getBaseHeaders()})).json()).map(s=>(s.FriendlyName=s.FriendlyName||s.LocalName,delete s.SampleRateHertz,delete s.Status,s))}async getVoicesByLanguage(e){return(await this.getVoices()).filter(n=>n.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(n=>n.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let t;return typeof e=="string"?t=parseFloat(e.replace("%","")):t=e,t>=0?`+${t}%`:`${t}%`}validateVolume(e){let t;return typeof e=="string"?t=parseInt(e.replace("%",""),10):t=e,`${t}%`}detectSSML(e){let t=e.trim();if(!/^<\?xml|^<speak/i.test(t))return{isValid:!0,isSSML:!1};let s=[],r=/<speak\b[^>]*>[\s\S]*<\/speak>/i.test(t),l=/<voice\b[^>]*>[\s\S]*<\/voice>/i.test(t);if(!r)throw new Error("Invalid SSML: Missing <speak> tag");if(!l)throw new Error("Invalid SSML: Missing <voice> tag");if(!/xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(t)&&r)throw new Error("Invalid SSML: Missing or incorrect namespace declaration");return{isValid:s.length===0,isSSML:r||l,errors:s.length>0?s:void 0}}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,t,n={}){let s=n.inputType||"auto",r=!1;if(s==="ssml")r=!0;else if(s==="text")r=!1;else{let i=this.detectSSML(e);r=i.isSSML,i.isSSML?(console.log("\u2192 Detected SSML input"),i.isValid||console.warn("\u26A0 SSML validation warnings:",i.errors)):console.log("\u2192 Detected plain text input")}if(r){let i=e.trim();return i.includes("xmlns=")||(i=i.replace(/<speak([^>]*)>/i,'<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">')),!/<voice\b[^>]*>/i.test(i)&&t&&(i=i.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i,`$1<voice name="${t}">$2</voice>$3`)),i}let l=this.validatePitch(n.pitch??0),m=this.validateRate(n.rate??0),u=this.validateVolume(n.volume??0),d=this.escapeXML(e);return`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
2
2
  <voice name="${t}">
3
- <prosody pitch="${l}" rate="${p}" volume="${d}">
4
- ${S}
3
+ <prosody pitch="${l}" rate="${m}" volume="${u}">
4
+ ${d}
5
5
  </prosody>
6
6
  </voice>
7
7
  </speak>`}nowRFC1123(){return new Date().toUTCString()}parseRFC1123(e){return new Date(e)}buildTTSConfigMessage(){return`X-Timestamp:${this.nowRFC1123()}\r
8
8
  Content-Type:application/json; charset=utf-8\r
9
9
  Path:speech.config\r
10
10
  \r
11
- {"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,t="en-US-JennyNeural",n={}){let s=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN);return new Promise((i,l)=>{this.audio_stream=[];let p=this.generateUUID(),d=`${a.WSS_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${s}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${p}`;console.log("WebSocket URL:",d),this.ws=new WebSocket(d);let S=this.getSSML(e,t,n),r=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("Synthesis timeout"))},3e4);this.ws.onopen=()=>{let o=this.buildTTSConfigMessage();this.ws.send(o);let h=this.nowRFC1123(),m=`X-RequestId:${p}\r
11
+ {"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,t="en-US-JennyNeural",n={}){let s=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN);return new Promise((r,l)=>{this.audio_stream=[];let m=this.generateUUID(),u=`${a.WSS_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${s}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${m}`;console.log("WebSocket URL:",u),this.ws=new WebSocket(u);let d=this.getSSML(e,t,n),i=!1,o,f=()=>{clearTimeout(o),o=window.setTimeout(()=>{i=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("WebSocket inactivity timeout - no response from server"))},3e4)};this.ws.onopen=()=>{f();let h=this.buildTTSConfigMessage();this.ws.send(h);let S=this.nowRFC1123(),w=`X-RequestId:${m}\r
12
12
  Content-Type:application/ssml+xml\r
13
- X-Timestamp:${h}\r
13
+ X-Timestamp:${S}\r
14
14
  Path:ssml\r
15
15
  \r
16
- ${S}`;this.ws.send(m)},this.ws.onmessage=o=>{if(typeof o.data=="string")o.data.includes("Path:turn.end")&&this.ws?.close();else{let h=new FileReader;h.onload=()=>{let m=h.result,c=new Uint8Array(m);this.processAudioData(c)},h.readAsArrayBuffer(o.data)}},this.ws.onerror=o=>{clearTimeout(r),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(r),i()}})}async*synthesizeStream(e,t="en-US-JennyNeural",n={}){this.audio_stream=[];let s=this.generateUUID(),i=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN),l=`${a.WSS_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${i}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${s}`;this.ws=new WebSocket(l);let p=this.getSSML(e,t,n),d=[],S=!1,r=null,o=null,h=c=>{d.push(c),o&&(o(),o=null)},m=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close()},3e4);for(this.ws.onopen=()=>{let c=this.buildTTSConfigMessage();this.ws.send(c);let g=this.nowRFC1123(),w=`X-RequestId:${s}\r
16
+ ${d}`;this.ws.send(w)},this.ws.onmessage=h=>{if(f(),typeof h.data=="string")h.data.includes("Path:turn.end")&&this.ws?.close();else{let S=new FileReader;S.onload=()=>{let w=S.result,c=new Uint8Array(w);this.processAudioData(c)},S.readAsArrayBuffer(h.data)}},this.ws.onerror=h=>{clearTimeout(o),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(o),i||r()}})}async*synthesizeStream(e,t="en-US-JennyNeural",n={}){this.audio_stream=[];let s=this.generateUUID(),r=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN),l=`${a.WSS_URL}?Ocp-Apim-Subscription-Key=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${r}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${s}`;this.ws=new WebSocket(l);let m=this.getSSML(e,t,n),u=[],d=!1,i=null,o=null,f=c=>{u.push(c),o&&(o(),o=null)},h=!1,S,w=()=>{clearTimeout(S),S=window.setTimeout(()=>{h=!0,i=new Error("WebSocket inactivity timeout - no response from server"),d=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),o&&(o(),o=null)},3e4)};for(this.ws.onopen=()=>{w();let c=this.buildTTSConfigMessage();this.ws.send(c);let g=this.nowRFC1123(),y=`X-RequestId:${s}\r
17
17
  Content-Type:application/ssml+xml\r
18
18
  X-Timestamp:${g}\r
19
19
  Path:ssml\r
20
20
  \r
21
- ${p}`;this.ws.send(w)},this.ws.onmessage=c=>{if(typeof c.data=="string")c.data.includes("Path:turn.end")&&this.ws?.close();else{let g=new FileReader;g.onload=()=>{let w=g.result,y=new Uint8Array(w),E=new TextEncoder().encode(`Path:audio\r
22
- `),x=this.indexOf(y,E);if(x!==-1){let M=y.slice(x+E.length);this.audio_stream.push(M),h(M)}},g.readAsArrayBuffer(c.data)}},this.ws.onerror=c=>{r=new Error("WebSocket error"),S=!0,o&&(o(),o=null)},this.ws.onclose=()=>{clearTimeout(m),S=!0,o&&(o(),o=null)};!S||d.length>0;){if(d.length===0){await new Promise(g=>o=g);continue}let c=d.shift();c&&(yield c)}if(r)throw r}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
23
- `),n=this.indexOf(e,t);if(n!==-1){let s=e.slice(n+t.length);this.audio_stream.push(s)}}indexOf(e,t){for(let n=0;n<=e.length-t.length;n++){let s=!0;for(let i=0;i<t.length;i++)if(e[n+i]!==t[i]){s=!1;break}if(s)return n}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),n=this.parseRFC1123(t),s=Math.floor(n.getTime()/1e3)+11644473600,l=(s-s%300)*1e7,d=new TextEncoder().encode(`${l}${e}`),S=await crypto.subtle.digest("SHA-256",d);return Array.from(new Uint8Array(S)).map(r=>r.toString(16).padStart(2,"0")).join("").toUpperCase()}getAudioData(){if(this.audio_stream.length===0)throw new Error("No audio data available");let e=this.audio_stream.reduce((s,i)=>s+i.length,0),t=new Uint8Array(e),n=0;for(let s of this.audio_stream)t.set(s,n),n+=s.length;return t}};export{C as EdgeTTS};
21
+ ${m}`;this.ws.send(y)},this.ws.onmessage=c=>{if(w(),typeof c.data=="string")c.data.includes("Path:turn.end")&&this.ws?.close();else{let g=new FileReader;g.onload=()=>{let y=g.result,x=new Uint8Array(y),M=new TextEncoder().encode(`Path:audio\r
22
+ `),T=this.indexOf(x,M);if(T!==-1){let v=x.slice(T+M.length);this.audio_stream.push(v),f(v)}},g.readAsArrayBuffer(c.data)}},this.ws.onerror=c=>{clearTimeout(S),i=new Error("WebSocket error"),d=!0,o&&(o(),o=null)},this.ws.onclose=()=>{clearTimeout(S),d=!0,o&&(o(),o=null)};!d||u.length>0;){if(u.length===0){await new Promise(g=>o=g);continue}let c=u.shift();c&&(yield c)}if(i)throw i}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
23
+ `),n=this.indexOf(e,t);if(n!==-1){let s=e.slice(n+t.length);this.audio_stream.push(s)}}indexOf(e,t){for(let n=0;n<=e.length-t.length;n++){let s=!0;for(let r=0;r<t.length;r++)if(e[n+r]!==t[r]){s=!1;break}if(s)return n}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),n=this.parseRFC1123(t),s=Math.floor(n.getTime()/1e3)+11644473600,l=(s-s%300)*1e7,u=new TextEncoder().encode(`${l}${e}`),d=await crypto.subtle.digest("SHA-256",u);return Array.from(new Uint8Array(d)).map(i=>i.toString(16).padStart(2,"0")).join("").toUpperCase()}getAudioData(){if(this.audio_stream.length===0)throw new Error("No audio data available");let e=this.audio_stream.reduce((s,r)=>s+r.length,0),t=new Uint8Array(e),n=0;for(let s of this.audio_stream)t.set(s,n),n+=s.length;return t}};export{C as EdgeTTS};
24
24
  //# sourceMappingURL=edge-tts.esm.min.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/config/constants.ts", "../../src/browser/EdgeTTS.browser.ts"],
4
- "sourcesContent": ["export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n", "import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"Synthesis timeout\"));\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(timeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n resolve();\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n"],
5
- "mappings": "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",
6
- "names": ["Constants", "bytes", "b", "EdgeTTS", "__publicField", "secMsGEC", "Constants", "voice", "locale", "gender", "c", "r", "pitch", "rate", "rateValue", "volume", "volumeValue", "content", "trimmedContent", "errors", "hasSpeakTag", "hasVoiceTag", "text", "options", "inputType", "treatAsSSML", "detection", "ssml", "escapedText", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "queue", "done", "error", "notify", "push", "chunk", "needle", "audioStartIndex", "audioChunk", "buffer", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "result", "offset"]
4
+ "sourcesContent": ["export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n", "import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"WebSocket inactivity timeout - no response from server\"));\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout \r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n if (!timedOut) {\r\n resolve();\r\n }\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n error = new Error(\"WebSocket inactivity timeout - no response from server\");\r\n done = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": ["Constants", "bytes", "b", "EdgeTTS", "__publicField", "secMsGEC", "Constants", "voice", "locale", "gender", "c", "r", "pitch", "rate", "rateValue", "volume", "volumeValue", "content", "trimmedContent", "errors", "hasSpeakTag", "hasVoiceTag", "text", "options", "inputType", "treatAsSSML", "detection", "ssml", "escapedText", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timedOut", "inactivityTimeout", "resetInactivityTimeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "queue", "done", "error", "notify", "push", "chunk", "needle", "audioStartIndex", "audioChunk", "buffer", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "result", "offset"]
7
7
  }
@@ -223,13 +223,20 @@ Path:speech.config\r
223
223
  console.log("WebSocket URL:", url);
224
224
  this.ws = new WebSocket(url);
225
225
  const SSML_text = this.getSSML(text, voice, options);
226
- const timeout = setTimeout(() => {
227
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
228
- this.ws.close();
229
- }
230
- reject(new Error("Synthesis timeout"));
231
- }, 3e4);
226
+ let timedOut = false;
227
+ let inactivityTimeout;
228
+ const resetInactivityTimeout = () => {
229
+ clearTimeout(inactivityTimeout);
230
+ inactivityTimeout = window.setTimeout(() => {
231
+ timedOut = true;
232
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
233
+ this.ws.close();
234
+ }
235
+ reject(new Error("WebSocket inactivity timeout - no response from server"));
236
+ }, 3e4);
237
+ };
232
238
  this.ws.onopen = () => {
239
+ resetInactivityTimeout();
233
240
  const message = this.buildTTSConfigMessage();
234
241
  this.ws.send(message);
235
242
  const timestamp = this.nowRFC1123();
@@ -242,6 +249,7 @@ ${SSML_text}`;
242
249
  this.ws.send(speechMessage);
243
250
  };
244
251
  this.ws.onmessage = (event) => {
252
+ resetInactivityTimeout();
245
253
  if (typeof event.data === "string") {
246
254
  if (event.data.includes("Path:turn.end")) {
247
255
  this.ws?.close();
@@ -257,15 +265,17 @@ ${SSML_text}`;
257
265
  }
258
266
  };
259
267
  this.ws.onerror = (err) => {
260
- clearTimeout(timeout);
268
+ clearTimeout(inactivityTimeout);
261
269
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
262
270
  this.ws.close();
263
271
  }
264
272
  reject(new Error("WebSocket error"));
265
273
  };
266
274
  this.ws.onclose = () => {
267
- clearTimeout(timeout);
268
- resolve();
275
+ clearTimeout(inactivityTimeout);
276
+ if (!timedOut) {
277
+ resolve();
278
+ }
269
279
  };
270
280
  });
271
281
  }
@@ -287,12 +297,25 @@ ${SSML_text}`;
287
297
  notify = null;
288
298
  }
289
299
  };
290
- const timeout = setTimeout(() => {
291
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
292
- this.ws.close();
293
- }
294
- }, 3e4);
300
+ let timedOut = false;
301
+ let inactivityTimeout;
302
+ const resetInactivityTimeout = () => {
303
+ clearTimeout(inactivityTimeout);
304
+ inactivityTimeout = window.setTimeout(() => {
305
+ timedOut = true;
306
+ error = new Error("WebSocket inactivity timeout - no response from server");
307
+ done = true;
308
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
309
+ this.ws.close();
310
+ }
311
+ if (notify) {
312
+ notify();
313
+ notify = null;
314
+ }
315
+ }, 3e4);
316
+ };
295
317
  this.ws.onopen = () => {
318
+ resetInactivityTimeout();
296
319
  const message = this.buildTTSConfigMessage();
297
320
  this.ws.send(message);
298
321
  const timestamp = this.nowRFC1123();
@@ -305,6 +328,7 @@ ${SSML_text}`;
305
328
  this.ws.send(speechMessage);
306
329
  };
307
330
  this.ws.onmessage = (event) => {
331
+ resetInactivityTimeout();
308
332
  if (typeof event.data === "string") {
309
333
  if (event.data.includes("Path:turn.end")) {
310
334
  this.ws?.close();
@@ -326,6 +350,7 @@ ${SSML_text}`;
326
350
  }
327
351
  };
328
352
  this.ws.onerror = (err) => {
353
+ clearTimeout(inactivityTimeout);
329
354
  error = new Error("WebSocket error");
330
355
  done = true;
331
356
  if (notify) {
@@ -334,7 +359,7 @@ ${SSML_text}`;
334
359
  }
335
360
  };
336
361
  this.ws.onclose = () => {
337
- clearTimeout(timeout);
362
+ clearTimeout(inactivityTimeout);
338
363
  done = true;
339
364
  if (notify) {
340
365
  notify();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/browser/EdgeTTS.browser.ts", "../../src/config/constants.ts"],
4
- "sourcesContent": ["import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"Synthesis timeout\"));\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(timeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n resolve();\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n", "export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"WebSocket inactivity timeout - no response from server\"));\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout \r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n if (!timedOut) {\r\n resolve();\r\n }\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n error = new Error(\"WebSocket inactivity timeout - no response from server\");\r\n done = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n", "export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
- "use strict";var EdgeTTSModule=(()=>{var w=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var U=(o,e,t)=>e in o?w(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var k=(o,e)=>{for(var t in e)w(o,t,{get:e[t],enumerable:!0})},_=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of L(e))!A.call(o,s)&&s!==t&&w(o,s,{get:()=>e[s],enumerable:!(n=v(e,s))||n.enumerable});return o};var b=o=>_(w({},"__esModule",{value:!0}),o);var y=(o,e,t)=>U(o,typeof e!="symbol"?e+"":e,t);var R={};k(R,{EdgeTTS:()=>E});var c={TRUSTED_CLIENT_TOKEN:"6A5AA1D4EAFF4E9FB37E23D68491D6F4",BASE_URL:"https://api.msedgeservices.com/tts/cognitiveservices",WSS_URL:"wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",VOICES_URL:"https://api.msedgeservices.com/tts/cognitiveservices/voices/list",CHROMIUM_FULL_VERSION:"142.0.3595.0",CHROMIUM_MAJOR_VERSION:"142",VERSION_MS_GEC:"1-142.0.3595",token32(){let o=new Uint8Array(16);return crypto.getRandomValues(o),Array.from(o).map(e=>e.toString(16).padStart(2,"0")).join("").toUpperCase()},getBaseHeaders(){return{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4",Cookie:"MUID="+this.token32()}},WSS_HEADERS:{Pragma:"no-cache","Cache-Control":"no-cache",Origin:"chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold","Sec-WebSocket-Protocol":"synthesize","Sec-WebSocket-Version":"13","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"},VOICE_HEADERS:{"Sec-CH-UA":'" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',"Sec-CH-UA-Mobile":"?0",Accept:"*/*","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty"}};var E=class{constructor(){y(this,"audio_stream",[]);y(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${c.VOICES_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}`,{headers:c.getBaseHeaders()})).json()).map(s=>(s.FriendlyName=s.FriendlyName||s.LocalName,delete s.SampleRateHertz,delete s.Status,s))}async getVoicesByLanguage(e){return(await this.getVoices()).filter(n=>n.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(n=>n.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let t;return typeof e=="string"?t=parseFloat(e.replace("%","")):t=e,t>=0?`+${t}%`:`${t}%`}validateVolume(e){let t;return typeof e=="string"?t=parseInt(e.replace("%",""),10):t=e,`${t}%`}detectSSML(e){let t=e.trim();if(!/^<\?xml|^<speak/i.test(t))return{isValid:!0,isSSML:!1};let s=[],i=/<speak\b[^>]*>[\s\S]*<\/speak>/i.test(t),d=/<voice\b[^>]*>[\s\S]*<\/voice>/i.test(t);if(!i)throw new Error("Invalid SSML: Missing <speak> tag");if(!d)throw new Error("Invalid SSML: Missing <voice> tag");if(!/xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(t)&&i)throw new Error("Invalid SSML: Missing or incorrect namespace declaration");return{isValid:s.length===0,isSSML:i||d,errors:s.length>0?s:void 0}}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,t,n={}){let s=n.inputType||"auto",i=!1;if(s==="ssml")i=!0;else if(s==="text")i=!1;else{let r=this.detectSSML(e);i=r.isSSML,r.isSSML?(console.log("\u2192 Detected SSML input"),r.isValid||console.warn("\u26A0 SSML validation warnings:",r.errors)):console.log("\u2192 Detected plain text input")}if(i){let r=e.trim();return r.includes("xmlns=")||(r=r.replace(/<speak([^>]*)>/i,'<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">')),!/<voice\b[^>]*>/i.test(r)&&t&&(r=r.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i,`$1<voice name="${t}">$2</voice>$3`)),r}let d=this.validatePitch(n.pitch??0),p=this.validateRate(n.rate??0),u=this.validateVolume(n.volume??0),S=this.escapeXML(e);return`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
1
+ "use strict";var EdgeTTSModule=(()=>{var y=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var U=(a,e,t)=>e in a?y(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var _=(a,e)=>{for(var t in e)y(a,t,{get:e[t],enumerable:!0})},R=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of b(e))!k.call(a,s)&&s!==t&&y(a,s,{get:()=>e[s],enumerable:!(n=A(e,s))||n.enumerable});return a};var N=a=>R(y({},"__esModule",{value:!0}),a);var x=(a,e,t)=>U(a,typeof e!="symbol"?e+"":e,t);var $={};_($,{EdgeTTS:()=>M});var c={TRUSTED_CLIENT_TOKEN:"6A5AA1D4EAFF4E9FB37E23D68491D6F4",BASE_URL:"https://api.msedgeservices.com/tts/cognitiveservices",WSS_URL:"wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",VOICES_URL:"https://api.msedgeservices.com/tts/cognitiveservices/voices/list",CHROMIUM_FULL_VERSION:"142.0.3595.0",CHROMIUM_MAJOR_VERSION:"142",VERSION_MS_GEC:"1-142.0.3595",token32(){let a=new Uint8Array(16);return crypto.getRandomValues(a),Array.from(a).map(e=>e.toString(16).padStart(2,"0")).join("").toUpperCase()},getBaseHeaders(){return{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4",Cookie:"MUID="+this.token32()}},WSS_HEADERS:{Pragma:"no-cache","Cache-Control":"no-cache",Origin:"chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold","Sec-WebSocket-Protocol":"synthesize","Sec-WebSocket-Version":"13","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"},VOICE_HEADERS:{"Sec-CH-UA":'" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',"Sec-CH-UA-Mobile":"?0",Accept:"*/*","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty"}};var M=class{constructor(){x(this,"audio_stream",[]);x(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${c.VOICES_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}`,{headers:c.getBaseHeaders()})).json()).map(s=>(s.FriendlyName=s.FriendlyName||s.LocalName,delete s.SampleRateHertz,delete s.Status,s))}async getVoicesByLanguage(e){return(await this.getVoices()).filter(n=>n.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(n=>n.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let t;return typeof e=="string"?t=parseFloat(e.replace("%","")):t=e,t>=0?`+${t}%`:`${t}%`}validateVolume(e){let t;return typeof e=="string"?t=parseInt(e.replace("%",""),10):t=e,`${t}%`}detectSSML(e){let t=e.trim();if(!/^<\?xml|^<speak/i.test(t))return{isValid:!0,isSSML:!1};let s=[],r=/<speak\b[^>]*>[\s\S]*<\/speak>/i.test(t),u=/<voice\b[^>]*>[\s\S]*<\/voice>/i.test(t);if(!r)throw new Error("Invalid SSML: Missing <speak> tag");if(!u)throw new Error("Invalid SSML: Missing <voice> tag");if(!/xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(t)&&r)throw new Error("Invalid SSML: Missing or incorrect namespace declaration");return{isValid:s.length===0,isSSML:r||u,errors:s.length>0?s:void 0}}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,t,n={}){let s=n.inputType||"auto",r=!1;if(s==="ssml")r=!0;else if(s==="text")r=!1;else{let i=this.detectSSML(e);r=i.isSSML,i.isSSML?(console.log("\u2192 Detected SSML input"),i.isValid||console.warn("\u26A0 SSML validation warnings:",i.errors)):console.log("\u2192 Detected plain text input")}if(r){let i=e.trim();return i.includes("xmlns=")||(i=i.replace(/<speak([^>]*)>/i,'<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">')),!/<voice\b[^>]*>/i.test(i)&&t&&(i=i.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i,`$1<voice name="${t}">$2</voice>$3`)),i}let u=this.validatePitch(n.pitch??0),m=this.validateRate(n.rate??0),d=this.validateVolume(n.volume??0),S=this.escapeXML(e);return`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
2
2
  <voice name="${t}">
3
- <prosody pitch="${d}" rate="${p}" volume="${u}">
3
+ <prosody pitch="${u}" rate="${m}" volume="${d}">
4
4
  ${S}
5
5
  </prosody>
6
6
  </voice>
@@ -8,18 +8,18 @@
8
8
  Content-Type:application/json; charset=utf-8\r
9
9
  Path:speech.config\r
10
10
  \r
11
- {"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,t="en-US-JennyNeural",n={}){let s=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN);return new Promise((i,d)=>{this.audio_stream=[];let p=this.generateUUID(),u=`${c.WSS_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${s}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${p}`;console.log("WebSocket URL:",u),this.ws=new WebSocket(u);let S=this.getSSML(e,t,n),r=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),d(new Error("Synthesis timeout"))},3e4);this.ws.onopen=()=>{let a=this.buildTTSConfigMessage();this.ws.send(a);let h=this.nowRFC1123(),m=`X-RequestId:${p}\r
11
+ {"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,t="en-US-JennyNeural",n={}){let s=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN);return new Promise((r,u)=>{this.audio_stream=[];let m=this.generateUUID(),d=`${c.WSS_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${s}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${m}`;console.log("WebSocket URL:",d),this.ws=new WebSocket(d);let S=this.getSSML(e,t,n),i=!1,o,f=()=>{clearTimeout(o),o=window.setTimeout(()=>{i=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),u(new Error("WebSocket inactivity timeout - no response from server"))},3e4)};this.ws.onopen=()=>{f();let h=this.buildTTSConfigMessage();this.ws.send(h);let p=this.nowRFC1123(),w=`X-RequestId:${m}\r
12
12
  Content-Type:application/ssml+xml\r
13
- X-Timestamp:${h}\r
13
+ X-Timestamp:${p}\r
14
14
  Path:ssml\r
15
15
  \r
16
- ${S}`;this.ws.send(m)},this.ws.onmessage=a=>{if(typeof a.data=="string")a.data.includes("Path:turn.end")&&this.ws?.close();else{let h=new FileReader;h.onload=()=>{let m=h.result,l=new Uint8Array(m);this.processAudioData(l)},h.readAsArrayBuffer(a.data)}},this.ws.onerror=a=>{clearTimeout(r),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),d(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(r),i()}})}async*synthesizeStream(e,t="en-US-JennyNeural",n={}){this.audio_stream=[];let s=this.generateUUID(),i=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN),d=`${c.WSS_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${i}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${s}`;this.ws=new WebSocket(d);let p=this.getSSML(e,t,n),u=[],S=!1,r=null,a=null,h=l=>{u.push(l),a&&(a(),a=null)},m=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close()},3e4);for(this.ws.onopen=()=>{let l=this.buildTTSConfigMessage();this.ws.send(l);let g=this.nowRFC1123(),f=`X-RequestId:${s}\r
16
+ ${S}`;this.ws.send(w)},this.ws.onmessage=h=>{if(f(),typeof h.data=="string")h.data.includes("Path:turn.end")&&this.ws?.close();else{let p=new FileReader;p.onload=()=>{let w=p.result,l=new Uint8Array(w);this.processAudioData(l)},p.readAsArrayBuffer(h.data)}},this.ws.onerror=h=>{clearTimeout(o),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),u(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(o),i||r()}})}async*synthesizeStream(e,t="en-US-JennyNeural",n={}){this.audio_stream=[];let s=this.generateUUID(),r=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN),u=`${c.WSS_URL}?Ocp-Apim-Subscription-Key=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${r}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${s}`;this.ws=new WebSocket(u);let m=this.getSSML(e,t,n),d=[],S=!1,i=null,o=null,f=l=>{d.push(l),o&&(o(),o=null)},h=!1,p,w=()=>{clearTimeout(p),p=window.setTimeout(()=>{h=!0,i=new Error("WebSocket inactivity timeout - no response from server"),S=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),o&&(o(),o=null)},3e4)};for(this.ws.onopen=()=>{w();let l=this.buildTTSConfigMessage();this.ws.send(l);let g=this.nowRFC1123(),E=`X-RequestId:${s}\r
17
17
  Content-Type:application/ssml+xml\r
18
18
  X-Timestamp:${g}\r
19
19
  Path:ssml\r
20
20
  \r
21
- ${p}`;this.ws.send(f)},this.ws.onmessage=l=>{if(typeof l.data=="string")l.data.includes("Path:turn.end")&&this.ws?.close();else{let g=new FileReader;g.onload=()=>{let f=g.result,x=new Uint8Array(f),M=new TextEncoder().encode(`Path:audio\r
22
- `),C=this.indexOf(x,M);if(C!==-1){let T=x.slice(C+M.length);this.audio_stream.push(T),h(T)}},g.readAsArrayBuffer(l.data)}},this.ws.onerror=l=>{r=new Error("WebSocket error"),S=!0,a&&(a(),a=null)},this.ws.onclose=()=>{clearTimeout(m),S=!0,a&&(a(),a=null)};!S||u.length>0;){if(u.length===0){await new Promise(g=>a=g);continue}let l=u.shift();l&&(yield l)}if(r)throw r}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
23
- `),n=this.indexOf(e,t);if(n!==-1){let s=e.slice(n+t.length);this.audio_stream.push(s)}}indexOf(e,t){for(let n=0;n<=e.length-t.length;n++){let s=!0;for(let i=0;i<t.length;i++)if(e[n+i]!==t[i]){s=!1;break}if(s)return n}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),n=this.parseRFC1123(t),s=Math.floor(n.getTime()/1e3)+11644473600,d=(s-s%300)*1e7,u=new TextEncoder().encode(`${d}${e}`),S=await crypto.subtle.digest("SHA-256",u);return Array.from(new Uint8Array(S)).map(r=>r.toString(16).padStart(2,"0")).join("").toUpperCase()}getAudioData(){if(this.audio_stream.length===0)throw new Error("No audio data available");let e=this.audio_stream.reduce((s,i)=>s+i.length,0),t=new Uint8Array(e),n=0;for(let s of this.audio_stream)t.set(s,n),n+=s.length;return t}};return b(R);})();
21
+ ${m}`;this.ws.send(E)},this.ws.onmessage=l=>{if(w(),typeof l.data=="string")l.data.includes("Path:turn.end")&&this.ws?.close();else{let g=new FileReader;g.onload=()=>{let E=g.result,T=new Uint8Array(E),v=new TextEncoder().encode(`Path:audio\r
22
+ `),C=this.indexOf(T,v);if(C!==-1){let L=T.slice(C+v.length);this.audio_stream.push(L),f(L)}},g.readAsArrayBuffer(l.data)}},this.ws.onerror=l=>{clearTimeout(p),i=new Error("WebSocket error"),S=!0,o&&(o(),o=null)},this.ws.onclose=()=>{clearTimeout(p),S=!0,o&&(o(),o=null)};!S||d.length>0;){if(d.length===0){await new Promise(g=>o=g);continue}let l=d.shift();l&&(yield l)}if(i)throw i}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
23
+ `),n=this.indexOf(e,t);if(n!==-1){let s=e.slice(n+t.length);this.audio_stream.push(s)}}indexOf(e,t){for(let n=0;n<=e.length-t.length;n++){let s=!0;for(let r=0;r<t.length;r++)if(e[n+r]!==t[r]){s=!1;break}if(s)return n}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),n=this.parseRFC1123(t),s=Math.floor(n.getTime()/1e3)+11644473600,u=(s-s%300)*1e7,d=new TextEncoder().encode(`${u}${e}`),S=await crypto.subtle.digest("SHA-256",d);return Array.from(new Uint8Array(S)).map(i=>i.toString(16).padStart(2,"0")).join("").toUpperCase()}getAudioData(){if(this.audio_stream.length===0)throw new Error("No audio data available");let e=this.audio_stream.reduce((s,r)=>s+r.length,0),t=new Uint8Array(e),n=0;for(let s of this.audio_stream)t.set(s,n),n+=s.length;return t}};return N($);})();
24
24
  window.EdgeTTS = EdgeTTSModule.EdgeTTS; window.EdgeTTSVoice = EdgeTTSModule.Voice;
25
25
  //# sourceMappingURL=edge-tts.umd.min.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/browser/EdgeTTS.browser.ts", "../../src/config/constants.ts"],
4
- "sourcesContent": ["import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"Synthesis timeout\"));\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(timeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n resolve();\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n const timeout = setTimeout(() => {\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n }, 30000);\r\n\r\n this.ws.onopen = () => {\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(timeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n", "export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n"],
5
- "mappings": "ykBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,ICAO,IAAMC,EAAY,CACrB,qBAAsB,mCACtB,SAAU,uDACV,QAAS,kEACT,WAAY,mEAEZ,sBAAuB,eACvB,uBAAwB,MACxB,eAAgB,eAEhB,SAAU,CACN,IAAMC,EAAQ,IAAI,WAAW,EAAE,EAC/B,cAAO,gBAAgBA,CAAK,EAErB,MAAM,KAAKA,CAAK,EAClB,IAAIC,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EACxC,KAAK,EAAE,EACP,YAAY,CACrB,EAEA,gBAAiB,CACb,MAAO,CACH,aAAc,gIACd,kBAAmB,0BACnB,kBAAmB,0EACnB,OAAU,QAAU,KAAK,QAAQ,CACrC,CACJ,EAEA,YAAa,CACT,OAAU,WACV,gBAAiB,WACjB,OAAU,sDACV,yBAA0B,aAC1B,wBAAyB,KACzB,aAAc,+HAClB,EAEA,cAAe,CACX,YAAa,sEACb,mBAAoB,KACpB,OAAU,MACV,iBAAkB,OAClB,iBAAkB,OAClB,iBAAkB,OACtB,CACJ,EDtBO,IAAMC,EAAN,KAAc,CAAd,cACHC,EAAA,KAAQ,eAA6B,CAAC,GACtCA,EAAA,KAAQ,MAER,MAAM,WAA8B,CAChC,IAAMC,EAAW,MAAM,KAAK,iBAAiBC,EAAU,oBAAoB,EAQ3E,OADa,MALI,MAAM,MACnB,GAAGA,EAAU,UAAU,8BAA8BA,EAAU,oBAAoB,eAAeD,CAAQ,uBAAuBC,EAAU,cAAc,GACzJ,CAAE,QAASA,EAAU,eAAe,CAAE,CAC1C,GAE4B,KAAK,GACrB,IAAKC,IACbA,EAAM,aAAeA,EAAM,cAAgBA,EAAM,UACjD,OAAOA,EAAM,gBACb,OAAOA,EAAM,OACNA,EACV,CACL,CAEA,MAAM,oBAAoBC,EAAkC,CAExD,OADe,MAAM,KAAK,UAAU,GACtB,OAAOD,GAASA,EAAM,OAAO,WAAWC,CAAM,CAAC,CACjE,CAEA,MAAM,kBAAkBC,EAA6C,CAEjE,OADe,MAAM,KAAK,UAAU,GACtB,OAAOF,GAASA,EAAM,SAAWE,CAAM,CACzD,CAEQ,cAAuB,CAC3B,MAAO,uCAAuC,QAAQ,QAAS,SAAUC,EAAG,CACxE,IAAMC,EAAI,KAAK,OAAO,EAAI,GAAK,EAE/B,OADUD,IAAM,IAAMC,EAAKA,EAAI,EAAM,GAC5B,SAAS,EAAE,CACxB,CAAC,CACL,CAEQ,cAAcC,EAAgC,CAClD,OAAI,OAAOA,GAAU,SACTA,GAAS,EAAI,IAAIA,CAAK,KAAO,GAAGA,CAAK,KAE1CA,CACX,CAEQ,aAAaC,EAA+B,CAChD,IAAIC,EACJ,OAAI,OAAOD,GAAS,SAChBC,EAAY,WAAWD,EAAK,QAAQ,IAAK,EAAE,CAAC,EAE5CC,EAAYD,EAETC,GAAa,EAAI,IAAIA,CAAS,IAAM,GAAGA,CAAS,GAC3D,CAEQ,eAAeC,EAAiC,CACpD,IAAIC,EACJ,OAAI,OAAOD,GAAW,SAClBC,EAAc,SAASD,EAAO,QAAQ,IAAK,EAAE,EAAG,EAAE,EAElDC,EAAcD,EAEX,GAAGC,CAAW,GACzB,CAEQ,WAAWC,EAAuC,CACtD,IAAMC,EAAiBD,EAAQ,KAAK,EAGpC,GAAI,CAFkB,mBAAmB,KAAKC,CAAc,EAGxD,MAAO,CAAE,QAAS,GAAM,OAAQ,EAAM,EAG1C,IAAMC,EAAmB,CAAC,EACpBC,EAAc,kCAAkC,KAAKF,CAAc,EACnEG,EAAc,kCAAkC,KAAKH,CAAc,EAEzE,GAAI,CAACE,EACD,MAAM,IAAI,MAAM,mCAAmC,EAGvD,GAAI,CAACC,EACD,MAAM,IAAI,MAAM,mCAAmC,EAIvD,GAAI,CADwB,sDAAsD,KAAKH,CAAc,GACzEE,EACxB,MAAM,IAAI,MAAM,0DAA0D,EAG9E,MAAO,CACH,QAASD,EAAO,SAAW,EAC3B,OAAQC,GAAeC,EACvB,OAAQF,EAAO,OAAS,EAAIA,EAAS,MACzC,CACJ,CAEQ,UAAUG,EAAsB,CACpC,OAAOA,EACF,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,QAAQ,CAC/B,CAEQ,QAAQL,EAAiBV,EAAegB,EAA4B,CAAC,EAAW,CACpF,IAAMC,EAAYD,EAAQ,WAAa,OACnCE,EAAc,GAElB,GAAID,IAAc,OACdC,EAAc,WACPD,IAAc,OACrBC,EAAc,OACX,CACH,IAAMC,EAAY,KAAK,WAAWT,CAAO,EACzCQ,EAAcC,EAAU,OAEpBA,EAAU,QACV,QAAQ,IAAI,4BAAuB,EAC9BA,EAAU,SACX,QAAQ,KAAK,mCAA+BA,EAAU,MAAM,GAGhE,QAAQ,IAAI,kCAA6B,CAEjD,CAEA,GAAID,EAAa,CACb,IAAIE,EAAOV,EAAQ,KAAK,EAExB,OAAKU,EAAK,SAAS,QAAQ,IACvBA,EAAOA,EAAK,QACR,kBACA,mGACJ,GAGA,CAAC,kBAAkB,KAAKA,CAAI,GAAKpB,IACjCoB,EAAOA,EAAK,QACR,uCACA,kBAAkBpB,CAAK,gBAC3B,GAGGoB,CACX,CAEA,IAAMf,EAAQ,KAAK,cAAcW,EAAQ,OAAS,CAAC,EAC7CV,EAAO,KAAK,aAAaU,EAAQ,MAAQ,CAAC,EAC1CR,EAAS,KAAK,eAAeQ,EAAQ,QAAU,CAAC,EAChDK,EAAc,KAAK,UAAUX,CAAO,EAE1C,MAAO;AAAA,mCACoBV,CAAK;AAAA,0CACEK,CAAK,WAAWC,CAAI,aAAaE,CAAM;AAAA,8BACnDa,CAAW;AAAA;AAAA;AAAA,yBAIrC,CAEQ,YAAqB,CACzB,OAAO,IAAI,KAAK,EAAE,YAAY,CAClC,CAEQ,aAAaC,EAAsB,CACvC,OAAO,IAAI,KAAKA,CAAM,CAC1B,CAEQ,uBAAgC,CAEpC,MAAO,eADW,KAAK,WAAW,CACH;AAAA;AAAA;AAAA;AAAA,sKAEnC,CAEA,MAAM,WAAWP,EAAcf,EAAgB,oBAAqBgB,EAA4B,CAAC,EAAkB,CAC/G,IAAMlB,EAAW,MAAM,KAAK,iBAAiBC,EAAU,oBAAoB,EAE3E,OAAO,IAAI,QAAQ,CAACwB,EAASC,IAAW,CACpC,KAAK,aAAe,CAAC,EACrB,IAAMC,EAAQ,KAAK,aAAa,EAC1BC,EAAM,GAAG3B,EAAU,OAAO,8BAA8BA,EAAU,oBAAoB,eAAeD,CAAQ,uBAAuBC,EAAU,cAAc,iBAAiB0B,CAAK,GAExL,QAAQ,IAAI,iBAAkBC,CAAG,EACjC,KAAK,GAAK,IAAI,UAAUA,CAAG,EAE3B,IAAMC,EAAY,KAAK,QAAQZ,EAAMf,EAAOgB,CAAO,EAE7CY,EAAU,WAAW,IAAM,CACzB,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,MAC5C,KAAK,GAAG,MAAM,EAElBJ,EAAO,IAAI,MAAM,mBAAmB,CAAC,CACzC,EAAG,GAAK,EAER,KAAK,GAAG,OAAS,IAAM,CACnB,IAAMK,EAAU,KAAK,sBAAsB,EAC3C,KAAK,GAAI,KAAKA,CAAO,EACrB,IAAMC,EAAY,KAAK,WAAW,EAC5BC,EAAgB,eAAeN,CAAK;AAAA;AAAA,cAAwDK,CAAS;AAAA;AAAA;AAAA,EAAwBH,CAAS,GAC5I,KAAK,GAAI,KAAKI,CAAa,CAC/B,EAEA,KAAK,GAAG,UAAaC,GAAU,CAC3B,GAAI,OAAOA,EAAM,MAAS,SAClBA,EAAM,KAAK,SAAS,eAAe,GACnC,KAAK,IAAI,MAAM,MAEhB,CACH,IAAMC,EAAS,IAAI,WACnBA,EAAO,OAAS,IAAM,CAClB,IAAMC,EAAcD,EAAO,OACrBE,EAAa,IAAI,WAAWD,CAAW,EAC7C,KAAK,iBAAiBC,CAAU,CACpC,EACAF,EAAO,kBAAkBD,EAAM,IAAI,CACvC,CACJ,EAEA,KAAK,GAAG,QAAWI,GAAa,CAC5B,aAAaR,CAAO,EAChB,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,MAC5C,KAAK,GAAG,MAAM,EAElBJ,EAAO,IAAI,MAAM,iBAAiB,CAAC,CACvC,EAEA,KAAK,GAAG,QAAU,IAAM,CACpB,aAAaI,CAAO,EACpBL,EAAQ,CACZ,CACJ,CAAC,CACL,CAEA,MAAO,iBAAiBR,EAAcf,EAAgB,oBAAqBgB,EAA4B,CAAC,EAA8C,CAClJ,KAAK,aAAe,CAAC,EAErB,IAAMS,EAAQ,KAAK,aAAa,EAC1B3B,EAAW,MAAM,KAAK,iBAAiBC,EAAU,oBAAoB,EAErE2B,EAAM,GAAG3B,EAAU,OAAO,8BAA8BA,EAAU,oBAAoB,eAAeD,CAAQ,uBAAuBC,EAAU,cAAc,iBAAiB0B,CAAK,GAExL,KAAK,GAAK,IAAI,UAAUC,CAAG,EAE3B,IAAMC,EAAY,KAAK,QAAQZ,EAAMf,EAAOgB,CAAO,EAE7CqB,EAAsB,CAAC,EACzBC,EAAO,GACPC,EAAsB,KACtBC,EAA8B,KAE5BC,EAAQC,GAAsB,CAChCL,EAAM,KAAKK,CAAK,EACZF,IACAA,EAAO,EACPA,EAAS,KAEjB,EAEMZ,EAAU,WAAW,IAAM,CACzB,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,MAC5C,KAAK,GAAG,MAAM,CAEtB,EAAG,GAAK,EAqDR,IAnDA,KAAK,GAAG,OAAS,IAAM,CACnB,IAAMC,EAAU,KAAK,sBAAsB,EAC3C,KAAK,GAAI,KAAKA,CAAO,EAErB,IAAMC,EAAY,KAAK,WAAW,EAC5BC,EAAgB,eAAeN,CAAK;AAAA;AAAA,cAAwDK,CAAS;AAAA;AAAA;AAAA,EAAwBH,CAAS,GAC5I,KAAK,GAAI,KAAKI,CAAa,CAC/B,EAEA,KAAK,GAAG,UAAaC,GAAU,CAC3B,GAAI,OAAOA,EAAM,MAAS,SAClBA,EAAM,KAAK,SAAS,eAAe,GACnC,KAAK,IAAI,MAAM,MAEhB,CACH,IAAMC,EAAS,IAAI,WACnBA,EAAO,OAAS,IAAM,CAClB,IAAMC,EAAcD,EAAO,OACrBE,EAAa,IAAI,WAAWD,CAAW,EAEvCS,EAAS,IAAI,YAAY,EAAE,OAAO;AAAA,CAAgB,EAClDC,EAAkB,KAAK,QAAQT,EAAYQ,CAAM,EAEvD,GAAIC,IAAoB,GAAI,CACxB,IAAMC,EAAaV,EAAW,MAAMS,EAAkBD,EAAO,MAAM,EACnE,KAAK,aAAa,KAAKE,CAAU,EACjCJ,EAAKI,CAAU,CACnB,CACJ,EACAZ,EAAO,kBAAkBD,EAAM,IAAI,CACvC,CACJ,EAEA,KAAK,GAAG,QAAWI,GAAa,CAC5BG,EAAQ,IAAI,MAAM,iBAAiB,EACnCD,EAAO,GACHE,IACAA,EAAO,EACPA,EAAS,KAEjB,EAEA,KAAK,GAAG,QAAU,IAAM,CACpB,aAAaZ,CAAO,EACpBU,EAAO,GACHE,IACAA,EAAO,EACPA,EAAS,KAEjB,EAEO,CAACF,GAAQD,EAAM,OAAS,GAAG,CAC9B,GAAIA,EAAM,SAAW,EAAG,CACpB,MAAM,IAAI,QAAcd,GAAYiB,EAASjB,CAAQ,EACrD,QACJ,CACA,IAAMmB,EAAQL,EAAM,MAAM,EACtBK,IACA,MAAMA,EAEd,CAEA,GAAIH,EACA,MAAMA,CAEd,CAEQ,iBAAiBO,EAA0B,CAC/C,IAAMH,EAAS,IAAI,YAAY,EAAE,OAAO;AAAA,CAAgB,EAClDC,EAAkB,KAAK,QAAQE,EAAQH,CAAM,EAEnD,GAAIC,IAAoB,GAAI,CACxB,IAAMC,EAAaC,EAAO,MAAMF,EAAkBD,EAAO,MAAM,EAC/D,KAAK,aAAa,KAAKE,CAAU,CACrC,CACJ,CAEQ,QAAQE,EAAiBC,EAA4B,CACzD,QAASC,EAAI,EAAGA,GAAKF,EAAI,OAASC,EAAO,OAAQC,IAAK,CAClD,IAAIC,EAAQ,GACZ,QAASC,EAAI,EAAGA,EAAIH,EAAO,OAAQG,IAC/B,GAAIJ,EAAIE,EAAIE,CAAC,IAAMH,EAAOG,CAAC,EAAG,CAC1BD,EAAQ,GACR,KACJ,CAEJ,GAAIA,EAAO,OAAOD,CACtB,CACA,MAAO,EACX,CAEA,MAAc,iBAAiBG,EAA6C,CACxE,IAAMC,EAAM,KAAK,WAAW,EACtBC,EAAY,KAAK,aAAaD,CAAG,EACjCE,EAAQ,KAAK,MAAMD,EAAU,QAAQ,EAAI,GAAI,EAAI,YAEjDE,GADUD,EAASA,EAAQ,KACF,IAGzBE,EADU,IAAI,YAAY,EACX,OAAO,GAAGD,CAAY,GAAGJ,CAAkB,EAAE,EAC5DM,EAAa,MAAM,OAAO,OAAO,OAAO,UAAWD,CAAI,EAE7D,OAAO,MAAM,KAAK,IAAI,WAAWC,CAAU,CAAC,EACvC,IAAIC,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EACxC,KAAK,EAAE,EACP,YAAY,CACrB,CAEA,cAA2B,CACvB,GAAI,KAAK,aAAa,SAAW,EAC7B,MAAM,IAAI,MAAM,yBAAyB,EAE7C,IAAMC,EAAc,KAAK,aAAa,OAAO,CAACC,EAAKnB,IAAUmB,EAAMnB,EAAM,OAAQ,CAAC,EAC5EoB,EAAS,IAAI,WAAWF,CAAW,EACrCG,EAAS,EACb,QAAWrB,KAAS,KAAK,aACrBoB,EAAO,IAAIpB,EAAOqB,CAAM,EACxBA,GAAUrB,EAAM,OAEpB,OAAOoB,CACX,CACJ",
6
- "names": ["EdgeTTS_browser_exports", "__export", "EdgeTTS", "Constants", "bytes", "b", "EdgeTTS", "__publicField", "secMsGEC", "Constants", "voice", "locale", "gender", "c", "r", "pitch", "rate", "rateValue", "volume", "volumeValue", "content", "trimmedContent", "errors", "hasSpeakTag", "hasVoiceTag", "text", "options", "inputType", "treatAsSSML", "detection", "ssml", "escapedText", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "queue", "done", "error", "notify", "push", "chunk", "needle", "audioStartIndex", "audioChunk", "buffer", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "result", "offset"]
4
+ "sourcesContent": ["import { Constants } from '../config/constants';\r\n\r\nexport interface Voice {\r\n Name: string;\r\n ShortName: string;\r\n Gender: string;\r\n Locale: string;\r\n FriendlyName: string;\r\n LocalName: string;\r\n}\r\n\r\nexport interface SynthesisOptions {\r\n pitch?: string | number;\r\n rate?: string | number;\r\n volume?: string | number;\r\n inputType?: 'auto' | 'ssml' | 'text';\r\n}\r\n\r\ninterface SSMLValidationResult {\r\n isValid: boolean;\r\n isSSML: boolean;\r\n errors?: string[];\r\n}\r\n\r\nexport class EdgeTTS {\r\n private audio_stream: Uint8Array[] = [];\r\n private ws?: WebSocket;\r\n\r\n async getVoices(): Promise<Voice[]> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n \r\n const response = await fetch(\r\n `${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,\r\n { headers: Constants.getBaseHeaders() }\r\n );\r\n \r\n const data = await response.json();\r\n return data.map((voice: any) => {\r\n voice.FriendlyName = voice.FriendlyName || voice.LocalName;\r\n delete voice.SampleRateHertz;\r\n delete voice.Status;\r\n return voice;\r\n });\r\n }\r\n\r\n async getVoicesByLanguage(locale: string): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Locale.startsWith(locale));\r\n }\r\n\r\n async getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]> {\r\n const voices = await this.getVoices();\r\n return voices.filter(voice => voice.Gender === gender);\r\n }\r\n\r\n private generateUUID(): string {\r\n return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n });\r\n }\r\n\r\n private validatePitch(pitch: string | number): string {\r\n if (typeof pitch === 'number') {\r\n return (pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`);\r\n }\r\n return pitch;\r\n }\r\n\r\n private validateRate(rate: string | number): string {\r\n let rateValue: number;\r\n if (typeof rate === 'string') {\r\n rateValue = parseFloat(rate.replace('%', ''));\r\n } else {\r\n rateValue = rate;\r\n }\r\n return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;\r\n }\r\n\r\n private validateVolume(volume: string | number): string {\r\n let volumeValue: number;\r\n if (typeof volume === 'string') {\r\n volumeValue = parseInt(volume.replace('%', ''), 10);\r\n } else {\r\n volumeValue = volume;\r\n }\r\n return `${volumeValue}%`;\r\n }\r\n\r\n private detectSSML(content: string): SSMLValidationResult {\r\n const trimmedContent = content.trim();\r\n const looksLikeSSML = /^<\\?xml|^<speak/i.test(trimmedContent);\r\n\r\n if (!looksLikeSSML) {\r\n return { isValid: true, isSSML: false };\r\n }\r\n\r\n const errors: string[] = [];\r\n const hasSpeakTag = /<speak\\b[^>]*>[\\s\\S]*<\\/speak>/i.test(trimmedContent);\r\n const hasVoiceTag = /<voice\\b[^>]*>[\\s\\S]*<\\/voice>/i.test(trimmedContent);\r\n\r\n if (!hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing <speak> tag');\r\n }\r\n\r\n if (!hasVoiceTag) {\r\n throw new Error('Invalid SSML: Missing <voice> tag');\r\n }\r\n\r\n const hasCorrectNamespace = /xmlns=\"http:\\/\\/www\\.w3\\.org\\/2001\\/10\\/synthesis\"/i.test(trimmedContent);\r\n if (!hasCorrectNamespace && hasSpeakTag) {\r\n throw new Error('Invalid SSML: Missing or incorrect namespace declaration');\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n isSSML: hasSpeakTag || hasVoiceTag,\r\n errors: errors.length > 0 ? errors : undefined\r\n };\r\n }\r\n\r\n private escapeXML(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n }\r\n\r\n private getSSML(content: string, voice: string, options: SynthesisOptions = {}): string {\r\n const inputType = options.inputType || 'auto';\r\n let treatAsSSML = false;\r\n\r\n if (inputType === 'ssml') {\r\n treatAsSSML = true;\r\n } else if (inputType === 'text') {\r\n treatAsSSML = false;\r\n } else { // 'auto'\r\n const detection = this.detectSSML(content);\r\n treatAsSSML = detection.isSSML;\r\n\r\n if (detection.isSSML) {\r\n console.log('\u2192 Detected SSML input');\r\n if (!detection.isValid) {\r\n console.warn('\u26A0 SSML validation warnings:', detection.errors);\r\n }\r\n } else {\r\n console.log('\u2192 Detected plain text input');\r\n }\r\n }\r\n\r\n if (treatAsSSML) {\r\n let ssml = content.trim();\r\n\r\n if (!ssml.includes('xmlns=')) {\r\n ssml = ssml.replace(\r\n /<speak([^>]*)>/i,\r\n '<speak$1 xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\">'\r\n );\r\n }\r\n\r\n if (!/<voice\\b[^>]*>/i.test(ssml) && voice) {\r\n ssml = ssml.replace(\r\n /(<speak[^>]*>)([\\s\\S]*?)(<\\/speak>)/i,\r\n `$1<voice name=\"${voice}\">$2</voice>$3`\r\n );\r\n }\r\n\r\n return ssml;\r\n }\r\n\r\n const pitch = this.validatePitch(options.pitch ?? 0);\r\n const rate = this.validateRate(options.rate ?? 0);\r\n const volume = this.validateVolume(options.volume ?? 0);\r\n const escapedText = this.escapeXML(content);\r\n\r\n return `<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"en-US\">\r\n <voice name=\"${voice}\">\r\n <prosody pitch=\"${pitch}\" rate=\"${rate}\" volume=\"${volume}\">\r\n ${escapedText}\r\n </prosody>\r\n </voice>\r\n </speak>`;\r\n }\r\n\r\n private nowRFC1123(): string {\r\n return new Date().toUTCString();\r\n }\r\n\r\n private parseRFC1123(rfcStr: string): Date {\r\n return new Date(rfcStr);\r\n }\r\n\r\n private buildTTSConfigMessage(): string {\r\n const timestamp = this.nowRFC1123();\r\n return `X-Timestamp:${timestamp}\\r\\nContent-Type:application/json; charset=utf-8\\r\\nPath:speech.config\\r\\n\\r\\n` +\r\n `{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":false,\"wordBoundaryEnabled\":true},\"outputFormat\":\"audio-24khz-48kbitrate-mono-mp3\"}}}}`;\r\n }\r\n\r\n async synthesize(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): Promise<void> {\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n return new Promise((resolve, reject) => {\r\n this.audio_stream = [];\r\n const reqId = this.generateUUID();\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n \r\n console.log('WebSocket URL:', url);\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n \r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error(\"WebSocket inactivity timeout - no response from server\"));\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout \r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n this.processAudioData(uint8Array);\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n reject(new Error('WebSocket error'));\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n if (!timedOut) {\r\n resolve();\r\n }\r\n };\r\n });\r\n }\r\n\r\n async *synthesizeStream(text: string, voice: string = 'en-US-JennyNeural', options: SynthesisOptions = {}): AsyncGenerator<Uint8Array, void, unknown> {\r\n this.audio_stream = [];\r\n\r\n const reqId = this.generateUUID();\r\n const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);\r\n\r\n const url = `${Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;\r\n\r\n this.ws = new WebSocket(url);\r\n\r\n const SSML_text = this.getSSML(text, voice, options);\r\n\r\n const queue: Uint8Array[] = [];\r\n let done = false;\r\n let error: Error | null = null;\r\n let notify: (() => void) | null = null;\r\n\r\n const push = (chunk: Uint8Array) => {\r\n queue.push(chunk);\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n let timedOut = false;\r\n let inactivityTimeout: number;\r\n \r\n const resetInactivityTimeout = () => {\r\n clearTimeout(inactivityTimeout);\r\n inactivityTimeout = window.setTimeout(() => {\r\n timedOut = true;\r\n error = new Error(\"WebSocket inactivity timeout - no response from server\");\r\n done = true;\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.close();\r\n }\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n }, 30000); // 30 seconds of inactivity\r\n };\r\n\r\n this.ws.onopen = () => {\r\n resetInactivityTimeout(); // start the inactivity timeout\r\n const message = this.buildTTSConfigMessage();\r\n this.ws!.send(message);\r\n\r\n const timestamp = this.nowRFC1123();\r\n const speechMessage = `X-RequestId:${reqId}\\r\\nContent-Type:application/ssml+xml\\r\\nX-Timestamp:${timestamp}\\r\\nPath:ssml\\r\\n\\r\\n${SSML_text}`;\r\n this.ws!.send(speechMessage);\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n resetInactivityTimeout(); // restart inactivity timeout\r\n if (typeof event.data === 'string') {\r\n if (event.data.includes('Path:turn.end')) {\r\n this.ws?.close();\r\n }\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const arrayBuffer = reader.result as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n \r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(uint8Array, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = uint8Array.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n push(audioChunk);\r\n }\r\n };\r\n reader.readAsArrayBuffer(event.data);\r\n }\r\n };\r\n\r\n this.ws.onerror = (err: any) => {\r\n clearTimeout(inactivityTimeout);\r\n error = new Error('WebSocket error');\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n this.ws.onclose = () => {\r\n clearTimeout(inactivityTimeout);\r\n done = true;\r\n if (notify) {\r\n notify();\r\n notify = null;\r\n }\r\n };\r\n\r\n while (!done || queue.length > 0) {\r\n if (queue.length === 0) {\r\n await new Promise<void>(resolve => (notify = resolve));\r\n continue;\r\n }\r\n const chunk = queue.shift();\r\n if (chunk) {\r\n yield chunk;\r\n }\r\n }\r\n\r\n if (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n private processAudioData(buffer: Uint8Array): void {\r\n const needle = new TextEncoder().encode(\"Path:audio\\r\\n\");\r\n const audioStartIndex = this.indexOf(buffer, needle);\r\n\r\n if (audioStartIndex !== -1) {\r\n const audioChunk = buffer.slice(audioStartIndex + needle.length);\r\n this.audio_stream.push(audioChunk);\r\n }\r\n }\r\n\r\n private indexOf(arr: Uint8Array, subarr: Uint8Array): number {\r\n for (let i = 0; i <= arr.length - subarr.length; i++) {\r\n let match = true;\r\n for (let j = 0; j < subarr.length; j++) {\r\n if (arr[i + j] !== subarr[j]) {\r\n match = false;\r\n break;\r\n }\r\n }\r\n if (match) return i;\r\n }\r\n return -1;\r\n }\r\n\r\n private async generateSecMsGec(trustedClientToken: string): Promise<string> {\r\n const now = this.nowRFC1123();\r\n const fixedDate = this.parseRFC1123(now);\r\n const ticks = Math.floor(fixedDate.getTime() / 1000) + 11644473600;\r\n const rounded = ticks - (ticks % 300);\r\n const windowsTicks = rounded * 10_000_000;\r\n\r\n const encoder = new TextEncoder();\r\n const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\r\n\r\n return Array.from(new Uint8Array(hashBuffer))\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n }\r\n\r\n getAudioData(): Uint8Array {\r\n if (this.audio_stream.length === 0) {\r\n throw new Error(\"No audio data available\");\r\n }\r\n const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.audio_stream) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n }\r\n}\r\n", "export const Constants = {\r\n TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',\r\n BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',\r\n WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',\r\n VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',\r\n \r\n CHROMIUM_FULL_VERSION: '142.0.3595.0',\r\n CHROMIUM_MAJOR_VERSION: '142',\r\n VERSION_MS_GEC: '1-142.0.3595',\r\n \r\n token32() {\r\n const bytes = new Uint8Array(16);\r\n crypto.getRandomValues(bytes);\r\n \r\n return Array.from(bytes)\r\n .map(b => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase();\r\n },\r\n \r\n getBaseHeaders() {\r\n return {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',\r\n 'Accept-Encoding': 'gzip, deflate, br, zstd',\r\n 'Accept-Language': 'es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,es-CO;q=0.5,es-MX;q=0.4',\r\n 'Cookie': 'MUID=' + this.token32()\r\n };\r\n },\r\n \r\n WSS_HEADERS: {\r\n 'Pragma': 'no-cache',\r\n 'Cache-Control': 'no-cache',\r\n 'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',\r\n 'Sec-WebSocket-Protocol': 'synthesize',\r\n 'Sec-WebSocket-Version': '13',\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0'\r\n },\r\n \r\n VOICE_HEADERS: {\r\n 'Sec-CH-UA': '\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"140\", \"Chromium\";v=\"140\"',\r\n 'Sec-CH-UA-Mobile': '?0',\r\n 'Accept': '*/*',\r\n 'Sec-Fetch-Site': 'none',\r\n 'Sec-Fetch-Mode': 'cors',\r\n 'Sec-Fetch-Dest': 'empty'\r\n }\r\n};\r\n"],
5
+ "mappings": "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",
6
+ "names": ["EdgeTTS_browser_exports", "__export", "EdgeTTS", "Constants", "bytes", "b", "EdgeTTS", "__publicField", "secMsGEC", "Constants", "voice", "locale", "gender", "c", "r", "pitch", "rate", "rateValue", "volume", "volumeValue", "content", "trimmedContent", "errors", "hasSpeakTag", "hasVoiceTag", "text", "options", "inputType", "treatAsSSML", "detection", "ssml", "escapedText", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timedOut", "inactivityTimeout", "resetInactivityTimeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "queue", "done", "error", "notify", "push", "chunk", "needle", "audioStartIndex", "audioChunk", "buffer", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "result", "offset"]
7
7
  }
@@ -6,7 +6,7 @@ const VoiceListCommand_1 = require("../commands/VoiceListCommand");
6
6
  const commander_1 = require("commander");
7
7
  const program = new commander_1.Command();
8
8
  program
9
- .version('1.6.1')
9
+ .version('1.6.2')
10
10
  .addCommand(SynthesizeCommand_1.SynthesizeCommand)
11
11
  .addCommand(VoiceListCommand_1.VoiceListCommand);
12
12
  try {
@@ -114,19 +114,25 @@ class EdgeTTS {
114
114
  this.audio_stream = [];
115
115
  const reqId = this.generateUUID();
116
116
  const url = `${constants_1.Constants.WSS_URL}?Ocp-Apim-Subscription-Key=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;
117
- console.log('WebSocket URL:', url);
118
117
  this.ws = new ws_1.default(url, {
119
118
  headers: constants_1.Constants.getBaseHeaders(),
120
119
  rejectUnauthorized: false
121
120
  });
122
121
  const SSML_text = this.getSSML(text, voice, options);
123
- const timeout = setTimeout(() => {
124
- if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
125
- this.ws.close();
126
- }
127
- reject(new Error("Synthesis timeout"));
128
- }, 30000);
122
+ let timedOut = false;
123
+ let inactivityTimeout;
124
+ const resetInactivityTimeout = () => {
125
+ clearTimeout(inactivityTimeout);
126
+ inactivityTimeout = setTimeout(() => {
127
+ timedOut = true;
128
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
129
+ this.ws.close();
130
+ }
131
+ reject(new Error("WebSocket inactivity timeout - no response from server"));
132
+ }, 30000); // 30 seconds of inactivity
133
+ };
129
134
  this.ws.on('open', () => {
135
+ resetInactivityTimeout(); // start the inactivity timeout
130
136
  const message = this.buildTTSConfigMessage();
131
137
  this.ws.send(message);
132
138
  const timestamp = this.nowRFC1123();
@@ -134,18 +140,21 @@ class EdgeTTS {
134
140
  this.ws.send(speechMessage);
135
141
  });
136
142
  this.ws.on('message', (data) => {
143
+ resetInactivityTimeout(); // restart inactivity timeout
137
144
  this.processAudioData(data);
138
145
  });
139
146
  this.ws.on('error', (err) => {
140
- clearTimeout(timeout);
147
+ clearTimeout(inactivityTimeout);
141
148
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
142
149
  this.ws.close();
143
150
  }
144
151
  reject(err);
145
152
  });
146
153
  this.ws.on('close', () => {
147
- clearTimeout(timeout);
148
- resolve();
154
+ clearTimeout(inactivityTimeout);
155
+ if (!timedOut) {
156
+ resolve();
157
+ }
149
158
  });
150
159
  });
151
160
  }
@@ -271,12 +280,25 @@ class EdgeTTS {
271
280
  notify = null;
272
281
  }
273
282
  };
274
- const timeout = setTimeout(() => {
275
- if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
276
- this.ws.close();
277
- }
278
- }, 30000);
283
+ let timedOut = false;
284
+ let inactivityTimeout;
285
+ const resetInactivityTimeout = () => {
286
+ clearTimeout(inactivityTimeout);
287
+ inactivityTimeout = setTimeout(() => {
288
+ timedOut = true;
289
+ error = new Error("WebSocket inactivity timeout - no response from server");
290
+ done = true;
291
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
292
+ this.ws.close();
293
+ }
294
+ if (notify) {
295
+ notify();
296
+ notify = null;
297
+ }
298
+ }, 30000); // 30 seconds of inactivity
299
+ };
279
300
  this.ws.on('open', () => {
301
+ resetInactivityTimeout(); // start the inactivity timeout
280
302
  const message = this.buildTTSConfigMessage();
281
303
  this.ws.send(message);
282
304
  const timestamp = this.nowRFC1123();
@@ -284,6 +306,7 @@ class EdgeTTS {
284
306
  this.ws.send(speechMessage);
285
307
  });
286
308
  this.ws.on('message', (data) => {
309
+ resetInactivityTimeout(); // restart inactivity timeout
287
310
  const buffer = ensureBuffer(data);
288
311
  const needle = buffer_1.Buffer.from('Path:audio\r\n');
289
312
  const audioStartIndex = buffer.indexOf(new Uint8Array(needle));
@@ -307,6 +330,7 @@ class EdgeTTS {
307
330
  }
308
331
  });
309
332
  this.ws.on('error', (err) => {
333
+ clearTimeout(inactivityTimeout);
310
334
  error = err;
311
335
  done = true;
312
336
  if (notify) {
@@ -315,7 +339,7 @@ class EdgeTTS {
315
339
  }
316
340
  });
317
341
  this.ws.on('close', () => {
318
- clearTimeout(timeout);
342
+ clearTimeout(inactivityTimeout);
319
343
  done = true;
320
344
  if (notify) {
321
345
  notify();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andresaya/edge-tts",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Edge TTS is a package that allows access to the online text-to-speech service used by Microsoft Edge without the need for Microsoft Edge, Windows, or an API key.",
5
5
  "main": "dist/index.js",
6
6
  "browser": "dist/browser/edge-tts.esm.js",