@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.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
AzureTtsClient: () => AzureTtsClient,
|
|
42
42
|
AzureTtsError: () => AzureTtsError,
|
|
43
43
|
AzureTtsSdkError: () => AzureTtsSdkError,
|
|
44
|
+
BatchChunkValidationError: () => BatchChunkValidationError,
|
|
44
45
|
ChunkValidationError: () => ChunkValidationError,
|
|
45
46
|
DEFAULT_OUTPUT_FORMAT: () => DEFAULT_OUTPUT_FORMAT,
|
|
46
47
|
MergeError: () => MergeError,
|
|
@@ -49,6 +50,7 @@ __export(index_exports, {
|
|
|
49
50
|
UnsupportedMergeFormatError: () => UnsupportedMergeFormatError,
|
|
50
51
|
canMergeAudioFormat: () => canMergeAudioFormat,
|
|
51
52
|
fetchAzureVoiceCatalog: () => fetchAzureVoiceCatalog,
|
|
53
|
+
getRetryAfterDelayMs: () => getRetryAfterDelayMs,
|
|
52
54
|
inspectAudioSpecification: () => inspectAudioSpecification,
|
|
53
55
|
mergeAudioBuffers: () => mergeAudioBuffers,
|
|
54
56
|
mergeSynthesisResults: () => mergeSynthesisResults,
|
|
@@ -64,7 +66,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
64
66
|
|
|
65
67
|
// src/errors.ts
|
|
66
68
|
var AzureTtsError = class extends Error {
|
|
67
|
-
constructor(status, statusText, responseBody, requestId) {
|
|
69
|
+
constructor(status, statusText, responseBody, requestId, responseHeaders) {
|
|
68
70
|
super(`Azure TTS request failed: ${status} ${statusText}`);
|
|
69
71
|
this.kind = "azure-api-error";
|
|
70
72
|
this.name = "AzureTtsError";
|
|
@@ -72,8 +74,37 @@ var AzureTtsError = class extends Error {
|
|
|
72
74
|
this.statusText = statusText;
|
|
73
75
|
this.responseBody = responseBody;
|
|
74
76
|
this.requestId = requestId;
|
|
77
|
+
const value = responseHeaders instanceof Headers ? responseHeaders.get("retry-after") : responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"];
|
|
78
|
+
const seconds = value ? Number(value.trim()) : NaN;
|
|
79
|
+
const date = value ? Date.parse(value) : NaN;
|
|
80
|
+
if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1e3;
|
|
81
|
+
else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
|
|
75
82
|
}
|
|
76
83
|
};
|
|
84
|
+
function getRetryAfterDelayMs(error) {
|
|
85
|
+
if (error instanceof AzureTtsError && error.retryAfterMs !== void 0) return error.retryAfterMs;
|
|
86
|
+
if (!error || typeof error !== "object") return void 0;
|
|
87
|
+
const candidate = error;
|
|
88
|
+
if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
|
|
89
|
+
const headers = candidate.headers ?? candidate.response?.headers;
|
|
90
|
+
if (headers instanceof Headers) {
|
|
91
|
+
const value = headers.get("retry-after");
|
|
92
|
+
if (!value) return void 0;
|
|
93
|
+
const seconds = Number(value.trim());
|
|
94
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
95
|
+
const date = Date.parse(value);
|
|
96
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
97
|
+
}
|
|
98
|
+
if (headers && typeof headers === "object") {
|
|
99
|
+
const value = headers["retry-after"] ?? headers["Retry-After"];
|
|
100
|
+
if (typeof value !== "string") return void 0;
|
|
101
|
+
const seconds = Number(value.trim());
|
|
102
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
103
|
+
const date = Date.parse(value);
|
|
104
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
105
|
+
}
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
77
108
|
var AzureTtsSdkError = class extends AzureTtsError {
|
|
78
109
|
constructor(errorDetails) {
|
|
79
110
|
super(0, "Speech SDK", errorDetails, null);
|
|
@@ -266,6 +297,8 @@ function formatAudioSpecification(format) {
|
|
|
266
297
|
const bitrateMatch = /(\d+)(?:kbitrate|kbps|kbits?)/i.exec(format);
|
|
267
298
|
const bitrate = bitrateMatch?.[1] ? Number(bitrateMatch[1]) * 1e3 : void 0;
|
|
268
299
|
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";
|
|
300
|
+
const bitDepthMatch = /(\d+)bit/i.exec(format);
|
|
301
|
+
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;
|
|
269
302
|
return {
|
|
270
303
|
format,
|
|
271
304
|
mimeType: resolveMimeType(format),
|
|
@@ -273,6 +306,9 @@ function formatAudioSpecification(format) {
|
|
|
273
306
|
sampleRate,
|
|
274
307
|
channels,
|
|
275
308
|
...bitrate ? { bitrate } : {},
|
|
309
|
+
...bitDepthMatch?.[1] ? { bitDepth: Number(bitDepthMatch[1]) } : {},
|
|
310
|
+
...container ? { container } : {},
|
|
311
|
+
isVbr: /vbr/i.test(format),
|
|
276
312
|
isCompressed: codec === "mp3" || codec === "opus" || codec === "silk"
|
|
277
313
|
};
|
|
278
314
|
}
|
|
@@ -308,6 +344,8 @@ function parseMp3Specification(buffer, format) {
|
|
|
308
344
|
sampleRate,
|
|
309
345
|
channels: (bytes[index + 3] ?? 0) >> 6 === 3 ? 1 : 2,
|
|
310
346
|
bitrate: bitrateKbps * 1e3,
|
|
347
|
+
container: "mp3-raw",
|
|
348
|
+
isVbr: false,
|
|
311
349
|
isCompressed: true
|
|
312
350
|
};
|
|
313
351
|
}
|
|
@@ -329,6 +367,9 @@ function inspectAudioSpecification(buffer, format) {
|
|
|
329
367
|
sampleRate,
|
|
330
368
|
channels,
|
|
331
369
|
...sampleRate && channels && bitsPerSample ? { bitrate: sampleRate * channels * bitsPerSample } : {},
|
|
370
|
+
bitDepth: bitsPerSample,
|
|
371
|
+
container: "riff-wave",
|
|
372
|
+
isVbr: false,
|
|
332
373
|
isCompressed: formatCode !== 1
|
|
333
374
|
};
|
|
334
375
|
}
|
|
@@ -339,7 +380,7 @@ function validateAudioSpecifications(specs) {
|
|
|
339
380
|
const first = specs[0];
|
|
340
381
|
if (!first) return;
|
|
341
382
|
const mismatch = specs.find(
|
|
342
|
-
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate
|
|
383
|
+
(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
|
|
343
384
|
);
|
|
344
385
|
if (mismatch)
|
|
345
386
|
throw new AudioFormatMismatchError(
|
|
@@ -542,9 +583,7 @@ async function synthesizeSsml(ssml, config) {
|
|
|
542
583
|
};
|
|
543
584
|
}
|
|
544
585
|
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath) {
|
|
545
|
-
|
|
546
|
-
Object.defineProperty(unmapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
547
|
-
return unmapped;
|
|
586
|
+
return { mappingStatus: "unmapped" };
|
|
548
587
|
}
|
|
549
588
|
const value = text ?? "";
|
|
550
589
|
let localStart = Number.isFinite(offsetHint) && (offsetHint ?? 0) >= 0 ? offsetHint : -1;
|
|
@@ -626,8 +665,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
626
665
|
...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
627
666
|
...requestId ? { requestId } : {}
|
|
628
667
|
};
|
|
629
|
-
if (event.mappingStatus === "unmapped")
|
|
668
|
+
if (event.mappingStatus === "unmapped") {
|
|
630
669
|
Object.defineProperty(mapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
670
|
+
Object.defineProperty(mapped, "toJSON", {
|
|
671
|
+
value: () => ({ ...mapped, mappingStatus: "unmapped" }),
|
|
672
|
+
enumerable: false
|
|
673
|
+
});
|
|
674
|
+
}
|
|
631
675
|
return mapped;
|
|
632
676
|
};
|
|
633
677
|
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
@@ -650,10 +694,11 @@ async function synthesizeSsml(ssml, config) {
|
|
|
650
694
|
abortHandler = () => rejectWithError(new SynthesisCancelledError());
|
|
651
695
|
config.signal.addEventListener("abort", abortHandler, { once: true });
|
|
652
696
|
}
|
|
653
|
-
|
|
697
|
+
const timeoutMs = config.timeouts?.perChunkMs ?? config.timeoutMs;
|
|
698
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
654
699
|
timeout = setTimeout(
|
|
655
|
-
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${
|
|
656
|
-
|
|
700
|
+
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`)),
|
|
701
|
+
timeoutMs
|
|
657
702
|
);
|
|
658
703
|
}
|
|
659
704
|
synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);
|
|
@@ -672,7 +717,9 @@ function isRetryableSynthesisError(error) {
|
|
|
672
717
|
if (typeof status === "number") return status === 429 || status >= 500 && status < 600;
|
|
673
718
|
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
674
719
|
}
|
|
675
|
-
function retryDelay(options, retryAttempt) {
|
|
720
|
+
function retryDelay(options, retryAttempt, error) {
|
|
721
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
722
|
+
if (retryAfterMs !== void 0) return retryAfterMs;
|
|
676
723
|
const base = Math.min(options.maxDelayMs, options.initialDelayMs * 2 ** Math.max(0, retryAttempt - 1));
|
|
677
724
|
return Math.floor(Math.random() * (base + 1));
|
|
678
725
|
}
|
|
@@ -704,7 +751,8 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
|
704
751
|
const options = retryOptions ? {
|
|
705
752
|
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
706
753
|
initialDelayMs: Math.max(0, retryOptions.initialDelayMs),
|
|
707
|
-
maxDelayMs: Math.max(0, retryOptions.maxDelayMs)
|
|
754
|
+
maxDelayMs: Math.max(0, retryOptions.maxDelayMs),
|
|
755
|
+
shouldRetry: retryOptions.shouldRetry
|
|
708
756
|
} : void 0;
|
|
709
757
|
let attempt = 0;
|
|
710
758
|
while (true) {
|
|
@@ -712,17 +760,56 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
|
712
760
|
try {
|
|
713
761
|
return await synthesizeSsml(ssml, config);
|
|
714
762
|
} catch (error) {
|
|
715
|
-
if (!options || attempt >= options.maxRetries || !
|
|
763
|
+
if (!options || attempt >= options.maxRetries || !(options.shouldRetry?.(error, attempt + 1) ?? isRetryableSynthesisError(error)))
|
|
764
|
+
throw error;
|
|
716
765
|
attempt += 1;
|
|
717
|
-
const delayMs = retryDelay(options, attempt);
|
|
766
|
+
const delayMs = retryDelay(options, attempt, error);
|
|
718
767
|
onRetry(attempt, delayMs);
|
|
719
768
|
await waitForRetry(delayMs, config.signal);
|
|
720
769
|
}
|
|
721
770
|
}
|
|
722
771
|
}
|
|
772
|
+
function createAbortScope(parent, timeoutMs) {
|
|
773
|
+
const controller = new AbortController();
|
|
774
|
+
let didTimeout = false;
|
|
775
|
+
const onAbort = () => controller.abort();
|
|
776
|
+
if (parent?.aborted) controller.abort();
|
|
777
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
778
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
779
|
+
didTimeout = true;
|
|
780
|
+
controller.abort();
|
|
781
|
+
}, timeoutMs) : void 0;
|
|
782
|
+
return {
|
|
783
|
+
signal: controller.signal,
|
|
784
|
+
timedOut: () => didTimeout,
|
|
785
|
+
dispose: () => {
|
|
786
|
+
if (timer) clearTimeout(timer);
|
|
787
|
+
parent?.removeEventListener("abort", onAbort);
|
|
788
|
+
},
|
|
789
|
+
abort: () => controller.abort()
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
async function synthesizeChunkWithTimeout(ssml, config, retryOptions, timeoutMs, onRetry) {
|
|
793
|
+
const scope = createAbortScope(config.signal, timeoutMs);
|
|
794
|
+
try {
|
|
795
|
+
return await synthesizeWithRetry(ssml, { ...config, signal: scope.signal }, retryOptions, onRetry);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
if (scope.timedOut()) throw new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`);
|
|
798
|
+
throw error;
|
|
799
|
+
} finally {
|
|
800
|
+
scope.dispose();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
723
803
|
async function synthesizeSsmlChunks(chunks, config) {
|
|
724
804
|
const results = new Array(chunks.length);
|
|
725
805
|
const totalChunks = chunks.length;
|
|
806
|
+
const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
807
|
+
for (const [index, cached] of cachedChunks) {
|
|
808
|
+
if (index >= 0 && index < totalChunks) results[index] = cached;
|
|
809
|
+
}
|
|
810
|
+
const requestedIndices = config.resumeChunkIndices ? new Set(config.resumeChunkIndices.filter((index) => index >= 0 && index < totalChunks)) : void 0;
|
|
811
|
+
const shouldSynthesize = (index) => !cachedChunks.has(index) && (requestedIndices === void 0 || requestedIndices.has(index));
|
|
812
|
+
const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
|
|
726
813
|
const report = (event) => config.onProgress?.(event);
|
|
727
814
|
for (const [index, chunk] of chunks.entries()) {
|
|
728
815
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
@@ -736,13 +823,17 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
736
823
|
durationMs: 0
|
|
737
824
|
});
|
|
738
825
|
}
|
|
739
|
-
let completed = 0;
|
|
826
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
740
827
|
let nextIndex = 0;
|
|
741
828
|
const concurrency = resolveConcurrency(config.concurrency, chunks.length);
|
|
829
|
+
let firstError;
|
|
830
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
742
831
|
const worker = async () => {
|
|
743
832
|
while (true) {
|
|
744
833
|
const index = nextIndex++;
|
|
745
834
|
if (index >= chunks.length) return;
|
|
835
|
+
if (!shouldSynthesize(index)) continue;
|
|
836
|
+
if (firstError && config.cancelOnFailure !== false) return;
|
|
746
837
|
const chunk = chunks[index];
|
|
747
838
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
748
839
|
report({
|
|
@@ -756,10 +847,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
756
847
|
});
|
|
757
848
|
const startedAt = Date.now();
|
|
758
849
|
try {
|
|
759
|
-
const result = await
|
|
850
|
+
const result = await synthesizeChunkWithTimeout(
|
|
760
851
|
input.ssml,
|
|
761
852
|
{
|
|
762
853
|
...config,
|
|
854
|
+
signal: scope.signal,
|
|
763
855
|
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
764
856
|
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
765
857
|
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
@@ -768,6 +860,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
768
860
|
onProgress: void 0
|
|
769
861
|
},
|
|
770
862
|
config.retryOptions,
|
|
863
|
+
config.timeouts?.chunkWithRetriesMs ?? config.timeouts?.perChunkMs ?? config.timeoutMs,
|
|
771
864
|
(retryAttempt, nextRetryDelayMs) => report({
|
|
772
865
|
currentChunk: completed,
|
|
773
866
|
totalChunks,
|
|
@@ -793,6 +886,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
793
886
|
durationMs: Date.now() - startedAt
|
|
794
887
|
});
|
|
795
888
|
} catch (error) {
|
|
889
|
+
failedIndices.add(index);
|
|
796
890
|
report({
|
|
797
891
|
currentChunk: completed,
|
|
798
892
|
totalChunks,
|
|
@@ -803,16 +897,36 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
803
897
|
durationMs: Date.now() - startedAt,
|
|
804
898
|
error
|
|
805
899
|
});
|
|
806
|
-
|
|
900
|
+
firstError ?? (firstError = scope.timedOut() ? new SynthesisTimeoutError(`Speech synthesis timed out after ${config.timeouts?.totalJobMs} ms.`) : error);
|
|
901
|
+
if (config.cancelOnFailure !== false) scope.abort();
|
|
902
|
+
return;
|
|
807
903
|
}
|
|
808
904
|
}
|
|
809
905
|
};
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
906
|
+
try {
|
|
907
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
908
|
+
if (firstError) throw firstError;
|
|
909
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
910
|
+
return await mergeSynthesisResults(orderedResults, {
|
|
911
|
+
format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
|
|
912
|
+
signal: scope.signal,
|
|
913
|
+
customMerger: config.customMerger,
|
|
914
|
+
outputMimeType: config.outputMimeType,
|
|
915
|
+
postMergeValidator: config.postMergeValidator
|
|
916
|
+
});
|
|
917
|
+
} catch (error) {
|
|
918
|
+
const partial = {
|
|
919
|
+
synthesizedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
920
|
+
completedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
921
|
+
pendingChunkIndices: chunks.flatMap((_chunk, chunkIndex) => results[chunkIndex] ? [] : [chunkIndex]),
|
|
922
|
+
failedChunkIndices: [...failedIndices],
|
|
923
|
+
totalChunks
|
|
924
|
+
};
|
|
925
|
+
if (error && typeof error === "object") error.partialResult = partial;
|
|
926
|
+
throw error;
|
|
927
|
+
} finally {
|
|
928
|
+
scope.dispose();
|
|
929
|
+
}
|
|
816
930
|
}
|
|
817
931
|
function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
|
|
818
932
|
const boundaries = [];
|
|
@@ -906,13 +1020,24 @@ function mergeSynthesisResults(results, options) {
|
|
|
906
1020
|
if (!(merged instanceof ArrayBuffer) || buffers.some((buffer) => buffer.byteLength > 0) && merged.byteLength === 0)
|
|
907
1021
|
throw new MergeError("The custom audio merger returned an invalid audio buffer.");
|
|
908
1022
|
if (signal.aborted) throw new SynthesisCancelledError();
|
|
909
|
-
|
|
1023
|
+
const result = createMergedResult(
|
|
910
1024
|
results,
|
|
911
1025
|
merged,
|
|
912
1026
|
format,
|
|
913
1027
|
inspectAudioSpecification(merged, format),
|
|
914
1028
|
resolvedOptions.outputMimeType
|
|
915
1029
|
);
|
|
1030
|
+
return Promise.resolve(
|
|
1031
|
+
resolvedOptions.postMergeValidator?.(result, {
|
|
1032
|
+
format,
|
|
1033
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
1034
|
+
inputSpecs,
|
|
1035
|
+
signal
|
|
1036
|
+
})
|
|
1037
|
+
).then((valid) => {
|
|
1038
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1039
|
+
return result;
|
|
1040
|
+
});
|
|
916
1041
|
}).catch((error) => {
|
|
917
1042
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
918
1043
|
throw error;
|
|
@@ -920,13 +1045,28 @@ function mergeSynthesisResults(results, options) {
|
|
|
920
1045
|
});
|
|
921
1046
|
}
|
|
922
1047
|
try {
|
|
923
|
-
|
|
1048
|
+
const result = createMergedResult(
|
|
924
1049
|
results,
|
|
925
1050
|
mergeAudioBuffers(buffers, { format }),
|
|
926
1051
|
format,
|
|
927
1052
|
inputSpecs[0],
|
|
928
1053
|
resolvedOptions.outputMimeType
|
|
929
1054
|
);
|
|
1055
|
+
if (resolvedOptions.postMergeValidator) {
|
|
1056
|
+
const validation = resolvedOptions.postMergeValidator(result, {
|
|
1057
|
+
format,
|
|
1058
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
1059
|
+
inputSpecs,
|
|
1060
|
+
signal
|
|
1061
|
+
});
|
|
1062
|
+
if (validation instanceof Promise)
|
|
1063
|
+
return validation.then((valid) => {
|
|
1064
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1065
|
+
return result;
|
|
1066
|
+
});
|
|
1067
|
+
if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
1068
|
+
}
|
|
1069
|
+
return result;
|
|
930
1070
|
} catch (error) {
|
|
931
1071
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
932
1072
|
throw error;
|
|
@@ -948,8 +1088,52 @@ var ChunkValidationError = class extends Error {
|
|
|
948
1088
|
this.diagnostics = diagnostics;
|
|
949
1089
|
}
|
|
950
1090
|
};
|
|
951
|
-
|
|
952
|
-
|
|
1091
|
+
var BatchChunkValidationError = class extends ChunkValidationError {
|
|
1092
|
+
constructor(chunkDiagnostics) {
|
|
1093
|
+
const first = chunkDiagnostics[0];
|
|
1094
|
+
super(first?.chunkIndex ?? -1, first?.diagnostics ?? []);
|
|
1095
|
+
this.name = "BatchChunkValidationError";
|
|
1096
|
+
this.message = `SSML validation failed for ${chunkDiagnostics.length} chunk(s); the Azure Speech API was not called.`;
|
|
1097
|
+
this.chunkDiagnostics = chunkDiagnostics;
|
|
1098
|
+
this.totalErrorCount = chunkDiagnostics.reduce((total, chunk) => total + chunk.diagnostics.length, 0);
|
|
1099
|
+
this.errorCount = this.totalErrorCount;
|
|
1100
|
+
this.totalErrors = this.totalErrorCount;
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
function failure(error, partialResult) {
|
|
1104
|
+
return {
|
|
1105
|
+
ok: false,
|
|
1106
|
+
success: false,
|
|
1107
|
+
status: error.kind,
|
|
1108
|
+
error,
|
|
1109
|
+
...partialResult ? { partialResult } : {}
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
function partialResultFrom(error) {
|
|
1113
|
+
if (!error || typeof error !== "object") return void 0;
|
|
1114
|
+
const partial = error.partialResult;
|
|
1115
|
+
if (!partial || typeof partial !== "object") return void 0;
|
|
1116
|
+
return partial;
|
|
1117
|
+
}
|
|
1118
|
+
function createSafeAbortScope(parent, timeoutMs) {
|
|
1119
|
+
const controller = new AbortController();
|
|
1120
|
+
let didTimeout = false;
|
|
1121
|
+
const onAbort = () => controller.abort();
|
|
1122
|
+
if (parent?.aborted) controller.abort();
|
|
1123
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
1124
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
1125
|
+
didTimeout = true;
|
|
1126
|
+
controller.abort();
|
|
1127
|
+
}, timeoutMs) : void 0;
|
|
1128
|
+
return {
|
|
1129
|
+
signal: controller.signal,
|
|
1130
|
+
timedOut: () => didTimeout,
|
|
1131
|
+
dispose: () => {
|
|
1132
|
+
if (timer) clearTimeout(timer);
|
|
1133
|
+
parent?.removeEventListener("abort", onAbort);
|
|
1134
|
+
},
|
|
1135
|
+
abort: () => controller.abort()
|
|
1136
|
+
};
|
|
953
1137
|
}
|
|
954
1138
|
function isRetryable(error) {
|
|
955
1139
|
if (error instanceof Error && /cancel|abort|tim(?:e|ed) ?out/i.test(error.message)) return false;
|
|
@@ -964,6 +1148,9 @@ function delayForRetry(options, attempt) {
|
|
|
964
1148
|
const base = Math.min(maxDelay, Math.max(0, options.initialDelayMs) * 2 ** Math.max(0, attempt - 1));
|
|
965
1149
|
return Math.floor(Math.random() * (base + 1));
|
|
966
1150
|
}
|
|
1151
|
+
function retryDelayForError(options, attempt, error) {
|
|
1152
|
+
return getRetryAfterDelayMs(error) ?? delayForRetry(options, attempt);
|
|
1153
|
+
}
|
|
967
1154
|
function resolveConcurrency2(value, total) {
|
|
968
1155
|
if (value === void 0) return 1;
|
|
969
1156
|
if (value === Infinity) return Math.max(1, total);
|
|
@@ -973,7 +1160,8 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
973
1160
|
const retry = options ? {
|
|
974
1161
|
maxRetries: Math.max(0, Math.floor(options.maxRetries)),
|
|
975
1162
|
initialDelayMs: options.initialDelayMs,
|
|
976
|
-
maxDelayMs: options.maxDelayMs
|
|
1163
|
+
maxDelayMs: options.maxDelayMs,
|
|
1164
|
+
shouldRetry: options.shouldRetry
|
|
977
1165
|
} : void 0;
|
|
978
1166
|
let attempt = 0;
|
|
979
1167
|
while (true) {
|
|
@@ -981,9 +1169,10 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
981
1169
|
try {
|
|
982
1170
|
return await synthesize();
|
|
983
1171
|
} catch (error) {
|
|
984
|
-
if (!retry || attempt >= retry.maxRetries || !
|
|
1172
|
+
if (!retry || attempt >= retry.maxRetries || !(retry.shouldRetry?.(error, attempt + 1) ?? isRetryable(error)))
|
|
1173
|
+
throw error;
|
|
985
1174
|
attempt += 1;
|
|
986
|
-
const delayMs =
|
|
1175
|
+
const delayMs = retryDelayForError(retry, attempt, error);
|
|
987
1176
|
onRetry(attempt, delayMs);
|
|
988
1177
|
if (delayMs > 0)
|
|
989
1178
|
await new Promise((resolve, reject) => {
|
|
@@ -1007,7 +1196,7 @@ function sharedValidationOptions(options, signal) {
|
|
|
1007
1196
|
const runner = (0, import_ssml_core2.createAzureUrlValidatorRunner)(validator, {
|
|
1008
1197
|
...options.urlValidation ?? {},
|
|
1009
1198
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
1010
|
-
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1199
|
+
...options.timeouts?.urlValidationMs !== void 0 ? { timeoutMs: options.timeouts.urlValidationMs } : options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1011
1200
|
...signal ? { signal } : {},
|
|
1012
1201
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
1013
1202
|
});
|
|
@@ -1036,7 +1225,11 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
1036
1225
|
ok: true,
|
|
1037
1226
|
success: true,
|
|
1038
1227
|
status: "success",
|
|
1039
|
-
value: await client.synthesizeSsml(ssml, {
|
|
1228
|
+
value: await client.synthesizeSsml(ssml, {
|
|
1229
|
+
signal: options.signal,
|
|
1230
|
+
timeoutMs: options.timeouts?.perChunkMs,
|
|
1231
|
+
timeouts: options.timeouts
|
|
1232
|
+
})
|
|
1040
1233
|
};
|
|
1041
1234
|
} catch (error) {
|
|
1042
1235
|
const synthesisError = toSynthesisError(error);
|
|
@@ -1044,7 +1237,10 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
1044
1237
|
}
|
|
1045
1238
|
}
|
|
1046
1239
|
async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
1047
|
-
const validationOptions = sharedValidationOptions(
|
|
1240
|
+
const validationOptions = sharedValidationOptions(
|
|
1241
|
+
{ ...options.validation ?? options, timeouts: options.timeouts },
|
|
1242
|
+
options.signal
|
|
1243
|
+
);
|
|
1048
1244
|
if (options.signal?.aborted) {
|
|
1049
1245
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
1050
1246
|
return failure(error);
|
|
@@ -1078,16 +1274,17 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1078
1274
|
return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
1079
1275
|
})
|
|
1080
1276
|
);
|
|
1081
|
-
const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
|
|
1082
1277
|
if (options.signal?.aborted) {
|
|
1083
1278
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
1084
1279
|
return failure(error);
|
|
1085
1280
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1281
|
+
const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
|
|
1282
|
+
if (chunkDiagnostics.length > 0) {
|
|
1283
|
+
const error = new BatchChunkValidationError(chunkDiagnostics);
|
|
1284
|
+
for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
|
|
1089
1285
|
return failure(error);
|
|
1090
1286
|
}
|
|
1287
|
+
let fallbackJobScope;
|
|
1091
1288
|
try {
|
|
1092
1289
|
if (client.synthesizeChunks) {
|
|
1093
1290
|
const normalizedChunks = chunks.map((chunk) => {
|
|
@@ -1099,20 +1296,39 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1099
1296
|
outputFormat: options.outputFormat,
|
|
1100
1297
|
signal: options.signal,
|
|
1101
1298
|
timeoutMs: options.timeoutMs,
|
|
1299
|
+
timeouts: options.timeouts,
|
|
1102
1300
|
sourceNodePath: options.sourceNodePath,
|
|
1103
1301
|
concurrency: options.concurrency,
|
|
1104
|
-
retryOptions: options.retryOptions
|
|
1302
|
+
retryOptions: options.retryOptions,
|
|
1303
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
1304
|
+
resumeChunks: options.resumeChunks,
|
|
1305
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1306
|
+
customMerger: options.customMerger,
|
|
1307
|
+
outputMimeType: options.outputMimeType,
|
|
1308
|
+
postMergeValidator: options.postMergeValidator
|
|
1105
1309
|
});
|
|
1106
1310
|
return { ok: true, success: true, status: "success", value };
|
|
1107
1311
|
}
|
|
1108
1312
|
const results = new Array(chunks.length);
|
|
1109
|
-
|
|
1313
|
+
const cachedChunks = new Map((options.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
1314
|
+
for (const [index, cached] of cachedChunks) {
|
|
1315
|
+
if (index >= 0 && index < chunks.length) results[index] = cached;
|
|
1316
|
+
}
|
|
1317
|
+
const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
|
|
1318
|
+
const shouldSynthesize = (index) => !cachedChunks.has(index) && (requestedIndices === void 0 || requestedIndices.has(index));
|
|
1319
|
+
const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
|
|
1320
|
+
fallbackJobScope = jobScope;
|
|
1321
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
1322
|
+
let firstError;
|
|
1323
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
1110
1324
|
let nextIndex = 0;
|
|
1111
1325
|
const concurrency = resolveConcurrency2(options.concurrency, chunks.length);
|
|
1112
1326
|
const worker = async () => {
|
|
1113
1327
|
while (true) {
|
|
1114
1328
|
const index = nextIndex++;
|
|
1115
1329
|
if (index >= chunks.length) return;
|
|
1330
|
+
if (!shouldSynthesize(index)) continue;
|
|
1331
|
+
if (failedIndices.size > 0 && options.cancelOnFailure !== false) return;
|
|
1116
1332
|
const chunk = chunks[index];
|
|
1117
1333
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
1118
1334
|
const sourceNodePath = input.sourceNodePath;
|
|
@@ -1120,28 +1336,40 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1120
1336
|
pending(index, "synthesizing");
|
|
1121
1337
|
const startedAt = Date.now();
|
|
1122
1338
|
try {
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1339
|
+
const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
|
|
1340
|
+
const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
|
|
1341
|
+
const chunkSignal = chunkScope?.signal ?? options.signal;
|
|
1342
|
+
let result;
|
|
1343
|
+
try {
|
|
1344
|
+
result = await retryableSynthesis(
|
|
1345
|
+
() => client.synthesizeSsml(input.ssml, {
|
|
1346
|
+
outputFormat: options.outputFormat,
|
|
1347
|
+
signal: chunkSignal,
|
|
1348
|
+
timeoutMs: options.timeouts?.perChunkMs ?? options.timeoutMs,
|
|
1349
|
+
sourceNodePath: input.sourceNodePath ?? options.sourceNodePath
|
|
1350
|
+
}),
|
|
1351
|
+
options.retryOptions,
|
|
1352
|
+
chunkSignal,
|
|
1353
|
+
(retryAttempt, nextRetryDelayMs) => options.onProgress?.({
|
|
1354
|
+
currentChunk: completed,
|
|
1355
|
+
totalChunks: chunks.length,
|
|
1356
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
1357
|
+
chunkIndex: index,
|
|
1358
|
+
originalTextRange: input.originalTextRange,
|
|
1359
|
+
status: "synthesizing",
|
|
1360
|
+
durationMs: Date.now() - startedAt,
|
|
1361
|
+
retryAttempt,
|
|
1362
|
+
nextRetryDelayMs,
|
|
1363
|
+
isRetrying: true
|
|
1364
|
+
})
|
|
1365
|
+
);
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
if (chunkScope?.timedOut())
|
|
1368
|
+
throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
|
|
1369
|
+
throw error;
|
|
1370
|
+
} finally {
|
|
1371
|
+
chunkScope?.dispose();
|
|
1372
|
+
}
|
|
1145
1373
|
results[index] = {
|
|
1146
1374
|
...result,
|
|
1147
1375
|
...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
|
|
@@ -1196,6 +1424,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1196
1424
|
durationMs: Date.now() - startedAt
|
|
1197
1425
|
});
|
|
1198
1426
|
} catch (error) {
|
|
1427
|
+
failedIndices.add(index);
|
|
1199
1428
|
options.onProgress?.({
|
|
1200
1429
|
currentChunk: completed,
|
|
1201
1430
|
totalChunks: chunks.length,
|
|
@@ -1206,24 +1435,42 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
1206
1435
|
durationMs: Date.now() - startedAt,
|
|
1207
1436
|
error
|
|
1208
1437
|
});
|
|
1209
|
-
|
|
1438
|
+
if (options.cancelOnFailure !== false) jobScope?.abort();
|
|
1439
|
+
firstError ?? (firstError = error);
|
|
1440
|
+
return;
|
|
1210
1441
|
}
|
|
1211
1442
|
}
|
|
1212
1443
|
};
|
|
1213
1444
|
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
1445
|
+
if (failedIndices.size > 0) {
|
|
1446
|
+
const error = firstError ?? new Error("One or more SSML chunks failed to synthesize.");
|
|
1447
|
+
error.partialResult = {
|
|
1448
|
+
synthesizedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
1449
|
+
completedChunks: results.flatMap((result, chunkIndex) => result ? [{ ...result, chunkIndex }] : []),
|
|
1450
|
+
pendingChunkIndices: chunks.flatMap((_chunk, chunkIndex) => results[chunkIndex] ? [] : [chunkIndex]),
|
|
1451
|
+
failedChunkIndices: [...failedIndices],
|
|
1452
|
+
totalChunks: chunks.length
|
|
1453
|
+
};
|
|
1454
|
+
throw error;
|
|
1455
|
+
}
|
|
1214
1456
|
const orderedResults = results.filter((result) => result !== void 0);
|
|
1215
1457
|
return {
|
|
1216
1458
|
ok: true,
|
|
1217
1459
|
success: true,
|
|
1218
1460
|
status: "success",
|
|
1219
|
-
value: mergeSynthesisResults(orderedResults, {
|
|
1461
|
+
value: await mergeSynthesisResults(orderedResults, {
|
|
1220
1462
|
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
|
|
1221
|
-
signal: options.signal
|
|
1463
|
+
signal: jobScope?.signal ?? options.signal,
|
|
1464
|
+
customMerger: options.customMerger,
|
|
1465
|
+
outputMimeType: options.outputMimeType,
|
|
1466
|
+
postMergeValidator: options.postMergeValidator
|
|
1222
1467
|
})
|
|
1223
1468
|
};
|
|
1224
1469
|
} catch (error) {
|
|
1225
1470
|
const synthesisError = toSynthesisError(error);
|
|
1226
|
-
return failure(synthesisError);
|
|
1471
|
+
return failure(synthesisError, partialResultFrom(error));
|
|
1472
|
+
} finally {
|
|
1473
|
+
fallbackJobScope?.dispose();
|
|
1227
1474
|
}
|
|
1228
1475
|
}
|
|
1229
1476
|
function withValidationSignal(options, signal) {
|
|
@@ -1244,14 +1491,14 @@ var AzureTtsClient = class {
|
|
|
1244
1491
|
__privateSet(this, _options, options);
|
|
1245
1492
|
}
|
|
1246
1493
|
async synthesize(ssml) {
|
|
1247
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1494
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1248
1495
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1249
1496
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1250
|
-
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
1497
|
+
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts };
|
|
1251
1498
|
return synthesizeSpeech(ssml, config);
|
|
1252
1499
|
}
|
|
1253
1500
|
async synthesizeSsml(ssml, options = {}) {
|
|
1254
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1501
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1255
1502
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1256
1503
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
1257
1504
|
return synthesizeSsml(ssml, {
|
|
@@ -1261,13 +1508,14 @@ var AzureTtsClient = class {
|
|
|
1261
1508
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1262
1509
|
signal: options.signal ?? signal,
|
|
1263
1510
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1511
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1264
1512
|
sourceNodePath: options.sourceNodePath,
|
|
1265
1513
|
sourceTextSegments: options.sourceTextSegments,
|
|
1266
1514
|
sourceMarkers: options.sourceMarkers
|
|
1267
1515
|
});
|
|
1268
1516
|
}
|
|
1269
1517
|
async synthesizeChunks(chunks, options = {}) {
|
|
1270
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
1518
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
1271
1519
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
1272
1520
|
return synthesizeSsmlChunks(chunks, {
|
|
1273
1521
|
endpoint,
|
|
@@ -1276,10 +1524,17 @@ var AzureTtsClient = class {
|
|
|
1276
1524
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
1277
1525
|
signal: options.signal ?? signal,
|
|
1278
1526
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
1527
|
+
timeouts: options.timeouts ?? timeouts,
|
|
1279
1528
|
sourceNodePath: options.sourceNodePath,
|
|
1280
1529
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1281
1530
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1282
|
-
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
1531
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
1532
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
1533
|
+
resumeChunks: options.resumeChunks,
|
|
1534
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
1535
|
+
customMerger: options.customMerger,
|
|
1536
|
+
outputMimeType: options.outputMimeType,
|
|
1537
|
+
postMergeValidator: options.postMergeValidator
|
|
1283
1538
|
});
|
|
1284
1539
|
}
|
|
1285
1540
|
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
@@ -1291,6 +1546,7 @@ var AzureTtsClient = class {
|
|
|
1291
1546
|
outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
|
|
1292
1547
|
signal: options.signal ?? __privateGet(this, _options).signal,
|
|
1293
1548
|
timeoutMs: options.timeoutMs ?? __privateGet(this, _options).timeoutMs,
|
|
1549
|
+
timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
|
|
1294
1550
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
1295
1551
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
1296
1552
|
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
@@ -1384,7 +1640,9 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
1384
1640
|
voiceCount: sortedVoices.length,
|
|
1385
1641
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1386
1642
|
apiVersion: AZURE_VOICE_API_VERSION,
|
|
1387
|
-
regions
|
|
1643
|
+
regions,
|
|
1644
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1645
|
+
regionDiffs: {}
|
|
1388
1646
|
}
|
|
1389
1647
|
};
|
|
1390
1648
|
}
|
|
@@ -1394,6 +1652,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
1394
1652
|
AzureTtsClient,
|
|
1395
1653
|
AzureTtsError,
|
|
1396
1654
|
AzureTtsSdkError,
|
|
1655
|
+
BatchChunkValidationError,
|
|
1397
1656
|
ChunkValidationError,
|
|
1398
1657
|
DEFAULT_OUTPUT_FORMAT,
|
|
1399
1658
|
MergeError,
|
|
@@ -1402,6 +1661,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
1402
1661
|
UnsupportedMergeFormatError,
|
|
1403
1662
|
canMergeAudioFormat,
|
|
1404
1663
|
fetchAzureVoiceCatalog,
|
|
1664
|
+
getRetryAfterDelayMs,
|
|
1405
1665
|
inspectAudioSpecification,
|
|
1406
1666
|
mergeAudioBuffers,
|
|
1407
1667
|
mergeSynthesisResults,
|