@andresaya/edge-tts 1.8.0 → 1.8.3
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 +543 -525
- package/dist/browser/edge-tts.esm.js +29 -32
- package/dist/browser/edge-tts.esm.js.map +2 -2
- package/dist/browser/edge-tts.esm.min.js +10 -10
- package/dist/browser/edge-tts.esm.min.js.map +3 -3
- package/dist/browser/edge-tts.umd.js +29 -32
- package/dist/browser/edge-tts.umd.js.map +2 -2
- package/dist/browser/edge-tts.umd.min.js +11 -11
- package/dist/browser/edge-tts.umd.min.js.map +3 -3
- package/dist/cli/edge-tts.js +3 -6
- package/dist/commands/VoiceListCommand.js +1 -1
- package/dist/config/constants.d.ts +1 -2
- package/dist/config/constants.js +9 -9
- package/dist/services/EdgeTTS.js +34 -21
- package/package.json +48 -54
package/dist/services/EdgeTTS.js
CHANGED
|
@@ -79,22 +79,26 @@ class EdgeTTS {
|
|
|
79
79
|
}
|
|
80
80
|
async getVoices() {
|
|
81
81
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
82
|
-
const httpsAgent = new https_1.default.Agent({ rejectUnauthorized: false });
|
|
83
82
|
const url = `${constants_1.Constants.VOICES_URL}` +
|
|
84
83
|
`?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}` +
|
|
85
84
|
`&Sec-MS-GEC=${secMsGEC}` +
|
|
86
85
|
`&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}`;
|
|
87
86
|
const headers = {
|
|
88
87
|
...constants_1.Constants.getBaseHeaders(),
|
|
88
|
+
...constants_1.Constants.VOICE_HEADERS,
|
|
89
89
|
"Accept-Encoding": "identity", // evita gzip/br/zstd
|
|
90
90
|
};
|
|
91
91
|
const voicesRaw = await new Promise((resolve, reject) => {
|
|
92
|
-
const req = https_1.default.request(url, { method: "GET", headers
|
|
92
|
+
const req = https_1.default.request(url, { method: "GET", headers }, (res) => {
|
|
93
93
|
const chunks = [];
|
|
94
94
|
res.on("data", (chunk) => chunks.push(buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(chunk)));
|
|
95
95
|
res.on("error", reject);
|
|
96
96
|
res.on("end", () => {
|
|
97
97
|
const body = buffer_1.Buffer.concat(chunks).toString("utf8");
|
|
98
|
+
if (res.statusCode !== 200) {
|
|
99
|
+
reject(new Error(`Voice list request failed with status ${res.statusCode}`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
98
102
|
try {
|
|
99
103
|
const parsed = JSON.parse(body);
|
|
100
104
|
// soporta array directo o { voices: [...] }
|
|
@@ -104,7 +108,7 @@ class EdgeTTS {
|
|
|
104
108
|
resolve(Array.isArray(voices) ? voices : []);
|
|
105
109
|
}
|
|
106
110
|
catch (e) {
|
|
107
|
-
reject(new Error("JSON
|
|
111
|
+
reject(new Error("Invalid voice list JSON: " + (e?.message || String(e))));
|
|
108
112
|
}
|
|
109
113
|
});
|
|
110
114
|
});
|
|
@@ -163,7 +167,7 @@ class EdgeTTS {
|
|
|
163
167
|
volumeValue = volume;
|
|
164
168
|
}
|
|
165
169
|
if (volumeValue < -100 || volumeValue > 100) {
|
|
166
|
-
throw new Error("
|
|
170
|
+
throw new Error("Invalid volume. Expected a value from -100% to 100%.");
|
|
167
171
|
}
|
|
168
172
|
return `${volumeValue}%`;
|
|
169
173
|
}
|
|
@@ -171,11 +175,11 @@ class EdgeTTS {
|
|
|
171
175
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
172
176
|
return new Promise((resolve, reject) => {
|
|
173
177
|
this.audio_stream = [];
|
|
178
|
+
this.word_boundaries = [];
|
|
174
179
|
const reqId = this.generateUUID();
|
|
175
|
-
const url = `${constants_1.Constants.WSS_URL}?
|
|
180
|
+
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}`;
|
|
176
181
|
this.ws = new ws_1.default(url, {
|
|
177
|
-
headers: constants_1.Constants.getBaseHeaders(),
|
|
178
|
-
rejectUnauthorized: false
|
|
182
|
+
headers: { ...constants_1.Constants.getBaseHeaders(), ...constants_1.Constants.WSS_HEADERS }
|
|
179
183
|
});
|
|
180
184
|
const SSML_text = this.getSSML(text, voice, options);
|
|
181
185
|
const outputFormat = options.outputFormat || 'audio-24khz-48kbitrate-mono-mp3';
|
|
@@ -232,13 +236,13 @@ class EdgeTTS {
|
|
|
232
236
|
const rate = this.validateRate(options.rate ?? 0);
|
|
233
237
|
const volume = this.validateVolume(options.volume ?? 0);
|
|
234
238
|
const escapedText = this.escapeXML(content);
|
|
235
|
-
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">
|
|
236
|
-
<voice name="${voice}">
|
|
237
|
-
<prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
|
|
238
|
-
${escapedText}
|
|
239
|
-
</prosody>
|
|
240
|
-
</voice>
|
|
241
|
-
</speak>
|
|
239
|
+
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">
|
|
240
|
+
<voice name="${voice}">
|
|
241
|
+
<prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
|
|
242
|
+
${escapedText}
|
|
243
|
+
</prosody>
|
|
244
|
+
</voice>
|
|
245
|
+
</speak>
|
|
242
246
|
`;
|
|
243
247
|
}
|
|
244
248
|
nowRFC1123(timeZone = 'UTC') {
|
|
@@ -266,12 +270,12 @@ class EdgeTTS {
|
|
|
266
270
|
}
|
|
267
271
|
async *synthesizeStream(text, voice = 'en-US-AnaNeural', options = {}) {
|
|
268
272
|
this.audio_stream = [];
|
|
273
|
+
this.word_boundaries = [];
|
|
269
274
|
const reqId = this.generateUUID();
|
|
270
275
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
271
|
-
const url = `${constants_1.Constants.WSS_URL}?
|
|
276
|
+
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}`;
|
|
272
277
|
this.ws = new ws_1.default(url, {
|
|
273
|
-
headers: constants_1.Constants.getBaseHeaders(),
|
|
274
|
-
rejectUnauthorized: false
|
|
278
|
+
headers: { ...constants_1.Constants.getBaseHeaders(), ...constants_1.Constants.WSS_HEADERS }
|
|
275
279
|
});
|
|
276
280
|
const SSML_text = this.getSSML(text, voice, options);
|
|
277
281
|
const outputFormat = options.outputFormat || 'audio-24khz-48kbitrate-mono-mp3';
|
|
@@ -431,10 +435,10 @@ class EdgeTTS {
|
|
|
431
435
|
if (this.audio_stream.length === 0) {
|
|
432
436
|
throw new Error("No audio data available");
|
|
433
437
|
}
|
|
434
|
-
//
|
|
438
|
+
// NOTE: MP3 is CBR; formats without a bitrate (opus) assume 48 kbit/s.
|
|
435
439
|
const bufferSize = this.toBuffer().length;
|
|
436
|
-
const
|
|
437
|
-
return
|
|
440
|
+
const kbps = Number(/(\d+)kbitrate/.exec(this.output_format)?.[1] ?? 48);
|
|
441
|
+
return (bufferSize * 8) / (kbps * 1000);
|
|
438
442
|
}
|
|
439
443
|
getFileExtension(format) {
|
|
440
444
|
if (format.includes('mp3'))
|
|
@@ -486,7 +490,16 @@ class EdgeTTS {
|
|
|
486
490
|
if (this.audio_stream.length === 0) {
|
|
487
491
|
throw new Error("No audio data available. Did you run synthesize() first?");
|
|
488
492
|
}
|
|
489
|
-
|
|
493
|
+
// When the service cannot speak the text it answers with a single empty
|
|
494
|
+
// chunk, so the stream is not empty but the audio is. Checking the byte
|
|
495
|
+
// count rather than the chunk count is what catches that.
|
|
496
|
+
const buffer = buffer_1.Buffer.concat(this.audio_stream);
|
|
497
|
+
if (buffer.length === 0) {
|
|
498
|
+
throw new Error("The service returned no audio. Voices are locale specific, so a voice cannot " +
|
|
499
|
+
"read text written in another script. Check that the voice matches the language " +
|
|
500
|
+
"of the text.");
|
|
501
|
+
}
|
|
502
|
+
return buffer;
|
|
490
503
|
}
|
|
491
504
|
async saveMetadata(outputPath) {
|
|
492
505
|
if (this.word_boundaries.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,54 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@andresaya/edge-tts",
|
|
3
|
-
"version": "1.8.
|
|
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
|
-
"main": "dist/index.js",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"microsoft
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"edge-tts": "dist/cli/edge-tts.js"
|
|
50
|
-
},
|
|
51
|
-
"engines": {
|
|
52
|
-
"bun": ">=0.5.0"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@andresaya/edge-tts",
|
|
3
|
+
"version": "1.8.3",
|
|
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
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"browser": "dist/browser/edge-tts.esm.js",
|
|
8
|
+
"unpkg": "dist/browser/edge-tts.umd.min.js",
|
|
9
|
+
"jsdelivr": "dist/browser/edge-tts.umd.min.js",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"edge",
|
|
12
|
+
"tts",
|
|
13
|
+
"text-to-speech",
|
|
14
|
+
"microsoft",
|
|
15
|
+
"microsoft edge",
|
|
16
|
+
"speech-synthesis"
|
|
17
|
+
],
|
|
18
|
+
"author": "Andres Aya",
|
|
19
|
+
"license": "GPL-3.0-only",
|
|
20
|
+
"homepage": "https://github.com/andresayac/edge-tts",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/andresayac/edge-tts/issues"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/bun": "latest",
|
|
26
|
+
"@types/ws": "^8.18.1",
|
|
27
|
+
"esbuild": "^0.25.12",
|
|
28
|
+
"typescript": "^5.6.2"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"commander": "^12.1.0",
|
|
32
|
+
"ws": "^8.18.3"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"bin": {
|
|
38
|
+
"edge-tts": "dist/cli/edge-tts.js"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"bun": ">=0.5.0"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"build:bin": "bun scripts/build.ts",
|
|
46
|
+
"build:browser": "bun scripts/build-browser.ts"
|
|
47
|
+
}
|
|
48
|
+
}
|