@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.
@@ -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, agent: httpsAgent }, (res) => {
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 inválido: " + (e?.message || String(e))));
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("Volume cannot be negative. Expected a value from -100% to 100% (or more).");
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}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;
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}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;
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
- // Estimate duration based on the size of the audio stream
438
+ // NOTE: MP3 is CBR; formats without a bitrate (opus) assume 48 kbit/s.
435
439
  const bufferSize = this.toBuffer().length;
436
- const estimatedDuration = bufferSize / (24000 * 3); // 24000 Hz sample rate, 3 bytes per sample (16-bit stereo)
437
- return estimatedDuration;
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
- return buffer_1.Buffer.concat(this.audio_stream);
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.0",
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
- "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",
9
- "keywords": [
10
- "edge",
11
- "tts",
12
- "text-to-speech",
13
- "microsoft",
14
- "microsoft edge",
15
- "speech-synthesis"
16
- ],
17
- "author": "Andres Aya",
18
- "license": "GPL-3.0-only",
19
- "homepage": "https://github.com/andresayac/edge-tts",
20
- "bugs": {
21
- "url": "https://github.com/andresayac/edge-tts/issues"
22
- },
23
- "scripts": {
24
- "build": "tsc",
25
- "build:bin": "bun scripts/build.ts",
26
- "build:browser": "bun scripts/build-browser.ts",
27
- "prepublishOnly": "npm run build && npm run build:bin && npm run build:browser"
28
- },
29
- "devDependencies": {
30
- "@types/bun": "latest",
31
- "@types/ws": "^8.18.1",
32
- "esbuild": "^0.25.12",
33
- "typescript": "^5.6.2"
34
- },
35
- "dependencies": {
36
- "@types/uuid": "^10.0.0",
37
- "commander": "^12.1.0",
38
- "events": "^3.3.0",
39
- "fs": "^0.0.1-security",
40
- "ws": "^8.18.3"
41
- },
42
- "peerDependencies": {
43
- "typescript": "^5.0.0"
44
- },
45
- "files": [
46
- "dist"
47
- ],
48
- "bin": {
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
+ }