@andresaya/edge-tts 1.4.4 → 1.6.0
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.
- package/README.md +52 -0
- package/dist/browser/edge-tts.esm.js +234 -0
- package/dist/browser/edge-tts.esm.js.map +7 -0
- package/dist/browser/edge-tts.esm.min.js +18 -0
- package/dist/browser/edge-tts.esm.min.js.map +7 -0
- package/dist/browser/edge-tts.umd.js +257 -0
- package/dist/browser/edge-tts.umd.js.map +7 -0
- package/dist/browser/edge-tts.umd.min.js +18 -0
- package/dist/browser/edge-tts.umd.min.js.map +7 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +5 -0
- package/dist/cli/edge-tts.js +1 -1
- package/dist/config/constants.d.ts +26 -1
- package/dist/config/constants.js +37 -3
- package/dist/services/EdgeTTS.js +4 -9
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -314,6 +314,58 @@ async function exploreVoices() {
|
|
|
314
314
|
exploreVoices().catch(console.error);
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
+
## Browser Support
|
|
318
|
+
|
|
319
|
+
This library can be used directly in web browsers via CDN or ES modules.
|
|
320
|
+
|
|
321
|
+
**⚠️ Important:** Currently, this library only works reliably with **Microsoft Edge browser**. We are working to extend support to other browsers. Community contributions and suggestions are welcome!
|
|
322
|
+
|
|
323
|
+
### CDN Usage (UMD)
|
|
324
|
+
|
|
325
|
+
```html
|
|
326
|
+
<!-- Load from CDN -->
|
|
327
|
+
<script src="https://unpkg.com/@andresaya/edge-tts@latest/dist/browser/edge-tts.umd.min.js"></script>
|
|
328
|
+
|
|
329
|
+
<script>
|
|
330
|
+
const tts = new EdgeTTS();
|
|
331
|
+
|
|
332
|
+
// Get available voices
|
|
333
|
+
tts.getVoices().then(voices => {
|
|
334
|
+
console.log('Available voices:', voices.length);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Synthesize speech
|
|
338
|
+
async function speak() {
|
|
339
|
+
await tts.synthesize("Hello from the browser!", 'en-US-AriaNeural');
|
|
340
|
+
const audioData = tts.getAudioData();
|
|
341
|
+
|
|
342
|
+
// Play audio
|
|
343
|
+
const audioBlob = new Blob([audioData], { type: 'audio/mp3' });
|
|
344
|
+
const audioUrl = URL.createObjectURL(audioBlob);
|
|
345
|
+
const audio = new Audio(audioUrl);
|
|
346
|
+
audio.play();
|
|
347
|
+
}
|
|
348
|
+
</script>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### ES Module Import
|
|
352
|
+
|
|
353
|
+
```html
|
|
354
|
+
<script type="module">
|
|
355
|
+
import { EdgeTTS } from 'https://unpkg.com/@andresaya/edge-tts@latest/dist/browser/edge-tts.esm.min.js';
|
|
356
|
+
|
|
357
|
+
const tts = new EdgeTTS();
|
|
358
|
+
|
|
359
|
+
// Use the library
|
|
360
|
+
const voices = await tts.getVoices();
|
|
361
|
+
console.log(voices);
|
|
362
|
+
</script>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Complete Browser Example
|
|
366
|
+
|
|
367
|
+
For a full working example with voice selection and synthesis, see [`examples/standalone.html`](examples/standalone.html).
|
|
368
|
+
|
|
317
369
|
## Voice Options
|
|
318
370
|
|
|
319
371
|
### Synthesis Parameters
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/config/constants.ts
|
|
6
|
+
var Constants = {
|
|
7
|
+
TRUSTED_CLIENT_TOKEN: "6A5AA1D4EAFF4E9FB37E23D68491D6F4",
|
|
8
|
+
BASE_URL: "https://api.msedgeservices.com/tts/cognitiveservices",
|
|
9
|
+
WSS_URL: "wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",
|
|
10
|
+
VOICES_URL: "https://api.msedgeservices.com/tts/cognitiveservices/voices/list",
|
|
11
|
+
CHROMIUM_FULL_VERSION: "142.0.3595.0",
|
|
12
|
+
CHROMIUM_MAJOR_VERSION: "142",
|
|
13
|
+
VERSION_MS_GEC: "1-142.0.3595",
|
|
14
|
+
token32() {
|
|
15
|
+
const bytes = new Uint8Array(16);
|
|
16
|
+
crypto.getRandomValues(bytes);
|
|
17
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
18
|
+
},
|
|
19
|
+
getBaseHeaders() {
|
|
20
|
+
return {
|
|
21
|
+
"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",
|
|
22
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
23
|
+
"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",
|
|
24
|
+
"Cookie": "MUID=" + this.token32()
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
WSS_HEADERS: {
|
|
28
|
+
"Pragma": "no-cache",
|
|
29
|
+
"Cache-Control": "no-cache",
|
|
30
|
+
"Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold",
|
|
31
|
+
"Sec-WebSocket-Protocol": "synthesize",
|
|
32
|
+
"Sec-WebSocket-Version": "13",
|
|
33
|
+
"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"
|
|
34
|
+
},
|
|
35
|
+
VOICE_HEADERS: {
|
|
36
|
+
"Sec-CH-UA": '" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',
|
|
37
|
+
"Sec-CH-UA-Mobile": "?0",
|
|
38
|
+
"Accept": "*/*",
|
|
39
|
+
"Sec-Fetch-Site": "none",
|
|
40
|
+
"Sec-Fetch-Mode": "cors",
|
|
41
|
+
"Sec-Fetch-Dest": "empty"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/browser/EdgeTTS.browser.ts
|
|
46
|
+
var EdgeTTS = class {
|
|
47
|
+
constructor() {
|
|
48
|
+
__publicField(this, "audio_stream", []);
|
|
49
|
+
__publicField(this, "ws");
|
|
50
|
+
}
|
|
51
|
+
async getVoices() {
|
|
52
|
+
const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);
|
|
53
|
+
const response = await fetch(
|
|
54
|
+
`${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,
|
|
55
|
+
{ headers: Constants.getBaseHeaders() }
|
|
56
|
+
);
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
return data.map((voice) => {
|
|
59
|
+
voice.FriendlyName = voice.FriendlyName || voice.LocalName;
|
|
60
|
+
delete voice.SampleRateHertz;
|
|
61
|
+
delete voice.Status;
|
|
62
|
+
return voice;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async getVoicesByLanguage(locale) {
|
|
66
|
+
const voices = await this.getVoices();
|
|
67
|
+
return voices.filter((voice) => voice.Locale.startsWith(locale));
|
|
68
|
+
}
|
|
69
|
+
async getVoicesByGender(gender) {
|
|
70
|
+
const voices = await this.getVoices();
|
|
71
|
+
return voices.filter((voice) => voice.Gender === gender);
|
|
72
|
+
}
|
|
73
|
+
generateUUID() {
|
|
74
|
+
return "xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
75
|
+
const r = Math.random() * 16 | 0;
|
|
76
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
77
|
+
return v.toString(16);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
validatePitch(pitch) {
|
|
81
|
+
if (typeof pitch === "number") {
|
|
82
|
+
return pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`;
|
|
83
|
+
}
|
|
84
|
+
return pitch;
|
|
85
|
+
}
|
|
86
|
+
validateRate(rate) {
|
|
87
|
+
let rateValue;
|
|
88
|
+
if (typeof rate === "string") {
|
|
89
|
+
rateValue = parseFloat(rate.replace("%", ""));
|
|
90
|
+
} else {
|
|
91
|
+
rateValue = rate;
|
|
92
|
+
}
|
|
93
|
+
return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;
|
|
94
|
+
}
|
|
95
|
+
validateVolume(volume) {
|
|
96
|
+
let volumeValue;
|
|
97
|
+
if (typeof volume === "string") {
|
|
98
|
+
volumeValue = parseInt(volume.replace("%", ""), 10);
|
|
99
|
+
} else {
|
|
100
|
+
volumeValue = volume;
|
|
101
|
+
}
|
|
102
|
+
return `${volumeValue}%`;
|
|
103
|
+
}
|
|
104
|
+
getSSML(text, voice, options = {}) {
|
|
105
|
+
const pitch = this.validatePitch(options.pitch ?? 0);
|
|
106
|
+
const rate = this.validateRate(options.rate ?? 0);
|
|
107
|
+
const volume = this.validateVolume(options.volume ?? 0);
|
|
108
|
+
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">
|
|
109
|
+
<voice name="${voice}">
|
|
110
|
+
<prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
|
|
111
|
+
${text}
|
|
112
|
+
</prosody>
|
|
113
|
+
</voice>
|
|
114
|
+
</speak>`;
|
|
115
|
+
}
|
|
116
|
+
nowRFC1123() {
|
|
117
|
+
return (/* @__PURE__ */ new Date()).toUTCString();
|
|
118
|
+
}
|
|
119
|
+
parseRFC1123(rfcStr) {
|
|
120
|
+
return new Date(rfcStr);
|
|
121
|
+
}
|
|
122
|
+
buildTTSConfigMessage() {
|
|
123
|
+
const timestamp = this.nowRFC1123();
|
|
124
|
+
return `X-Timestamp:${timestamp}\r
|
|
125
|
+
Content-Type:application/json; charset=utf-8\r
|
|
126
|
+
Path:speech.config\r
|
|
127
|
+
\r
|
|
128
|
+
{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`;
|
|
129
|
+
}
|
|
130
|
+
async synthesize(text, voice = "en-US-JennyNeural", options = {}) {
|
|
131
|
+
const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
this.audio_stream = [];
|
|
134
|
+
const reqId = this.generateUUID();
|
|
135
|
+
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}`;
|
|
136
|
+
console.log("WebSocket URL:", url);
|
|
137
|
+
this.ws = new WebSocket(url);
|
|
138
|
+
const SSML_text = this.getSSML(text, voice, options);
|
|
139
|
+
const timeout = setTimeout(() => {
|
|
140
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
141
|
+
this.ws.close();
|
|
142
|
+
}
|
|
143
|
+
reject(new Error("Synthesis timeout"));
|
|
144
|
+
}, 3e4);
|
|
145
|
+
this.ws.onopen = () => {
|
|
146
|
+
const message = this.buildTTSConfigMessage();
|
|
147
|
+
this.ws.send(message);
|
|
148
|
+
const timestamp = this.nowRFC1123();
|
|
149
|
+
const speechMessage = `X-RequestId:${reqId}\r
|
|
150
|
+
Content-Type:application/ssml+xml\r
|
|
151
|
+
X-Timestamp:${timestamp}\r
|
|
152
|
+
Path:ssml\r
|
|
153
|
+
\r
|
|
154
|
+
${SSML_text}`;
|
|
155
|
+
this.ws.send(speechMessage);
|
|
156
|
+
};
|
|
157
|
+
this.ws.onmessage = (event) => {
|
|
158
|
+
if (typeof event.data === "string") {
|
|
159
|
+
if (event.data.includes("Path:turn.end")) {
|
|
160
|
+
this.ws?.close();
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
const reader = new FileReader();
|
|
164
|
+
reader.onload = () => {
|
|
165
|
+
const arrayBuffer = reader.result;
|
|
166
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
167
|
+
this.processAudioData(uint8Array);
|
|
168
|
+
};
|
|
169
|
+
reader.readAsArrayBuffer(event.data);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
this.ws.onerror = (err) => {
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
175
|
+
this.ws.close();
|
|
176
|
+
}
|
|
177
|
+
reject(new Error("WebSocket error"));
|
|
178
|
+
};
|
|
179
|
+
this.ws.onclose = () => {
|
|
180
|
+
clearTimeout(timeout);
|
|
181
|
+
resolve();
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
processAudioData(buffer) {
|
|
186
|
+
const needle = new TextEncoder().encode("Path:audio\r\n");
|
|
187
|
+
const audioStartIndex = this.indexOf(buffer, needle);
|
|
188
|
+
if (audioStartIndex !== -1) {
|
|
189
|
+
const audioChunk = buffer.slice(audioStartIndex + needle.length);
|
|
190
|
+
this.audio_stream.push(audioChunk);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
indexOf(arr, subarr) {
|
|
194
|
+
for (let i = 0; i <= arr.length - subarr.length; i++) {
|
|
195
|
+
let match = true;
|
|
196
|
+
for (let j = 0; j < subarr.length; j++) {
|
|
197
|
+
if (arr[i + j] !== subarr[j]) {
|
|
198
|
+
match = false;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (match) return i;
|
|
203
|
+
}
|
|
204
|
+
return -1;
|
|
205
|
+
}
|
|
206
|
+
async generateSecMsGec(trustedClientToken) {
|
|
207
|
+
const now = this.nowRFC1123();
|
|
208
|
+
const fixedDate = this.parseRFC1123(now);
|
|
209
|
+
const ticks = Math.floor(fixedDate.getTime() / 1e3) + 11644473600;
|
|
210
|
+
const rounded = ticks - ticks % 300;
|
|
211
|
+
const windowsTicks = rounded * 1e7;
|
|
212
|
+
const encoder = new TextEncoder();
|
|
213
|
+
const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);
|
|
214
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
215
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
216
|
+
}
|
|
217
|
+
getAudioData() {
|
|
218
|
+
if (this.audio_stream.length === 0) {
|
|
219
|
+
throw new Error("No audio data available");
|
|
220
|
+
}
|
|
221
|
+
const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
222
|
+
const result = new Uint8Array(totalLength);
|
|
223
|
+
let offset = 0;
|
|
224
|
+
for (const chunk of this.audio_stream) {
|
|
225
|
+
result.set(chunk, offset);
|
|
226
|
+
offset += chunk.length;
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
export {
|
|
232
|
+
EdgeTTS
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=edge-tts.esm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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}\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 getSSML(text: string, voice: string, options: SynthesisOptions = {}): string {\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\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 ${text}\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 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": ";;;;;AAAO,IAAM,YAAY;AAAA,EACrB,sBAAsB;AAAA,EACtB,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EAEZ,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAEhB,UAAU;AACN,UAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,WAAO,gBAAgB,KAAK;AAE5B,WAAO,MAAM,KAAK,KAAK,EAClB,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EACxC,KAAK,EAAE,EACP,YAAY;AAAA,EACrB;AAAA,EAEA,iBAAiB;AACb,WAAO;AAAA,MACH,cAAc;AAAA,MACd,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB,UAAU,UAAU,KAAK,QAAQ;AAAA,IACrC;AAAA,EACJ;AAAA,EAEA,aAAa;AAAA,IACT,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,0BAA0B;AAAA,IAC1B,yBAAyB;AAAA,IACzB,cAAc;AAAA,EAClB;AAAA,EAEA,eAAe;AAAA,IACX,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,EACtB;AACJ;;;AC7BO,IAAM,UAAN,MAAc;AAAA,EAAd;AACH,wBAAQ,gBAA6B,CAAC;AACtC,wBAAQ;AAAA;AAAA,EAER,MAAM,YAA8B;AAChC,UAAM,WAAW,MAAM,KAAK,iBAAiB,UAAU,oBAAoB;AAE3E,UAAM,WAAW,MAAM;AAAA,MACnB,GAAG,UAAU,UAAU,8BAA8B,UAAU,oBAAoB,eAAe,QAAQ,uBAAuB,UAAU,cAAc;AAAA,MACzJ,EAAE,SAAS,UAAU,eAAe,EAAE;AAAA,IAC1C;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,KAAK,IAAI,CAAC,UAAe;AAC5B,YAAM,eAAe,MAAM,gBAAgB,MAAM;AACjD,aAAO,MAAM;AACb,aAAO,MAAM;AACb,aAAO;AAAA,IACX,CAAC;AAAA,EACL;AAAA,EAEA,MAAM,oBAAoB,QAAkC;AACxD,UAAM,SAAS,MAAM,KAAK,UAAU;AACpC,WAAO,OAAO,OAAO,WAAS,MAAM,OAAO,WAAW,MAAM,CAAC;AAAA,EACjE;AAAA,EAEA,MAAM,kBAAkB,QAA6C;AACjE,UAAM,SAAS,MAAM,KAAK,UAAU;AACpC,WAAO,OAAO,OAAO,WAAS,MAAM,WAAW,MAAM;AAAA,EACzD;AAAA,EAEQ,eAAuB;AAC3B,WAAO,uCAAuC,QAAQ,SAAS,SAAU,GAAG;AACxE,YAAM,IAAI,KAAK,OAAO,IAAI,KAAK;AAC/B,YAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAM;AACrC,aAAO,EAAE,SAAS,EAAE;AAAA,IACxB,CAAC;AAAA,EACL;AAAA,EAEQ,cAAc,OAAgC;AAClD,QAAI,OAAO,UAAU,UAAU;AAC3B,aAAQ,SAAS,IAAI,IAAI,KAAK,OAAO,GAAG,KAAK;AAAA,IACjD;AACA,WAAO;AAAA,EACX;AAAA,EAEQ,aAAa,MAA+B;AAChD,QAAI;AACJ,QAAI,OAAO,SAAS,UAAU;AAC1B,kBAAY,WAAW,KAAK,QAAQ,KAAK,EAAE,CAAC;AAAA,IAChD,OAAO;AACH,kBAAY;AAAA,IAChB;AACA,WAAO,aAAa,IAAI,IAAI,SAAS,MAAM,GAAG,SAAS;AAAA,EAC3D;AAAA,EAEQ,eAAe,QAAiC;AACpD,QAAI;AACJ,QAAI,OAAO,WAAW,UAAU;AAC5B,oBAAc,SAAS,OAAO,QAAQ,KAAK,EAAE,GAAG,EAAE;AAAA,IACtD,OAAO;AACH,oBAAc;AAAA,IAClB;AACA,WAAO,GAAG,WAAW;AAAA,EACzB;AAAA,EAEQ,QAAQ,MAAc,OAAe,UAA4B,CAAC,GAAW;AACjF,UAAM,QAAQ,KAAK,cAAc,QAAQ,SAAS,CAAC;AACnD,UAAM,OAAO,KAAK,aAAa,QAAQ,QAAQ,CAAC;AAChD,UAAM,SAAS,KAAK,eAAe,QAAQ,UAAU,CAAC;AAEtD,WAAO;AAAA,mCACoB,KAAK;AAAA,0CACE,KAAK,WAAW,IAAI,aAAa,MAAM;AAAA,8BACnD,IAAI;AAAA;AAAA;AAAA;AAAA,EAI9B;AAAA,EAEQ,aAAqB;AACzB,YAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,EAClC;AAAA,EAEQ,aAAa,QAAsB;AACvC,WAAO,IAAI,KAAK,MAAM;AAAA,EAC1B;AAAA,EAEQ,wBAAgC;AACpC,UAAM,YAAY,KAAK,WAAW;AAClC,WAAO,eAAe,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAEnC;AAAA,EAEA,MAAM,WAAW,MAAc,QAAgB,qBAAqB,UAA4B,CAAC,GAAkB;AAC/G,UAAM,WAAW,MAAM,KAAK,iBAAiB,UAAU,oBAAoB;AAE3E,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,WAAK,eAAe,CAAC;AACrB,YAAM,QAAQ,KAAK,aAAa;AAChC,YAAM,MAAM,GAAG,UAAU,OAAO,8BAA8B,UAAU,oBAAoB,eAAe,QAAQ,uBAAuB,UAAU,cAAc,iBAAiB,KAAK;AAExL,cAAQ,IAAI,kBAAkB,GAAG;AACjC,WAAK,KAAK,IAAI,UAAU,GAAG;AAE3B,YAAM,YAAY,KAAK,QAAQ,MAAM,OAAO,OAAO;AAEnD,YAAM,UAAU,WAAW,MAAM;AAC7B,YAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AAClD,eAAK,GAAG,MAAM;AAAA,QAClB;AACA,eAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,MACzC,GAAG,GAAK;AAER,WAAK,GAAG,SAAS,MAAM;AACnB,cAAM,UAAU,KAAK,sBAAsB;AAC3C,aAAK,GAAI,KAAK,OAAO;AACrB,cAAM,YAAY,KAAK,WAAW;AAClC,cAAM,gBAAgB,eAAe,KAAK;AAAA;AAAA,cAAwD,SAAS;AAAA;AAAA;AAAA,EAAwB,SAAS;AAC5I,aAAK,GAAI,KAAK,aAAa;AAAA,MAC/B;AAEA,WAAK,GAAG,YAAY,CAAC,UAAU;AAC3B,YAAI,OAAO,MAAM,SAAS,UAAU;AAChC,cAAI,MAAM,KAAK,SAAS,eAAe,GAAG;AACtC,iBAAK,IAAI,MAAM;AAAA,UACnB;AAAA,QACJ,OAAO;AACH,gBAAM,SAAS,IAAI,WAAW;AAC9B,iBAAO,SAAS,MAAM;AAClB,kBAAM,cAAc,OAAO;AAC3B,kBAAM,aAAa,IAAI,WAAW,WAAW;AAC7C,iBAAK,iBAAiB,UAAU;AAAA,UACpC;AACA,iBAAO,kBAAkB,MAAM,IAAI;AAAA,QACvC;AAAA,MACJ;AAEA,WAAK,GAAG,UAAU,CAAC,QAAa;AAC5B,qBAAa,OAAO;AACpB,YAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AAClD,eAAK,GAAG,MAAM;AAAA,QAClB;AACA,eAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,MACvC;AAEA,WAAK,GAAG,UAAU,MAAM;AACpB,qBAAa,OAAO;AACpB,gBAAQ;AAAA,MACZ;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEQ,iBAAiB,QAA0B;AAC/C,UAAM,SAAS,IAAI,YAAY,EAAE,OAAO,gBAAgB;AACxD,UAAM,kBAAkB,KAAK,QAAQ,QAAQ,MAAM;AAEnD,QAAI,oBAAoB,IAAI;AACxB,YAAM,aAAa,OAAO,MAAM,kBAAkB,OAAO,MAAM;AAC/D,WAAK,aAAa,KAAK,UAAU;AAAA,IACrC;AAAA,EACJ;AAAA,EAEQ,QAAQ,KAAiB,QAA4B;AACzD,aAAS,IAAI,GAAG,KAAK,IAAI,SAAS,OAAO,QAAQ,KAAK;AAClD,UAAI,QAAQ;AACZ,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,YAAI,IAAI,IAAI,CAAC,MAAM,OAAO,CAAC,GAAG;AAC1B,kBAAQ;AACR;AAAA,QACJ;AAAA,MACJ;AACA,UAAI,MAAO,QAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAc,iBAAiB,oBAA6C;AACxE,UAAM,MAAM,KAAK,WAAW;AAC5B,UAAM,YAAY,KAAK,aAAa,GAAG;AACvC,UAAM,QAAQ,KAAK,MAAM,UAAU,QAAQ,IAAI,GAAI,IAAI;AACvD,UAAM,UAAU,QAAS,QAAQ;AACjC,UAAM,eAAe,UAAU;AAE/B,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,QAAQ,OAAO,GAAG,YAAY,GAAG,kBAAkB,EAAE;AAClE,UAAM,aAAa,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI;AAE7D,WAAO,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC,EACvC,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EACxC,KAAK,EAAE,EACP,YAAY;AAAA,EACrB;AAAA,EAEA,eAA2B;AACvB,QAAI,KAAK,aAAa,WAAW,GAAG;AAChC,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC7C;AACA,UAAM,cAAc,KAAK,aAAa,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAClF,UAAM,SAAS,IAAI,WAAW,WAAW;AACzC,QAAI,SAAS;AACb,eAAW,SAAS,KAAK,cAAc;AACnC,aAAO,IAAI,OAAO,MAAM;AACxB,gBAAU,MAAM;AAAA,IACpB;AACA,WAAO;AAAA,EACX;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var y=Object.defineProperty;var x=(o,e,t)=>e in o?y(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var h=(o,e,t)=>x(o,typeof e!="symbol"?e+"":e,t);var i={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 S=class{constructor(){h(this,"audio_stream",[]);h(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(i.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${i.VOICES_URL}?Ocp-Apim-Subscription-Key=${i.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${i.VERSION_MS_GEC}`,{headers:i.getBaseHeaders()})).json()).map(n=>(n.FriendlyName=n.FriendlyName||n.LocalName,delete n.SampleRateHertz,delete n.Status,n))}async getVoicesByLanguage(e){return(await this.getVoices()).filter(s=>s.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(s=>s.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}%`}getSSML(e,t,s={}){let n=this.validatePitch(s.pitch??0),r=this.validateRate(s.rate??0),a=this.validateVolume(s.volume??0);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
|
+
<voice name="${t}">
|
|
3
|
+
<prosody pitch="${n}" rate="${r}" volume="${a}">
|
|
4
|
+
${e}
|
|
5
|
+
</prosody>
|
|
6
|
+
</voice>
|
|
7
|
+
</speak>`}nowRFC1123(){return new Date().toUTCString()}parseRFC1123(e){return new Date(e)}buildTTSConfigMessage(){return`X-Timestamp:${this.nowRFC1123()}\r
|
|
8
|
+
Content-Type:application/json; charset=utf-8\r
|
|
9
|
+
Path:speech.config\r
|
|
10
|
+
\r
|
|
11
|
+
{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,t="en-US-JennyNeural",s={}){let n=await this.generateSecMsGec(i.TRUSTED_CLIENT_TOKEN);return new Promise((r,a)=>{this.audio_stream=[];let p=this.generateUUID(),l=`${i.WSS_URL}?Ocp-Apim-Subscription-Key=${i.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${n}&Sec-MS-GEC-Version=${i.VERSION_MS_GEC}&ConnectionId=${p}`;console.log("WebSocket URL:",l),this.ws=new WebSocket(l);let g=this.getSSML(e,t,s),m=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),a(new Error("Synthesis timeout"))},3e4);this.ws.onopen=()=>{let c=this.buildTTSConfigMessage();this.ws.send(c);let d=this.nowRFC1123(),u=`X-RequestId:${p}\r
|
|
12
|
+
Content-Type:application/ssml+xml\r
|
|
13
|
+
X-Timestamp:${d}\r
|
|
14
|
+
Path:ssml\r
|
|
15
|
+
\r
|
|
16
|
+
${g}`;this.ws.send(u)},this.ws.onmessage=c=>{if(typeof c.data=="string")c.data.includes("Path:turn.end")&&this.ws?.close();else{let d=new FileReader;d.onload=()=>{let u=d.result,w=new Uint8Array(u);this.processAudioData(w)},d.readAsArrayBuffer(c.data)}},this.ws.onerror=c=>{clearTimeout(m),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),a(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(m),r()}})}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
|
|
17
|
+
`),s=this.indexOf(e,t);if(s!==-1){let n=e.slice(s+t.length);this.audio_stream.push(n)}}indexOf(e,t){for(let s=0;s<=e.length-t.length;s++){let n=!0;for(let r=0;r<t.length;r++)if(e[s+r]!==t[r]){n=!1;break}if(n)return s}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),s=this.parseRFC1123(t),n=Math.floor(s.getTime()/1e3)+11644473600,a=(n-n%300)*1e7,l=new TextEncoder().encode(`${a}${e}`),g=await crypto.subtle.digest("SHA-256",l);return Array.from(new Uint8Array(g)).map(m=>m.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((n,r)=>n+r.length,0),t=new Uint8Array(e),s=0;for(let n of this.audio_stream)t.set(n,s),s+=n.length;return t}};export{S as EdgeTTS};
|
|
18
|
+
//# sourceMappingURL=edge-tts.esm.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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}\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 getSSML(text: string, voice: string, options: SynthesisOptions = {}): string {\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\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 ${text}\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 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": "oKAAO,IAAMA,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,EC7BO,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,QAAQC,EAAcV,EAAeW,EAA4B,CAAC,EAAW,CACjF,IAAMN,EAAQ,KAAK,cAAcM,EAAQ,OAAS,CAAC,EAC7CL,EAAO,KAAK,aAAaK,EAAQ,MAAQ,CAAC,EAC1CH,EAAS,KAAK,eAAeG,EAAQ,QAAU,CAAC,EAEtD,MAAO;AAAA,mCACoBX,CAAK;AAAA,0CACEK,CAAK,WAAWC,CAAI,aAAaE,CAAM;AAAA,8BACnDE,CAAI;AAAA;AAAA;AAAA,yBAI9B,CAEQ,YAAqB,CACzB,OAAO,IAAI,KAAK,EAAE,YAAY,CAClC,CAEQ,aAAaE,EAAsB,CACvC,OAAO,IAAI,KAAKA,CAAM,CAC1B,CAEQ,uBAAgC,CAEpC,MAAO,eADW,KAAK,WAAW,CACH;AAAA;AAAA;AAAA;AAAA,sKAEnC,CAEA,MAAM,WAAWF,EAAcV,EAAgB,oBAAqBW,EAA4B,CAAC,EAAkB,CAC/G,IAAMb,EAAW,MAAM,KAAK,iBAAiBC,EAAU,oBAAoB,EAE3E,OAAO,IAAI,QAAQ,CAACc,EAASC,IAAW,CACpC,KAAK,aAAe,CAAC,EACrB,IAAMC,EAAQ,KAAK,aAAa,EAC1BC,EAAM,GAAGjB,EAAU,OAAO,8BAA8BA,EAAU,oBAAoB,eAAeD,CAAQ,uBAAuBC,EAAU,cAAc,iBAAiBgB,CAAK,GAExL,QAAQ,IAAI,iBAAkBC,CAAG,EACjC,KAAK,GAAK,IAAI,UAAUA,CAAG,EAE3B,IAAMC,EAAY,KAAK,QAAQP,EAAMV,EAAOW,CAAO,EAE7CO,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,CAEQ,iBAAiBc,EAA0B,CAC/C,IAAMC,EAAS,IAAI,YAAY,EAAE,OAAO;AAAA,CAAgB,EAClDC,EAAkB,KAAK,QAAQF,EAAQC,CAAM,EAEnD,GAAIC,IAAoB,GAAI,CACxB,IAAMC,EAAaH,EAAO,MAAME,EAAkBD,EAAO,MAAM,EAC/D,KAAK,aAAa,KAAKE,CAAU,CACrC,CACJ,CAEQ,QAAQC,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,EAAKC,IAAUD,EAAMC,EAAM,OAAQ,CAAC,EAC5EC,EAAS,IAAI,WAAWH,CAAW,EACrCI,EAAS,EACb,QAAWF,KAAS,KAAK,aACrBC,EAAO,IAAID,EAAOE,CAAM,EACxBA,GAAUF,EAAM,OAEpB,OAAOC,CACX,CACJ",
|
|
6
|
+
"names": ["Constants", "bytes", "b", "EdgeTTS", "__publicField", "secMsGEC", "Constants", "voice", "locale", "gender", "c", "r", "pitch", "rate", "rateValue", "volume", "volumeValue", "text", "options", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "buffer", "needle", "audioStartIndex", "audioChunk", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "chunk", "result", "offset"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var EdgeTTS = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
+
|
|
23
|
+
// src/browser/EdgeTTS.browser.ts
|
|
24
|
+
var EdgeTTS_browser_exports = {};
|
|
25
|
+
__export(EdgeTTS_browser_exports, {
|
|
26
|
+
EdgeTTS: () => EdgeTTS
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// src/config/constants.ts
|
|
30
|
+
var Constants = {
|
|
31
|
+
TRUSTED_CLIENT_TOKEN: "6A5AA1D4EAFF4E9FB37E23D68491D6F4",
|
|
32
|
+
BASE_URL: "https://api.msedgeservices.com/tts/cognitiveservices",
|
|
33
|
+
WSS_URL: "wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1",
|
|
34
|
+
VOICES_URL: "https://api.msedgeservices.com/tts/cognitiveservices/voices/list",
|
|
35
|
+
CHROMIUM_FULL_VERSION: "142.0.3595.0",
|
|
36
|
+
CHROMIUM_MAJOR_VERSION: "142",
|
|
37
|
+
VERSION_MS_GEC: "1-142.0.3595",
|
|
38
|
+
token32() {
|
|
39
|
+
const bytes = new Uint8Array(16);
|
|
40
|
+
crypto.getRandomValues(bytes);
|
|
41
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
42
|
+
},
|
|
43
|
+
getBaseHeaders() {
|
|
44
|
+
return {
|
|
45
|
+
"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",
|
|
46
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
47
|
+
"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",
|
|
48
|
+
"Cookie": "MUID=" + this.token32()
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
WSS_HEADERS: {
|
|
52
|
+
"Pragma": "no-cache",
|
|
53
|
+
"Cache-Control": "no-cache",
|
|
54
|
+
"Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold",
|
|
55
|
+
"Sec-WebSocket-Protocol": "synthesize",
|
|
56
|
+
"Sec-WebSocket-Version": "13",
|
|
57
|
+
"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"
|
|
58
|
+
},
|
|
59
|
+
VOICE_HEADERS: {
|
|
60
|
+
"Sec-CH-UA": '" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',
|
|
61
|
+
"Sec-CH-UA-Mobile": "?0",
|
|
62
|
+
"Accept": "*/*",
|
|
63
|
+
"Sec-Fetch-Site": "none",
|
|
64
|
+
"Sec-Fetch-Mode": "cors",
|
|
65
|
+
"Sec-Fetch-Dest": "empty"
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/browser/EdgeTTS.browser.ts
|
|
70
|
+
var EdgeTTS = class {
|
|
71
|
+
constructor() {
|
|
72
|
+
__publicField(this, "audio_stream", []);
|
|
73
|
+
__publicField(this, "ws");
|
|
74
|
+
}
|
|
75
|
+
async getVoices() {
|
|
76
|
+
const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);
|
|
77
|
+
const response = await fetch(
|
|
78
|
+
`${Constants.VOICES_URL}?Ocp-Apim-Subscription-Key=${Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${Constants.VERSION_MS_GEC}`,
|
|
79
|
+
{ headers: Constants.getBaseHeaders() }
|
|
80
|
+
);
|
|
81
|
+
const data = await response.json();
|
|
82
|
+
return data.map((voice) => {
|
|
83
|
+
voice.FriendlyName = voice.FriendlyName || voice.LocalName;
|
|
84
|
+
delete voice.SampleRateHertz;
|
|
85
|
+
delete voice.Status;
|
|
86
|
+
return voice;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async getVoicesByLanguage(locale) {
|
|
90
|
+
const voices = await this.getVoices();
|
|
91
|
+
return voices.filter((voice) => voice.Locale.startsWith(locale));
|
|
92
|
+
}
|
|
93
|
+
async getVoicesByGender(gender) {
|
|
94
|
+
const voices = await this.getVoices();
|
|
95
|
+
return voices.filter((voice) => voice.Gender === gender);
|
|
96
|
+
}
|
|
97
|
+
generateUUID() {
|
|
98
|
+
return "xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
99
|
+
const r = Math.random() * 16 | 0;
|
|
100
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
101
|
+
return v.toString(16);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
validatePitch(pitch) {
|
|
105
|
+
if (typeof pitch === "number") {
|
|
106
|
+
return pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`;
|
|
107
|
+
}
|
|
108
|
+
return pitch;
|
|
109
|
+
}
|
|
110
|
+
validateRate(rate) {
|
|
111
|
+
let rateValue;
|
|
112
|
+
if (typeof rate === "string") {
|
|
113
|
+
rateValue = parseFloat(rate.replace("%", ""));
|
|
114
|
+
} else {
|
|
115
|
+
rateValue = rate;
|
|
116
|
+
}
|
|
117
|
+
return rateValue >= 0 ? `+${rateValue}%` : `${rateValue}%`;
|
|
118
|
+
}
|
|
119
|
+
validateVolume(volume) {
|
|
120
|
+
let volumeValue;
|
|
121
|
+
if (typeof volume === "string") {
|
|
122
|
+
volumeValue = parseInt(volume.replace("%", ""), 10);
|
|
123
|
+
} else {
|
|
124
|
+
volumeValue = volume;
|
|
125
|
+
}
|
|
126
|
+
return `${volumeValue}%`;
|
|
127
|
+
}
|
|
128
|
+
getSSML(text, voice, options = {}) {
|
|
129
|
+
const pitch = this.validatePitch(options.pitch ?? 0);
|
|
130
|
+
const rate = this.validateRate(options.rate ?? 0);
|
|
131
|
+
const volume = this.validateVolume(options.volume ?? 0);
|
|
132
|
+
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">
|
|
133
|
+
<voice name="${voice}">
|
|
134
|
+
<prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
|
|
135
|
+
${text}
|
|
136
|
+
</prosody>
|
|
137
|
+
</voice>
|
|
138
|
+
</speak>`;
|
|
139
|
+
}
|
|
140
|
+
nowRFC1123() {
|
|
141
|
+
return (/* @__PURE__ */ new Date()).toUTCString();
|
|
142
|
+
}
|
|
143
|
+
parseRFC1123(rfcStr) {
|
|
144
|
+
return new Date(rfcStr);
|
|
145
|
+
}
|
|
146
|
+
buildTTSConfigMessage() {
|
|
147
|
+
const timestamp = this.nowRFC1123();
|
|
148
|
+
return `X-Timestamp:${timestamp}\r
|
|
149
|
+
Content-Type:application/json; charset=utf-8\r
|
|
150
|
+
Path:speech.config\r
|
|
151
|
+
\r
|
|
152
|
+
{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`;
|
|
153
|
+
}
|
|
154
|
+
async synthesize(text, voice = "en-US-JennyNeural", options = {}) {
|
|
155
|
+
const secMsGEC = await this.generateSecMsGec(Constants.TRUSTED_CLIENT_TOKEN);
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
this.audio_stream = [];
|
|
158
|
+
const reqId = this.generateUUID();
|
|
159
|
+
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}`;
|
|
160
|
+
console.log("WebSocket URL:", url);
|
|
161
|
+
this.ws = new WebSocket(url);
|
|
162
|
+
const SSML_text = this.getSSML(text, voice, options);
|
|
163
|
+
const timeout = setTimeout(() => {
|
|
164
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
165
|
+
this.ws.close();
|
|
166
|
+
}
|
|
167
|
+
reject(new Error("Synthesis timeout"));
|
|
168
|
+
}, 3e4);
|
|
169
|
+
this.ws.onopen = () => {
|
|
170
|
+
const message = this.buildTTSConfigMessage();
|
|
171
|
+
this.ws.send(message);
|
|
172
|
+
const timestamp = this.nowRFC1123();
|
|
173
|
+
const speechMessage = `X-RequestId:${reqId}\r
|
|
174
|
+
Content-Type:application/ssml+xml\r
|
|
175
|
+
X-Timestamp:${timestamp}\r
|
|
176
|
+
Path:ssml\r
|
|
177
|
+
\r
|
|
178
|
+
${SSML_text}`;
|
|
179
|
+
this.ws.send(speechMessage);
|
|
180
|
+
};
|
|
181
|
+
this.ws.onmessage = (event) => {
|
|
182
|
+
if (typeof event.data === "string") {
|
|
183
|
+
if (event.data.includes("Path:turn.end")) {
|
|
184
|
+
this.ws?.close();
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
const reader = new FileReader();
|
|
188
|
+
reader.onload = () => {
|
|
189
|
+
const arrayBuffer = reader.result;
|
|
190
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
191
|
+
this.processAudioData(uint8Array);
|
|
192
|
+
};
|
|
193
|
+
reader.readAsArrayBuffer(event.data);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
this.ws.onerror = (err) => {
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
199
|
+
this.ws.close();
|
|
200
|
+
}
|
|
201
|
+
reject(new Error("WebSocket error"));
|
|
202
|
+
};
|
|
203
|
+
this.ws.onclose = () => {
|
|
204
|
+
clearTimeout(timeout);
|
|
205
|
+
resolve();
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
processAudioData(buffer) {
|
|
210
|
+
const needle = new TextEncoder().encode("Path:audio\r\n");
|
|
211
|
+
const audioStartIndex = this.indexOf(buffer, needle);
|
|
212
|
+
if (audioStartIndex !== -1) {
|
|
213
|
+
const audioChunk = buffer.slice(audioStartIndex + needle.length);
|
|
214
|
+
this.audio_stream.push(audioChunk);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
indexOf(arr, subarr) {
|
|
218
|
+
for (let i = 0; i <= arr.length - subarr.length; i++) {
|
|
219
|
+
let match = true;
|
|
220
|
+
for (let j = 0; j < subarr.length; j++) {
|
|
221
|
+
if (arr[i + j] !== subarr[j]) {
|
|
222
|
+
match = false;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (match) return i;
|
|
227
|
+
}
|
|
228
|
+
return -1;
|
|
229
|
+
}
|
|
230
|
+
async generateSecMsGec(trustedClientToken) {
|
|
231
|
+
const now = this.nowRFC1123();
|
|
232
|
+
const fixedDate = this.parseRFC1123(now);
|
|
233
|
+
const ticks = Math.floor(fixedDate.getTime() / 1e3) + 11644473600;
|
|
234
|
+
const rounded = ticks - ticks % 300;
|
|
235
|
+
const windowsTicks = rounded * 1e7;
|
|
236
|
+
const encoder = new TextEncoder();
|
|
237
|
+
const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);
|
|
238
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
239
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
240
|
+
}
|
|
241
|
+
getAudioData() {
|
|
242
|
+
if (this.audio_stream.length === 0) {
|
|
243
|
+
throw new Error("No audio data available");
|
|
244
|
+
}
|
|
245
|
+
const totalLength = this.audio_stream.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
246
|
+
const result = new Uint8Array(totalLength);
|
|
247
|
+
let offset = 0;
|
|
248
|
+
for (const chunk of this.audio_stream) {
|
|
249
|
+
result.set(chunk, offset);
|
|
250
|
+
offset += chunk.length;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
return __toCommonJS(EdgeTTS_browser_exports);
|
|
256
|
+
})();
|
|
257
|
+
//# sourceMappingURL=edge-tts.umd.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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}\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 getSSML(text: string, voice: string, options: SynthesisOptions = {}): string {\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\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 ${text}\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 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": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";var EdgeTTS=(()=>{var p=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var C=(r,e,t)=>e in r?p(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var v=(r,e)=>{for(var t in e)p(r,t,{get:e[t],enumerable:!0})},A=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of E(e))!f.call(r,s)&&s!==t&&p(r,s,{get:()=>e[s],enumerable:!(n=x(e,s))||n.enumerable});return r};var T=r=>A(p({},"__esModule",{value:!0}),r);var S=(r,e,t)=>C(r,typeof e!="symbol"?e+"":e,t);var U={};v(U,{EdgeTTS:()=>w});var o={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 r=new Uint8Array(16);return crypto.getRandomValues(r),Array.from(r).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 w=class{constructor(){S(this,"audio_stream",[]);S(this,"ws")}async getVoices(){let e=await this.generateSecMsGec(o.TRUSTED_CLIENT_TOKEN);return(await(await fetch(`${o.VOICES_URL}?Ocp-Apim-Subscription-Key=${o.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${o.VERSION_MS_GEC}`,{headers:o.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}%`}getSSML(e,t,n={}){let s=this.validatePitch(n.pitch??0),i=this.validateRate(n.rate??0),a=this.validateVolume(n.volume??0);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
|
+
<voice name="${t}">
|
|
3
|
+
<prosody pitch="${s}" rate="${i}" volume="${a}">
|
|
4
|
+
${e}
|
|
5
|
+
</prosody>
|
|
6
|
+
</voice>
|
|
7
|
+
</speak>`}nowRFC1123(){return new Date().toUTCString()}parseRFC1123(e){return new Date(e)}buildTTSConfigMessage(){return`X-Timestamp:${this.nowRFC1123()}\r
|
|
8
|
+
Content-Type:application/json; charset=utf-8\r
|
|
9
|
+
Path:speech.config\r
|
|
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(o.TRUSTED_CLIENT_TOKEN);return new Promise((i,a)=>{this.audio_stream=[];let g=this.generateUUID(),l=`${o.WSS_URL}?Ocp-Apim-Subscription-Key=${o.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${s}&Sec-MS-GEC-Version=${o.VERSION_MS_GEC}&ConnectionId=${g}`;console.log("WebSocket URL:",l),this.ws=new WebSocket(l);let u=this.getSSML(e,t,n),m=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),a(new Error("Synthesis timeout"))},3e4);this.ws.onopen=()=>{let c=this.buildTTSConfigMessage();this.ws.send(c);let d=this.nowRFC1123(),h=`X-RequestId:${g}\r
|
|
12
|
+
Content-Type:application/ssml+xml\r
|
|
13
|
+
X-Timestamp:${d}\r
|
|
14
|
+
Path:ssml\r
|
|
15
|
+
\r
|
|
16
|
+
${u}`;this.ws.send(h)},this.ws.onmessage=c=>{if(typeof c.data=="string")c.data.includes("Path:turn.end")&&this.ws?.close();else{let d=new FileReader;d.onload=()=>{let h=d.result,y=new Uint8Array(h);this.processAudioData(y)},d.readAsArrayBuffer(c.data)}},this.ws.onerror=c=>{clearTimeout(m),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),a(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(m),i()}})}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r
|
|
17
|
+
`),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,a=(s-s%300)*1e7,l=new TextEncoder().encode(`${a}${e}`),u=await crypto.subtle.digest("SHA-256",l);return Array.from(new Uint8Array(u)).map(m=>m.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 T(U);})();
|
|
18
|
+
//# sourceMappingURL=edge-tts.umd.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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}\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 getSSML(text: string, voice: string, options: SynthesisOptions = {}): string {\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\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 ${text}\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 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", "text", "options", "rfcStr", "resolve", "reject", "reqId", "url", "SSML_text", "timeout", "message", "timestamp", "speechMessage", "event", "reader", "arrayBuffer", "uint8Array", "err", "buffer", "needle", "audioStartIndex", "audioChunk", "arr", "subarr", "i", "match", "j", "trustedClientToken", "now", "fixedDate", "ticks", "windowsTicks", "data", "hashBuffer", "b", "totalLength", "acc", "chunk", "result", "offset"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { EdgeTTS, type Voice, type SynthesisOptions, type WordBoundary } from './browser/EdgeTTS.browser';
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EdgeTTS = void 0;
|
|
4
|
+
var EdgeTTS_browser_1 = require("./browser/EdgeTTS.browser");
|
|
5
|
+
Object.defineProperty(exports, "EdgeTTS", { enumerable: true, get: function () { return EdgeTTS_browser_1.EdgeTTS; } });
|
package/dist/cli/edge-tts.js
CHANGED
|
@@ -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.
|
|
9
|
+
.version('1.6.0')
|
|
10
10
|
.addCommand(SynthesizeCommand_1.SynthesizeCommand)
|
|
11
11
|
.addCommand(VoiceListCommand_1.VoiceListCommand);
|
|
12
12
|
try {
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
export declare const Constants: {
|
|
2
2
|
TRUSTED_CLIENT_TOKEN: string;
|
|
3
|
+
BASE_URL: string;
|
|
3
4
|
WSS_URL: string;
|
|
4
5
|
VOICES_URL: string;
|
|
5
|
-
|
|
6
|
+
CHROMIUM_FULL_VERSION: string;
|
|
7
|
+
CHROMIUM_MAJOR_VERSION: string;
|
|
6
8
|
VERSION_MS_GEC: string;
|
|
9
|
+
token32(): string;
|
|
10
|
+
getBaseHeaders(): {
|
|
11
|
+
'User-Agent': string;
|
|
12
|
+
'Accept-Encoding': string;
|
|
13
|
+
'Accept-Language': string;
|
|
14
|
+
Cookie: string;
|
|
15
|
+
};
|
|
16
|
+
WSS_HEADERS: {
|
|
17
|
+
Pragma: string;
|
|
18
|
+
'Cache-Control': string;
|
|
19
|
+
Origin: string;
|
|
20
|
+
'Sec-WebSocket-Protocol': string;
|
|
21
|
+
'Sec-WebSocket-Version': string;
|
|
22
|
+
'User-Agent': string;
|
|
23
|
+
};
|
|
24
|
+
VOICE_HEADERS: {
|
|
25
|
+
'Sec-CH-UA': string;
|
|
26
|
+
'Sec-CH-UA-Mobile': string;
|
|
27
|
+
Accept: string;
|
|
28
|
+
'Sec-Fetch-Site': string;
|
|
29
|
+
'Sec-Fetch-Mode': string;
|
|
30
|
+
'Sec-Fetch-Dest': string;
|
|
31
|
+
};
|
|
7
32
|
};
|
package/dist/config/constants.js
CHANGED
|
@@ -3,8 +3,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Constants = void 0;
|
|
4
4
|
exports.Constants = {
|
|
5
5
|
TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',
|
|
6
|
-
|
|
6
|
+
BASE_URL: 'https://api.msedgeservices.com/tts/cognitiveservices',
|
|
7
|
+
WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',
|
|
7
8
|
VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
CHROMIUM_FULL_VERSION: '142.0.3595.0',
|
|
10
|
+
CHROMIUM_MAJOR_VERSION: '142',
|
|
11
|
+
VERSION_MS_GEC: '1-142.0.3595',
|
|
12
|
+
token32() {
|
|
13
|
+
const bytes = new Uint8Array(16);
|
|
14
|
+
crypto.getRandomValues(bytes);
|
|
15
|
+
return Array.from(bytes)
|
|
16
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
17
|
+
.join('')
|
|
18
|
+
.toUpperCase();
|
|
19
|
+
},
|
|
20
|
+
getBaseHeaders() {
|
|
21
|
+
return {
|
|
22
|
+
'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',
|
|
23
|
+
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
|
24
|
+
'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',
|
|
25
|
+
'Cookie': 'MUID=' + this.token32()
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
WSS_HEADERS: {
|
|
29
|
+
'Pragma': 'no-cache',
|
|
30
|
+
'Cache-Control': 'no-cache',
|
|
31
|
+
'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',
|
|
32
|
+
'Sec-WebSocket-Protocol': 'synthesize',
|
|
33
|
+
'Sec-WebSocket-Version': '13',
|
|
34
|
+
'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'
|
|
35
|
+
},
|
|
36
|
+
VOICE_HEADERS: {
|
|
37
|
+
'Sec-CH-UA': '" Not;A Brand";v="99", "Microsoft Edge";v="140", "Chromium";v="140"',
|
|
38
|
+
'Sec-CH-UA-Mobile': '?0',
|
|
39
|
+
'Accept': '*/*',
|
|
40
|
+
'Sec-Fetch-Site': 'none',
|
|
41
|
+
'Sec-Fetch-Mode': 'cors',
|
|
42
|
+
'Sec-Fetch-Dest': 'empty'
|
|
43
|
+
}
|
|
10
44
|
};
|
package/dist/services/EdgeTTS.js
CHANGED
|
@@ -36,9 +36,7 @@ class EdgeTTS {
|
|
|
36
36
|
});
|
|
37
37
|
const response = await new Promise((resolve, reject) => {
|
|
38
38
|
const req = https_1.default.request(`${constants_1.Constants.VOICES_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}`, {
|
|
39
|
-
headers:
|
|
40
|
-
"User-Agent": constants_1.Constants.USER_AGENT
|
|
41
|
-
},
|
|
39
|
+
headers: constants_1.Constants.getBaseHeaders(),
|
|
42
40
|
agent: httpsAgent
|
|
43
41
|
}, (res) => {
|
|
44
42
|
let data = '';
|
|
@@ -116,10 +114,9 @@ class EdgeTTS {
|
|
|
116
114
|
this.audio_stream = [];
|
|
117
115
|
const reqId = this.generateUUID();
|
|
118
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);
|
|
119
118
|
this.ws = new ws_1.default(url, {
|
|
120
|
-
headers:
|
|
121
|
-
"User-Agent": constants_1.Constants.USER_AGENT
|
|
122
|
-
},
|
|
119
|
+
headers: constants_1.Constants.getBaseHeaders(),
|
|
123
120
|
rejectUnauthorized: false
|
|
124
121
|
});
|
|
125
122
|
const SSML_text = this.getSSML(text, voice, options);
|
|
@@ -259,9 +256,7 @@ class EdgeTTS {
|
|
|
259
256
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
260
257
|
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}`;
|
|
261
258
|
this.ws = new ws_1.default(url, {
|
|
262
|
-
headers:
|
|
263
|
-
"User-Agent": constants_1.Constants.USER_AGENT
|
|
264
|
-
},
|
|
259
|
+
headers: constants_1.Constants.getBaseHeaders(),
|
|
265
260
|
rejectUnauthorized: false
|
|
266
261
|
});
|
|
267
262
|
const SSML_text = this.getSSML(text, voice, options);
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andresaya/edge-tts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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
|
+
"browser": "dist/browser/edge-tts.esm.js",
|
|
7
|
+
"unpkg": "dist/browser/edge-tts.umd.min.js",
|
|
8
|
+
"jsdelivr": "dist/browser/edge-tts.umd.min.js",
|
|
6
9
|
"keywords": [
|
|
7
10
|
"edge",
|
|
8
11
|
"tts",
|
|
@@ -20,11 +23,13 @@
|
|
|
20
23
|
"scripts": {
|
|
21
24
|
"build": "tsc",
|
|
22
25
|
"build:bin": "bun scripts/build.ts",
|
|
23
|
-
"
|
|
26
|
+
"build:browser": "bun scripts/build-browser.ts",
|
|
27
|
+
"prepublishOnly": "npm run build && npm run build:bin && npm run build:browser"
|
|
24
28
|
},
|
|
25
29
|
"devDependencies": {
|
|
26
30
|
"@types/bun": "latest",
|
|
27
31
|
"@types/ws": "^8.18.1",
|
|
32
|
+
"esbuild": "^0.25.12",
|
|
28
33
|
"typescript": "^5.6.2"
|
|
29
34
|
},
|
|
30
35
|
"dependencies": {
|