@andresaya/edge-tts 1.3.2 → 1.4.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 CHANGED
@@ -11,8 +11,10 @@
11
11
  - **Audio Information**: Get detailed information about generated audio (size, duration, format).
12
12
  - **Audio Export Options**: Export synthesized audio in different formats (raw, base64, or directly to a file).
13
13
  - **Streaming Support**: Stream audio data in real-time for better performance.
14
+ - **Word Boundaries Metadata**: Get word boundary information with precise timestamps.
14
15
  - **Command-Line Interface**: Use a simple CLI for easy access to functionality.
15
16
  - **Easy Integration**: Modular structure allows for easy inclusion in existing projects.
17
+ - **SSML Custom**: 🥳🥳 Edge TTS accepts raw SSML with all characteristics of Azure AI Speech.
16
18
 
17
19
  ## Installation
18
20
 
@@ -40,6 +42,11 @@ To synthesize speech from text:
40
42
  edge-tts synthesize -t "Hello, world!" -o hello_world_audio
41
43
  ```
42
44
 
45
+ From file in format SSML:
46
+ ```bash
47
+ edge-tts synthesize -f ssml.txt --ssml -o salida
48
+ ```
49
+
43
50
  To list available voices:
44
51
  ```bash
45
52
  edge-tts voice-list
@@ -84,6 +91,49 @@ const maleVoices = await tts.getVoicesByGender('Male');
84
91
 
85
92
  ### Text Synthesis
86
93
 
