@andresaya/edge-tts 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/edge-tts.js +1 -1
- package/dist/config/constants.d.ts +1 -0
- package/dist/config/constants.js +1 -0
- package/dist/config/formats.d.ts +81 -0
- package/dist/config/formats.js +54 -0
- package/dist/services/EdgeTTS.d.ts +11 -1
- package/dist/services/EdgeTTS.js +126 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Edge TTS
|
|
2
|
+
[](https://deepwiki.com/andresayac/edge-tts)
|
|
2
3
|
|
|
3
4
|
**Edge TTS** is a powerful Text-to-Speech (TTS) package that leverages Microsoft's Edge capabilities. This package allows you to synthesize speech from text and manage voice options easily through a command-line interface (CLI).
|
|
4
5
|
|
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.2
|
|
9
|
+
.version('1.3.2')
|
|
10
10
|
.addCommand(SynthesizeCommand_1.SynthesizeCommand)
|
|
11
11
|
.addCommand(VoiceListCommand_1.VoiceListCommand);
|
|
12
12
|
try {
|
package/dist/config/constants.js
CHANGED
|
@@ -5,4 +5,5 @@ exports.Constants = {
|
|
|
5
5
|
TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',
|
|
6
6
|
WSS_URL: 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1',
|
|
7
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'
|
|
8
9
|
};
|
|
@@ -0,0 +1,81 @@
|
|
|
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 };
|
|
@@ -0,0 +1,54 @@
|
|
|
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;
|
|
@@ -16,6 +16,8 @@ export declare class EdgeTTS {
|
|
|
16
16
|
private audio_format;
|
|
17
17
|
private ws;
|
|
18
18
|
getVoices(): Promise<Voice[]>;
|
|
19
|
+
getVoicesByLanguage(locale: string): Promise<Voice[]>;
|
|
20
|
+
getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]>;
|
|
19
21
|
private generateUUID;
|
|
20
22
|
private validatePitch;
|
|
21
23
|
private validateRate;
|
|
@@ -23,8 +25,16 @@ export declare class EdgeTTS {
|
|
|
23
25
|
synthesize(text: string, voice?: string, options?: SynthesisOptions): Promise<void>;
|
|
24
26
|
private getSSML;
|
|
25
27
|
private buildTTSConfigMessage;
|
|
28
|
+
synthesizeStream(text: string, voice?: string, options?: SynthesisOptions): AsyncGenerator<Uint8Array, void, unknown>;
|
|
26
29
|
private processAudioData;
|
|
27
|
-
|
|
30
|
+
private generateSecMsGec;
|
|
31
|
+
getDuration(): number;
|
|
32
|
+
getAudioInfo(): {
|
|
33
|
+
size: number;
|
|
34
|
+
format: string;
|
|
35
|
+
estimatedDuration: number;
|
|
36
|
+
};
|
|
37
|
+
toFile(outputPath: string, format?: string): Promise<string>;
|
|
28
38
|
toRaw(): string;
|
|
29
39
|
toBase64(): string;
|
|
30
40
|
toBuffer(): Buffer;
|
package/dist/services/EdgeTTS.js
CHANGED
|
@@ -28,7 +28,11 @@ class EdgeTTS {
|
|
|
28
28
|
audio_format = 'mp3';
|
|
29
29
|
ws;
|
|
30
30
|
async getVoices() {
|
|
31
|
-
const response = await fetch(`${constants_1.Constants.VOICES_URL}?trustedclienttoken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}
|
|
31
|
+
const response = await fetch(`${constants_1.Constants.VOICES_URL}?trustedclienttoken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}`, {
|
|
32
|
+
headers: {
|
|
33
|
+
"User-Agent": constants_1.Constants.USER_AGENT
|
|
34
|
+
},
|
|
35
|
+
});
|
|
32
36
|
const data = await response.json();
|
|
33
37
|
return data.map((voice) => {
|
|
34
38
|
delete voice.VoiceTag;
|
|
@@ -37,6 +41,14 @@ class EdgeTTS {
|
|
|
37
41
|
return voice;
|
|
38
42
|
});
|
|
39
43
|
}
|
|
44
|
+
async getVoicesByLanguage(locale) {
|
|
45
|
+
const voices = await this.getVoices();
|
|
46
|
+
return voices.filter(voice => voice.Locale.startsWith(locale));
|
|
47
|
+
}
|
|
48
|
+
async getVoicesByGender(gender) {
|
|
49
|
+
const voices = await this.getVoices();
|
|
50
|
+
return voices.filter(voice => voice.Gender === gender);
|
|
51
|
+
}
|
|
40
52
|
generateUUID() {
|
|
41
53
|
return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
42
54
|
const r = Math.random() * 16 | 0;
|
|
@@ -84,10 +96,16 @@ class EdgeTTS {
|
|
|
84
96
|
return `${volumeValue}%`;
|
|
85
97
|
}
|
|
86
98
|
async synthesize(text, voice = 'en-US-AnaNeural', options = {}) {
|
|
99
|
+
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
87
100
|
return new Promise((resolve, reject) => {
|
|
88
101
|
this.audio_stream = [];
|
|
89
102
|
const req_id = this.generateUUID();
|
|
90
|
-
|
|
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
|
+
this.ws = new ws_1.default(url, {
|
|
105
|
+
headers: {
|
|
106
|
+
"User-Agent": constants_1.Constants.USER_AGENT
|
|
107
|
+
}
|
|
108
|
+
});
|
|
91
109
|
const SSML_text = this.getSSML(text, voice, options);
|
|
92
110
|
const timeout = setTimeout(() => {
|
|
93
111
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
@@ -130,6 +148,79 @@ class EdgeTTS {
|
|
|
130
148
|
return `X-Timestamp:${new Date().toISOString()}Z\r\nContent-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n` +
|
|
131
149
|
`{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`;
|
|
132
150
|
}
|
|
151
|
+
async *synthesizeStream(text, voice = 'en-US-AnaNeural', options = {}) {
|
|
152
|
+
this.audio_stream = [];
|
|
153
|
+
const req_id = this.generateUUID();
|
|
154
|
+
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);
|
|
157
|
+
const SSML_text = this.getSSML(text, voice, options);
|
|
158
|
+
const queue = [];
|
|
159
|
+
let done = false;
|
|
160
|
+
let error = null;
|
|
161
|
+
let notify = null;
|
|
162
|
+
const push = (chunk) => {
|
|
163
|
+
queue.push(chunk);
|
|
164
|
+
if (notify) {
|
|
165
|
+
notify();
|
|
166
|
+
notify = null;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const timeout = setTimeout(() => {
|
|
170
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
171
|
+
this.ws.close();
|
|
172
|
+
}
|
|
173
|
+
}, 30000);
|
|
174
|
+
this.ws.on('open', () => {
|
|
175
|
+
const message = this.buildTTSConfigMessage();
|
|
176
|
+
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}`;
|
|
178
|
+
this.ws.send(speechMessage);
|
|
179
|
+
});
|
|
180
|
+
this.ws.on('message', (data) => {
|
|
181
|
+
const buffer = ensureBuffer(data);
|
|
182
|
+
const needle = buffer_1.Buffer.from('Path:audio\r\n');
|
|
183
|
+
const audioStartIndex = buffer.indexOf(new Uint8Array(needle));
|
|
184
|
+
if (audioStartIndex !== -1) {
|
|
185
|
+
const audioChunk = buffer.subarray(audioStartIndex + needle.length);
|
|
186
|
+
const chunk = new Uint8Array(audioChunk);
|
|
187
|
+
this.audio_stream.push(chunk);
|
|
188
|
+
push(chunk);
|
|
189
|
+
}
|
|
190
|
+
if (buffer.toString().includes('Path:turn.end')) {
|
|
191
|
+
this.ws?.close();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
this.ws.on('error', (err) => {
|
|
195
|
+
error = err;
|
|
196
|
+
done = true;
|
|
197
|
+
if (notify) {
|
|
198
|
+
notify();
|
|
199
|
+
notify = null;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
this.ws.on('close', () => {
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
done = true;
|
|
205
|
+
if (notify) {
|
|
206
|
+
notify();
|
|
207
|
+
notify = null;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
while (!done || queue.length > 0) {
|
|
211
|
+
if (queue.length === 0) {
|
|
212
|
+
await new Promise(resolve => (notify = resolve));
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const chunk = queue.shift();
|
|
216
|
+
if (chunk) {
|
|
217
|
+
yield chunk;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (error) {
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
133
224
|
processAudioData(data) {
|
|
134
225
|
const buffer = ensureBuffer(data);
|
|
135
226
|
const needle = buffer_1.Buffer.from("Path:audio\r\n");
|
|
@@ -142,9 +233,40 @@ class EdgeTTS {
|
|
|
142
233
|
this.ws?.close();
|
|
143
234
|
}
|
|
144
235
|
}
|
|
145
|
-
async
|
|
236
|
+
async generateSecMsGec(trustedClientToken) {
|
|
237
|
+
const ticks = Math.floor(Date.now() / 1000) + 11644473600;
|
|
238
|
+
const rounded = ticks - (ticks % 300);
|
|
239
|
+
const windowsTicks = rounded * 10000000;
|
|
240
|
+
const encoder = new TextEncoder();
|
|
241
|
+
const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);
|
|
242
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
243
|
+
return Array.from(new Uint8Array(hashBuffer))
|
|
244
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
245
|
+
.join('')
|
|
246
|
+
.toUpperCase();
|
|
247
|
+
}
|
|
248
|
+
getDuration() {
|
|
249
|
+
if (this.audio_stream.length === 0) {
|
|
250
|
+
throw new Error("No audio data available");
|
|
251
|
+
}
|
|
252
|
+
// Estimate duration based on the size of the audio stream
|
|
253
|
+
const bufferSize = this.toBuffer().length;
|
|
254
|
+
const estimatedDuration = bufferSize / (24000 * 3); // 24000 Hz sample rate, 3 bytes per sample (16-bit stereo)
|
|
255
|
+
return estimatedDuration;
|
|
256
|
+
}
|
|
257
|
+
getAudioInfo() {
|
|
258
|
+
const buffer = this.toBuffer();
|
|
259
|
+
return {
|
|
260
|
+
size: buffer.length,
|
|
261
|
+
format: this.audio_format,
|
|
262
|
+
estimatedDuration: this.getDuration()
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
async toFile(outputPath, format = this.audio_format) {
|
|
266
|
+
if (!format || typeof format !== 'string')
|
|
267
|
+
format = this.audio_format;
|
|
146
268
|
const audioBuffer = this.toBuffer();
|
|
147
|
-
const finalPath = `${outputPath}.${
|
|
269
|
+
const finalPath = `${outputPath}.${format}`;
|
|
148
270
|
await (0, promises_1.writeFile)(finalPath, new Uint8Array(audioBuffer));
|
|
149
271
|
return finalPath;
|
|
150
272
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andresaya/edge-tts",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Edge TTS is a package that allows access to the online text-to-speech service used by Microsoft Edge without the need for Microsoft Edge, Windows, or an API key.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"keywords": [
|