@andresaya/edge-tts 1.7.1 → 1.8.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 +0 -94
- package/dist/browser/edge-tts.esm.js +55 -117
- 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 +55 -117
- package/dist/browser/edge-tts.umd.js.map +2 -2
- package/dist/browser/edge-tts.umd.min.js +8 -8
- package/dist/browser/edge-tts.umd.min.js.map +3 -3
- package/dist/cli/edge-tts.js +1 -1
- package/dist/commands/SynthesizeCommand.js +2 -4
- package/dist/config/constants.d.ts +0 -33
- package/dist/config/constants.js +13 -48
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/services/EdgeTTS.d.ts +1 -2
- package/dist/services/EdgeTTS.js +79 -77
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EdgeTTS = void 0;
|
|
3
|
+
exports.Constants = exports.EdgeTTS = void 0;
|
|
4
4
|
var EdgeTTS_1 = require("./services/EdgeTTS");
|
|
5
5
|
Object.defineProperty(exports, "EdgeTTS", { enumerable: true, get: function () { return EdgeTTS_1.EdgeTTS; } });
|
|
6
|
+
var constants_1 = require("./config/constants");
|
|
7
|
+
Object.defineProperty(exports, "Constants", { enumerable: true, get: function () { return constants_1.Constants; } });
|
|
@@ -11,7 +11,6 @@ export interface SynthesisOptions {
|
|
|
11
11
|
pitch?: string | number;
|
|
12
12
|
rate?: string | number;
|
|
13
13
|
volume?: string | number;
|
|
14
|
-
inputType?: 'auto' | 'ssml' | 'text';
|
|
15
14
|
outputFormat?: string;
|
|
16
15
|
}
|
|
17
16
|
export interface WordBoundary {
|
|
@@ -26,6 +25,7 @@ export declare class EdgeTTS {
|
|
|
26
25
|
private output_format;
|
|
27
26
|
private word_boundaries;
|
|
28
27
|
private ws;
|
|
28
|
+
normalizeVoices(data: any[]): Promise<Voice[]>;
|
|
29
29
|
getVoices(): Promise<Voice[]>;
|
|
30
30
|
getVoicesByLanguage(locale: string): Promise<Voice[]>;
|
|
31
31
|
getVoicesByGender(gender: 'Male' | 'Female'): Promise<Voice[]>;
|
|
@@ -34,7 +34,6 @@ export declare class EdgeTTS {
|
|
|
34
34
|
private validateRate;
|
|
35
35
|
private validateVolume;
|
|
36
36
|
synthesize(text: string, voice?: string, options?: SynthesisOptions): Promise<void>;
|
|
37
|
-
private detectSSML;
|
|
38
37
|
private escapeXML;
|
|
39
38
|
private getSSML;
|
|
40
39
|
private nowRFC1123;
|
package/dist/services/EdgeTTS.js
CHANGED
|
@@ -30,30 +30,88 @@ class EdgeTTS {
|
|
|
30
30
|
output_format = 'audio-24khz-48kbitrate-mono-mp3';
|
|
31
31
|
word_boundaries = [];
|
|
32
32
|
ws;
|
|
33
|
+
async normalizeVoices(data) {
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const v of data || []) {
|
|
36
|
+
const short = v?.ShortName || "";
|
|
37
|
+
const locale = v?.Locale || "";
|
|
38
|
+
// base: remove locale prefix and Neural/NeuralHD suffix
|
|
39
|
+
let base = short.replace(/^[a-z]{2}-[A-Z]{2}-/, "");
|
|
40
|
+
base = base.replace(/NeuralHD$/, "").replace(/Neural$/, "").trim();
|
|
41
|
+
// VoiceType: if NeuralHD/Neural in Name or ShortName
|
|
42
|
+
const mix = `${v?.Name || ""} ${short}`;
|
|
43
|
+
const voiceType = v?.VoiceType || (/NeuralHD/i.test(mix) ? "NeuralHD" : "Neural");
|
|
44
|
+
// LocaleName: prefer LocaleName -> LanguageName -> locale
|
|
45
|
+
const localeName = v?.LocaleName || (locale || null);
|
|
46
|
+
// DisplayName: prefer DisplayName -> FriendlyName -> base -> short
|
|
47
|
+
let display = v?.DisplayName || v?.FriendlyName || base || short;
|
|
48
|
+
display = display.replace(/^Microsoft\s+/i, "");
|
|
49
|
+
display = display.split(" - ")[0].trim();
|
|
50
|
+
display = display.replace(/\s*Online\s*\(Natural\)\s*/i, " ");
|
|
51
|
+
display = display.replace(/\s*Online\s*/i, " ");
|
|
52
|
+
display = display.replace(/\s+/g, " ").trim();
|
|
53
|
+
// VoiceTag parsing
|
|
54
|
+
const tag = (v?.VoiceTag && typeof v.VoiceTag === "object") ? v.VoiceTag : {};
|
|
55
|
+
const tailored = Array.isArray(tag.TailoredScenarios)
|
|
56
|
+
? tag.TailoredScenarios
|
|
57
|
+
: (Array.isArray(tag.ContentCategories) ? tag.ContentCategories : []);
|
|
58
|
+
const personalities = Array.isArray(tag.VoicePersonalities)
|
|
59
|
+
? tag.VoicePersonalities
|
|
60
|
+
: [];
|
|
61
|
+
out.push({
|
|
62
|
+
Name: short || (v?.Name || ""),
|
|
63
|
+
DisplayName: display,
|
|
64
|
+
LocalName: display,
|
|
65
|
+
ShortName: short || (v?.Name || ""),
|
|
66
|
+
Gender: v?.Gender ?? null,
|
|
67
|
+
Locale: locale || null,
|
|
68
|
+
LocaleName: localeName,
|
|
69
|
+
SecondaryLocaleList: Array.isArray(v?.SecondaryLocaleList) ? v.SecondaryLocaleList : [],
|
|
70
|
+
VoiceType: voiceType,
|
|
71
|
+
VoiceTag: {
|
|
72
|
+
TailoredScenarios: tailored,
|
|
73
|
+
VoicePersonalities: personalities,
|
|
74
|
+
},
|
|
75
|
+
FriendlyName: `${display} (${voiceType}) - ${localeName}`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
33
80
|
async getVoices() {
|
|
34
81
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
35
|
-
const httpsAgent = new https_1.default.Agent({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
82
|
+
const httpsAgent = new https_1.default.Agent({ rejectUnauthorized: false });
|
|
83
|
+
const url = `${constants_1.Constants.VOICES_URL}` +
|
|
84
|
+
`?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}` +
|
|
85
|
+
`&Sec-MS-GEC=${secMsGEC}` +
|
|
86
|
+
`&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}`;
|
|
87
|
+
const headers = {
|
|
88
|
+
...constants_1.Constants.getBaseHeaders(),
|
|
89
|
+
"Accept-Encoding": "identity", // evita gzip/br/zstd
|
|
90
|
+
};
|
|
91
|
+
const voicesRaw = await new Promise((resolve, reject) => {
|
|
92
|
+
const req = https_1.default.request(url, { method: "GET", headers, agent: httpsAgent }, (res) => {
|
|
93
|
+
const chunks = [];
|
|
94
|
+
res.on("data", (chunk) => chunks.push(buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(chunk)));
|
|
95
|
+
res.on("error", reject);
|
|
96
|
+
res.on("end", () => {
|
|
97
|
+
const body = buffer_1.Buffer.concat(chunks).toString("utf8");
|
|
98
|
+
try {
|
|
99
|
+
const parsed = JSON.parse(body);
|
|
100
|
+
// soporta array directo o { voices: [...] }
|
|
101
|
+
const voices = Array.isArray(parsed)
|
|
102
|
+
? parsed
|
|
103
|
+
: (parsed.voices || parsed.Voices || []);
|
|
104
|
+
resolve(Array.isArray(voices) ? voices : []);
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
reject(new Error("JSON inválido: " + (e?.message || String(e))));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
46
110
|
});
|
|
47
|
-
req.on(
|
|
111
|
+
req.on("error", reject);
|
|
48
112
|
req.end();
|
|
49
113
|
});
|
|
50
|
-
|
|
51
|
-
return data.map((voice) => {
|
|
52
|
-
voice.FriendlyName = voice.FriendlyName || voice.LocalName;
|
|
53
|
-
delete voice.SampleRateHertz;
|
|
54
|
-
delete voice.Status;
|
|
55
|
-
return voice;
|
|
56
|
-
});
|
|
114
|
+
return this.normalizeVoices(voicesRaw);
|
|
57
115
|
}
|
|
58
116
|
async getVoicesByLanguage(locale) {
|
|
59
117
|
const voices = await this.getVoices();
|
|
@@ -114,7 +172,7 @@ class EdgeTTS {
|
|
|
114
172
|
return new Promise((resolve, reject) => {
|
|
115
173
|
this.audio_stream = [];
|
|
116
174
|
const reqId = this.generateUUID();
|
|
117
|
-
const url = `${constants_1.Constants.WSS_URL}?
|
|
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}`;
|
|
118
176
|
this.ws = new ws_1.default(url, {
|
|
119
177
|
headers: constants_1.Constants.getBaseHeaders(),
|
|
120
178
|
rejectUnauthorized: false
|
|
@@ -161,31 +219,6 @@ class EdgeTTS {
|
|
|
161
219
|
});
|
|
162
220
|
});
|
|
163
221
|
}
|
|
164
|
-
detectSSML(content) {
|
|
165
|
-
const trimmedContent = content.trim();
|
|
166
|
-
const looksLikeSSML = /^<\?xml|^<speak/i.test(trimmedContent);
|
|
167
|
-
if (!looksLikeSSML) {
|
|
168
|
-
return { isValid: true, isSSML: false };
|
|
169
|
-
}
|
|
170
|
-
const errors = [];
|
|
171
|
-
const hasSpeakTag = /<speak\b[^>]*>[\s\S]*<\/speak>/i.test(trimmedContent);
|
|
172
|
-
const hasVoiceTag = /<voice\b[^>]*>[\s\S]*<\/voice>/i.test(trimmedContent);
|
|
173
|
-
if (!hasSpeakTag) {
|
|
174
|
-
throw new Error('Invalid SSML: Missing <speak> tag');
|
|
175
|
-
}
|
|
176
|
-
if (!hasVoiceTag) {
|
|
177
|
-
throw new Error('Invalid SSML: Missing <voice> tag');
|
|
178
|
-
}
|
|
179
|
-
const hasCorrectNamespace = /xmlns="http:\/\/www\.w3\.org\/2001\/10\/synthesis"/i.test(trimmedContent);
|
|
180
|
-
if (!hasCorrectNamespace && hasSpeakTag) {
|
|
181
|
-
throw new Error('Invalid SSML: Missing or incorrect namespace declaration');
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
isValid: errors.length === 0,
|
|
185
|
-
isSSML: hasSpeakTag || hasVoiceTag,
|
|
186
|
-
errors: errors.length > 0 ? errors : undefined
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
222
|
escapeXML(text) {
|
|
190
223
|
return text
|
|
191
224
|
.replace(/&/g, '&')
|
|
@@ -195,37 +228,6 @@ class EdgeTTS {
|
|
|
195
228
|
.replace(/'/g, ''');
|
|
196
229
|
}
|
|
197
230
|
getSSML(content, voice, options = {}) {
|
|
198
|
-
const inputType = options.inputType || 'auto';
|
|
199
|
-
let treatAsSSML = false;
|
|
200
|
-
if (inputType === 'ssml') {
|
|
201
|
-
treatAsSSML = true;
|
|
202
|
-
}
|
|
203
|
-
else if (inputType === 'text') {
|
|
204
|
-
treatAsSSML = false;
|
|
205
|
-
}
|
|
206
|
-
else { // 'auto'
|
|
207
|
-
const detection = this.detectSSML(content);
|
|
208
|
-
treatAsSSML = detection.isSSML;
|
|
209
|
-
if (detection.isSSML) {
|
|
210
|
-
console.log('→ Detected SSML input');
|
|
211
|
-
if (!detection.isValid) {
|
|
212
|
-
console.warn('⚠ SSML validation warnings:', detection.errors);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
console.log('→ Detected plain text input');
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (treatAsSSML) {
|
|
220
|
-
let ssml = content.trim();
|
|
221
|
-
if (!ssml.includes('xmlns=')) {
|
|
222
|
-
ssml = ssml.replace(/<speak([^>]*)>/i, '<speak$1 xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts">');
|
|
223
|
-
}
|
|
224
|
-
if (!/<voice\b[^>]*>/i.test(ssml) && voice) {
|
|
225
|
-
ssml = ssml.replace(/(<speak[^>]*>)([\s\S]*?)(<\/speak>)/i, `$1<voice name="${voice}">$2</voice>$3`);
|
|
226
|
-
}
|
|
227
|
-
return ssml;
|
|
228
|
-
}
|
|
229
231
|
const pitch = this.validatePitch(options.pitch ?? 0);
|
|
230
232
|
const rate = this.validateRate(options.rate ?? 0);
|
|
231
233
|
const volume = this.validateVolume(options.volume ?? 0);
|
|
@@ -266,7 +268,7 @@ class EdgeTTS {
|
|
|
266
268
|
this.audio_stream = [];
|
|
267
269
|
const reqId = this.generateUUID();
|
|
268
270
|
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
269
|
-
const url = `${constants_1.Constants.WSS_URL}?
|
|
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}`;
|
|
270
272
|
this.ws = new ws_1.default(url, {
|
|
271
273
|
headers: constants_1.Constants.getBaseHeaders(),
|
|
272
274
|
rejectUnauthorized: false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andresaya/edge-tts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.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
6
|
"browser": "dist/browser/edge-tts.esm.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dist"
|
|
47
47
|
],
|
|
48
48
|
"bin": {
|
|
49
|
-
"edge-tts": "
|
|
49
|
+
"edge-tts": "dist/cli/edge-tts.js"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
52
|
"bun": ">=0.5.0"
|