ssml-builder-js 2.16.0 → 2.18.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 +10 -2
- package/dist/{chunk-NKLZGITR.mjs → chunk-2SYLELUT.mjs} +4 -2
- package/dist/{chunk-NKLZGITR.mjs.map → chunk-2SYLELUT.mjs.map} +1 -1
- package/dist/{chunk-HI74FTKY.mjs → chunk-5AZWURHW.mjs} +4 -2
- package/dist/{chunk-HI74FTKY.mjs.map → chunk-5AZWURHW.mjs.map} +1 -1
- package/dist/{chunk-F2EMU3HM.mjs → chunk-SLZ7PE6W.mjs} +2 -2
- package/dist/core.d.mts +4 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.js +3 -1
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/elements.js +3 -1
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-DR5Qz43p.d.mts → index.d-B2WddTa4.d.mts} +12 -1
- package/dist/{index.d-DR5Qz43p.d.ts → index.d-B2WddTa4.d.ts} +12 -1
- package/dist/index.d.mts +223 -127
- package/dist/index.d.ts +223 -127
- package/dist/index.js +558 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +551 -100
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +5 -2
- package/dist/react.d.ts +5 -2
- package/dist/react.js +111 -25
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +110 -26
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-F2EMU3HM.mjs.map → chunk-SLZ7PE6W.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -18,12 +18,12 @@ import {
|
|
|
18
18
|
validateAzureSsmlChunks,
|
|
19
19
|
validateSsml,
|
|
20
20
|
validateSsmlStructureIntegrity
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-2SYLELUT.mjs";
|
|
22
22
|
import {
|
|
23
23
|
createAzureUrlValidatorRunner as createAzureUrlValidatorRunner2,
|
|
24
24
|
getSsmlSourceMap as getSsmlSourceMap2,
|
|
25
25
|
validateAzureSsml as validateAzureSsml2
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-5AZWURHW.mjs";
|
|
27
27
|
import {
|
|
28
28
|
__privateAdd,
|
|
29
29
|
__privateGet,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
|
|
33
33
|
// packages/azure-tts-client/src/errors.ts
|
|
34
34
|
var AzureTtsError = class extends Error {
|
|
35
|
-
constructor(status, statusText, responseBody, requestId) {
|
|
35
|
+
constructor(status, statusText, responseBody, requestId, responseHeaders) {
|
|
36
36
|
super(`Azure TTS request failed: ${status} ${statusText}`);
|
|
37
37
|
this.kind = "azure-api-error";
|
|
38
38
|
this.name = "AzureTtsError";
|
|
@@ -40,8 +40,37 @@ var AzureTtsError = class extends Error {
|
|
|
40
40
|
this.statusText = statusText;
|
|
41
41
|
this.responseBody = responseBody;
|
|
42
42
|
this.requestId = requestId;
|
|
43
|
+
const value = responseHeaders instanceof Headers ? responseHeaders.get("retry-after") : responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"];
|
|
44
|
+
const seconds = value ? Number(value.trim()) : NaN;
|
|
45
|
+
const date = value ? Date.parse(value) : NaN;
|
|
46
|
+
if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1e3;
|
|
47
|
+
else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
|
|
43
48
|
}
|
|
44
49
|
};
|
|
50
|
+
function getRetryAfterDelayMs(error) {
|
|
51
|
+
if (error instanceof AzureTtsError && error.retryAfterMs !== void 0) return error.retryAfterMs;
|
|
52
|
+
if (!error || typeof error !== "object") return void 0;
|
|
53
|
+
const candidate = error;
|
|
54
|
+
if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
|
|
55
|
+
const headers = candidate.headers ?? candidate.response?.headers;
|
|
56
|
+
if (headers instanceof Headers) {
|
|
57
|
+
const value = headers.get("retry-after");
|
|
58
|
+
if (!value) return void 0;
|
|
59
|
+
const seconds = Number(value.trim());
|
|
60
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
61
|
+
const date = Date.parse(value);
|
|
62
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
63
|
+
}
|
|
64
|
+
if (headers && typeof headers === "object") {
|
|
65
|
+
const value = headers["retry-after"] ?? headers["Retry-After"];
|
|
66
|
+
if (typeof value !== "string") return void 0;
|
|
67
|
+
const seconds = Number(value.trim());
|
|
68
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
69
|
+
const date = Date.parse(value);
|
|
70
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
71
|
+
}
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
45
74
|
var AzureTtsSdkError = class extends AzureTtsError {
|
|
46
75
|
constructor(errorDetails) {
|
|
47
76
|
super(0, "Speech SDK", errorDetails, null);
|
|
@@ -151,6 +180,9 @@ var OUTPUT_FORMATS = {
|
|
|
151
180
|
function resolveMimeType(outputFormat) {
|
|
152
181
|
if (/(?:wav|wave|riff)/i.test(outputFormat)) return "audio/wav";
|
|
153
182
|
if (/(?:mp3|mpeg)/i.test(outputFormat)) return "audio/mpeg";
|
|
183
|
+
if (/mulaw|mu-law/i.test(outputFormat)) return "audio/basic";
|
|
184
|
+
if (/alaw|a-law/i.test(outputFormat)) return "audio/alaw";
|
|
185
|
+
if (/siren/i.test(outputFormat)) return "audio/siren";
|
|
154
186
|
if (/ogg/i.test(outputFormat)) return "audio/ogg";
|
|
155
187
|
if (/webm/i.test(outputFormat)) return "audio/webm";
|
|
156
188
|
if (/raw/i.test(outputFormat)) return "audio/L16";
|
|
@@ -179,6 +211,27 @@ function createSpeechConfig(config) {
|
|
|
179
211
|
}
|
|
180
212
|
|
|
181
213
|
// packages/azure-tts-client/src/synthesis.ts
|
|
214
|
+
function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
|
|
215
|
+
const readAttribute = (name) => {
|
|
216
|
+
const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
|
|
217
|
+
return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
|
|
218
|
+
};
|
|
219
|
+
const payload = JSON.stringify({
|
|
220
|
+
ssml,
|
|
221
|
+
outputFormat,
|
|
222
|
+
voice: readAttribute("(?:name|voice)"),
|
|
223
|
+
language: readAttribute("(?:xml:lang|lang)"),
|
|
224
|
+
rate: readAttribute("rate"),
|
|
225
|
+
pitch: readAttribute("pitch")
|
|
226
|
+
});
|
|
227
|
+
let hash = 0xcbf29ce484222325n;
|
|
228
|
+
const mask = 0xffffffffffffffffn;
|
|
229
|
+
for (let index = 0; index < payload.length; index += 1) {
|
|
230
|
+
hash ^= BigInt(payload.charCodeAt(index));
|
|
231
|
+
hash = hash * 0x100000001b3n & mask;
|
|
232
|
+
}
|
|
233
|
+
return `fnv1a64-${hash.toString(16).padStart(16, "0")}`;
|
|
234
|
+
}
|
|
182
235
|
function ascii(bytes, offset, value) {
|
|
183
236
|
return [...value].every((character, index) => bytes[offset + index] === character.charCodeAt(0));
|
|
184
237
|
}
|
|
@@ -218,9 +271,11 @@ function parseWav(buffer) {
|
|
|
218
271
|
}
|
|
219
272
|
return { chunks, data, format };
|
|
220
273
|
}
|
|
221
|
-
function
|
|
222
|
-
const match =
|
|
223
|
-
|
|
274
|
+
function formatSampleRate(format) {
|
|
275
|
+
const match = /(?:^|-)(\d+)(khz|hz)(?:-|$)/i.exec(format);
|
|
276
|
+
if (!match?.[1] || !match[2]) return 0;
|
|
277
|
+
const value = Number(match[1]);
|
|
278
|
+
return match[2].toLowerCase() === "khz" ? value * 1e3 : value;
|
|
224
279
|
}
|
|
225
280
|
function formatChannels(format, fallback) {
|
|
226
281
|
if (/stereo|2ch|dual/i.test(format)) return 2;
|
|
@@ -228,11 +283,13 @@ function formatChannels(format, fallback) {
|
|
|
228
283
|
return fallback;
|
|
229
284
|
}
|
|
230
285
|
function formatAudioSpecification(format) {
|
|
231
|
-
const sampleRate =
|
|
286
|
+
const sampleRate = formatSampleRate(format);
|
|
232
287
|
const channels = formatChannels(format, 0);
|
|
233
288
|
const bitrateMatch = /(\d+)(?:kbitrate|kbps|kbits?)/i.exec(format);
|
|
234
289
|
const bitrate = bitrateMatch?.[1] ? Number(bitrateMatch[1]) * 1e3 : void 0;
|
|
235
|
-
const codec = /mp3|mpeg/i.test(format) ? "mp3" : /opus/i.test(format) ? "opus" : /silk/i.test(format) ? "silk" : /
|
|
290
|
+
const codec = /mp3|mpeg/i.test(format) ? "mp3" : /opus/i.test(format) ? "opus" : /silk/i.test(format) ? "silk" : /mulaw|mu-law/i.test(format) ? "mulaw" : /alaw|a-law/i.test(format) ? "alaw" : /siren/i.test(format) ? "siren" : /pcm/i.test(format) ? "pcm" : "unknown";
|
|
291
|
+
const bitDepthMatch = /(\d+)bit/i.exec(format);
|
|
292
|
+
const container = /(?:wav|wave|riff)/i.test(format) ? "riff-wave" : /mp3|mpeg/i.test(format) ? "mp3-raw" : /ogg/i.test(format) ? "ogg" : /webm/i.test(format) ? "webm" : /raw/i.test(format) ? "raw" : void 0;
|
|
236
293
|
return {
|
|
237
294
|
format,
|
|
238
295
|
mimeType: resolveMimeType(format),
|
|
@@ -240,7 +297,10 @@ function formatAudioSpecification(format) {
|
|
|
240
297
|
sampleRate,
|
|
241
298
|
channels,
|
|
242
299
|
...bitrate ? { bitrate } : {},
|
|
243
|
-
|
|
300
|
+
...bitDepthMatch?.[1] ? { bitDepth: Number(bitDepthMatch[1]) } : {},
|
|
301
|
+
...container ? { container } : {},
|
|
302
|
+
isVbr: /vbr/i.test(format),
|
|
303
|
+
isCompressed: codec !== "pcm" && codec !== "unknown"
|
|
244
304
|
};
|
|
245
305
|
}
|
|
246
306
|
function parseMp3Specification(buffer, format) {
|
|
@@ -275,6 +335,8 @@ function parseMp3Specification(buffer, format) {
|
|
|
275
335
|
sampleRate,
|
|
276
336
|
channels: (bytes[index + 3] ?? 0) >> 6 === 3 ? 1 : 2,
|
|
277
337
|
bitrate: bitrateKbps * 1e3,
|
|
338
|
+
container: "mp3-raw",
|
|
339
|
+
isVbr: false,
|
|
278
340
|
isCompressed: true
|
|
279
341
|
};
|
|
280
342
|
}
|
|
@@ -289,24 +351,43 @@ function inspectAudioSpecification(buffer, format) {
|
|
|
289
351
|
const channels = view.getUint16(2, true);
|
|
290
352
|
const bitsPerSample = parsed.format.byteLength >= 16 ? view.getUint16(14, true) : 0;
|
|
291
353
|
const formatCode = view.getUint16(0, true);
|
|
354
|
+
const namedCodec = formatAudioSpecification(format).codec;
|
|
355
|
+
const codec = formatCode === 1 ? "pcm" : formatCode === 6 ? "alaw" : formatCode === 7 ? "mulaw" : namedCodec === "siren" ? "siren" : "unknown";
|
|
292
356
|
return {
|
|
293
357
|
format,
|
|
294
358
|
mimeType: "audio/wav",
|
|
295
|
-
codec
|
|
359
|
+
codec,
|
|
296
360
|
sampleRate,
|
|
297
361
|
channels,
|
|
298
362
|
...sampleRate && channels && bitsPerSample ? { bitrate: sampleRate * channels * bitsPerSample } : {},
|
|
299
|
-
|
|
363
|
+
bitDepth: bitsPerSample,
|
|
364
|
+
container: "riff-wave",
|
|
365
|
+
isVbr: false,
|
|
366
|
+
isCompressed: codec !== "pcm" && codec !== "unknown"
|
|
300
367
|
};
|
|
301
368
|
}
|
|
302
369
|
if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
|
|
303
|
-
|
|
370
|
+
const specification = formatAudioSpecification(format);
|
|
371
|
+
if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
|
|
372
|
+
return specification;
|
|
373
|
+
}
|
|
374
|
+
function validateRawAudioBuffer(buffer, specification) {
|
|
375
|
+
if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
|
|
376
|
+
throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
|
|
377
|
+
}
|
|
378
|
+
if (specification.codec === "siren" || specification.codec === "silk") return;
|
|
379
|
+
const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
|
|
380
|
+
if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`RAW audio buffer size ${buffer.byteLength} is not aligned to ${bytesPerFrame}-byte audio frames for "${specification.format}".`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
304
385
|
}
|
|
305
386
|
function validateAudioSpecifications(specs) {
|
|
306
387
|
const first = specs[0];
|
|
307
388
|
if (!first) return;
|
|
308
389
|
const mismatch = specs.find(
|
|
309
|
-
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate
|
|
390
|
+
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || spec.codec !== first.codec || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate || first.bitDepth !== void 0 && spec.bitDepth !== void 0 && spec.bitDepth !== first.bitDepth || first.container !== void 0 && spec.container !== void 0 && spec.container !== first.container || first.isVbr !== void 0 && spec.isVbr !== void 0 && spec.isVbr !== first.isVbr
|
|
310
391
|
);
|
|
311
392
|
if (mismatch)
|
|
312
393
|
throw new AudioFormatMismatchError(
|
|
@@ -389,6 +470,30 @@ function isWavFormat(format) {
|
|
|
389
470
|
function isRawFormat(format) {
|
|
390
471
|
return /^raw(?:-|$)/i.test(format);
|
|
391
472
|
}
|
|
473
|
+
function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
|
|
474
|
+
if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
|
|
475
|
+
throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
|
|
476
|
+
}
|
|
477
|
+
const specification = inspectAudioSpecification(merged, format);
|
|
478
|
+
if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
|
|
479
|
+
const firstInput = inputSpecs[0];
|
|
480
|
+
if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
|
|
481
|
+
throw new AudioFormatMismatchError("The custom audio merger returned an incompatible audio stream.", [
|
|
482
|
+
...inputSpecs,
|
|
483
|
+
specification
|
|
484
|
+
]);
|
|
485
|
+
}
|
|
486
|
+
if (isRawFormat(format)) {
|
|
487
|
+
const expectedSize = buffers.reduce((total, input) => total + input.byteLength, 0);
|
|
488
|
+
if (merged.byteLength !== expectedSize) {
|
|
489
|
+
throw new MergeError(
|
|
490
|
+
`The custom audio merger returned ${merged.byteLength} bytes; ${expectedSize} were expected.`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
validateRawAudioBuffer(merged, specification);
|
|
494
|
+
}
|
|
495
|
+
return specification;
|
|
496
|
+
}
|
|
392
497
|
function resolveMergeAudioFormat(format) {
|
|
393
498
|
if (isWavFormat(format)) return "wav";
|
|
394
499
|
if (isMp3Format(format)) return "mp3";
|
|
@@ -441,7 +546,7 @@ function closeSpeechResources(speechConfig, synthesizer) {
|
|
|
441
546
|
}
|
|
442
547
|
}
|
|
443
548
|
var ticksToMilliseconds = (ticks) => Math.max(0, ticks) / 1e4;
|
|
444
|
-
async function
|
|
549
|
+
async function synthesizeSsmlOnce(ssml, config) {
|
|
445
550
|
if (config.signal?.aborted) {
|
|
446
551
|
throw new SynthesisCancelledError();
|
|
447
552
|
}
|
|
@@ -509,9 +614,7 @@ async function synthesizeSsml(ssml, config) {
|
|
|
509
614
|
};
|
|
510
615
|
}
|
|
511
616
|
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath) {
|
|
512
|
-
|
|
513
|
-
Object.defineProperty(unmapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
514
|
-
return unmapped;
|
|
617
|
+
return { mappingStatus: "unmapped" };
|
|
515
618
|
}
|
|
516
619
|
const value = text ?? "";
|
|
517
620
|
let localStart = Number.isFinite(offsetHint) && (offsetHint ?? 0) >= 0 ? offsetHint : -1;
|
|
@@ -573,6 +676,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
573
676
|
rejectWithError(err);
|
|
574
677
|
return;
|
|
575
678
|
}
|
|
679
|
+
let audioSpec;
|
|
680
|
+
try {
|
|
681
|
+
audioSpec = inspectAudioSpecification(result.audioData, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
rejectWithError(error);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
576
686
|
settled = true;
|
|
577
687
|
cleanup();
|
|
578
688
|
closeResources();
|
|
@@ -593,8 +703,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
593
703
|
...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
594
704
|
...requestId ? { requestId } : {}
|
|
595
705
|
};
|
|
596
|
-
if (event.mappingStatus === "unmapped")
|
|
706
|
+
if (event.mappingStatus === "unmapped") {
|
|
597
707
|
Object.defineProperty(mapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
708
|
+
Object.defineProperty(mapped, "toJSON", {
|
|
709
|
+
value: () => ({ ...mapped, mappingStatus: "unmapped" }),
|
|
710
|
+
enumerable: false
|
|
711
|
+
});
|
|
712
|
+
}
|
|
598
713
|
return mapped;
|
|
599
714
|
};
|
|
600
715
|
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
@@ -603,8 +718,8 @@ async function synthesizeSsml(ssml, config) {
|
|
|
603
718
|
resolve({
|
|
604
719
|
audioData: result.audioData,
|
|
605
720
|
durationMs,
|
|
606
|
-
audioSpec
|
|
607
|
-
mimeType: resolveMimeType(config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
721
|
+
audioSpec,
|
|
722
|
+
mimeType: config.outputMimeType ?? resolveMimeType(config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
608
723
|
...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
|
|
609
724
|
...requestId ? { requestId } : {},
|
|
610
725
|
...sourceBoundaries.length > 0 ? { boundaries: sourceBoundaries, wordBoundary: sourceBoundaries, wordBoundaries: sourceBoundaries } : {},
|
|
@@ -617,10 +732,11 @@ async function synthesizeSsml(ssml, config) {
|
|
|
617
732
|
abortHandler = () => rejectWithError(new SynthesisCancelledError());
|
|
618
733
|
config.signal.addEventListener("abort", abortHandler, { once: true });
|
|
619
734
|
}
|
|
620
|
-
|
|
735
|
+
const timeoutMs = config.timeouts?.perChunkMs ?? config.timeouts?.totalJobMs ?? config.timeoutMs;
|
|
736
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
621
737
|
timeout = setTimeout(
|
|
622
|
-
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${
|
|
623
|
-
|
|
738
|
+
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`)),
|
|
739
|
+
timeoutMs
|
|
624
740
|
);
|
|
625
741
|
}
|
|
626
742
|
synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);
|
|
@@ -639,7 +755,9 @@ function isRetryableSynthesisError(error) {
|
|
|
639
755
|
if (typeof status === "number") return status === 429 || status >= 500 && status < 600;
|
|
640
756
|
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
641
757
|
}
|
|
642
|
-
function retryDelay(options, retryAttempt) {
|
|
758
|
+
function retryDelay(options, retryAttempt, error) {
|
|
759
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
760
|
+
if (retryAfterMs !== void 0) return retryAfterMs;
|
|
643
761
|
const base = Math.min(options.maxDelayMs, options.initialDelayMs * 2 ** Math.max(0, retryAttempt - 1));
|
|
644
762
|
return Math.floor(Math.random() * (base + 1));
|
|
645
763
|
}
|
|
@@ -667,32 +785,102 @@ async function waitForRetry(delayMs, signal) {
|
|
|
667
785
|
}
|
|
668
786
|
});
|
|
669
787
|
}
|
|
670
|
-
async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
788
|
+
async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadlineAtMs) {
|
|
671
789
|
const options = retryOptions ? {
|
|
672
790
|
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
673
791
|
initialDelayMs: Math.max(0, retryOptions.initialDelayMs),
|
|
674
|
-
maxDelayMs: Math.max(0, retryOptions.maxDelayMs)
|
|
792
|
+
maxDelayMs: Math.max(0, retryOptions.maxDelayMs),
|
|
793
|
+
shouldRetry: retryOptions.shouldRetry
|
|
675
794
|
} : void 0;
|
|
676
795
|
let attempt = 0;
|
|
677
796
|
while (true) {
|
|
678
797
|
if (config.signal?.aborted) throw new SynthesisCancelledError();
|
|
679
798
|
try {
|
|
680
|
-
return await
|
|
799
|
+
return await synthesizeSsmlOnce(ssml, config);
|
|
681
800
|
} catch (error) {
|
|
682
|
-
if (!options || attempt >= options.maxRetries || !
|
|
801
|
+
if (!options || attempt >= options.maxRetries || !(options.shouldRetry?.(error, attempt + 1) ?? isRetryableSynthesisError(error)))
|
|
802
|
+
throw error;
|
|
683
803
|
attempt += 1;
|
|
684
|
-
const delayMs = retryDelay(options, attempt);
|
|
804
|
+
const delayMs = retryDelay(options, attempt, error);
|
|
805
|
+
const remainingMs = deadlineAtMs === void 0 ? void 0 : Math.max(0, deadlineAtMs - Date.now());
|
|
806
|
+
if (getRetryAfterDelayMs(error) !== void 0 && (delayMs > options.maxDelayMs || remainingMs !== void 0 && delayMs > remainingMs)) {
|
|
807
|
+
throw new SynthesisTimeoutError(
|
|
808
|
+
remainingMs === void 0 ? `Retry-After exceeded maxDelayMs (${options.maxDelayMs} ms).` : `Retry-After exceeded the remaining total job timeout (${remainingMs} ms).`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
685
811
|
onRetry(attempt, delayMs);
|
|
686
|
-
await waitForRetry(delayMs, config.signal);
|
|
812
|
+
await waitForRetry(Math.min(delayMs, remainingMs ?? delayMs), config.signal);
|
|
687
813
|
}
|
|
688
814
|
}
|
|
689
815
|
}
|
|
816
|
+
async function synthesizeSsml(ssml, config) {
|
|
817
|
+
const totalJobMs = config.timeouts?.totalJobMs;
|
|
818
|
+
const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
|
|
819
|
+
if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
|
|
820
|
+
return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
|
|
821
|
+
}
|
|
822
|
+
function createAbortScope(parent, timeoutMs) {
|
|
823
|
+
const controller = new AbortController();
|
|
824
|
+
let didTimeout = false;
|
|
825
|
+
const onAbort = () => controller.abort();
|
|
826
|
+
if (parent?.aborted) controller.abort();
|
|
827
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
828
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
829
|
+
didTimeout = true;
|
|
830
|
+
controller.abort();
|
|
831
|
+
}, timeoutMs) : void 0;
|
|
832
|
+
return {
|
|
833
|
+
signal: controller.signal,
|
|
834
|
+
timedOut: () => didTimeout,
|
|
835
|
+
dispose: () => {
|
|
836
|
+
if (timer) clearTimeout(timer);
|
|
837
|
+
parent?.removeEventListener("abort", onAbort);
|
|
838
|
+
},
|
|
839
|
+
abort: () => controller.abort()
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
async function synthesizeChunkWithTimeout(ssml, config, retryOptions, timeoutMs, onRetry, deadlineAtMs) {
|
|
843
|
+
const scope = createAbortScope(config.signal, timeoutMs);
|
|
844
|
+
try {
|
|
845
|
+
return await synthesizeWithRetry(ssml, { ...config, signal: scope.signal }, retryOptions, onRetry, deadlineAtMs);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
if (scope.timedOut()) throw new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`);
|
|
848
|
+
throw error;
|
|
849
|
+
} finally {
|
|
850
|
+
scope.dispose();
|
|
851
|
+
}
|
|
852
|
+
}
|
|
690
853
|
async function synthesizeSsmlChunks(chunks, config) {
|
|
691
|
-
const results = new Array(chunks.length);
|
|
692
854
|
const totalChunks = chunks.length;
|
|
855
|
+
const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
|
|
856
|
+
const fingerprints = inputs.map(
|
|
857
|
+
(chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
|
|
858
|
+
);
|
|
859
|
+
const results = new Array(totalChunks);
|
|
860
|
+
const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
861
|
+
const invalidCachedIndices = /* @__PURE__ */ new Set();
|
|
862
|
+
const chunkStates = inputs.map((_chunk, chunkIndex) => ({
|
|
863
|
+
chunkIndex,
|
|
864
|
+
status: "pending",
|
|
865
|
+
canResume: true
|
|
866
|
+
}));
|
|
867
|
+
for (const [index, cached] of cachedChunks) {
|
|
868
|
+
if (index < 0 || index >= totalChunks) continue;
|
|
869
|
+
const isValid = config.resumeValidation === "disabled" || cached.fingerprint === fingerprints[index];
|
|
870
|
+
if (isValid) {
|
|
871
|
+
results[index] = { ...cached };
|
|
872
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: results[index] };
|
|
873
|
+
} else {
|
|
874
|
+
invalidCachedIndices.add(index);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
const requestedIndices = config.resumeChunkIndices ? new Set(config.resumeChunkIndices.filter((index) => index >= 0 && index < totalChunks)) : void 0;
|
|
878
|
+
const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
|
|
879
|
+
const jobStartedAt = Date.now();
|
|
880
|
+
const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
|
|
881
|
+
const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
|
|
693
882
|
const report = (event) => config.onProgress?.(event);
|
|
694
|
-
for (const [index,
|
|
695
|
-
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
883
|
+
for (const [index, input] of inputs.entries()) {
|
|
696
884
|
report({
|
|
697
885
|
currentChunk: index,
|
|
698
886
|
totalChunks,
|
|
@@ -703,15 +891,18 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
703
891
|
durationMs: 0
|
|
704
892
|
});
|
|
705
893
|
}
|
|
706
|
-
let completed = 0;
|
|
894
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
707
895
|
let nextIndex = 0;
|
|
708
896
|
const concurrency = resolveConcurrency(config.concurrency, chunks.length);
|
|
897
|
+
let firstError;
|
|
898
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
709
899
|
const worker = async () => {
|
|
710
900
|
while (true) {
|
|
711
901
|
const index = nextIndex++;
|
|
712
902
|
if (index >= chunks.length) return;
|
|
713
|
-
|
|
714
|
-
|
|
903
|
+
if (!shouldSynthesize(index)) continue;
|
|
904
|
+
if (firstError && config.cancelOnFailure !== false) return;
|
|
905
|
+
const input = inputs[index];
|
|
715
906
|
report({
|
|
716
907
|
currentChunk: completed,
|
|
717
908
|
totalChunks,
|
|
@@ -723,10 +914,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
723
914
|
});
|
|
724
915
|
const startedAt = Date.now();
|
|
725
916
|
try {
|
|
726
|
-
const result = await
|
|
917
|
+
const result = await synthesizeChunkWithTimeout(
|
|
727
918
|
input.ssml,
|
|
728
919
|
{
|
|
729
920
|
...config,
|
|
921
|
+
signal: scope.signal,
|
|
730
922
|
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
731
923
|
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
732
924
|
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
@@ -735,6 +927,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
735
927
|
onProgress: void 0
|
|
736
928
|
},
|
|
737
929
|
config.retryOptions,
|
|
930
|
+
config.timeouts?.chunkWithRetriesMs ?? config.timeouts?.perChunkMs ?? config.timeoutMs,
|
|
738
931
|
(retryAttempt, nextRetryDelayMs) => report({
|
|
739
932
|
currentChunk: completed,
|
|
740
933
|
totalChunks,
|
|
@@ -746,9 +939,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
746
939
|
retryAttempt,
|
|
747
940
|
nextRetryDelayMs,
|
|
748
941
|
isRetrying: true
|
|
749
|
-
})
|
|
942
|
+
}),
|
|
943
|
+
jobDeadlineAt
|
|
750
944
|
);
|
|
751
945
|
results[index] = result;
|
|
946
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result };
|
|
752
947
|
completed += 1;
|
|
753
948
|
report({
|
|
754
949
|
currentChunk: completed,
|
|
@@ -760,6 +955,16 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
760
955
|
durationMs: Date.now() - startedAt
|
|
761
956
|
});
|
|
762
957
|
} catch (error) {
|
|
958
|
+
const wasCancelled = firstError !== void 0 || scope.signal.aborted && !scope.timedOut();
|
|
959
|
+
firstError ?? (firstError = scope.timedOut() ? new SynthesisTimeoutError(`Speech synthesis timed out after ${config.timeouts?.totalJobMs} ms.`) : error);
|
|
960
|
+
if (!wasCancelled) failedIndices.add(index);
|
|
961
|
+
chunkStates[index] = {
|
|
962
|
+
chunkIndex: index,
|
|
963
|
+
status: wasCancelled ? "cancelled" : "failed",
|
|
964
|
+
isOriginalFailure: !wasCancelled,
|
|
965
|
+
canResume: true,
|
|
966
|
+
error
|
|
967
|
+
};
|
|
763
968
|
report({
|
|
764
969
|
currentChunk: completed,
|
|
765
970
|
totalChunks,
|
|
@@ -770,16 +975,49 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
770
975
|
durationMs: Date.now() - startedAt,
|
|
771
976
|
error
|
|
772
977
|
});
|
|
773
|
-
|
|
978
|
+
if (config.cancelOnFailure !== false) scope.abort();
|
|
979
|
+
return;
|
|
774
980
|
}
|
|
775
981
|
}
|
|
776
982
|
};
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
983
|
+
try {
|
|
984
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
985
|
+
if (firstError) throw firstError;
|
|
986
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
987
|
+
return await mergeSynthesisResults(orderedResults, {
|
|
988
|
+
format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
|
|
989
|
+
signal: scope.signal,
|
|
990
|
+
customMerger: config.customMerger,
|
|
991
|
+
outputMimeType: config.outputMimeType,
|
|
992
|
+
postMergeValidator: config.postMergeValidator
|
|
993
|
+
});
|
|
994
|
+
} catch (error) {
|
|
995
|
+
if (firstError && config.cancelOnFailure !== false) {
|
|
996
|
+
for (const [chunkIndex, state] of chunkStates.entries()) {
|
|
997
|
+
if (state.status === "pending" && shouldSynthesize(chunkIndex)) {
|
|
998
|
+
chunkStates[chunkIndex] = { chunkIndex, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
const synthesizedChunks = results.flatMap(
|
|
1003
|
+
(result, chunkIndex) => result ? [{ ...result, chunkIndex, fingerprint: fingerprints[chunkIndex] ?? "" }] : []
|
|
1004
|
+
);
|
|
1005
|
+
const partial = {
|
|
1006
|
+
synthesizedChunks,
|
|
1007
|
+
completedChunks: synthesizedChunks,
|
|
1008
|
+
pendingChunkIndices: chunkStates.flatMap(
|
|
1009
|
+
(state) => state.status === "pending" || state.status === "cancelled" || state.status === "failed" ? [state.chunkIndex] : []
|
|
1010
|
+
),
|
|
1011
|
+
failedChunkIndices: [...failedIndices],
|
|
1012
|
+
cancelledChunkIndices: chunkStates.filter((state) => state.status === "cancelled").map((state) => state.chunkIndex),
|
|
1013
|
+
chunkStates,
|
|
1014
|
+
totalChunks
|
|
1015
|
+
};
|
|
1016
|
+
if (error && typeof error === "object") error.partialResult = partial;
|
|
1017
|
+
throw error;
|
|
1018
|
+
} finally {
|
|
1019
|
+
scope.dispose();
|
|
1020
|
+
}
|
|
783
1021
|
}
|
|
784
1022
|
function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
|
|
785
1023
|
const boundaries = [];
|
|
@@ -870,16 +1108,26 @@ function mergeSynthesisResults(results, options) {
|
|
|
870
1108
|
})
|
|
871
1109
|
).then((merged) => {
|
|
872
1110
|
if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
|
|
873
|
-
if (!(merged instanceof ArrayBuffer) || buffers.some((buffer) => buffer.byteLength > 0) && merged.byteLength === 0)
|
|
874
|
-
throw new MergeError("The custom audio merger returned an invalid audio buffer.");
|
|
875
1111
|
if (signal.aborted) throw new SynthesisCancelledError();
|
|
876
|
-
|
|
877
|
-
results,
|
|
1112
|
+
const mergedSpec = validateMergedAudioBuffer(
|
|
878
1113
|
merged,
|
|
879
1114
|
format,
|
|
880
|
-
|
|
881
|
-
|
|
1115
|
+
buffers,
|
|
1116
|
+
inputSpecs,
|
|
1117
|
+
resolvedOptions.outputMimeType ?? resolveMimeType(format)
|
|
882
1118
|
);
|
|
1119
|
+
const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
|
|
1120
|
+
return Promise.resolve(
|
|
1121
|
+
resolvedOptions.postMergeValidator?.(result, {
|
|
1122
|
+
format,
|
|
1123
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
1124
|
+
inputSpecs,
|
|
1125
|
+
signal
|
|
1126
|
+
})
|
|
1127
|
+
).then((valid) => {
|
|
1128
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1129
|
+
return result;
|
|
1130
|
+
});
|
|
883
1131
|
}).catch((error) => {
|
|
884
1132
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
885
1133
|
throw error;
|
|
@@ -887,13 +1135,28 @@ function mergeSynthesisResults(results, options) {
|
|
|
887
1135
|
});
|
|
888
1136
|
}
|
|
889
1137
|
try {
|
|
890
|
-
|
|
1138
|
+
const result = createMergedResult(
|
|
891
1139
|
results,
|
|
892
1140
|
mergeAudioBuffers(buffers, { format }),
|
|
893
1141
|
format,
|
|
894
1142
|
inputSpecs[0],
|
|
895
1143
|
resolvedOptions.outputMimeType
|
|
896
1144
|
);
|
|
1145
|
+
if (resolvedOptions.postMergeValidator) {
|
|
1146
|
+
const validation = resolvedOptions.postMergeValidator(result, {
|
|
1147
|
+
format,
|
|
1148
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
1149
|
+
inputSpecs,
|
|
1150
|
+
signal
|
|
1151
|
+
});
|
|
1152
|
+
if (validation instanceof Promise)
|
|
1153
|
+
return validation.then((valid) => {
|
|
1154
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1155
|
+
return result;
|
|
1156
|
+
});
|
|
1157
|
+
if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1158
|
+
}
|
|
1159
|
+
return result;
|
|
897
1160
|
} catch (error) {
|
|
898
1161
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
899
1162
|
throw error;
|
|
@@ -914,8 +1177,52 @@ var ChunkValidationError = class extends Error {
|
|
|
914
1177
|
this.diagnostics = diagnostics;
|
|
915
1178
|
}
|
|
916
1179
|
};
|
|
917
|
-
|
|
918
|
-
|
|
1180
|
+
var BatchChunkValidationError = class extends ChunkValidationError {
|
|
1181
|
+
constructor(chunkDiagnostics) {
|
|
1182
|
+
const first = chunkDiagnostics[0];
|
|
1183
|
+
super(first?.chunkIndex ?? -1, first?.diagnostics ?? []);
|
|
1184
|
+
this.name = "BatchChunkValidationError";
|
|
1185
|
+
this.message = `SSML validation failed for ${chunkDiagnostics.length} chunk(s); the Azure Speech API was not called.`;
|
|
1186
|
+
this.chunkDiagnostics = chunkDiagnostics;
|
|
1187
|
+
this.totalErrorCount = chunkDiagnostics.reduce((total, chunk) => total + chunk.diagnostics.length, 0);
|
|
1188
|
+
this.errorCount = this.totalErrorCount;
|
|
1189
|
+
this.totalErrors = this.totalErrorCount;
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
function failure(error, partialResult) {
|
|
1193
|
+
return {
|
|
1194
|
+
ok: false,
|
|
1195
|
+
success: false,
|
|
1196
|
+
status: error.kind,
|
|
1197
|
+
error,
|
|
1198
|
+
...partialResult ? { partialResult } : {}
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function partialResultFrom(error) {
|
|
1202
|
+
if (!error || typeof error !== "object") return void 0;
|
|
1203
|
+
const partial = error.partialResult;
|
|
1204
|
+
if (!partial || typeof partial !== "object") return void 0;
|
|
1205
|
+
return partial;
|
|
1206
|
+
}
|
|
1207
|
+
function createSafeAbortScope(parent, timeoutMs) {
|
|
1208
|
+
const controller = new AbortController();
|
|
1209
|
+
let didTimeout = false;
|
|
1210
|
+
const onAbort = () => controller.abort();
|
|
1211
|
+
if (parent?.aborted) controller.abort();
|
|
1212
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
1213
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
1214
|
+
didTimeout = true;
|
|
1215
|
+
controller.abort();
|
|
1216
|
+
}, timeoutMs) : void 0;
|
|
1217
|
+
return {
|
|
1218
|
+
signal: controller.signal,
|
|
1219
|
+
timedOut: () => didTimeout,
|
|
1220
|
+
dispose: () => {
|
|
1221
|
+
if (timer) clearTimeout(timer);
|
|
1222
|
+
parent?.removeEventListener("abort", onAbort);
|
|
1223
|
+
},
|
|
1224
|
+
abort: () => controller.abort()
|
|
1225
|
+
};
|
|
919
1226
|
}
|
|
920
1227
|
function isRetryable(error) {
|
|
921
1228
|
if (error instanceof Error && /cancel|abort|tim(?:e|ed) ?out/i.test(error.message)) return false;
|
|
@@ -930,16 +1237,20 @@ function delayForRetry(options, attempt) {
|
|
|
930
1237
|
const base = Math.min(maxDelay, Math.max(0, options.initialDelayMs) * 2 ** Math.max(0, attempt - 1));
|
|
931
1238
|
return Math.floor(Math.random() * (base + 1));
|
|
932
1239
|
}
|
|
1240
|
+
function retryDelayForError(options, attempt, error) {
|
|
1241
|
+
return getRetryAfterDelayMs(error) ?? delayForRetry(options, attempt);
|
|
1242
|
+
}
|
|
933
1243
|
function resolveConcurrency2(value, total) {
|
|
934
1244
|
if (value === void 0) return 1;
|
|
935
1245
|
if (value === Infinity) return Math.max(1, total);
|
|
936
1246
|
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
|
|
937
1247
|
}
|
|
938
|
-
async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
1248
|
+
async function retryableSynthesis(synthesize, options, signal, onRetry, deadlineAtMs) {
|
|
939
1249
|
const retry = options ? {
|
|
940
1250
|
maxRetries: Math.max(0, Math.floor(options.maxRetries)),
|
|
941
1251
|
initialDelayMs: options.initialDelayMs,
|
|
942
|
-
maxDelayMs: options.maxDelayMs
|
|
1252
|
+
maxDelayMs: options.maxDelayMs,
|
|
1253
|
+
shouldRetry: options.shouldRetry
|
|
943
1254
|
} : void 0;
|
|
944
1255
|
let attempt = 0;
|
|
945
1256
|
while (true) {
|
|
@@ -947,9 +1258,15 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
947
1258
|
try {
|
|
948
1259
|
return await synthesize();
|
|
949
1260
|
} catch (error) {
|
|
950
|
-
if (!retry || attempt >= retry.maxRetries || !
|
|
1261
|
+
if (!retry || attempt >= retry.maxRetries || !(retry.shouldRetry?.(error, attempt + 1) ?? isRetryable(error)))
|
|
1262
|
+
throw error;
|
|
951
1263
|
attempt += 1;
|
|
952
|
-
const delayMs =
|
|
1264
|
+
const delayMs = retryDelayForError(retry, attempt, error);
|
|
1265
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
1266
|
+
const remainingMs = deadlineAtMs === void 0 ? void 0 : Math.max(0, deadlineAtMs - Date.now());
|
|
1267
|
+
if (retryAfterMs !== void 0 && (retryAfterMs > retry.maxDelayMs || remainingMs !== void 0 && retryAfterMs > remainingMs)) {
|
|
1268
|
+
throw new Error("Speech synthesis timed out because Retry-After exceeded the available retry budget.");
|
|
1269
|
+
}
|
|
953
1270
|
onRetry(attempt, delayMs);
|
|
954
1271
|
if (delayMs > 0)
|
|
955
1272
|
await new Promise((resolve, reject) => {
|
|
@@ -973,7 +1290,7 @@ function sharedValidationOptions(options, signal) {
|
|
|
973
1290
|
const runner = createAzureUrlValidatorRunner2(validator, {
|
|
974
1291
|
...options.urlValidation ?? {},
|
|
975
1292
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
976
|
-
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1293
|
+
...options.timeouts?.urlValidationMs !== void 0 ? { timeoutMs: options.timeouts.urlValidationMs } : options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
977
1294
|
...signal ? { signal } : {},
|
|
978
1295
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
979
1296
|
});
|
|
@@ -997,20 +1314,31 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
997
1314
|
diagnostics: errors
|
|
998
1315
|
});
|
|
999
1316
|
}
|
|
1317
|
+
const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
|
|
1000
1318
|
try {
|
|
1001
1319
|
return {
|
|
1002
1320
|
ok: true,
|
|
1003
1321
|
success: true,
|
|
1004
1322
|
status: "success",
|
|
1005
|
-
value: await client.synthesizeSsml(ssml, {
|
|
1323
|
+
value: await client.synthesizeSsml(ssml, {
|
|
1324
|
+
signal: jobScope?.signal ?? options.signal,
|
|
1325
|
+
timeoutMs: options.timeouts?.perChunkMs,
|
|
1326
|
+
timeouts: options.timeouts
|
|
1327
|
+
})
|
|
1006
1328
|
};
|
|
1007
1329
|
} catch (error) {
|
|
1330
|
+
if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
|
|
1008
1331
|
const synthesisError = toSynthesisError(error);
|
|
1009
1332
|
return failure(synthesisError);
|
|
1333
|
+
} finally {
|
|
1334
|
+
jobScope?.dispose();
|
|
1010
1335
|
}
|
|
1011
1336
|
}
|
|
1012
1337
|
async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
1013
|
-
const validationOptions = sharedValidationOptions(
|
|
1338
|
+
const validationOptions = sharedValidationOptions(
|
|
1339
|
+
{ ...options.validation ?? options, timeouts: options.timeouts },
|
|
1340
|
+
options.signal
|
|
1341
|
+
);
|
|
1014
1342
|
if (options.signal?.aborted) {
|
|
1015
1343
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
1016
1344
|
return failure(error);
|
|
@@ -1044,16 +1372,17 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1044
1372
|
return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
1045
1373
|
})
|
|
1046
1374
|
);
|
|
1047
|
-
const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
|
|
1048
1375
|
if (options.signal?.aborted) {
|
|
1049
1376
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
1050
1377
|
return failure(error);
|
|
1051
1378
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1379
|
+
const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
|
|
1380
|
+
if (chunkDiagnostics.length > 0) {
|
|
1381
|
+
const error = new BatchChunkValidationError(chunkDiagnostics);
|
|
1382
|
+
for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
|
|
1055
1383
|
return failure(error);
|
|
1056
1384
|
}
|
|
1385
|
+
let fallbackJobScope;
|
|
1057
1386
|
try {
|
|
1058
1387
|
if (client.synthesizeChunks) {
|
|
1059
1388
|
const normalizedChunks = chunks.map((chunk) => {
|
|
@@ -1065,20 +1394,57 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1065
1394
|
outputFormat: options.outputFormat,
|
|
1066
1395
|
signal: options.signal,
|
|
1067
1396
|
timeoutMs: options.timeoutMs,
|
|
1397
|
+
timeouts: options.timeouts,
|
|
1068
1398
|
sourceNodePath: options.sourceNodePath,
|
|
1069
1399
|
concurrency: options.concurrency,
|
|
1070
|
-
retryOptions: options.retryOptions
|
|
1400
|
+
retryOptions: options.retryOptions,
|
|
1401
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
1402
|
+
resumeChunks: options.resumeChunks,
|
|
1403
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1404
|
+
customMerger: options.customMerger,
|
|
1405
|
+
outputMimeType: options.outputMimeType,
|
|
1406
|
+
postMergeValidator: options.postMergeValidator,
|
|
1407
|
+
resumeValidation: options.resumeValidation
|
|
1071
1408
|
});
|
|
1072
1409
|
return { ok: true, success: true, status: "success", value };
|
|
1073
1410
|
}
|
|
1411
|
+
const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
|
|
1412
|
+
const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
|
|
1074
1413
|
const results = new Array(chunks.length);
|
|
1075
|
-
|
|
1414
|
+
const chunkStates = inputs.map((_chunk, chunkIndex) => ({
|
|
1415
|
+
chunkIndex,
|
|
1416
|
+
status: "pending",
|
|
1417
|
+
canResume: true
|
|
1418
|
+
}));
|
|
1419
|
+
const cachedChunks = new Map((options.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
1420
|
+
const invalidCachedIndices = /* @__PURE__ */ new Set();
|
|
1421
|
+
for (const [index, cached] of cachedChunks) {
|
|
1422
|
+
if (index < 0 || index >= chunks.length) continue;
|
|
1423
|
+
if (options.resumeValidation === "disabled" || cached.fingerprint === fingerprints[index]) {
|
|
1424
|
+
results[index] = cached;
|
|
1425
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: cached };
|
|
1426
|
+
} else invalidCachedIndices.add(index);
|
|
1427
|
+
}
|
|
1428
|
+
const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
|
|
1429
|
+
const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
|
|
1430
|
+
const jobStartedAt = Date.now();
|
|
1431
|
+
const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
|
|
1432
|
+
const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
|
|
1433
|
+
fallbackJobScope = jobScope;
|
|
1434
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
1435
|
+
let firstError;
|
|
1436
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
1076
1437
|
let nextIndex = 0;
|
|
1077
1438
|
const concurrency = resolveConcurrency2(options.concurrency, chunks.length);
|
|
1078
1439
|
const worker = async () => {
|
|
1079
1440
|
while (true) {
|
|
1080
1441
|
const index = nextIndex++;
|
|
1081
1442
|
if (index >= chunks.length) return;
|
|
1443
|
+
if (!shouldSynthesize(index)) continue;
|
|
1444
|
+
if (firstError && options.cancelOnFailure !== false) {
|
|
1445
|
+
chunkStates[index] = { chunkIndex: index, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1082
1448
|
const chunk = chunks[index];
|
|
1083
1449
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
1084
1450
|
const sourceNodePath = input.sourceNodePath;
|
|
@@ -1086,28 +1452,41 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1086
1452
|
pending(index, "synthesizing");
|
|
1087
1453
|
const startedAt = Date.now();
|
|
1088
1454
|
try {
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1455
|
+
const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
|
|
1456
|
+
const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
|
|
1457
|
+
const chunkSignal = chunkScope?.signal ?? options.signal;
|
|
1458
|
+
let result;
|
|
1459
|
+
try {
|
|
1460
|
+
result = await retryableSynthesis(
|
|
1461
|
+
() => client.synthesizeSsml(input.ssml, {
|
|
1462
|
+
outputFormat: options.outputFormat,
|
|
1463
|
+
signal: chunkSignal,
|
|
1464
|
+
timeoutMs: options.timeouts?.perChunkMs ?? options.timeoutMs,
|
|
1465
|
+
sourceNodePath: input.sourceNodePath ?? options.sourceNodePath
|
|
1466
|
+
}),
|
|
1467
|
+
options.retryOptions,
|
|
1468
|
+
chunkSignal,
|
|
1469
|
+
(retryAttempt, nextRetryDelayMs) => options.onProgress?.({
|
|
1470
|
+
currentChunk: completed,
|
|
1471
|
+
totalChunks: chunks.length,
|
|
1472
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
1473
|
+
chunkIndex: index,
|
|
1474
|
+
originalTextRange: input.originalTextRange,
|
|
1475
|
+
status: "synthesizing",
|
|
1476
|
+
durationMs: Date.now() - startedAt,
|
|
1477
|
+
retryAttempt,
|
|
1478
|
+
nextRetryDelayMs,
|
|
1479
|
+
isRetrying: true
|
|
1480
|
+
}),
|
|
1481
|
+
jobDeadlineAt
|
|
1482
|
+
);
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (chunkScope?.timedOut())
|
|
1485
|
+
throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
|
|
1486
|
+
throw error;
|
|
1487
|
+
} finally {
|
|
1488
|
+
chunkScope?.dispose();
|
|
1489
|
+
}
|
|
1111
1490
|
results[index] = {
|
|
1112
1491
|
...result,
|
|
1113
1492
|
...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
|
|
@@ -1151,6 +1530,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1151
1530
|
}))
|
|
1152
1531
|
} : {}
|
|
1153
1532
|
};
|
|
1533
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: results[index] };
|
|
1154
1534
|
completed += 1;
|
|
1155
1535
|
options.onProgress?.({
|
|
1156
1536
|
currentChunk: completed,
|
|
@@ -1162,6 +1542,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1162
1542
|
durationMs: Date.now() - startedAt
|
|
1163
1543
|
});
|
|
1164
1544
|
} catch (error) {
|
|
1545
|
+
const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
|
|
1546
|
+
firstError ?? (firstError = error);
|
|
1547
|
+
if (!wasCancelled) failedIndices.add(index);
|
|
1548
|
+
chunkStates[index] = {
|
|
1549
|
+
chunkIndex: index,
|
|
1550
|
+
status: wasCancelled ? "cancelled" : "failed",
|
|
1551
|
+
isOriginalFailure: !wasCancelled,
|
|
1552
|
+
canResume: true,
|
|
1553
|
+
error
|
|
1554
|
+
};
|
|
1165
1555
|
options.onProgress?.({
|
|
1166
1556
|
currentChunk: completed,
|
|
1167
1557
|
totalChunks: chunks.length,
|
|
@@ -1172,24 +1562,55 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1172
1562
|
durationMs: Date.now() - startedAt,
|
|
1173
1563
|
error
|
|
1174
1564
|
});
|
|
1175
|
-
|
|
1565
|
+
if (options.cancelOnFailure !== false) jobScope?.abort();
|
|
1566
|
+
return;
|
|
1176
1567
|
}
|
|
1177
1568
|
}
|
|
1178
1569
|
};
|
|
1179
1570
|
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
1571
|
+
if (firstError && options.cancelOnFailure !== false) {
|
|
1572
|
+
for (const [chunkIndex, state] of chunkStates.entries()) {
|
|
1573
|
+
if (state.status === "pending" && shouldSynthesize(chunkIndex)) {
|
|
1574
|
+
chunkStates[chunkIndex] = { chunkIndex, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
if (failedIndices.size > 0) {
|
|
1579
|
+
const error = firstError ?? new Error("One or more SSML chunks failed to synthesize.");
|
|
1580
|
+
const synthesizedChunks = results.flatMap(
|
|
1581
|
+
(result, chunkIndex) => result ? [{ ...result, chunkIndex, fingerprint: fingerprints[chunkIndex] ?? "" }] : []
|
|
1582
|
+
);
|
|
1583
|
+
error.partialResult = {
|
|
1584
|
+
synthesizedChunks,
|
|
1585
|
+
completedChunks: synthesizedChunks,
|
|
1586
|
+
pendingChunkIndices: chunkStates.flatMap(
|
|
1587
|
+
(state) => state.status === "pending" || state.status === "cancelled" || state.status === "failed" ? [state.chunkIndex] : []
|
|
1588
|
+
),
|
|
1589
|
+
failedChunkIndices: [...failedIndices],
|
|
1590
|
+
cancelledChunkIndices: chunkStates.filter((state) => state.status === "cancelled").map((state) => state.chunkIndex),
|
|
1591
|
+
chunkStates,
|
|
1592
|
+
totalChunks: chunks.length
|
|
1593
|
+
};
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1180
1596
|
const orderedResults = results.filter((result) => result !== void 0);
|
|
1181
1597
|
return {
|
|
1182
1598
|
ok: true,
|
|
1183
1599
|
success: true,
|
|
1184
1600
|
status: "success",
|
|
1185
|
-
value: mergeSynthesisResults(orderedResults, {
|
|
1601
|
+
value: await mergeSynthesisResults(orderedResults, {
|
|
1186
1602
|
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
|
|
1187
|
-
signal: options.signal
|
|
1603
|
+
signal: jobScope?.signal ?? options.signal,
|
|
1604
|
+
customMerger: options.customMerger,
|
|
1605
|
+
outputMimeType: options.outputMimeType,
|
|
1606
|
+
postMergeValidator: options.postMergeValidator
|
|
1188
1607
|
})
|
|
1189
1608
|
};
|
|
1190
1609
|
} catch (error) {
|
|
1191
1610
|
const synthesisError = toSynthesisError(error);
|
|
1192
|
-
return failure(synthesisError);
|
|
1611
|
+
return failure(synthesisError, partialResultFrom(error));
|
|
1612
|
+
} finally {
|
|
1613
|
+
fallbackJobScope?.dispose();
|
|
1193
1614
|
}
|
|
1194
1615
|
}
|
|
1195
1616
|
function withValidationSignal(options, signal) {
|
|
@@ -1210,14 +1631,23 @@ var AzureTtsClient = class {
|
|
|
1210
1631
|
__privateSet(this, _options, options);
|
|
1211
1632
|
}
|
|
1212
1633
|
async synthesize(ssml) {
|
|
1213
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1634
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1214
1635
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1215
1636
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1216
|
-
const config = {
|
|
1637
|
+
const config = {
|
|
1638
|
+
endpoint,
|
|
1639
|
+
region,
|
|
1640
|
+
subscriptionKey,
|
|
1641
|
+
outputFormat,
|
|
1642
|
+
signal,
|
|
1643
|
+
timeoutMs,
|
|
1644
|
+
timeouts,
|
|
1645
|
+
retryOptions: __privateGet(this, _options).retryOptions
|
|
1646
|
+
};
|
|
1217
1647
|
return synthesizeSpeech(ssml, config);
|
|
1218
1648
|
}
|
|
1219
1649
|
async synthesizeSsml(ssml, options = {}) {
|
|
1220
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1650
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1221
1651
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1222
1652
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1223
1653
|
return synthesizeSsml(ssml, {
|
|
@@ -1227,13 +1657,20 @@ var AzureTtsClient = class {
|
|
|
1227
1657
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1228
1658
|
signal: options.signal ?? signal,
|
|
1229
1659
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1660
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1230
1661
|
sourceNodePath: options.sourceNodePath,
|
|
1231
1662
|
sourceTextSegments: options.sourceTextSegments,
|
|
1232
|
-
sourceMarkers: options.sourceMarkers
|
|
1663
|
+
sourceMarkers: options.sourceMarkers,
|
|
1664
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
1665
|
+
cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
|
|
1666
|
+
customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
|
|
1667
|
+
outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
|
|
1668
|
+
postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
|
|
1669
|
+
resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
|
|
1233
1670
|
});
|
|
1234
1671
|
}
|
|
1235
1672
|
async synthesizeChunks(chunks, options = {}) {
|
|
1236
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1673
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1237
1674
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1238
1675
|
return synthesizeSsmlChunks(chunks, {
|
|
1239
1676
|
endpoint,
|
|
@@ -1242,10 +1679,18 @@ var AzureTtsClient = class {
|
|
|
1242
1679
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1243
1680
|
signal: options.signal ?? signal,
|
|
1244
1681
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1682
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1245
1683
|
sourceNodePath: options.sourceNodePath,
|
|
1246
1684
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1247
1685
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1248
|
-
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
1686
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
1687
|
+
cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
|
|
1688
|
+
resumeChunks: options.resumeChunks,
|
|
1689
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1690
|
+
customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
|
|
1691
|
+
outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
|
|
1692
|
+
postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
|
|
1693
|
+
resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
|
|
1249
1694
|
});
|
|
1250
1695
|
}
|
|
1251
1696
|
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
@@ -1257,6 +1702,7 @@ var AzureTtsClient = class {
|
|
|
1257
1702
|
outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
|
|
1258
1703
|
signal: options.signal ?? __privateGet(this, _options).signal,
|
|
1259
1704
|
timeoutMs: options.timeoutMs ?? __privateGet(this, _options).timeoutMs,
|
|
1705
|
+
timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
|
|
1260
1706
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1261
1707
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1262
1708
|
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
@@ -1350,7 +1796,9 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
1350
1796
|
voiceCount: sortedVoices.length,
|
|
1351
1797
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1352
1798
|
apiVersion: AZURE_VOICE_API_VERSION,
|
|
1353
|
-
regions
|
|
1799
|
+
regions,
|
|
1800
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1801
|
+
regionDiffs: {}
|
|
1354
1802
|
}
|
|
1355
1803
|
};
|
|
1356
1804
|
}
|
|
@@ -1359,6 +1807,7 @@ export {
|
|
|
1359
1807
|
AzureTtsClient,
|
|
1360
1808
|
AzureTtsError,
|
|
1361
1809
|
AzureTtsSdkError,
|
|
1810
|
+
BatchChunkValidationError,
|
|
1362
1811
|
ChunkValidationError,
|
|
1363
1812
|
DEFAULT_OUTPUT_FORMAT,
|
|
1364
1813
|
MergeError,
|
|
@@ -1369,6 +1818,7 @@ export {
|
|
|
1369
1818
|
buildPartialSsml,
|
|
1370
1819
|
buildSsml,
|
|
1371
1820
|
canMergeAudioFormat,
|
|
1821
|
+
computeChunkFingerprint,
|
|
1372
1822
|
createAzureUrlValidatorRunner,
|
|
1373
1823
|
extractSsmlText,
|
|
1374
1824
|
extractSsmlTranslatableText,
|
|
@@ -1376,6 +1826,7 @@ export {
|
|
|
1376
1826
|
fromPlainTextToSsml,
|
|
1377
1827
|
getAzureVoiceCatalogMetadata,
|
|
1378
1828
|
getBuiltInVoiceCatalogMetadata,
|
|
1829
|
+
getRetryAfterDelayMs,
|
|
1379
1830
|
getSsmlSourceMap,
|
|
1380
1831
|
inspectAudioSpecification,
|
|
1381
1832
|
isValidAzureAudioDuration,
|