@ssml-builder-js/azure-tts-client 2.16.0 → 2.17.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/CHANGELOG.md +11 -0
- package/dist/index.d.mts +77 -10
- package/dist/index.d.ts +77 -10
- package/dist/index.js +330 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +13 -4
- package/src/errors.ts +44 -2
- package/src/index.ts +15 -2
- package/src/safe.ts +210 -41
- package/src/synthesis.ts +180 -30
- package/src/types.ts +58 -0
- package/src/voiceCatalog.ts +4 -0
- package/test/v217-pipeline.test.ts +125 -0
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
8
8
|
|
|
9
9
|
// src/errors.ts
|
|
10
10
|
var AzureTtsError = class extends Error {
|
|
11
|
-
constructor(status, statusText, responseBody, requestId) {
|
|
11
|
+
constructor(status, statusText, responseBody, requestId, responseHeaders) {
|
|
12
12
|
super(`Azure TTS request failed: ${status} ${statusText}`);
|
|
13
13
|
this.kind = "azure-api-error";
|
|
14
14
|
this.name = "AzureTtsError";
|
|
@@ -16,8 +16,37 @@ var AzureTtsError = class extends Error {
|
|
|
16
16
|
this.statusText = statusText;
|
|
17
17
|
this.responseBody = responseBody;
|
|
18
18
|
this.requestId = requestId;
|
|
19
|
+
const value = responseHeaders instanceof Headers ? responseHeaders.get("retry-after") : responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"];
|
|
20
|
+
const seconds = value ? Number(value.trim()) : NaN;
|
|
21
|
+
const date = value ? Date.parse(value) : NaN;
|
|
22
|
+
if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1e3;
|
|
23
|
+
else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
|
|
19
24
|
}
|
|
20
25
|
};
|
|
26
|
+
function getRetryAfterDelayMs(error) {
|
|
27
|
+
if (error instanceof AzureTtsError && error.retryAfterMs !== void 0) return error.retryAfterMs;
|
|
28
|
+
if (!error || typeof error !== "object") return void 0;
|
|
29
|
+
const candidate = error;
|
|
30
|
+
if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
|
|
31
|
+
const headers = candidate.headers ?? candidate.response?.headers;
|
|
32
|
+
if (headers instanceof Headers) {
|
|
33
|
+
const value = headers.get("retry-after");
|
|
34
|
+
if (!value) return void 0;
|
|
35
|
+
const seconds = Number(value.trim());
|
|
36
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
37
|
+
const date = Date.parse(value);
|
|
38
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
39
|
+
}
|
|
40
|
+
if (headers && typeof headers === "object") {
|
|
41
|
+
const value = headers["retry-after"] ?? headers["Retry-After"];
|
|
42
|
+
if (typeof value !== "string") return void 0;
|
|
43
|
+
const seconds = Number(value.trim());
|
|
44
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
45
|
+
const date = Date.parse(value);
|
|
46
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
47
|
+
}
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
21
50
|
var AzureTtsSdkError = class extends AzureTtsError {
|
|
22
51
|
constructor(errorDetails) {
|
|
23
52
|
super(0, "Speech SDK", errorDetails, null);
|
|
@@ -210,6 +239,8 @@ function formatAudioSpecification(format) {
|
|
|
210
239
|
const bitrateMatch = /(\d+)(?:kbitrate|kbps|kbits?)/i.exec(format);
|
|
211
240
|
const bitrate = bitrateMatch?.[1] ? Number(bitrateMatch[1]) * 1e3 : void 0;
|
|
212
241
|
const codec = /mp3|mpeg/i.test(format) ? "mp3" : /opus/i.test(format) ? "opus" : /silk/i.test(format) ? "silk" : /pcm|mulaw|alaw|siren/i.test(format) ? "pcm" : "unknown";
|
|
242
|
+
const bitDepthMatch = /(\d+)bit/i.exec(format);
|
|
243
|
+
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;
|
|
213
244
|
return {
|
|
214
245
|
format,
|
|
215
246
|
mimeType: resolveMimeType(format),
|
|
@@ -217,6 +248,9 @@ function formatAudioSpecification(format) {
|
|
|
217
248
|
sampleRate,
|
|
218
249
|
channels,
|
|
219
250
|
...bitrate ? { bitrate } : {},
|
|
251
|
+
...bitDepthMatch?.[1] ? { bitDepth: Number(bitDepthMatch[1]) } : {},
|
|
252
|
+
...container ? { container } : {},
|
|
253
|
+
isVbr: /vbr/i.test(format),
|
|
220
254
|
isCompressed: codec === "mp3" || codec === "opus" || codec === "silk"
|
|
221
255
|
};
|
|
222
256
|
}
|
|
@@ -252,6 +286,8 @@ function parseMp3Specification(buffer, format) {
|
|
|
252
286
|
sampleRate,
|
|
253
287
|
channels: (bytes[index + 3] ?? 0) >> 6 === 3 ? 1 : 2,
|
|
254
288
|
bitrate: bitrateKbps * 1e3,
|
|
289
|
+
container: "mp3-raw",
|
|
290
|
+
isVbr: false,
|
|
255
291
|
isCompressed: true
|
|
256
292
|
};
|
|
257
293
|
}
|
|
@@ -273,6 +309,9 @@ function inspectAudioSpecification(buffer, format) {
|
|
|
273
309
|
sampleRate,
|
|
274
310
|
channels,
|
|
275
311
|
...sampleRate && channels && bitsPerSample ? { bitrate: sampleRate * channels * bitsPerSample } : {},
|
|
312
|
+
bitDepth: bitsPerSample,
|
|
313
|
+
container: "riff-wave",
|
|
314
|
+
isVbr: false,
|
|
276
315
|
isCompressed: formatCode !== 1
|
|
277
316
|
};
|
|
278
317
|
}
|
|
@@ -283,7 +322,7 @@ function validateAudioSpecifications(specs) {
|
|
|
283
322
|
const first = specs[0];
|
|
284
323
|
if (!first) return;
|
|
285
324
|
const mismatch = specs.find(
|
|
286
|
-
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate
|
|
325
|
+
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || 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
|
|
287
326
|
);
|
|
288
327
|
if (mismatch)
|
|
289
328
|
throw new AudioFormatMismatchError(
|
|
@@ -486,9 +525,7 @@ async function synthesizeSsml(ssml, config) {
|
|
|
486
525
|
};
|
|
487
526
|
}
|
|
488
527
|
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath) {
|
|
489
|
-
|
|
490
|
-
Object.defineProperty(unmapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
491
|
-
return unmapped;
|
|
528
|
+
return { mappingStatus: "unmapped" };
|
|
492
529
|
}
|
|
493
530
|
const value = text ?? "";
|
|
494
531
|
let localStart = Number.isFinite(offsetHint) && (offsetHint ?? 0) >= 0 ? offsetHint : -1;
|
|
@@ -570,8 +607,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
570
607
|
...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
571
608
|
...requestId ? { requestId } : {}
|
|
572
609
|
};
|
|
573
|
-
if (event.mappingStatus === "unmapped")
|
|
610
|
+
if (event.mappingStatus === "unmapped") {
|
|
574
611
|
Object.defineProperty(mapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
612
|
+
Object.defineProperty(mapped, "toJSON", {
|
|
613
|
+
value: () => ({ ...mapped, mappingStatus: "unmapped" }),
|
|
614
|
+
enumerable: false
|
|
615
|
+
});
|
|
616
|
+
}
|
|
575
617
|
return mapped;
|
|
576
618
|
};
|
|
577
619
|
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
@@ -594,10 +636,11 @@ async function synthesizeSsml(ssml, config) {
|
|
|
594
636
|
abortHandler = () => rejectWithError(new SynthesisCancelledError());
|
|
595
637
|
config.signal.addEventListener("abort", abortHandler, { once: true });
|
|
596
638
|
}
|
|
597
|
-
|
|
639
|
+
const timeoutMs = config.timeouts?.perChunkMs ?? config.timeoutMs;
|
|
640
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
598
641
|
timeout = setTimeout(
|
|
599
|
-
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${
|
|
600
|
-
|
|
642
|
+
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`)),
|
|
643
|
+
timeoutMs
|
|
601
644
|
);
|
|
602
645
|
}
|
|
603
646
|
synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);
|
|
@@ -616,7 +659,9 @@ function isRetryableSynthesisError(error) {
|
|
|
616
659
|
if (typeof status === "number") return status === 429 || status >= 500 && status < 600;
|
|
617
660
|
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
618
661
|
}
|
|
619
|
-
function retryDelay(options, retryAttempt) {
|
|
662
|
+
function retryDelay(options, retryAttempt, error) {
|
|
663
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
664
|
+
if (retryAfterMs !== void 0) return retryAfterMs;
|
|
620
665
|
const base = Math.min(options.maxDelayMs, options.initialDelayMs * 2 ** Math.max(0, retryAttempt - 1));
|
|
621
666
|
return Math.floor(Math.random() * (base + 1));
|
|
622
667
|
}
|
|
@@ -648,7 +693,8 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
|
648
693
|
const options = retryOptions ? {
|
|
649
694
|
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
650
695
|
initialDelayMs: Math.max(0, retryOptions.initialDelayMs),
|
|
651
|
-
maxDelayMs: Math.max(0, retryOptions.maxDelayMs)
|
|
696
|
+
maxDelayMs: Math.max(0, retryOptions.maxDelayMs),
|
|
697
|
+
shouldRetry: retryOptions.shouldRetry
|
|
652
698
|
} : void 0;
|
|
653
699
|
let attempt = 0;
|
|
654
700
|
while (true) {
|
|
@@ -656,17 +702,56 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
|
656
702
|
try {
|
|
657
703
|
return await synthesizeSsml(ssml, config);
|
|
658
704
|
} catch (error) {
|
|
659
|
-
if (!options || attempt >= options.maxRetries || !
|
|
705
|
+
if (!options || attempt >= options.maxRetries || !(options.shouldRetry?.(error, attempt + 1) ?? isRetryableSynthesisError(error)))
|
|
706
|
+
throw error;
|
|
660
707
|
attempt += 1;
|
|
661
|
-
const delayMs = retryDelay(options, attempt);
|
|
708
|
+
const delayMs = retryDelay(options, attempt, error);
|
|
662
709
|
onRetry(attempt, delayMs);
|
|
663
710
|
await waitForRetry(delayMs, config.signal);
|
|
664
711
|
}
|
|
665
712
|
}
|
|
666
713
|
}
|
|
714
|
+
function createAbortScope(parent, timeoutMs) {
|
|
715
|
+
const controller = new AbortController();
|
|
716
|
+
let didTimeout = false;
|
|
717
|
+
const onAbort = () => controller.abort();
|
|
718
|
+
if (parent?.aborted) controller.abort();
|
|
719
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
720
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
721
|
+
didTimeout = true;
|
|
722
|
+
controller.abort();
|
|
723
|
+
}, timeoutMs) : void 0;
|
|
724
|
+
return {
|
|
725
|
+
signal: controller.signal,
|
|
726
|
+
timedOut: () => didTimeout,
|
|
727
|
+
dispose: () => {
|
|
728
|
+
if (timer) clearTimeout(timer);
|
|
729
|
+
parent?.removeEventListener("abort", onAbort);
|
|
730
|
+
},
|
|
731
|
+
abort: () => controller.abort()
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
async function synthesizeChunkWithTimeout(ssml, config, retryOptions, timeoutMs, onRetry) {
|
|
735
|
+
const scope = createAbortScope(config.signal, timeoutMs);
|
|
736
|
+
try {
|
|
737
|
+
return await synthesizeWithRetry(ssml, { ...config, signal: scope.signal }, retryOptions, onRetry);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (scope.timedOut()) throw new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`);
|
|
740
|
+
throw error;
|
|
741
|
+
} finally {
|
|
742
|
+
scope.dispose();
|
|
743
|
+
}
|
|
744
|
+
}
|
|
667
745
|
async function synthesizeSsmlChunks(chunks, config) {
|
|
668
746
|
const results = new Array(chunks.length);
|
|
669
747
|
const totalChunks = chunks.length;
|
|
748
|
+
const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
749
|
+
for (const [index, cached] of cachedChunks) {
|
|
750
|
+
if (index >= 0 && index < totalChunks) results[index] = cached;
|
|
751
|
+
}
|
|
752
|
+
const requestedIndices = config.resumeChunkIndices ? new Set(config.resumeChunkIndices.filter((index) => index >= 0 && index < totalChunks)) : void 0;
|
|
753
|
+
const shouldSynthesize = (index) => !cachedChunks.has(index) && (requestedIndices === void 0 || requestedIndices.has(index));
|
|
754
|
+
const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
|
|
670
755
|
const report = (event) => config.onProgress?.(event);
|
|
671
756
|
for (const [index, chunk] of chunks.entries()) {
|
|
672
757
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
@@ -680,13 +765,17 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
680
765
|
durationMs: 0
|
|
681
766
|
});
|
|
682
767
|
}
|
|
683
|
-
let completed = 0;
|
|
768
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
684
769
|
let nextIndex = 0;
|
|
685
770
|
const concurrency = resolveConcurrency(config.concurrency, chunks.length);
|
|
771
|
+
let firstError;
|
|
772
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
686
773
|
const worker = async () => {
|
|
687
774
|
while (true) {
|
|
688
775
|
const index = nextIndex++;
|
|
689
776
|
if (index >= chunks.length) return;
|
|
777
|
+
if (!shouldSynthesize(index)) continue;
|
|
778
|
+
if (firstError && config.cancelOnFailure !== false) return;
|
|
690
779
|
const chunk = chunks[index];
|
|
691
780
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
692
781
|
report({
|
|
@@ -700,10 +789,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
700
789
|
});
|
|
701
790
|
const startedAt = Date.now();
|
|
702
791
|
try {
|
|
703
|
-
const result = await
|
|
792
|
+
const result = await synthesizeChunkWithTimeout(
|
|
704
793
|
input.ssml,
|
|
705
794
|
{
|
|
706
795
|
...config,
|
|
796
|
+
signal: scope.signal,
|
|
707
797
|
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
708
798
|
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
709
799
|
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
@@ -712,6 +802,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
712
802
|
onProgress: void 0
|
|
713
803
|
},
|
|
714
804
|
config.retryOptions,
|
|
805
|
+
config.timeouts?.chunkWithRetriesMs ?? config.timeouts?.perChunkMs ?? config.timeoutMs,
|
|
715
806
|
(retryAttempt, nextRetryDelayMs) => report({
|
|
716
807
|
currentChunk: completed,
|
|
717
808
|
totalChunks,
|
|
@@ -737,6 +828,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
737
828
|
durationMs: Date.now() - startedAt
|
|
738
829
|
});
|
|
739
830
|
} catch (error) {
|
|
831
|
+
failedIndices.add(index);
|
|
740
832
|
report({
|
|
741
833
|
currentChunk: completed,
|
|
742
834
|
totalChunks,
|
|
@@ -747,16 +839,36 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
747
839
|
durationMs: Date.now() - startedAt,
|
|
748
840
|
error
|
|
749
841
|
});
|
|
750
|
-
|
|
842
|
+
firstError ?? (firstError = scope.timedOut() ? new SynthesisTimeoutError(`Speech synthesis timed out after ${config.timeouts?.totalJobMs} ms.`) : error);
|
|
843
|
+
if (config.cancelOnFailure !== false) scope.abort();
|
|
844
|
+
return;
|
|
751
845
|
}
|
|
752
846
|
}
|
|
753
847
|
};
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
848
|
+
try {
|
|
849
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
850
|
+
if (firstError) throw firstError;
|
|
851
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
852
|
+
return await mergeSynthesisResults(orderedResults, {
|
|
853
|
+
format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
|
|
854
|
+
signal: scope.signal,
|
|
855
|
+
customMerger: config.customMerger,
|
|
856
|
+
outputMimeType: config.outputMimeType,
|
|
857
|
+
postMergeValidator: config.postMergeValidator
|
|
858
|
+
});
|
|
859
|
+
} catch (error) {
|
|
860
|
+
const partial = {
|
|
861
|
+
synthesizedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
862
|
+
completedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
863
|
+
pendingChunkIndices: chunks.flatMap((_chunk, chunkIndex) => results[chunkIndex] ? [] : [chunkIndex]),
|
|
864
|
+
failedChunkIndices: [...failedIndices],
|
|
865
|
+
totalChunks
|
|
866
|
+
};
|
|
867
|
+
if (error && typeof error === "object") error.partialResult = partial;
|
|
868
|
+
throw error;
|
|
869
|
+
} finally {
|
|
870
|
+
scope.dispose();
|
|
871
|
+
}
|
|
760
872
|
}
|
|
761
873
|
function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
|
|
762
874
|
const boundaries = [];
|
|
@@ -850,13 +962,24 @@ function mergeSynthesisResults(results, options) {
|
|
|
850
962
|
if (!(merged instanceof ArrayBuffer) || buffers.some((buffer) => buffer.byteLength > 0) && merged.byteLength === 0)
|
|
851
963
|
throw new MergeError("The custom audio merger returned an invalid audio buffer.");
|
|
852
964
|
if (signal.aborted) throw new SynthesisCancelledError();
|
|
853
|
-
|
|
965
|
+
const result = createMergedResult(
|
|
854
966
|
results,
|
|
855
967
|
merged,
|
|
856
968
|
format,
|
|
857
969
|
inspectAudioSpecification(merged, format),
|
|
858
970
|
resolvedOptions.outputMimeType
|
|
859
971
|
);
|
|
972
|
+
return Promise.resolve(
|
|
973
|
+
resolvedOptions.postMergeValidator?.(result, {
|
|
974
|
+
format,
|
|
975
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
976
|
+
inputSpecs,
|
|
977
|
+
signal
|
|
978
|
+
})
|
|
979
|
+
).then((valid) => {
|
|
980
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
981
|
+
return result;
|
|
982
|
+
});
|
|
860
983
|
}).catch((error) => {
|
|
861
984
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
862
985
|
throw error;
|
|
@@ -864,13 +987,28 @@ function mergeSynthesisResults(results, options) {
|
|
|
864
987
|
});
|
|
865
988
|
}
|
|
866
989
|
try {
|
|
867
|
-
|
|
990
|
+
const result = createMergedResult(
|
|
868
991
|
results,
|
|
869
992
|
mergeAudioBuffers(buffers, { format }),
|
|
870
993
|
format,
|
|
871
994
|
inputSpecs[0],
|
|
872
995
|
resolvedOptions.outputMimeType
|
|
873
996
|
);
|
|
997
|
+
if (resolvedOptions.postMergeValidator) {
|
|
998
|
+
const validation = resolvedOptions.postMergeValidator(result, {
|
|
999
|
+
format,
|
|
1000
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
1001
|
+
inputSpecs,
|
|
1002
|
+
signal
|
|
1003
|
+
});
|
|
1004
|
+
if (validation instanceof Promise)
|
|
1005
|
+
return validation.then((valid) => {
|
|
1006
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1007
|
+
return result;
|
|
1008
|
+
});
|
|
1009
|
+
if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1010
|
+
}
|
|
1011
|
+
return result;
|
|
874
1012
|
} catch (error) {
|
|
875
1013
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
876
1014
|
throw error;
|
|
@@ -895,8 +1033,52 @@ var ChunkValidationError = class extends Error {
|
|
|
895
1033
|
this.diagnostics = diagnostics;
|
|
896
1034
|
}
|
|
897
1035
|
};
|
|
898
|
-
|
|
899
|
-
|
|
1036
|
+
var BatchChunkValidationError = class extends ChunkValidationError {
|
|
1037
|
+
constructor(chunkDiagnostics) {
|
|
1038
|
+
const first = chunkDiagnostics[0];
|
|
1039
|
+
super(first?.chunkIndex ?? -1, first?.diagnostics ?? []);
|
|
1040
|
+
this.name = "BatchChunkValidationError";
|
|
1041
|
+
this.message = `SSML validation failed for ${chunkDiagnostics.length} chunk(s); the Azure Speech API was not called.`;
|
|
1042
|
+
this.chunkDiagnostics = chunkDiagnostics;
|
|
1043
|
+
this.totalErrorCount = chunkDiagnostics.reduce((total, chunk) => total + chunk.diagnostics.length, 0);
|
|
1044
|
+
this.errorCount = this.totalErrorCount;
|
|
1045
|
+
this.totalErrors = this.totalErrorCount;
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
function failure(error, partialResult) {
|
|
1049
|
+
return {
|
|
1050
|
+
ok: false,
|
|
1051
|
+
success: false,
|
|
1052
|
+
status: error.kind,
|
|
1053
|
+
error,
|
|
1054
|
+
...partialResult ? { partialResult } : {}
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
function partialResultFrom(error) {
|
|
1058
|
+
if (!error || typeof error !== "object") return void 0;
|
|
1059
|
+
const partial = error.partialResult;
|
|
1060
|
+
if (!partial || typeof partial !== "object") return void 0;
|
|
1061
|
+
return partial;
|
|
1062
|
+
}
|
|
1063
|
+
function createSafeAbortScope(parent, timeoutMs) {
|
|
1064
|
+
const controller = new AbortController();
|
|
1065
|
+
let didTimeout = false;
|
|
1066
|
+
const onAbort = () => controller.abort();
|
|
1067
|
+
if (parent?.aborted) controller.abort();
|
|
1068
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
1069
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
1070
|
+
didTimeout = true;
|
|
1071
|
+
controller.abort();
|
|
1072
|
+
}, timeoutMs) : void 0;
|
|
1073
|
+
return {
|
|
1074
|
+
signal: controller.signal,
|
|
1075
|
+
timedOut: () => didTimeout,
|
|
1076
|
+
dispose: () => {
|
|
1077
|
+
if (timer) clearTimeout(timer);
|
|
1078
|
+
parent?.removeEventListener("abort", onAbort);
|
|
1079
|
+
},
|
|
1080
|
+
abort: () => controller.abort()
|
|
1081
|
+
};
|
|
900
1082
|
}
|
|
901
1083
|
function isRetryable(error) {
|
|
902
1084
|
if (error instanceof Error && /cancel|abort|tim(?:e|ed) ?out/i.test(error.message)) return false;
|
|
@@ -911,6 +1093,9 @@ function delayForRetry(options, attempt) {
|
|
|
911
1093
|
const base = Math.min(maxDelay, Math.max(0, options.initialDelayMs) * 2 ** Math.max(0, attempt - 1));
|
|
912
1094
|
return Math.floor(Math.random() * (base + 1));
|
|
913
1095
|
}
|
|
1096
|
+
function retryDelayForError(options, attempt, error) {
|
|
1097
|
+
return getRetryAfterDelayMs(error) ?? delayForRetry(options, attempt);
|
|
1098
|
+
}
|
|
914
1099
|
function resolveConcurrency2(value, total) {
|
|
915
1100
|
if (value === void 0) return 1;
|
|
916
1101
|
if (value === Infinity) return Math.max(1, total);
|
|
@@ -920,7 +1105,8 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
920
1105
|
const retry = options ? {
|
|
921
1106
|
maxRetries: Math.max(0, Math.floor(options.maxRetries)),
|
|
922
1107
|
initialDelayMs: options.initialDelayMs,
|
|
923
|
-
maxDelayMs: options.maxDelayMs
|
|
1108
|
+
maxDelayMs: options.maxDelayMs,
|
|
1109
|
+
shouldRetry: options.shouldRetry
|
|
924
1110
|
} : void 0;
|
|
925
1111
|
let attempt = 0;
|
|
926
1112
|
while (true) {
|
|
@@ -928,9 +1114,10 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
928
1114
|
try {
|
|
929
1115
|
return await synthesize();
|
|
930
1116
|
} catch (error) {
|
|
931
|
-
if (!retry || attempt >= retry.maxRetries || !
|
|
1117
|
+
if (!retry || attempt >= retry.maxRetries || !(retry.shouldRetry?.(error, attempt + 1) ?? isRetryable(error)))
|
|
1118
|
+
throw error;
|
|
932
1119
|
attempt += 1;
|
|
933
|
-
const delayMs =
|
|
1120
|
+
const delayMs = retryDelayForError(retry, attempt, error);
|
|
934
1121
|
onRetry(attempt, delayMs);
|
|
935
1122
|
if (delayMs > 0)
|
|
936
1123
|
await new Promise((resolve, reject) => {
|
|
@@ -954,7 +1141,7 @@ function sharedValidationOptions(options, signal) {
|
|
|
954
1141
|
const runner = createAzureUrlValidatorRunner(validator, {
|
|
955
1142
|
...options.urlValidation ?? {},
|
|
956
1143
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
957
|
-
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1144
|
+
...options.timeouts?.urlValidationMs !== void 0 ? { timeoutMs: options.timeouts.urlValidationMs } : options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
958
1145
|
...signal ? { signal } : {},
|
|
959
1146
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
960
1147
|
});
|
|
@@ -983,7 +1170,11 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
983
1170
|
ok: true,
|
|
984
1171
|
success: true,
|
|
985
1172
|
status: "success",
|
|
986
|
-
value: await client.synthesizeSsml(ssml, {
|
|
1173
|
+
value: await client.synthesizeSsml(ssml, {
|
|
1174
|
+
signal: options.signal,
|
|
1175
|
+
timeoutMs: options.timeouts?.perChunkMs,
|
|
1176
|
+
timeouts: options.timeouts
|
|
1177
|
+
})
|
|
987
1178
|
};
|
|
988
1179
|
} catch (error) {
|
|
989
1180
|
const synthesisError = toSynthesisError(error);
|
|
@@ -991,7 +1182,10 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
991
1182
|
}
|
|
992
1183
|
}
|
|
993
1184
|
async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
994
|
-
const validationOptions = sharedValidationOptions(
|
|
1185
|
+
const validationOptions = sharedValidationOptions(
|
|
1186
|
+
{ ...options.validation ?? options, timeouts: options.timeouts },
|
|
1187
|
+
options.signal
|
|
1188
|
+
);
|
|
995
1189
|
if (options.signal?.aborted) {
|
|
996
1190
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
997
1191
|
return failure(error);
|
|
@@ -1025,16 +1219,17 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1025
1219
|
return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
1026
1220
|
})
|
|
1027
1221
|
);
|
|
1028
|
-
const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
|
|
1029
1222
|
if (options.signal?.aborted) {
|
|
1030
1223
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
1031
1224
|
return failure(error);
|
|
1032
1225
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1226
|
+
const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
|
|
1227
|
+
if (chunkDiagnostics.length > 0) {
|
|
1228
|
+
const error = new BatchChunkValidationError(chunkDiagnostics);
|
|
1229
|
+
for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
|
|
1036
1230
|
return failure(error);
|
|
1037
1231
|
}
|
|
1232
|
+
let fallbackJobScope;
|
|
1038
1233
|
try {
|
|
1039
1234
|
if (client.synthesizeChunks) {
|
|
1040
1235
|
const normalizedChunks = chunks.map((chunk) => {
|
|
@@ -1046,20 +1241,39 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1046
1241
|
outputFormat: options.outputFormat,
|
|
1047
1242
|
signal: options.signal,
|
|
1048
1243
|
timeoutMs: options.timeoutMs,
|
|
1244
|
+
timeouts: options.timeouts,
|
|
1049
1245
|
sourceNodePath: options.sourceNodePath,
|
|
1050
1246
|
concurrency: options.concurrency,
|
|
1051
|
-
retryOptions: options.retryOptions
|
|
1247
|
+
retryOptions: options.retryOptions,
|
|
1248
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
1249
|
+
resumeChunks: options.resumeChunks,
|
|
1250
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1251
|
+
customMerger: options.customMerger,
|
|
1252
|
+
outputMimeType: options.outputMimeType,
|
|
1253
|
+
postMergeValidator: options.postMergeValidator
|
|
1052
1254
|
});
|
|
1053
1255
|
return { ok: true, success: true, status: "success", value };
|
|
1054
1256
|
}
|
|
1055
1257
|
const results = new Array(chunks.length);
|
|
1056
|
-
|
|
1258
|
+
const cachedChunks = new Map((options.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
1259
|
+
for (const [index, cached] of cachedChunks) {
|
|
1260
|
+
if (index >= 0 && index < chunks.length) results[index] = cached;
|
|
1261
|
+
}
|
|
1262
|
+
const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
|
|
1263
|
+
const shouldSynthesize = (index) => !cachedChunks.has(index) && (requestedIndices === void 0 || requestedIndices.has(index));
|
|
1264
|
+
const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
|
|
1265
|
+
fallbackJobScope = jobScope;
|
|
1266
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
1267
|
+
let firstError;
|
|
1268
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
1057
1269
|
let nextIndex = 0;
|
|
1058
1270
|
const concurrency = resolveConcurrency2(options.concurrency, chunks.length);
|
|
1059
1271
|
const worker = async () => {
|
|
1060
1272
|
while (true) {
|
|
1061
1273
|
const index = nextIndex++;
|
|
1062
1274
|
if (index >= chunks.length) return;
|
|
1275
|
+
if (!shouldSynthesize(index)) continue;
|
|
1276
|
+
if (failedIndices.size > 0 && options.cancelOnFailure !== false) return;
|
|
1063
1277
|
const chunk = chunks[index];
|
|
1064
1278
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
1065
1279
|
const sourceNodePath = input.sourceNodePath;
|
|
@@ -1067,28 +1281,40 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1067
1281
|
pending(index, "synthesizing");
|
|
1068
1282
|
const startedAt = Date.now();
|
|
1069
1283
|
try {
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1284
|
+
const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
|
|
1285
|
+
const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
|
|
1286
|
+
const chunkSignal = chunkScope?.signal ?? options.signal;
|
|
1287
|
+
let result;
|
|
1288
|
+
try {
|
|
1289
|
+
result = await retryableSynthesis(
|
|
1290
|
+
() => client.synthesizeSsml(input.ssml, {
|
|
1291
|
+
outputFormat: options.outputFormat,
|
|
1292
|
+
signal: chunkSignal,
|
|
1293
|
+
timeoutMs: options.timeouts?.perChunkMs ?? options.timeoutMs,
|
|
1294
|
+
sourceNodePath: input.sourceNodePath ?? options.sourceNodePath
|
|
1295
|
+
}),
|
|
1296
|
+
options.retryOptions,
|
|
1297
|
+
chunkSignal,
|
|
1298
|
+
(retryAttempt, nextRetryDelayMs) => options.onProgress?.({
|
|
1299
|
+
currentChunk: completed,
|
|
1300
|
+
totalChunks: chunks.length,
|
|
1301
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
1302
|
+
chunkIndex: index,
|
|
1303
|
+
originalTextRange: input.originalTextRange,
|
|
1304
|
+
status: "synthesizing",
|
|
1305
|
+
durationMs: Date.now() - startedAt,
|
|
1306
|
+
retryAttempt,
|
|
1307
|
+
nextRetryDelayMs,
|
|
1308
|
+
isRetrying: true
|
|
1309
|
+
})
|
|
1310
|
+
);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
if (chunkScope?.timedOut())
|
|
1313
|
+
throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
|
|
1314
|
+
throw error;
|
|
1315
|
+
} finally {
|
|
1316
|
+
chunkScope?.dispose();
|
|
1317
|
+
}
|
|
1092
1318
|
results[index] = {
|
|
1093
1319
|
...result,
|
|
1094
1320
|
...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
|
|
@@ -1143,6 +1369,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1143
1369
|
durationMs: Date.now() - startedAt
|
|
1144
1370
|
});
|
|
1145
1371
|
} catch (error) {
|
|
1372
|
+
failedIndices.add(index);
|
|
1146
1373
|
options.onProgress?.({
|
|
1147
1374
|
currentChunk: completed,
|
|
1148
1375
|
totalChunks: chunks.length,
|
|
@@ -1153,24 +1380,42 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1153
1380
|
durationMs: Date.now() - startedAt,
|
|
1154
1381
|
error
|
|
1155
1382
|
});
|
|
1156
|
-
|
|
1383
|
+
if (options.cancelOnFailure !== false) jobScope?.abort();
|
|
1384
|
+
firstError ?? (firstError = error);
|
|
1385
|
+
return;
|
|
1157
1386
|
}
|
|
1158
1387
|
}
|
|
1159
1388
|
};
|
|
1160
1389
|
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
1390
|
+
if (failedIndices.size > 0) {
|
|
1391
|
+
const error = firstError ?? new Error("One or more SSML chunks failed to synthesize.");
|
|
1392
|
+
error.partialResult = {
|
|
1393
|
+
synthesizedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
1394
|
+
completedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
1395
|
+
pendingChunkIndices: chunks.flatMap((_chunk, chunkIndex) => results[chunkIndex] ? [] : [chunkIndex]),
|
|
1396
|
+
failedChunkIndices: [...failedIndices],
|
|
1397
|
+
totalChunks: chunks.length
|
|
1398
|
+
};
|
|
1399
|
+
throw error;
|
|
1400
|
+
}
|
|
1161
1401
|
const orderedResults = results.filter((result) => result !== void 0);
|
|
1162
1402
|
return {
|
|
1163
1403
|
ok: true,
|
|
1164
1404
|
success: true,
|
|
1165
1405
|
status: "success",
|
|
1166
|
-
value: mergeSynthesisResults(orderedResults, {
|
|
1406
|
+
value: await mergeSynthesisResults(orderedResults, {
|
|
1167
1407
|
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
|
|
1168
|
-
signal: options.signal
|
|
1408
|
+
signal: jobScope?.signal ?? options.signal,
|
|
1409
|
+
customMerger: options.customMerger,
|
|
1410
|
+
outputMimeType: options.outputMimeType,
|
|
1411
|
+
postMergeValidator: options.postMergeValidator
|
|
1169
1412
|
})
|
|
1170
1413
|
};
|
|
1171
1414
|
} catch (error) {
|
|
1172
1415
|
const synthesisError = toSynthesisError(error);
|
|
1173
|
-
return failure(synthesisError);
|
|
1416
|
+
return failure(synthesisError, partialResultFrom(error));
|
|
1417
|
+
} finally {
|
|
1418
|
+
fallbackJobScope?.dispose();
|
|
1174
1419
|
}
|
|
1175
1420
|
}
|
|
1176
1421
|
function withValidationSignal(options, signal) {
|
|
@@ -1191,14 +1436,14 @@ var AzureTtsClient = class {
|
|
|
1191
1436
|
__privateSet(this, _options, options);
|
|
1192
1437
|
}
|
|
1193
1438
|
async synthesize(ssml) {
|
|
1194
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1439
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1195
1440
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1196
1441
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1197
|
-
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
1442
|
+
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts };
|
|
1198
1443
|
return synthesizeSpeech(ssml, config);
|
|
1199
1444
|
}
|
|
1200
1445
|
async synthesizeSsml(ssml, options = {}) {
|
|
1201
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1446
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1202
1447
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1203
1448
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1204
1449
|
return synthesizeSsml(ssml, {
|
|
@@ -1208,13 +1453,14 @@ var AzureTtsClient = class {
|
|
|
1208
1453
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1209
1454
|
signal: options.signal ?? signal,
|
|
1210
1455
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1456
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1211
1457
|
sourceNodePath: options.sourceNodePath,
|
|
1212
1458
|
sourceTextSegments: options.sourceTextSegments,
|
|
1213
1459
|
sourceMarkers: options.sourceMarkers
|
|
1214
1460
|
});
|
|
1215
1461
|
}
|
|
1216
1462
|
async synthesizeChunks(chunks, options = {}) {
|
|
1217
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1463
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1218
1464
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1219
1465
|
return synthesizeSsmlChunks(chunks, {
|
|
1220
1466
|
endpoint,
|
|
@@ -1223,10 +1469,17 @@ var AzureTtsClient = class {
|
|
|
1223
1469
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1224
1470
|
signal: options.signal ?? signal,
|
|
1225
1471
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1472
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1226
1473
|
sourceNodePath: options.sourceNodePath,
|
|
1227
1474
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1228
1475
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1229
|
-
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
1476
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
1477
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
1478
|
+
resumeChunks: options.resumeChunks,
|
|
1479
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1480
|
+
customMerger: options.customMerger,
|
|
1481
|
+
outputMimeType: options.outputMimeType,
|
|
1482
|
+
postMergeValidator: options.postMergeValidator
|
|
1230
1483
|
});
|
|
1231
1484
|
}
|
|
1232
1485
|
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
@@ -1238,6 +1491,7 @@ var AzureTtsClient = class {
|
|
|
1238
1491
|
outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
|
|
1239
1492
|
signal: options.signal ?? __privateGet(this, _options).signal,
|
|
1240
1493
|
timeoutMs: options.timeoutMs ?? __privateGet(this, _options).timeoutMs,
|
|
1494
|
+
timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
|
|
1241
1495
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1242
1496
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1243
1497
|
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
@@ -1331,7 +1585,9 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
1331
1585
|
voiceCount: sortedVoices.length,
|
|
1332
1586
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1333
1587
|
apiVersion: AZURE_VOICE_API_VERSION,
|
|
1334
|
-
regions
|
|
1588
|
+
regions,
|
|
1589
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1590
|
+
regionDiffs: {}
|
|
1335
1591
|
}
|
|
1336
1592
|
};
|
|
1337
1593
|
}
|
|
@@ -1340,6 +1596,7 @@ export {
|
|
|
1340
1596
|
AzureTtsClient,
|
|
1341
1597
|
AzureTtsError,
|
|
1342
1598
|
AzureTtsSdkError,
|
|
1599
|
+
BatchChunkValidationError,
|
|
1343
1600
|
ChunkValidationError,
|
|
1344
1601
|
DEFAULT_OUTPUT_FORMAT,
|
|
1345
1602
|
MergeError,
|
|
@@ -1348,6 +1605,7 @@ export {
|
|
|
1348
1605
|
UnsupportedMergeFormatError,
|
|
1349
1606
|
canMergeAudioFormat,
|
|
1350
1607
|
fetchAzureVoiceCatalog,
|
|
1608
|
+
getRetryAfterDelayMs,
|
|
1351
1609
|
inspectAudioSpecification,
|
|
1352
1610
|
mergeAudioBuffers,
|
|
1353
1611
|
mergeSynthesisResults,
|