94
+ #### Custom SSML (Advanced)
95
+ Edge TTS accepts raw SSML so you can control prosody, styles, pauses, pronunciations, and more. You can pass SSML from code or the CLI. By default the library auto-detects if your input is SSML; you can also force the mode.
96
+
97
+ More information
98
+ [Azure AI Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-structure)
99
+
100
+ ### SSML-builder
101
+ [@andresaya/ssml-builder](https://www.npmjs.com/package/@andresaya/ssml-builder) A powerful, type-safe TypeScript library for building Speech Synthesis Markup Language (SSML) documents. Create expressive text-to-speech applications with Azure Speech Service and other SSML-compliant engines.
102
+
103
+ #### What the library does for you
104
+
105
+ - Auto-detect / force mode: options.inputType may be 'auto' | 'ssml' | 'text' (default: auto).
106
+ - Validation: Throws helpful errors if SSML is malformed (e.g., missing <speak>, <voice>, or the synthesis namespace).
107
+ - Voice injection: If your SSML lacks <voice>, it injects one with the voice you passed.
108
+ - Text wrapping: If you pass plain text (or inputType: 'text'), it wraps it in a valid SSML envelope using your rate, pitch, and volume.
109
+
110
+ ```js
111
+ import { EdgeTTS } from '@andresaya/edge-tts';
112
+
113
+ const tts = new EdgeTTS();
114
+
115
+ const ssml = `
116
+ <speak version="1.0"
117
+ xmlns="http://www.w3.org/2001/10/synthesis"
118
+ xmlns:mstts="https://www.w3.org/2001/mstts"
119
+ xml:lang="es-CO">
120
+ <voice name="es-CO-GonzaloNeural">
121
+ <mstts:express-as style="narration-professional">
122
+ <prosody rate="+5%" pitch="+10Hz" volume="+0%">
123
+ Hola, este es un ejemplo de <emphasis>SSML</emphasis>.
124
+ <break time="400ms" />
125
+ El número es <say-as interpret-as="cardinal">2025</say-as>.
126
+ La palabra se pronuncia
127
+ <phoneme alphabet="ipa" ph="ˈxola">hola</phoneme>.
128
+ </prosody>
129
+ </mstts:express-as>
130
+ </voice>
131
+ </speak>`.trim();
132
+
133
+ // Auto-detects SSML, or force it with inputType: 'ssml'
134
+ await tts.synthesize(ssml, 'es-CO-GonzaloNeural', { inputType: 'ssml' });
135
+ ```
136
+
87
137
  #### Basic Synthesis
88
138
  ```js
89
139
  // Simple synthesis with default voice
@@ -151,6 +201,17 @@ console.log(`Audio saved to: ${filePath}`);
151
201
  // Creates: output_audio.mp3
152
202
  ```
153
203
 
204
+ ### Word Boundaries Metadata
205
+
206
+ ```php
207
+ // Get word boundaries with timestamps
208
+ $boundaries = $tts->getWordBoundaries();
209
+
210
+ // Save metadata to file
211
+ $tts->saveMetadata('metadata.json');
212
+ ```
213
+
214
+
154
215
  ## Examples
155
216
 
156
217
  ### Complete Example with Voice Selection
@@ -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.3.2')
9
+ .version('1.4.0')
10
10
  .addCommand(SynthesizeCommand_1.SynthesizeCommand)
11
11
  .addCommand(VoiceListCommand_1.VoiceListCommand);
12
12
  try {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SynthesizeCommand = void 0;
4
4
  const commander_1 = require("commander");
5
5
  const EdgeTTS_1 = require("../services/EdgeTTS");
6
+ const promises_1 = require("fs/promises");
6
7
  exports.SynthesizeCommand = new commander_1.Command('synthesize')
7
8
  .description('Edge TTS: synthesize text to audio')
8
9
  .option('-t, --text <text>', 'Text to convert to audio')
@@ -11,14 +12,36 @@ exports.SynthesizeCommand = new commander_1.Command('synthesize')
11
12
  .option('-l, --volume [volume]', 'Volume of speech', '0%')
12
13
  .option('-p, --pitch [pitch]', 'Pitch of speech', '0Hz')
13
14
  .option('-o, --output [output]', 'Output file name', `output_${Date.now()}`)
15
+ .option('-f, --file [file]', 'Input file (text or SSML)')
16
+ .option('--ssml', 'Force interpret input as SSML (auto-detect by default)')
14
17
  .action(async (options) => {
15
- const { text, voice, pitch, rate, volume, output } = options;
16
- if (!text) {
17
- console.error('Text is required');
18
+ const { text, voice, pitch, rate, volume, output, file, ssml: forceSSML } = options;
19
+ if (!text && !file) {
20
+ console.error('Error: Text (-t) or file (-f) is required');
18
21
  process.exit(1);
19
22
  }
23
+ if (text && file) {
24
+ console.error('Error: Cannot use both text (-t) and file (-f) options');
25
+ process.exit(1);
26
+ }
27
+ let content = text;
28
+ if (file) {
29
+ try {
30
+ content = await (0, promises_1.readFile)(file, 'utf-8');
31
+ console.log(`Reading from file: ${file}`);
32
+ }
33
+ catch (error) {
34
+ console.error(`Error reading file: ${error}`);
35
+ process.exit(1);
36
+ }
37
+ }
20
38
  const tts = new EdgeTTS_1.EdgeTTS();
21
- await tts.synthesize(text, voice, { pitch, rate, volume });
22
- await tts.toFile(`${output}`);
23
- console.log(`Audio file generated: ${output}.mp3`);
39
+ await tts.synthesize(content, voice, {
40
+ pitch,
41
+ rate,
42
+ volume,
43
+ inputType: forceSSML ? 'ssml' : 'auto'
44
+ });
45
+ const savedPath = await tts.toFile(output);
46
+ console.log(`Audio file generated: ${savedPath}`);
24
47
  });
@@ -3,4 +3,5 @@ export declare const Constants: {
3
3
  WSS_URL: string;
4
4
  VOICES_URL: string;
5
5
  USER_AGENT: string;
6
+ VERSION_MS_GEC: string;
6
7
  };
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Constants = void 0;
4
4
  exports.Constants = {
5
5
  TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',
6
- WSS_URL: 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1',
7
- VOICES_URL: 'https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list',
8
- USER_AGENT: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0'
6
+ WSS_URL: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',
7
+ VOICES_URL: 'https://api.msedgeservices.com/tts/cognitiveservices/voices/list',
8
+ USER_AGENT: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
9
+ VERSION_MS_GEC: '1-140.0.3485.14'
9
10
  };
@@ -5,15 +5,24 @@ export interface Voice {
5
5
  Gender: string;
6
6
  Locale: string;
7
7
  FriendlyName: string;
8
+ LocalName: string;
8
9
  }
9
10
  export interface SynthesisOptions {
10
11
  pitch?: string | number;
11
12
  rate?: string | number;
12
13
  volume?: string | number;
14
+ inputType?: 'auto' | 'ssml' | 'text';
15
+ }
16
+ export interface WordBoundary {
17
+ type: "WordBoundary";
18
+ offset: number;
19
+ duration: number;
20
+ text: string;
13
21
  }
14
22
  export declare class EdgeTTS {
15
23
  private audio_stream;
16
24
  private audio_format;
25
+ private word_boundaries;
17
26
  private ws;
18
27
  getVoices(): Promise<Voice[]>;
19
28
  getVoicesByLanguage(locale: string): Promise<Voice[]>;
@@ -23,10 +32,13 @@ export declare class EdgeTTS {
23
32
  private validateRate;
24
33
  private validateVolume;
25
34
  synthesize(text: string, voice?: string, options?: SynthesisOptions): Promise<void>;
35
+ private detectSSML;
36
+ private escapeXML;
26
37
  private getSSML;
27
38
  private buildTTSConfigMessage;
28
39
  synthesizeStream(text: string, voice?: string, options?: SynthesisOptions): AsyncGenerator<Uint8Array, void, unknown>;
29
40
  private processAudioData;
41
+ private parseMetadata;
30
42
  private generateSecMsGec;
31
43
  getDuration(): number;
32
44
  getAudioInfo(): {
@@ -38,4 +50,6 @@ export declare class EdgeTTS {
38
50
  toRaw(): string;
39
51
  toBase64(): string;
40
52
  toBuffer(): Buffer;
53
+ saveMetadata(outputPath: string): Promise<void>;
54
+ getWordBoundaries(): WordBoundary[];
41
55
  }
@@ -26,17 +26,19 @@ function ensureBuffer(data) {
26
26
  class EdgeTTS {
27
27
  audio_stream = [];
28
28
  audio_format = 'mp3';
29
+ word_boundaries = [];
29
30
  ws;
30
31
  async getVoices() {
31
- const response = await fetch(`${constants_1.Constants.VOICES_URL}?trustedclienttoken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}`, {
32
+ const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
33
+ const response = await fetch(`${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}`, {
32
34
  headers: {
33
35
  "User-Agent": constants_1.Constants.USER_AGENT
34
36
  },
35
37
  });
36
38
  const data = await response.json();
37
39
  return data.map((voice) => {
38
- delete voice.VoiceTag;
39
- delete voice.SuggestedCodec;
40
+ voice.FriendlyName = voice.FriendlyName || voice.LocalName;
41
+ delete voice.SampleRateHertz;
40
42
  delete voice.Status;
41
43
  return voice;
42
44
  });
@@ -99,8 +101,8 @@ class EdgeTTS {
99
101
  const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
100
102
  return new Promise((resolve, reject) => {
101
103
  this.audio_stream = [];
102
- const req_id = this.generateUUID();
103
- const url = `${constants_1.Constants.WSS_URL}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=1-130.0.2849.68&ConnectionId=${req_id}`;
104
+ const reqId = this.generateUUID();
105
+ 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}`;
104
106
  this.ws = new ws_1.default(url, {
105
107
  headers: {
106
108
  "User-Agent": constants_1.Constants.USER_AGENT
@@ -116,7 +118,7 @@ class EdgeTTS {
116
118
  this.ws.on('open', () => {
117
119
  const message = this.buildTTSConfigMessage();
118
120
  this.ws.send(message);
119
- const speechMessage = `X-RequestId:${req_id}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:${new Date().toISOString()}Z\r\nPath:ssml\r\n\r\n${SSML_text}`;
121
+ const speechMessage = `X-RequestId:${reqId}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:${new Date().toISOString()}Z\r\nPath:ssml\r\n\r\n${SSML_text}`;
120
122
  this.ws.send(speechMessage);
121
123
  });
122
124
  this.ws.on('message', (data) => {
@@ -135,14 +137,83 @@ class EdgeTTS {
135
137
  });
136
138
  });
137
139
  }
138
- getSSML(text, voice, options = {}) {
139
- if (typeof options.pitch === 'string') {
140
- options.pitch = options.pitch.replace('hz', 'Hz');
140
+ detectSSML(content) {
141
+ const trimmedContent = content.trim();
142
+ const looksLikeSSML = /^<\?xml|^<speak/i.test(trimmedContent);
143
+ if (!looksLikeSSML) {
144
+ return { isValid: true, isSSML: false };
145
+ }
146
+ const errors = [];
147
+ const hasSpeakTag = /<speak\b[^>]*>[\s\S]*<\/speak>/i.test(trimmedContent);
148
+ const hasVoiceTag = /<voice\b[^>]*>[\s\S]*<\/voice>/i.test(trimmedContent);
149
+ if (!hasSpeakTag) {
150
+ throw new Error('Invalid SSML: Missing <speak> tag');
151
+ }
152
+ if (!hasVoiceTag) {
153
+ throw new Error('Invalid SSML: Missing <voice> tag');
154
+ }
155
+ const hasCorrectNamespace = /xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(trimmedContent);
156
+ if (!hasCorrectNamespace && hasSpeakTag) {
157
+ throw new Error('Invalid SSML: Missing or incorrect namespace declaration');
158
+ }
159
+ return {
160
+ isValid: errors.length === 0,
161
+ isSSML: hasSpeakTag || hasVoiceTag,
162
+ errors: errors.length > 0 ? errors : undefined
163
+ };
164
+ }
165
+ escapeXML(text) {
166
+ return text
167
+ .replace(/&/g, '&amp;')
168
+ .replace(/</g, '&lt;')
169
+ .replace(/>/g, '&gt;')
170
+ .replace(/"/g, '&quot;')
171
+ .replace(/'/g, '&apos;');
172
+ }
173
+ getSSML(content, voice, options = {}) {
174
+ const inputType = options.inputType || 'auto';
175
+ let treatAsSSML = false;
176
+ if (inputType === 'ssml') {
177
+ treatAsSSML = true;
178
+ }
179
+ else if (inputType === 'text') {
180
+ treatAsSSML = false;
181
+ }
182
+ else { // 'auto'
183
+ const detection = this.detectSSML(content);
184
+ treatAsSSML = detection.isSSML;
185
+ if (detection.isSSML) {
186
+ console.log('→ Detected SSML input');
187
+ if (!detection.isValid) {
188
+ console.warn('⚠ SSML validation warnings:', detection.errors);
189
+ }
190
+ }
191
+ else {
192
+ console.log('→ Detected plain text input');
193
+ }
194
+ }
195
+ if (treatAsSSML) {
196
+ let ssml = content.trim();
197
+ if (!ssml.includes('xmlns=')) {
198
+ ssml = ssml.replace(/<speak([^>]*)>/i, '<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">');
199
+ }
200
+ if (!/<voice\b[^>]*>/i.test(ssml) && voice) {
201
+ ssml = ssml.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i, `$1<voice name="${voice}">$2</voice>$3`);
202
+ }
203
+ return ssml;
141
204
  }
142
205
  const pitch = this.validatePitch(options.pitch ?? 0);
143
206
  const rate = this.validateRate(options.rate ?? 0);
144
207
  const volume = this.validateVolume(options.volume ?? 0);
145
- return `<speak version='1.0' xml:lang='en-US'><voice name='${voice}'><prosody pitch='${pitch}' rate='${rate}' volume='${volume}'>${text}</prosody></voice></speak>`;
208
+ const escapedText = this.escapeXML(content);
209
+ 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">
210
+ <voice name="${voice}">
211
+ <prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
212
+ ${escapedText}
213
+ </prosody>
214
+ </voice>
215
+ </speak>
216
+ `;
146
217
  }
147
218
  buildTTSConfigMessage() {
148
219
  return `X-Timestamp:${new Date().toISOString()}Z\r\nContent-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n` +
@@ -150,10 +221,14 @@ class EdgeTTS {
150
221
  }
151
222
  async *synthesizeStream(text, voice = 'en-US-AnaNeural', options = {}) {
152
223
  this.audio_stream = [];
153
- const req_id = this.generateUUID();
224
+ const reqId = this.generateUUID();
154
225
  const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
155
- const url = `${constants_1.Constants.WSS_URL}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=1-130.0.2849.68&ConnectionId=${req_id}`;
156
- this.ws = new ws_1.default(url);
226
+ 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}`;
227
+ this.ws = new ws_1.default(url, {
228
+ headers: {
229
+ "User-Agent": constants_1.Constants.USER_AGENT
230
+ }
231
+ });
157
232
  const SSML_text = this.getSSML(text, voice, options);
158
233
  const queue = [];
159
234
  let done = false;
@@ -174,7 +249,7 @@ class EdgeTTS {
174
249
  this.ws.on('open', () => {
175
250
  const message = this.buildTTSConfigMessage();
176
251
  this.ws.send(message);
177
- const speechMessage = `X-RequestId:${req_id}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:${new Date().toISOString()}Z\r\nPath:ssml\r\n\r\n${SSML_text}`;
252
+ const speechMessage = `X-RequestId:${reqId}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:${new Date().toISOString()}Z\r\nPath:ssml\r\n\r\n${SSML_text}`;
178
253
  this.ws.send(speechMessage);
179
254
  });
180
255
  this.ws.on('message', (data) => {
@@ -187,6 +262,15 @@ class EdgeTTS {
187
262
  this.audio_stream.push(chunk);
188
263
  push(chunk);
189
264
  }
265
+ if (buffer.toString().includes("Path:audio.metadata")) {
266
+ const metadataStart = buffer.indexOf("\r\n\r\n") + 4;
267
+ const metadataJson = buffer.toString().substring(metadataStart);
268
+ const meta = this.parseMetadata(metadataJson);
269
+ if (meta !== null) {
270
+ this.word_boundaries.push(meta);
271
+ }
272
+ return;
273
+ }
190
274
  if (buffer.toString().includes('Path:turn.end')) {
191
275
  this.ws?.close();
192
276
  }
@@ -229,10 +313,44 @@ class EdgeTTS {
229
313
  const audioChunk = buffer.subarray(audioStartIndex + needle.length);
230
314
  this.audio_stream.push(new Uint8Array(audioChunk));
231
315
  }
316
+ if (buffer.toString().includes("Path:audio.metadata")) {
317
+ const metadataStart = buffer.indexOf("\r\n\r\n") + 4;
318
+ const metadataJson = buffer.toString().substring(metadataStart);
319
+ const meta = this.parseMetadata(metadataJson);
320
+ if (meta !== null) {
321
+ this.word_boundaries.push(meta);
322
+ }
323
+ return;
324
+ }
232
325
  if (buffer.toString().includes("Path:turn.end")) {
233
326
  this.ws?.close();
234
327
  }
235
328
  }
329
+ parseMetadata(data, offsetCompensation = 0) {
330
+ let metadata;
331
+ try {
332
+ metadata = JSON.parse(data);
333
+ }
334
+ catch {
335
+ return null;
336
+ }
337
+ if (!metadata.Metadata) {
338
+ return null;
339
+ }
340
+ for (const metaObj of metadata.Metadata) {
341
+ if (metaObj.Type === "WordBoundary") {
342
+ const currentOffset = metaObj.Data.Offset + offsetCompensation;
343
+ const currentDuration = metaObj.Data.Duration;
344
+ return {
345
+ type: "WordBoundary",
346
+ offset: currentOffset,
347
+ duration: currentDuration,
348
+ text: metaObj.Data.text?.Text,
349
+ };
350
+ }
351
+ }
352
+ return null;
353
+ }
236
354
  async generateSecMsGec(trustedClientToken) {
237
355
  const ticks = Math.floor(Date.now() / 1000) + 11644473600;
238
356
  const rounded = ticks - (ticks % 300);
@@ -282,5 +400,15 @@ class EdgeTTS {
282
400
  }
283
401
  return buffer_1.Buffer.concat(this.audio_stream);
284
402
  }
403
+ async saveMetadata(outputPath) {
404
+ if (this.word_boundaries.length === 0) {
405
+ throw new Error("No metadata available to save.");
406
+ }
407
+ const json = JSON.stringify(this.word_boundaries, null, 4);
408
+ await (0, promises_1.writeFile)(outputPath, json);
409
+ }
410
+ getWordBoundaries() {
411
+ return this.word_boundaries;
412
+ }
285
413
  }
286
414
  exports.EdgeTTS = EdgeTTS;
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "@andresaya/edge-tts",
3
- "version": "1.3.2",
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
- "keywords": [
7
- "edge",
8
- "tts",
9
- "text-to-speech",
10
- "microsoft",
11
- "microsoft edge",
12
- "speech-synthesis"
13
- ],
14
- "author": "Andres Aya",
15
- "license": "GPL-3.0-only",
16
- "homepage": "https://github.com/andresayac/edge-tts",
17
- "bugs": {
18
- "url": "https://github.com/andresayac/edge-tts/issues"
19
- },
20
- "scripts": {
21
- "build": "tsc",
22
- "build:bin": "bun scripts/build.ts"
23
- },
24
- "devDependencies": {
25
- "@types/bun": "latest",
26
- "@types/ws": "^8.18.1",
27
- "typescript": "^5.6.2"
28
- },
29
- "dependencies": {
30
- "@types/uuid": "^10.0.0",
31
- "commander": "^12.1.0",
32
- "events": "^3.3.0",
33
- "fs": "^0.0.1-security",
34
- "ws": "^8.18.0"
35
- },
36
- "peerDependencies": {
37
- "typescript": "^5.0.0"
38
- },
39
- "files": [
40
- "dist"
41
- ],
42
- "bin": {
43
- "edge-tts": "./dist/cli/edge-tts.js"
44
- },
45
- "engines": {
46
- "bun": ">=0.5.0"
47
- }
48
- }
1
+ {
2
+ "name": "@andresaya/edge-tts",
3
+ "version": "1.4.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
+ "keywords": [
7
+ "edge",
8
+ "tts",
9
+ "text-to-speech",
10
+ "microsoft",
11
+ "microsoft edge",
12
+ "speech-synthesis"
13
+ ],
14
+ "author": "Andres Aya",
15
+ "license": "GPL-3.0-only",
16
+ "homepage": "https://github.com/andresayac/edge-tts",
17
+ "bugs": {
18
+ "url": "https://github.com/andresayac/edge-tts/issues"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "build:bin": "bun scripts/build.ts"
23
+ },
24
+ "devDependencies": {
25
+ "@types/bun": "latest",
26
+ "@types/ws": "^8.18.1",
27
+ "typescript": "^5.6.2"
28
+ },
29
+ "dependencies": {
30
+ "@types/uuid": "^10.0.0",
31
+ "commander": "^12.1.0",
32
+ "events": "^3.3.0",
33
+ "fs": "^0.0.1-security",
34
+ "ws": "^8.18.0"
35
+ },
36
+ "peerDependencies": {
37
+ "typescript": "^5.0.0"
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "bin": {
43
+ "edge-tts": "./dist/cli/edge-tts.js"
44
+ },
45
+ "engines": {
46
+ "bun": ">=0.5.0"
47
+ }
48
+ }
@@ -1,81 +0,0 @@
1
- export declare const AUDIO_FORMATS_STREAM: {
2
- readonly 'raw-8khz-8bit-mono-alaw': "audio/basic";
3
- readonly 'raw-8khz-8bit-mono-mulaw': "audio/basic";
4
- readonly 'raw-8khz-16bit-mono-pcm': "audio/basic";
5
- readonly 'raw-16khz-16bit-mono-pcm': "audio/basic";
6
- readonly 'raw-22050hz-16bit-mono-pcm': "audio/basic";
7
- readonly 'raw-24khz-16bit-mono-pcm': "audio/basic";
8
- readonly 'raw-44100hz-16bit-mono-pcm': "audio/basic";
9
- readonly 'raw-48khz-16bit-mono-pcm': "audio/basic";
10
- readonly 'raw-16khz-16bit-mono-truesilk': "audio/SILK";
11
- readonly 'raw-24khz-16bit-mono-truesilk': "audio/SILK";
12
- readonly 'audio-16khz-32kbitrate-mono-mp3': "audio/mpeg";
13
- readonly 'audio-16khz-64kbitrate-mono-mp3': "audio/mpeg";
14
- readonly 'audio-16khz-128kbitrate-mono-mp3': "audio/mpeg";
15
- readonly 'audio-24khz-48kbitrate-mono-mp3': "audio/mpeg";
16
- readonly 'audio-24khz-96kbitrate-mono-mp3': "audio/mpeg";
17
- readonly 'audio-24khz-160kbitrate-mono-mp3': "audio/mpeg";
18
- readonly 'audio-48khz-96kbitrate-mono-mp3': "audio/mpeg";
19
- readonly 'audio-48khz-192kbitrate-mono-mp3': "audio/mpeg";
20
- readonly 'audio-16khz-16bit-32kbps-mono-opus': "audio/opus";
21
- readonly 'audio-24khz-16bit-24kbps-mono-opus': "audio/opus";
22
- readonly 'audio-24khz-16bit-48kbps-mono-opus': "audio/opus";
23
- readonly 'ogg-16khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=16000";
24
- readonly 'ogg-24khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=24000";
25
- readonly 'ogg-48khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=48000";
26
- readonly 'webm-16khz-16bit-mono-opus': "audio/webm; codec=opus";
27
- readonly 'webm-24khz-16bit-mono-opus': "audio/webm; codec=opus";
28
- readonly 'webm-24khz-16bit-24kbps-mono-opus': "audio/webm; codec=opus";
29
- readonly 'amr-wb-16000hz': "audio/amr-wb";
30
- readonly 'g722-16khz-64kbps': "audio/g722";
31
- };
32
- export declare const AUDIO_FORMATS_NON_STREAM: {
33
- readonly 'riff-8khz-8bit-mono-alaw': "audio/x-wav";
34
- readonly 'riff-8khz-8bit-mono-mulaw': "audio/x-wav";
35
- readonly 'riff-8khz-16bit-mono-pcm': "audio/x-wav";
36
- readonly 'riff-22050hz-16bit-mono-pcm': "audio/x-wav";
37
- readonly 'riff-24khz-16bit-mono-pcm': "audio/x-wav";
38
- readonly 'riff-44100hz-16bit-mono-pcm': "audio/x-wav";
39
- readonly 'riff-48khz-16bit-mono-pcm': "audio/x-wav";
40
- };
41
- type AudioOutputFormat = keyof typeof AUDIO_FORMATS_STREAM;
42
- declare const ALL_AUDIO_FORMATS: {
43
- 'riff-8khz-8bit-mono-alaw': "audio/x-wav";
44
- 'riff-8khz-8bit-mono-mulaw': "audio/x-wav";
45
- 'riff-8khz-16bit-mono-pcm': "audio/x-wav";
46
- 'riff-22050hz-16bit-mono-pcm': "audio/x-wav";
47
- 'riff-24khz-16bit-mono-pcm': "audio/x-wav";
48
- 'riff-44100hz-16bit-mono-pcm': "audio/x-wav";
49
- 'riff-48khz-16bit-mono-pcm': "audio/x-wav";
50
- 'raw-8khz-8bit-mono-alaw': "audio/basic";
51
- 'raw-8khz-8bit-mono-mulaw': "audio/basic";
52
- 'raw-8khz-16bit-mono-pcm': "audio/basic";
53
- 'raw-16khz-16bit-mono-pcm': "audio/basic";
54
- 'raw-22050hz-16bit-mono-pcm': "audio/basic";
55
- 'raw-24khz-16bit-mono-pcm': "audio/basic";
56
- 'raw-44100hz-16bit-mono-pcm': "audio/basic";
57
- 'raw-48khz-16bit-mono-pcm': "audio/basic";
58
- 'raw-16khz-16bit-mono-truesilk': "audio/SILK";
59
- 'raw-24khz-16bit-mono-truesilk': "audio/SILK";
60
- 'audio-16khz-32kbitrate-mono-mp3': "audio/mpeg";
61
- 'audio-16khz-64kbitrate-mono-mp3': "audio/mpeg";
62
- 'audio-16khz-128kbitrate-mono-mp3': "audio/mpeg";
63
- 'audio-24khz-48kbitrate-mono-mp3': "audio/mpeg";
64
- 'audio-24khz-96kbitrate-mono-mp3': "audio/mpeg";
65
- 'audio-24khz-160kbitrate-mono-mp3': "audio/mpeg";
66
- 'audio-48khz-96kbitrate-mono-mp3': "audio/mpeg";
67
- 'audio-48khz-192kbitrate-mono-mp3': "audio/mpeg";
68
- 'audio-16khz-16bit-32kbps-mono-opus': "audio/opus";
69
- 'audio-24khz-16bit-24kbps-mono-opus': "audio/opus";
70
- 'audio-24khz-16bit-48kbps-mono-opus': "audio/opus";
71
- 'ogg-16khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=16000";
72
- 'ogg-24khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=24000";
73
- 'ogg-48khz-16bit-mono-opus': "audio/ogg; codecs=opus; rate=48000";
74
- 'webm-16khz-16bit-mono-opus': "audio/webm; codec=opus";
75
- 'webm-24khz-16bit-mono-opus': "audio/webm; codec=opus";
76
- 'webm-24khz-16bit-24kbps-mono-opus': "audio/webm; codec=opus";
77
- 'amr-wb-16000hz': "audio/amr-wb";
78
- 'g722-16khz-64kbps': "audio/g722";
79
- };
80
- declare const FORMAT_CONTENT_TYPE: Map<string, "audio/basic" | "audio/SILK" | "audio/mpeg" | "audio/opus" | "audio/ogg; codecs=opus; rate=16000" | "audio/ogg; codecs=opus; rate=24000" | "audio/ogg; codecs=opus; rate=48000" | "audio/webm; codec=opus" | "audio/amr-wb" | "audio/g722" | "audio/x-wav">;
81
- export { ALL_AUDIO_FORMATS, FORMAT_CONTENT_TYPE, AudioOutputFormat };
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FORMAT_CONTENT_TYPE = exports.ALL_AUDIO_FORMATS = exports.AUDIO_FORMATS_NON_STREAM = exports.AUDIO_FORMATS_STREAM = void 0;
4
- exports.AUDIO_FORMATS_STREAM = {
5
- // RAW PCM
6
- 'raw-8khz-8bit-mono-alaw': 'audio/basic',
7
- 'raw-8khz-8bit-mono-mulaw': 'audio/basic',
8
- 'raw-8khz-16bit-mono-pcm': 'audio/basic',
9
- 'raw-16khz-16bit-mono-pcm': 'audio/basic',
10
- 'raw-22050hz-16bit-mono-pcm': 'audio/basic',
11
- 'raw-24khz-16bit-mono-pcm': 'audio/basic',
12
- 'raw-44100hz-16bit-mono-pcm': 'audio/basic',
13
- 'raw-48khz-16bit-mono-pcm': 'audio/basic',
14
- // TRUESILK
15
- 'raw-16khz-16bit-mono-truesilk': 'audio/SILK',
16
- 'raw-24khz-16bit-mono-truesilk': 'audio/SILK',
17
- // MP3
18
- 'audio-16khz-32kbitrate-mono-mp3': 'audio/mpeg',
19
- 'audio-16khz-64kbitrate-mono-mp3': 'audio/mpeg',
20
- 'audio-16khz-128kbitrate-mono-mp3': 'audio/mpeg',
21
- 'audio-24khz-48kbitrate-mono-mp3': 'audio/mpeg',
22
- 'audio-24khz-96kbitrate-mono-mp3': 'audio/mpeg',
23
- 'audio-24khz-160kbitrate-mono-mp3': 'audio/mpeg',
24
- 'audio-48khz-96kbitrate-mono-mp3': 'audio/mpeg',
25
- 'audio-48khz-192kbitrate-mono-mp3': 'audio/mpeg',
26
- // OPUS in container
27
- 'audio-16khz-16bit-32kbps-mono-opus': 'audio/opus',
28
- 'audio-24khz-16bit-24kbps-mono-opus': 'audio/opus',
29
- 'audio-24khz-16bit-48kbps-mono-opus': 'audio/opus',
30
- // OGG
31
- 'ogg-16khz-16bit-mono-opus': 'audio/ogg; codecs=opus; rate=16000',
32
- 'ogg-24khz-16bit-mono-opus': 'audio/ogg; codecs=opus; rate=24000',
33
- 'ogg-48khz-16bit-mono-opus': 'audio/ogg; codecs=opus; rate=48000',
34
- // WEBM
35
- 'webm-16khz-16bit-mono-opus': 'audio/webm; codec=opus',
36
- 'webm-24khz-16bit-mono-opus': 'audio/webm; codec=opus',
37
- 'webm-24khz-16bit-24kbps-mono-opus': 'audio/webm; codec=opus',
38
- 'amr-wb-16000hz': 'audio/amr-wb',
39
- 'g722-16khz-64kbps': 'audio/g722'
40
- };
41
- exports.AUDIO_FORMATS_NON_STREAM = {
42
- // RIFF (WAV)
43
- 'riff-8khz-8bit-mono-alaw': 'audio/x-wav',
44
- 'riff-8khz-8bit-mono-mulaw': 'audio/x-wav',
45
- 'riff-8khz-16bit-mono-pcm': 'audio/x-wav',
46
- 'riff-22050hz-16bit-mono-pcm': 'audio/x-wav',
47
- 'riff-24khz-16bit-mono-pcm': 'audio/x-wav',
48
- 'riff-44100hz-16bit-mono-pcm': 'audio/x-wav',
49
- 'riff-48khz-16bit-mono-pcm': 'audio/x-wav',
50
- };
51
- const ALL_AUDIO_FORMATS = { ...exports.AUDIO_FORMATS_STREAM, ...exports.AUDIO_FORMATS_NON_STREAM };
52
- exports.ALL_AUDIO_FORMATS = ALL_AUDIO_FORMATS;
53
- const FORMAT_CONTENT_TYPE = new Map(Object.entries(ALL_AUDIO_FORMATS));
54
- exports.FORMAT_CONTENT_TYPE = FORMAT_CONTENT_TYPE;