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.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(src_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,
|
|
@@ -51,6 +52,7 @@ __export(src_exports, {
|
|
|
51
52
|
buildPartialSsml: () => buildPartialSsml,
|
|
52
53
|
buildSsml: () => buildSsml,
|
|
53
54
|
canMergeAudioFormat: () => canMergeAudioFormat,
|
|
55
|
+
computeChunkFingerprint: () => computeChunkFingerprint,
|
|
54
56
|
createAzureUrlValidatorRunner: () => createAzureUrlValidatorRunner,
|
|
55
57
|
extractSsmlText: () => extractSsmlText,
|
|
56
58
|
extractSsmlTranslatableText: () => extractSsmlTranslatableText,
|
|
@@ -58,6 +60,7 @@ __export(src_exports, {
|
|
|
58
60
|
fromPlainTextToSsml: () => fromPlainTextToSsml,
|
|
59
61
|
getAzureVoiceCatalogMetadata: () => getAzureVoiceCatalogMetadata,
|
|
60
62
|
getBuiltInVoiceCatalogMetadata: () => getBuiltInVoiceCatalogMetadata,
|
|
63
|
+
getRetryAfterDelayMs: () => getRetryAfterDelayMs,
|
|
61
64
|
getSsmlSourceMap: () => getSsmlSourceMap,
|
|
62
65
|
inspectAudioSpecification: () => inspectAudioSpecification,
|
|
63
66
|
isValidAzureAudioDuration: () => isValidAzureAudioDuration,
|
|
@@ -2723,7 +2726,9 @@ var AZURE_VOICE_CATALOG_METADATA = {
|
|
|
2723
2726
|
apiVersion: "2025-10-01",
|
|
2724
2727
|
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
2725
2728
|
regions: [],
|
|
2726
|
-
voiceCount: AZURE_VOICE_DEFINITIONS.length
|
|
2729
|
+
voiceCount: AZURE_VOICE_DEFINITIONS.length,
|
|
2730
|
+
expiresAt: "2026-09-04T00:00:00.000Z",
|
|
2731
|
+
regionDiffs: {}
|
|
2727
2732
|
};
|
|
2728
2733
|
|
|
2729
2734
|
// packages/ssml-core/src/voiceCatalog.ts
|
|
@@ -2737,7 +2742,7 @@ var getBuiltInVoiceCatalogMetadata = getAzureVoiceCatalogMetadata;
|
|
|
2737
2742
|
|
|
2738
2743
|
// packages/azure-tts-client/src/errors.ts
|
|
2739
2744
|
var AzureTtsError = class extends Error {
|
|
2740
|
-
constructor(status, statusText, responseBody, requestId) {
|
|
2745
|
+
constructor(status, statusText, responseBody, requestId, responseHeaders) {
|
|
2741
2746
|
super(`Azure TTS request failed: ${status} ${statusText}`);
|
|
2742
2747
|
this.kind = "azure-api-error";
|
|
2743
2748
|
this.name = "AzureTtsError";
|
|
@@ -2745,8 +2750,37 @@ var AzureTtsError = class extends Error {
|
|
|
2745
2750
|
this.statusText = statusText;
|
|
2746
2751
|
this.responseBody = responseBody;
|
|
2747
2752
|
this.requestId = requestId;
|
|
2753
|
+
const value = responseHeaders instanceof Headers ? responseHeaders.get("retry-after") : responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"];
|
|
2754
|
+
const seconds = value ? Number(value.trim()) : NaN;
|
|
2755
|
+
const date = value ? Date.parse(value) : NaN;
|
|
2756
|
+
if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1e3;
|
|
2757
|
+
else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
|
|
2748
2758
|
}
|
|
2749
2759
|
};
|
|
2760
|
+
function getRetryAfterDelayMs(error) {
|
|
2761
|
+
if (error instanceof AzureTtsError && error.retryAfterMs !== void 0) return error.retryAfterMs;
|
|
2762
|
+
if (!error || typeof error !== "object") return void 0;
|
|
2763
|
+
const candidate = error;
|
|
2764
|
+
if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
|
|
2765
|
+
const headers = candidate.headers ?? candidate.response?.headers;
|
|
2766
|
+
if (headers instanceof Headers) {
|
|
2767
|
+
const value = headers.get("retry-after");
|
|
2768
|
+
if (!value) return void 0;
|
|
2769
|
+
const seconds = Number(value.trim());
|
|
2770
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
2771
|
+
const date = Date.parse(value);
|
|
2772
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
2773
|
+
}
|
|
2774
|
+
if (headers && typeof headers === "object") {
|
|
2775
|
+
const value = headers["retry-after"] ?? headers["Retry-After"];
|
|
2776
|
+
if (typeof value !== "string") return void 0;
|
|
2777
|
+
const seconds = Number(value.trim());
|
|
2778
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
2779
|
+
const date = Date.parse(value);
|
|
2780
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
2781
|
+
}
|
|
2782
|
+
return void 0;
|
|
2783
|
+
}
|
|
2750
2784
|
var AzureTtsSdkError = class extends AzureTtsError {
|
|
2751
2785
|
constructor(errorDetails) {
|
|
2752
2786
|
super(0, "Speech SDK", errorDetails, null);
|
|
@@ -4498,7 +4532,9 @@ var AZURE_VOICE_CATALOG_METADATA2 = {
|
|
|
4498
4532
|
apiVersion: "2025-10-01",
|
|
4499
4533
|
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
4500
4534
|
regions: [],
|
|
4501
|
-
voiceCount: AZURE_VOICE_DEFINITIONS2.length
|
|
4535
|
+
voiceCount: AZURE_VOICE_DEFINITIONS2.length,
|
|
4536
|
+
expiresAt: "2026-09-04T00:00:00.000Z",
|
|
4537
|
+
regionDiffs: {}
|
|
4502
4538
|
};
|
|
4503
4539
|
|
|
4504
4540
|
// packages/azure-tts-client/src/outputFormats.ts
|
|
@@ -4548,6 +4584,9 @@ var OUTPUT_FORMATS = {
|
|
|
4548
4584
|
function resolveMimeType(outputFormat) {
|
|
4549
4585
|
if (/(?:wav|wave|riff)/i.test(outputFormat)) return "audio/wav";
|
|
4550
4586
|
if (/(?:mp3|mpeg)/i.test(outputFormat)) return "audio/mpeg";
|
|
4587
|
+
if (/mulaw|mu-law/i.test(outputFormat)) return "audio/basic";
|
|
4588
|
+
if (/alaw|a-law/i.test(outputFormat)) return "audio/alaw";
|
|
4589
|
+
if (/siren/i.test(outputFormat)) return "audio/siren";
|
|
4551
4590
|
if (/ogg/i.test(outputFormat)) return "audio/ogg";
|
|
4552
4591
|
if (/webm/i.test(outputFormat)) return "audio/webm";
|
|
4553
4592
|
if (/raw/i.test(outputFormat)) return "audio/L16";
|
|
@@ -4576,6 +4615,27 @@ function createSpeechConfig(config) {
|
|
|
4576
4615
|
}
|
|
4577
4616
|
|
|
4578
4617
|
// packages/azure-tts-client/src/synthesis.ts
|
|
4618
|
+
function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
|
|
4619
|
+
const readAttribute3 = (name) => {
|
|
4620
|
+
const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
|
|
4621
|
+
return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
|
|
4622
|
+
};
|
|
4623
|
+
const payload = JSON.stringify({
|
|
4624
|
+
ssml,
|
|
4625
|
+
outputFormat,
|
|
4626
|
+
voice: readAttribute3("(?:name|voice)"),
|
|
4627
|
+
language: readAttribute3("(?:xml:lang|lang)"),
|
|
4628
|
+
rate: readAttribute3("rate"),
|
|
4629
|
+
pitch: readAttribute3("pitch")
|
|
4630
|
+
});
|
|
4631
|
+
let hash = 0xcbf29ce484222325n;
|
|
4632
|
+
const mask = 0xffffffffffffffffn;
|
|
4633
|
+
for (let index = 0; index < payload.length; index += 1) {
|
|
4634
|
+
hash ^= BigInt(payload.charCodeAt(index));
|
|
4635
|
+
hash = hash * 0x100000001b3n & mask;
|
|
4636
|
+
}
|
|
4637
|
+
return `fnv1a64-${hash.toString(16).padStart(16, "0")}`;
|
|
4638
|
+
}
|
|
4579
4639
|
function ascii(bytes, offset, value) {
|
|
4580
4640
|
return [...value].every((character, index) => bytes[offset + index] === character.charCodeAt(0));
|
|
4581
4641
|
}
|
|
@@ -4615,9 +4675,11 @@ function parseWav(buffer) {
|
|
|
4615
4675
|
}
|
|
4616
4676
|
return { chunks, data, format };
|
|
4617
4677
|
}
|
|
4618
|
-
function
|
|
4619
|
-
const match =
|
|
4620
|
-
|
|
4678
|
+
function formatSampleRate(format) {
|
|
4679
|
+
const match = /(?:^|-)(\d+)(khz|hz)(?:-|$)/i.exec(format);
|
|
4680
|
+
if (!match?.[1] || !match[2]) return 0;
|
|
4681
|
+
const value = Number(match[1]);
|
|
4682
|
+
return match[2].toLowerCase() === "khz" ? value * 1e3 : value;
|
|
4621
4683
|
}
|
|
4622
4684
|
function formatChannels(format, fallback) {
|
|
4623
4685
|
if (/stereo|2ch|dual/i.test(format)) return 2;
|
|
@@ -4625,11 +4687,13 @@ function formatChannels(format, fallback) {
|
|
|
4625
4687
|
return fallback;
|
|
4626
4688
|
}
|
|
4627
4689
|
function formatAudioSpecification(format) {
|
|
4628
|
-
const sampleRate =
|
|
4690
|
+
const sampleRate = formatSampleRate(format);
|
|
4629
4691
|
const channels = formatChannels(format, 0);
|
|
4630
4692
|
const bitrateMatch = /(\d+)(?:kbitrate|kbps|kbits?)/i.exec(format);
|
|
4631
4693
|
const bitrate = bitrateMatch?.[1] ? Number(bitrateMatch[1]) * 1e3 : void 0;
|
|
4632
|
-
const codec = /mp3|mpeg/i.test(format) ? "mp3" : /opus/i.test(format) ? "opus" : /silk/i.test(format) ? "silk" : /
|
|
4694
|
+
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";
|
|
4695
|
+
const bitDepthMatch = /(\d+)bit/i.exec(format);
|
|
4696
|
+
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;
|
|
4633
4697
|
return {
|
|
4634
4698
|
format,
|
|
4635
4699
|
mimeType: resolveMimeType(format),
|
|
@@ -4637,7 +4701,10 @@ function formatAudioSpecification(format) {
|
|
|
4637
4701
|
sampleRate,
|
|
4638
4702
|
channels,
|
|
4639
4703
|
...bitrate ? { bitrate } : {},
|
|
4640
|
-
|
|
4704
|
+
...bitDepthMatch?.[1] ? { bitDepth: Number(bitDepthMatch[1]) } : {},
|
|
4705
|
+
...container ? { container } : {},
|
|
4706
|
+
isVbr: /vbr/i.test(format),
|
|
4707
|
+
isCompressed: codec !== "pcm" && codec !== "unknown"
|
|
4641
4708
|
};
|
|
4642
4709
|
}
|
|
4643
4710
|
function parseMp3Specification(buffer, format) {
|
|
@@ -4672,6 +4739,8 @@ function parseMp3Specification(buffer, format) {
|
|
|
4672
4739
|
sampleRate,
|
|
4673
4740
|
channels: (bytes[index + 3] ?? 0) >> 6 === 3 ? 1 : 2,
|
|
4674
4741
|
bitrate: bitrateKbps * 1e3,
|
|
4742
|
+
container: "mp3-raw",
|
|
4743
|
+
isVbr: false,
|
|
4675
4744
|
isCompressed: true
|
|
4676
4745
|
};
|
|
4677
4746
|
}
|
|
@@ -4686,24 +4755,43 @@ function inspectAudioSpecification(buffer, format) {
|
|
|
4686
4755
|
const channels = view.getUint16(2, true);
|
|
4687
4756
|
const bitsPerSample = parsed.format.byteLength >= 16 ? view.getUint16(14, true) : 0;
|
|
4688
4757
|
const formatCode = view.getUint16(0, true);
|
|
4758
|
+
const namedCodec = formatAudioSpecification(format).codec;
|
|
4759
|
+
const codec = formatCode === 1 ? "pcm" : formatCode === 6 ? "alaw" : formatCode === 7 ? "mulaw" : namedCodec === "siren" ? "siren" : "unknown";
|
|
4689
4760
|
return {
|
|
4690
4761
|
format,
|
|
4691
4762
|
mimeType: "audio/wav",
|
|
4692
|
-
codec
|
|
4763
|
+
codec,
|
|
4693
4764
|
sampleRate,
|
|
4694
4765
|
channels,
|
|
4695
4766
|
...sampleRate && channels && bitsPerSample ? { bitrate: sampleRate * channels * bitsPerSample } : {},
|
|
4696
|
-
|
|
4767
|
+
bitDepth: bitsPerSample,
|
|
4768
|
+
container: "riff-wave",
|
|
4769
|
+
isVbr: false,
|
|
4770
|
+
isCompressed: codec !== "pcm" && codec !== "unknown"
|
|
4697
4771
|
};
|
|
4698
4772
|
}
|
|
4699
4773
|
if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
|
|
4700
|
-
|
|
4774
|
+
const specification = formatAudioSpecification(format);
|
|
4775
|
+
if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
|
|
4776
|
+
return specification;
|
|
4777
|
+
}
|
|
4778
|
+
function validateRawAudioBuffer(buffer, specification) {
|
|
4779
|
+
if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
|
|
4780
|
+
throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
|
|
4781
|
+
}
|
|
4782
|
+
if (specification.codec === "siren" || specification.codec === "silk") return;
|
|
4783
|
+
const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
|
|
4784
|
+
if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
|
|
4785
|
+
throw new Error(
|
|
4786
|
+
`RAW audio buffer size ${buffer.byteLength} is not aligned to ${bytesPerFrame}-byte audio frames for "${specification.format}".`
|
|
4787
|
+
);
|
|
4788
|
+
}
|
|
4701
4789
|
}
|
|
4702
4790
|
function validateAudioSpecifications(specs) {
|
|
4703
4791
|
const first = specs[0];
|
|
4704
4792
|
if (!first) return;
|
|
4705
4793
|
const mismatch = specs.find(
|
|
4706
|
-
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate
|
|
4794
|
+
(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
|
|
4707
4795
|
);
|
|
4708
4796
|
if (mismatch)
|
|
4709
4797
|
throw new AudioFormatMismatchError(
|
|
@@ -4786,6 +4874,30 @@ function isWavFormat(format) {
|
|
|
4786
4874
|
function isRawFormat(format) {
|
|
4787
4875
|
return /^raw(?:-|$)/i.test(format);
|
|
4788
4876
|
}
|
|
4877
|
+
function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
|
|
4878
|
+
if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
|
|
4879
|
+
throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
|
|
4880
|
+
}
|
|
4881
|
+
const specification = inspectAudioSpecification(merged, format);
|
|
4882
|
+
if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
|
|
4883
|
+
const firstInput = inputSpecs[0];
|
|
4884
|
+
if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
|
|
4885
|
+
throw new AudioFormatMismatchError("The custom audio merger returned an incompatible audio stream.", [
|
|
4886
|
+
...inputSpecs,
|
|
4887
|
+
specification
|
|
4888
|
+
]);
|
|
4889
|
+
}
|
|
4890
|
+
if (isRawFormat(format)) {
|
|
4891
|
+
const expectedSize = buffers.reduce((total, input) => total + input.byteLength, 0);
|
|
4892
|
+
if (merged.byteLength !== expectedSize) {
|
|
4893
|
+
throw new MergeError(
|
|
4894
|
+
`The custom audio merger returned ${merged.byteLength} bytes; ${expectedSize} were expected.`
|
|
4895
|
+
);
|
|
4896
|
+
}
|
|
4897
|
+
validateRawAudioBuffer(merged, specification);
|
|
4898
|
+
}
|
|
4899
|
+
return specification;
|
|
4900
|
+
}
|
|
4789
4901
|
function resolveMergeAudioFormat(format) {
|
|
4790
4902
|
if (isWavFormat(format)) return "wav";
|
|
4791
4903
|
if (isMp3Format(format)) return "mp3";
|
|
@@ -4838,7 +4950,7 @@ function closeSpeechResources(speechConfig, synthesizer) {
|
|
|
4838
4950
|
}
|
|
4839
4951
|
}
|
|
4840
4952
|
var ticksToMilliseconds = (ticks) => Math.max(0, ticks) / 1e4;
|
|
4841
|
-
async function
|
|
4953
|
+
async function synthesizeSsmlOnce(ssml, config) {
|
|
4842
4954
|
if (config.signal?.aborted) {
|
|
4843
4955
|
throw new SynthesisCancelledError();
|
|
4844
4956
|
}
|
|
@@ -4906,9 +5018,7 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4906
5018
|
};
|
|
4907
5019
|
}
|
|
4908
5020
|
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath) {
|
|
4909
|
-
|
|
4910
|
-
Object.defineProperty(unmapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
4911
|
-
return unmapped;
|
|
5021
|
+
return { mappingStatus: "unmapped" };
|
|
4912
5022
|
}
|
|
4913
5023
|
const value = text ?? "";
|
|
4914
5024
|
let localStart = Number.isFinite(offsetHint) && (offsetHint ?? 0) >= 0 ? offsetHint : -1;
|
|
@@ -4970,6 +5080,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4970
5080
|
rejectWithError(err);
|
|
4971
5081
|
return;
|
|
4972
5082
|
}
|
|
5083
|
+
let audioSpec;
|
|
5084
|
+
try {
|
|
5085
|
+
audioSpec = inspectAudioSpecification(result.audioData, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT);
|
|
5086
|
+
} catch (error) {
|
|
5087
|
+
rejectWithError(error);
|
|
5088
|
+
return;
|
|
5089
|
+
}
|
|
4973
5090
|
settled = true;
|
|
4974
5091
|
cleanup();
|
|
4975
5092
|
closeResources();
|
|
@@ -4990,8 +5107,13 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4990
5107
|
...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
4991
5108
|
...requestId ? { requestId } : {}
|
|
4992
5109
|
};
|
|
4993
|
-
if (event.mappingStatus === "unmapped")
|
|
5110
|
+
if (event.mappingStatus === "unmapped") {
|
|
4994
5111
|
Object.defineProperty(mapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
5112
|
+
Object.defineProperty(mapped, "toJSON", {
|
|
5113
|
+
value: () => ({ ...mapped, mappingStatus: "unmapped" }),
|
|
5114
|
+
enumerable: false
|
|
5115
|
+
});
|
|
5116
|
+
}
|
|
4995
5117
|
return mapped;
|
|
4996
5118
|
};
|
|
4997
5119
|
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
@@ -5000,8 +5122,8 @@ async function synthesizeSsml(ssml, config) {
|
|
|
5000
5122
|
resolve({
|
|
5001
5123
|
audioData: result.audioData,
|
|
5002
5124
|
durationMs,
|
|
5003
|
-
audioSpec
|
|
5004
|
-
mimeType: resolveMimeType(config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
5125
|
+
audioSpec,
|
|
5126
|
+
mimeType: config.outputMimeType ?? resolveMimeType(config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
5005
5127
|
...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
|
|
5006
5128
|
...requestId ? { requestId } : {},
|
|
5007
5129
|
...sourceBoundaries.length > 0 ? { boundaries: sourceBoundaries, wordBoundary: sourceBoundaries, wordBoundaries: sourceBoundaries } : {},
|
|
@@ -5014,10 +5136,11 @@ async function synthesizeSsml(ssml, config) {
|
|
|
5014
5136
|
abortHandler = () => rejectWithError(new SynthesisCancelledError());
|
|
5015
5137
|
config.signal.addEventListener("abort", abortHandler, { once: true });
|
|
5016
5138
|
}
|
|
5017
|
-
|
|
5139
|
+
const timeoutMs = config.timeouts?.perChunkMs ?? config.timeouts?.totalJobMs ?? config.timeoutMs;
|
|
5140
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
5018
5141
|
timeout = setTimeout(
|
|
5019
|
-
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${
|
|
5020
|
-
|
|
5142
|
+
() => rejectWithError(new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`)),
|
|
5143
|
+
timeoutMs
|
|
5021
5144
|
);
|
|
5022
5145
|
}
|
|
5023
5146
|
synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);
|
|
@@ -5036,7 +5159,9 @@ function isRetryableSynthesisError(error) {
|
|
|
5036
5159
|
if (typeof status === "number") return status === 429 || status >= 500 && status < 600;
|
|
5037
5160
|
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
5038
5161
|
}
|
|
5039
|
-
function retryDelay(options, retryAttempt) {
|
|
5162
|
+
function retryDelay(options, retryAttempt, error) {
|
|
5163
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
5164
|
+
if (retryAfterMs !== void 0) return retryAfterMs;
|
|
5040
5165
|
const base = Math.min(options.maxDelayMs, options.initialDelayMs * 2 ** Math.max(0, retryAttempt - 1));
|
|
5041
5166
|
return Math.floor(Math.random() * (base + 1));
|
|
5042
5167
|
}
|
|
@@ -5064,32 +5189,102 @@ async function waitForRetry(delayMs, signal) {
|
|
|
5064
5189
|
}
|
|
5065
5190
|
});
|
|
5066
5191
|
}
|
|
5067
|
-
async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
5192
|
+
async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadlineAtMs) {
|
|
5068
5193
|
const options = retryOptions ? {
|
|
5069
5194
|
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
5070
5195
|
initialDelayMs: Math.max(0, retryOptions.initialDelayMs),
|
|
5071
|
-
maxDelayMs: Math.max(0, retryOptions.maxDelayMs)
|
|
5196
|
+
maxDelayMs: Math.max(0, retryOptions.maxDelayMs),
|
|
5197
|
+
shouldRetry: retryOptions.shouldRetry
|
|
5072
5198
|
} : void 0;
|
|
5073
5199
|
let attempt = 0;
|
|
5074
5200
|
while (true) {
|
|
5075
5201
|
if (config.signal?.aborted) throw new SynthesisCancelledError();
|
|
5076
5202
|
try {
|
|
5077
|
-
return await
|
|
5203
|
+
return await synthesizeSsmlOnce(ssml, config);
|
|
5078
5204
|
} catch (error) {
|
|
5079
|
-
if (!options || attempt >= options.maxRetries || !
|
|
5205
|
+
if (!options || attempt >= options.maxRetries || !(options.shouldRetry?.(error, attempt + 1) ?? isRetryableSynthesisError(error)))
|
|
5206
|
+
throw error;
|
|
5080
5207
|
attempt += 1;
|
|
5081
|
-
const delayMs = retryDelay(options, attempt);
|
|
5208
|
+
const delayMs = retryDelay(options, attempt, error);
|
|
5209
|
+
const remainingMs = deadlineAtMs === void 0 ? void 0 : Math.max(0, deadlineAtMs - Date.now());
|
|
5210
|
+
if (getRetryAfterDelayMs(error) !== void 0 && (delayMs > options.maxDelayMs || remainingMs !== void 0 && delayMs > remainingMs)) {
|
|
5211
|
+
throw new SynthesisTimeoutError(
|
|
5212
|
+
remainingMs === void 0 ? `Retry-After exceeded maxDelayMs (${options.maxDelayMs} ms).` : `Retry-After exceeded the remaining total job timeout (${remainingMs} ms).`
|
|
5213
|
+
);
|
|
5214
|
+
}
|
|
5082
5215
|
onRetry(attempt, delayMs);
|
|
5083
|
-
await waitForRetry(delayMs, config.signal);
|
|
5216
|
+
await waitForRetry(Math.min(delayMs, remainingMs ?? delayMs), config.signal);
|
|
5084
5217
|
}
|
|
5085
5218
|
}
|
|
5086
5219
|
}
|
|
5220
|
+
async function synthesizeSsml(ssml, config) {
|
|
5221
|
+
const totalJobMs = config.timeouts?.totalJobMs;
|
|
5222
|
+
const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
|
|
5223
|
+
if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
|
|
5224
|
+
return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
|
|
5225
|
+
}
|
|
5226
|
+
function createAbortScope(parent, timeoutMs) {
|
|
5227
|
+
const controller = new AbortController();
|
|
5228
|
+
let didTimeout = false;
|
|
5229
|
+
const onAbort = () => controller.abort();
|
|
5230
|
+
if (parent?.aborted) controller.abort();
|
|
5231
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
5232
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
5233
|
+
didTimeout = true;
|
|
5234
|
+
controller.abort();
|
|
5235
|
+
}, timeoutMs) : void 0;
|
|
5236
|
+
return {
|
|
5237
|
+
signal: controller.signal,
|
|
5238
|
+
timedOut: () => didTimeout,
|
|
5239
|
+
dispose: () => {
|
|
5240
|
+
if (timer) clearTimeout(timer);
|
|
5241
|
+
parent?.removeEventListener("abort", onAbort);
|
|
5242
|
+
},
|
|
5243
|
+
abort: () => controller.abort()
|
|
5244
|
+
};
|
|
5245
|
+
}
|
|
5246
|
+
async function synthesizeChunkWithTimeout(ssml, config, retryOptions, timeoutMs, onRetry, deadlineAtMs) {
|
|
5247
|
+
const scope = createAbortScope(config.signal, timeoutMs);
|
|
5248
|
+
try {
|
|
5249
|
+
return await synthesizeWithRetry(ssml, { ...config, signal: scope.signal }, retryOptions, onRetry, deadlineAtMs);
|
|
5250
|
+
} catch (error) {
|
|
5251
|
+
if (scope.timedOut()) throw new SynthesisTimeoutError(`Speech synthesis timed out after ${timeoutMs} ms.`);
|
|
5252
|
+
throw error;
|
|
5253
|
+
} finally {
|
|
5254
|
+
scope.dispose();
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5087
5257
|
async function synthesizeSsmlChunks(chunks, config) {
|
|
5088
|
-
const results = new Array(chunks.length);
|
|
5089
5258
|
const totalChunks = chunks.length;
|
|
5259
|
+
const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
|
|
5260
|
+
const fingerprints = inputs.map(
|
|
5261
|
+
(chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
|
|
5262
|
+
);
|
|
5263
|
+
const results = new Array(totalChunks);
|
|
5264
|
+
const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
5265
|
+
const invalidCachedIndices = /* @__PURE__ */ new Set();
|
|
5266
|
+
const chunkStates = inputs.map((_chunk, chunkIndex) => ({
|
|
5267
|
+
chunkIndex,
|
|
5268
|
+
status: "pending",
|
|
5269
|
+
canResume: true
|
|
5270
|
+
}));
|
|
5271
|
+
for (const [index, cached] of cachedChunks) {
|
|
5272
|
+
if (index < 0 || index >= totalChunks) continue;
|
|
5273
|
+
const isValid = config.resumeValidation === "disabled" || cached.fingerprint === fingerprints[index];
|
|
5274
|
+
if (isValid) {
|
|
5275
|
+
results[index] = { ...cached };
|
|
5276
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: results[index] };
|
|
5277
|
+
} else {
|
|
5278
|
+
invalidCachedIndices.add(index);
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
const requestedIndices = config.resumeChunkIndices ? new Set(config.resumeChunkIndices.filter((index) => index >= 0 && index < totalChunks)) : void 0;
|
|
5282
|
+
const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
|
|
5283
|
+
const jobStartedAt = Date.now();
|
|
5284
|
+
const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
|
|
5285
|
+
const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
|
|
5090
5286
|
const report = (event) => config.onProgress?.(event);
|
|
5091
|
-
for (const [index,
|
|
5092
|
-
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
5287
|
+
for (const [index, input] of inputs.entries()) {
|
|
5093
5288
|
report({
|
|
5094
5289
|
currentChunk: index,
|
|
5095
5290
|
totalChunks,
|
|
@@ -5100,15 +5295,18 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5100
5295
|
durationMs: 0
|
|
5101
5296
|
});
|
|
5102
5297
|
}
|
|
5103
|
-
let completed = 0;
|
|
5298
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
5104
5299
|
let nextIndex = 0;
|
|
5105
5300
|
const concurrency = resolveConcurrency(config.concurrency, chunks.length);
|
|
5301
|
+
let firstError;
|
|
5302
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
5106
5303
|
const worker = async () => {
|
|
5107
5304
|
while (true) {
|
|
5108
5305
|
const index = nextIndex++;
|
|
5109
5306
|
if (index >= chunks.length) return;
|
|
5110
|
-
|
|
5111
|
-
|
|
5307
|
+
if (!shouldSynthesize(index)) continue;
|
|
5308
|
+
if (firstError && config.cancelOnFailure !== false) return;
|
|
5309
|
+
const input = inputs[index];
|
|
5112
5310
|
report({
|
|
5113
5311
|
currentChunk: completed,
|
|
5114
5312
|
totalChunks,
|
|
@@ -5120,10 +5318,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5120
5318
|
});
|
|
5121
5319
|
const startedAt = Date.now();
|
|
5122
5320
|
try {
|
|
5123
|
-
const result = await
|
|
5321
|
+
const result = await synthesizeChunkWithTimeout(
|
|
5124
5322
|
input.ssml,
|
|
5125
5323
|
{
|
|
5126
5324
|
...config,
|
|
5325
|
+
signal: scope.signal,
|
|
5127
5326
|
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
5128
5327
|
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
5129
5328
|
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
@@ -5132,6 +5331,7 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5132
5331
|
onProgress: void 0
|
|
5133
5332
|
},
|
|
5134
5333
|
config.retryOptions,
|
|
5334
|
+
config.timeouts?.chunkWithRetriesMs ?? config.timeouts?.perChunkMs ?? config.timeoutMs,
|
|
5135
5335
|
(retryAttempt, nextRetryDelayMs) => report({
|
|
5136
5336
|
currentChunk: completed,
|
|
5137
5337
|
totalChunks,
|
|
@@ -5143,9 +5343,11 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5143
5343
|
retryAttempt,
|
|
5144
5344
|
nextRetryDelayMs,
|
|
5145
5345
|
isRetrying: true
|
|
5146
|
-
})
|
|
5346
|
+
}),
|
|
5347
|
+
jobDeadlineAt
|
|
5147
5348
|
);
|
|
5148
5349
|
results[index] = result;
|
|
5350
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result };
|
|
5149
5351
|
completed += 1;
|
|
5150
5352
|
report({
|
|
5151
5353
|
currentChunk: completed,
|
|
@@ -5157,6 +5359,16 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5157
5359
|
durationMs: Date.now() - startedAt
|
|
5158
5360
|
});
|
|
5159
5361
|
} catch (error) {
|
|
5362
|
+
const wasCancelled = firstError !== void 0 || scope.signal.aborted && !scope.timedOut();
|
|
5363
|
+
firstError ?? (firstError = scope.timedOut() ? new SynthesisTimeoutError(`Speech synthesis timed out after ${config.timeouts?.totalJobMs} ms.`) : error);
|
|
5364
|
+
if (!wasCancelled) failedIndices.add(index);
|
|
5365
|
+
chunkStates[index] = {
|
|
5366
|
+
chunkIndex: index,
|
|
5367
|
+
status: wasCancelled ? "cancelled" : "failed",
|
|
5368
|
+
isOriginalFailure: !wasCancelled,
|
|
5369
|
+
canResume: true,
|
|
5370
|
+
error
|
|
5371
|
+
};
|
|
5160
5372
|
report({
|
|
5161
5373
|
currentChunk: completed,
|
|
5162
5374
|
totalChunks,
|
|
@@ -5167,16 +5379,49 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
5167
5379
|
durationMs: Date.now() - startedAt,
|
|
5168
5380
|
error
|
|
5169
5381
|
});
|
|
5170
|
-
|
|
5382
|
+
if (config.cancelOnFailure !== false) scope.abort();
|
|
5383
|
+
return;
|
|
5171
5384
|
}
|
|
5172
5385
|
}
|
|
5173
5386
|
};
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5387
|
+
try {
|
|
5388
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
5389
|
+
if (firstError) throw firstError;
|
|
5390
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
5391
|
+
return await mergeSynthesisResults(orderedResults, {
|
|
5392
|
+
format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
|
|
5393
|
+
signal: scope.signal,
|
|
5394
|
+
customMerger: config.customMerger,
|
|
5395
|
+
outputMimeType: config.outputMimeType,
|
|
5396
|
+
postMergeValidator: config.postMergeValidator
|
|
5397
|
+
});
|
|
5398
|
+
} catch (error) {
|
|
5399
|
+
if (firstError && config.cancelOnFailure !== false) {
|
|
5400
|
+
for (const [chunkIndex, state] of chunkStates.entries()) {
|
|
5401
|
+
if (state.status === "pending" && shouldSynthesize(chunkIndex)) {
|
|
5402
|
+
chunkStates[chunkIndex] = { chunkIndex, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
const synthesizedChunks = results.flatMap(
|
|
5407
|
+
(result, chunkIndex) => result ? [{ ...result, chunkIndex, fingerprint: fingerprints[chunkIndex] ?? "" }] : []
|
|
5408
|
+
);
|
|
5409
|
+
const partial = {
|
|
5410
|
+
synthesizedChunks,
|
|
5411
|
+
completedChunks: synthesizedChunks,
|
|
5412
|
+
pendingChunkIndices: chunkStates.flatMap(
|
|
5413
|
+
(state) => state.status === "pending" || state.status === "cancelled" || state.status === "failed" ? [state.chunkIndex] : []
|
|
5414
|
+
),
|
|
5415
|
+
failedChunkIndices: [...failedIndices],
|
|
5416
|
+
cancelledChunkIndices: chunkStates.filter((state) => state.status === "cancelled").map((state) => state.chunkIndex),
|
|
5417
|
+
chunkStates,
|
|
5418
|
+
totalChunks
|
|
5419
|
+
};
|
|
5420
|
+
if (error && typeof error === "object") error.partialResult = partial;
|
|
5421
|
+
throw error;
|
|
5422
|
+
} finally {
|
|
5423
|
+
scope.dispose();
|
|
5424
|
+
}
|
|
5180
5425
|
}
|
|
5181
5426
|
function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
|
|
5182
5427
|
const boundaries = [];
|
|
@@ -5267,16 +5512,26 @@ function mergeSynthesisResults(results, options) {
|
|
|
5267
5512
|
})
|
|
5268
5513
|
).then((merged) => {
|
|
5269
5514
|
if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
|
|
5270
|
-
if (!(merged instanceof ArrayBuffer) || buffers.some((buffer) => buffer.byteLength > 0) && merged.byteLength === 0)
|
|
5271
|
-
throw new MergeError("The custom audio merger returned an invalid audio buffer.");
|
|
5272
5515
|
if (signal.aborted) throw new SynthesisCancelledError();
|
|
5273
|
-
|
|
5274
|
-
results,
|
|
5516
|
+
const mergedSpec = validateMergedAudioBuffer(
|
|
5275
5517
|
merged,
|
|
5276
5518
|
format,
|
|
5277
|
-
|
|
5278
|
-
|
|
5519
|
+
buffers,
|
|
5520
|
+
inputSpecs,
|
|
5521
|
+
resolvedOptions.outputMimeType ?? resolveMimeType(format)
|
|
5279
5522
|
);
|
|
5523
|
+
const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
|
|
5524
|
+
return Promise.resolve(
|
|
5525
|
+
resolvedOptions.postMergeValidator?.(result, {
|
|
5526
|
+
format,
|
|
5527
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
5528
|
+
inputSpecs,
|
|
5529
|
+
signal
|
|
5530
|
+
})
|
|
5531
|
+
).then((valid) => {
|
|
5532
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
5533
|
+
return result;
|
|
5534
|
+
});
|
|
5280
5535
|
}).catch((error) => {
|
|
5281
5536
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
5282
5537
|
throw error;
|
|
@@ -5284,13 +5539,28 @@ function mergeSynthesisResults(results, options) {
|
|
|
5284
5539
|
});
|
|
5285
5540
|
}
|
|
5286
5541
|
try {
|
|
5287
|
-
|
|
5542
|
+
const result = createMergedResult(
|
|
5288
5543
|
results,
|
|
5289
5544
|
mergeAudioBuffers(buffers, { format }),
|
|
5290
5545
|
format,
|
|
5291
5546
|
inputSpecs[0],
|
|
5292
5547
|
resolvedOptions.outputMimeType
|
|
5293
5548
|
);
|
|
5549
|
+
if (resolvedOptions.postMergeValidator) {
|
|
5550
|
+
const validation = resolvedOptions.postMergeValidator(result, {
|
|
5551
|
+
format,
|
|
5552
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
5553
|
+
inputSpecs,
|
|
5554
|
+
signal
|
|
5555
|
+
});
|
|
5556
|
+
if (validation instanceof Promise)
|
|
5557
|
+
return validation.then((valid) => {
|
|
5558
|
+
if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
5559
|
+
return result;
|
|
5560
|
+
});
|
|
5561
|
+
if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
|
|
5562
|
+
}
|
|
5563
|
+
return result;
|
|
5294
5564
|
} catch (error) {
|
|
5295
5565
|
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
5296
5566
|
throw error;
|
|
@@ -5311,8 +5581,52 @@ var ChunkValidationError = class extends Error {
|
|
|
5311
5581
|
this.diagnostics = diagnostics;
|
|
5312
5582
|
}
|
|
5313
5583
|
};
|
|
5314
|
-
|
|
5315
|
-
|
|
5584
|
+
var BatchChunkValidationError = class extends ChunkValidationError {
|
|
5585
|
+
constructor(chunkDiagnostics) {
|
|
5586
|
+
const first = chunkDiagnostics[0];
|
|
5587
|
+
super(first?.chunkIndex ?? -1, first?.diagnostics ?? []);
|
|
5588
|
+
this.name = "BatchChunkValidationError";
|
|
5589
|
+
this.message = `SSML validation failed for ${chunkDiagnostics.length} chunk(s); the Azure Speech API was not called.`;
|
|
5590
|
+
this.chunkDiagnostics = chunkDiagnostics;
|
|
5591
|
+
this.totalErrorCount = chunkDiagnostics.reduce((total, chunk) => total + chunk.diagnostics.length, 0);
|
|
5592
|
+
this.errorCount = this.totalErrorCount;
|
|
5593
|
+
this.totalErrors = this.totalErrorCount;
|
|
5594
|
+
}
|
|
5595
|
+
};
|
|
5596
|
+
function failure(error, partialResult) {
|
|
5597
|
+
return {
|
|
5598
|
+
ok: false,
|
|
5599
|
+
success: false,
|
|
5600
|
+
status: error.kind,
|
|
5601
|
+
error,
|
|
5602
|
+
...partialResult ? { partialResult } : {}
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
5605
|
+
function partialResultFrom(error) {
|
|
5606
|
+
if (!error || typeof error !== "object") return void 0;
|
|
5607
|
+
const partial = error.partialResult;
|
|
5608
|
+
if (!partial || typeof partial !== "object") return void 0;
|
|
5609
|
+
return partial;
|
|
5610
|
+
}
|
|
5611
|
+
function createSafeAbortScope(parent, timeoutMs) {
|
|
5612
|
+
const controller = new AbortController();
|
|
5613
|
+
let didTimeout = false;
|
|
5614
|
+
const onAbort = () => controller.abort();
|
|
5615
|
+
if (parent?.aborted) controller.abort();
|
|
5616
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
5617
|
+
const timer = timeoutMs !== void 0 && timeoutMs > 0 ? setTimeout(() => {
|
|
5618
|
+
didTimeout = true;
|
|
5619
|
+
controller.abort();
|
|
5620
|
+
}, timeoutMs) : void 0;
|
|
5621
|
+
return {
|
|
5622
|
+
signal: controller.signal,
|
|
5623
|
+
timedOut: () => didTimeout,
|
|
5624
|
+
dispose: () => {
|
|
5625
|
+
if (timer) clearTimeout(timer);
|
|
5626
|
+
parent?.removeEventListener("abort", onAbort);
|
|
5627
|
+
},
|
|
5628
|
+
abort: () => controller.abort()
|
|
5629
|
+
};
|
|
5316
5630
|
}
|
|
5317
5631
|
function isRetryable(error) {
|
|
5318
5632
|
if (error instanceof Error && /cancel|abort|tim(?:e|ed) ?out/i.test(error.message)) return false;
|
|
@@ -5327,16 +5641,20 @@ function delayForRetry(options, attempt) {
|
|
|
5327
5641
|
const base = Math.min(maxDelay, Math.max(0, options.initialDelayMs) * 2 ** Math.max(0, attempt - 1));
|
|
5328
5642
|
return Math.floor(Math.random() * (base + 1));
|
|
5329
5643
|
}
|
|
5644
|
+
function retryDelayForError(options, attempt, error) {
|
|
5645
|
+
return getRetryAfterDelayMs(error) ?? delayForRetry(options, attempt);
|
|
5646
|
+
}
|
|
5330
5647
|
function resolveConcurrency2(value, total) {
|
|
5331
5648
|
if (value === void 0) return 1;
|
|
5332
5649
|
if (value === Infinity) return Math.max(1, total);
|
|
5333
5650
|
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
|
|
5334
5651
|
}
|
|
5335
|
-
async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
5652
|
+
async function retryableSynthesis(synthesize, options, signal, onRetry, deadlineAtMs) {
|
|
5336
5653
|
const retry = options ? {
|
|
5337
5654
|
maxRetries: Math.max(0, Math.floor(options.maxRetries)),
|
|
5338
5655
|
initialDelayMs: options.initialDelayMs,
|
|
5339
|
-
maxDelayMs: options.maxDelayMs
|
|
5656
|
+
maxDelayMs: options.maxDelayMs,
|
|
5657
|
+
shouldRetry: options.shouldRetry
|
|
5340
5658
|
} : void 0;
|
|
5341
5659
|
let attempt = 0;
|
|
5342
5660
|
while (true) {
|
|
@@ -5344,9 +5662,15 @@ async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
|
5344
5662
|
try {
|
|
5345
5663
|
return await synthesize();
|
|
5346
5664
|
} catch (error) {
|
|
5347
|
-
if (!retry || attempt >= retry.maxRetries || !
|
|
5665
|
+
if (!retry || attempt >= retry.maxRetries || !(retry.shouldRetry?.(error, attempt + 1) ?? isRetryable(error)))
|
|
5666
|
+
throw error;
|
|
5348
5667
|
attempt += 1;
|
|
5349
|
-
const delayMs =
|
|
5668
|
+
const delayMs = retryDelayForError(retry, attempt, error);
|
|
5669
|
+
const retryAfterMs = getRetryAfterDelayMs(error);
|
|
5670
|
+
const remainingMs = deadlineAtMs === void 0 ? void 0 : Math.max(0, deadlineAtMs - Date.now());
|
|
5671
|
+
if (retryAfterMs !== void 0 && (retryAfterMs > retry.maxDelayMs || remainingMs !== void 0 && retryAfterMs > remainingMs)) {
|
|
5672
|
+
throw new Error("Speech synthesis timed out because Retry-After exceeded the available retry budget.");
|
|
5673
|
+
}
|
|
5350
5674
|
onRetry(attempt, delayMs);
|
|
5351
5675
|
if (delayMs > 0)
|
|
5352
5676
|
await new Promise((resolve, reject) => {
|
|
@@ -5370,7 +5694,7 @@ function sharedValidationOptions(options, signal) {
|
|
|
5370
5694
|
const runner = createAzureUrlValidatorRunner2(validator, {
|
|
5371
5695
|
...options.urlValidation ?? {},
|
|
5372
5696
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
5373
|
-
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
5697
|
+
...options.timeouts?.urlValidationMs !== void 0 ? { timeoutMs: options.timeouts.urlValidationMs } : options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
5374
5698
|
...signal ? { signal } : {},
|
|
5375
5699
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
5376
5700
|
});
|
|
@@ -5394,20 +5718,31 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
5394
5718
|
diagnostics: errors
|
|
5395
5719
|
});
|
|
5396
5720
|
}
|
|
5721
|
+
const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
|
|
5397
5722
|
try {
|
|
5398
5723
|
return {
|
|
5399
5724
|
ok: true,
|
|
5400
5725
|
success: true,
|
|
5401
5726
|
status: "success",
|
|
5402
|
-
value: await client.synthesizeSsml(ssml, {
|
|
5727
|
+
value: await client.synthesizeSsml(ssml, {
|
|
5728
|
+
signal: jobScope?.signal ?? options.signal,
|
|
5729
|
+
timeoutMs: options.timeouts?.perChunkMs,
|
|
5730
|
+
timeouts: options.timeouts
|
|
5731
|
+
})
|
|
5403
5732
|
};
|
|
5404
5733
|
} catch (error) {
|
|
5734
|
+
if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
|
|
5405
5735
|
const synthesisError = toSynthesisError(error);
|
|
5406
5736
|
return failure(synthesisError);
|
|
5737
|
+
} finally {
|
|
5738
|
+
jobScope?.dispose();
|
|
5407
5739
|
}
|
|
5408
5740
|
}
|
|
5409
5741
|
async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
5410
|
-
const validationOptions = sharedValidationOptions(
|
|
5742
|
+
const validationOptions = sharedValidationOptions(
|
|
5743
|
+
{ ...options.validation ?? options, timeouts: options.timeouts },
|
|
5744
|
+
options.signal
|
|
5745
|
+
);
|
|
5411
5746
|
if (options.signal?.aborted) {
|
|
5412
5747
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
5413
5748
|
return failure(error);
|
|
@@ -5441,16 +5776,17 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5441
5776
|
return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
5442
5777
|
})
|
|
5443
5778
|
);
|
|
5444
|
-
const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
|
|
5445
5779
|
if (options.signal?.aborted) {
|
|
5446
5780
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
5447
5781
|
return failure(error);
|
|
5448
5782
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5783
|
+
const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
|
|
5784
|
+
if (chunkDiagnostics.length > 0) {
|
|
5785
|
+
const error = new BatchChunkValidationError(chunkDiagnostics);
|
|
5786
|
+
for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
|
|
5452
5787
|
return failure(error);
|
|
5453
5788
|
}
|
|
5789
|
+
let fallbackJobScope;
|
|
5454
5790
|
try {
|
|
5455
5791
|
if (client.synthesizeChunks) {
|
|
5456
5792
|
const normalizedChunks = chunks.map((chunk) => {
|
|
@@ -5462,20 +5798,57 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5462
5798
|
outputFormat: options.outputFormat,
|
|
5463
5799
|
signal: options.signal,
|
|
5464
5800
|
timeoutMs: options.timeoutMs,
|
|
5801
|
+
timeouts: options.timeouts,
|
|
5465
5802
|
sourceNodePath: options.sourceNodePath,
|
|
5466
5803
|
concurrency: options.concurrency,
|
|
5467
|
-
retryOptions: options.retryOptions
|
|
5804
|
+
retryOptions: options.retryOptions,
|
|
5805
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
5806
|
+
resumeChunks: options.resumeChunks,
|
|
5807
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
5808
|
+
customMerger: options.customMerger,
|
|
5809
|
+
outputMimeType: options.outputMimeType,
|
|
5810
|
+
postMergeValidator: options.postMergeValidator,
|
|
5811
|
+
resumeValidation: options.resumeValidation
|
|
5468
5812
|
});
|
|
5469
5813
|
return { ok: true, success: true, status: "success", value };
|
|
5470
5814
|
}
|
|
5815
|
+
const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
|
|
5816
|
+
const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
|
|
5471
5817
|
const results = new Array(chunks.length);
|
|
5472
|
-
|
|
5818
|
+
const chunkStates = inputs.map((_chunk, chunkIndex) => ({
|
|
5819
|
+
chunkIndex,
|
|
5820
|
+
status: "pending",
|
|
5821
|
+
canResume: true
|
|
5822
|
+
}));
|
|
5823
|
+
const cachedChunks = new Map((options.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
|
|
5824
|
+
const invalidCachedIndices = /* @__PURE__ */ new Set();
|
|
5825
|
+
for (const [index, cached] of cachedChunks) {
|
|
5826
|
+
if (index < 0 || index >= chunks.length) continue;
|
|
5827
|
+
if (options.resumeValidation === "disabled" || cached.fingerprint === fingerprints[index]) {
|
|
5828
|
+
results[index] = cached;
|
|
5829
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: cached };
|
|
5830
|
+
} else invalidCachedIndices.add(index);
|
|
5831
|
+
}
|
|
5832
|
+
const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
|
|
5833
|
+
const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
|
|
5834
|
+
const jobStartedAt = Date.now();
|
|
5835
|
+
const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
|
|
5836
|
+
const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
|
|
5837
|
+
fallbackJobScope = jobScope;
|
|
5838
|
+
const failedIndices = /* @__PURE__ */ new Set();
|
|
5839
|
+
let firstError;
|
|
5840
|
+
let completed = [...results].filter((result) => result !== void 0).length;
|
|
5473
5841
|
let nextIndex = 0;
|
|
5474
5842
|
const concurrency = resolveConcurrency2(options.concurrency, chunks.length);
|
|
5475
5843
|
const worker = async () => {
|
|
5476
5844
|
while (true) {
|
|
5477
5845
|
const index = nextIndex++;
|
|
5478
5846
|
if (index >= chunks.length) return;
|
|
5847
|
+
if (!shouldSynthesize(index)) continue;
|
|
5848
|
+
if (firstError && options.cancelOnFailure !== false) {
|
|
5849
|
+
chunkStates[index] = { chunkIndex: index, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
5479
5852
|
const chunk = chunks[index];
|
|
5480
5853
|
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
5481
5854
|
const sourceNodePath = input.sourceNodePath;
|
|
@@ -5483,28 +5856,41 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5483
5856
|
pending(index, "synthesizing");
|
|
5484
5857
|
const startedAt = Date.now();
|
|
5485
5858
|
try {
|
|
5486
|
-
const
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5859
|
+
const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
|
|
5860
|
+
const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
|
|
5861
|
+
const chunkSignal = chunkScope?.signal ?? options.signal;
|
|
5862
|
+
let result;
|
|
5863
|
+
try {
|
|
5864
|
+
result = await retryableSynthesis(
|
|
5865
|
+
() => client.synthesizeSsml(input.ssml, {
|
|
5866
|
+
outputFormat: options.outputFormat,
|
|
5867
|
+
signal: chunkSignal,
|
|
5868
|
+
timeoutMs: options.timeouts?.perChunkMs ?? options.timeoutMs,
|
|
5869
|
+
sourceNodePath: input.sourceNodePath ?? options.sourceNodePath
|
|
5870
|
+
}),
|
|
5871
|
+
options.retryOptions,
|
|
5872
|
+
chunkSignal,
|
|
5873
|
+
(retryAttempt, nextRetryDelayMs) => options.onProgress?.({
|
|
5874
|
+
currentChunk: completed,
|
|
5875
|
+
totalChunks: chunks.length,
|
|
5876
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
5877
|
+
chunkIndex: index,
|
|
5878
|
+
originalTextRange: input.originalTextRange,
|
|
5879
|
+
status: "synthesizing",
|
|
5880
|
+
durationMs: Date.now() - startedAt,
|
|
5881
|
+
retryAttempt,
|
|
5882
|
+
nextRetryDelayMs,
|
|
5883
|
+
isRetrying: true
|
|
5884
|
+
}),
|
|
5885
|
+
jobDeadlineAt
|
|
5886
|
+
);
|
|
5887
|
+
} catch (error) {
|
|
5888
|
+
if (chunkScope?.timedOut())
|
|
5889
|
+
throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
|
|
5890
|
+
throw error;
|
|
5891
|
+
} finally {
|
|
5892
|
+
chunkScope?.dispose();
|
|
5893
|
+
}
|
|
5508
5894
|
results[index] = {
|
|
5509
5895
|
...result,
|
|
5510
5896
|
...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
|
|
@@ -5548,6 +5934,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5548
5934
|
}))
|
|
5549
5935
|
} : {}
|
|
5550
5936
|
};
|
|
5937
|
+
chunkStates[index] = { chunkIndex: index, status: "succeeded", canResume: true, result: results[index] };
|
|
5551
5938
|
completed += 1;
|
|
5552
5939
|
options.onProgress?.({
|
|
5553
5940
|
currentChunk: completed,
|
|
@@ -5559,6 +5946,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5559
5946
|
durationMs: Date.now() - startedAt
|
|
5560
5947
|
});
|
|
5561
5948
|
} catch (error) {
|
|
5949
|
+
const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
|
|
5950
|
+
firstError ?? (firstError = error);
|
|
5951
|
+
if (!wasCancelled) failedIndices.add(index);
|
|
5952
|
+
chunkStates[index] = {
|
|
5953
|
+
chunkIndex: index,
|
|
5954
|
+
status: wasCancelled ? "cancelled" : "failed",
|
|
5955
|
+
isOriginalFailure: !wasCancelled,
|
|
5956
|
+
canResume: true,
|
|
5957
|
+
error
|
|
5958
|
+
};
|
|
5562
5959
|
options.onProgress?.({
|
|
5563
5960
|
currentChunk: completed,
|
|
5564
5961
|
totalChunks: chunks.length,
|
|
@@ -5569,24 +5966,55 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5569
5966
|
durationMs: Date.now() - startedAt,
|
|
5570
5967
|
error
|
|
5571
5968
|
});
|
|
5572
|
-
|
|
5969
|
+
if (options.cancelOnFailure !== false) jobScope?.abort();
|
|
5970
|
+
return;
|
|
5573
5971
|
}
|
|
5574
5972
|
}
|
|
5575
5973
|
};
|
|
5576
5974
|
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
5975
|
+
if (firstError && options.cancelOnFailure !== false) {
|
|
5976
|
+
for (const [chunkIndex, state] of chunkStates.entries()) {
|
|
5977
|
+
if (state.status === "pending" && shouldSynthesize(chunkIndex)) {
|
|
5978
|
+
chunkStates[chunkIndex] = { chunkIndex, status: "cancelled", isOriginalFailure: false, canResume: true };
|
|
5979
|
+
}
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
if (failedIndices.size > 0) {
|
|
5983
|
+
const error = firstError ?? new Error("One or more SSML chunks failed to synthesize.");
|
|
5984
|
+
const synthesizedChunks = results.flatMap(
|
|
5985
|
+
(result, chunkIndex) => result ? [{ ...result, chunkIndex, fingerprint: fingerprints[chunkIndex] ?? "" }] : []
|
|
5986
|
+
);
|
|
5987
|
+
error.partialResult = {
|
|
5988
|
+
synthesizedChunks,
|
|
5989
|
+
completedChunks: synthesizedChunks,
|
|
5990
|
+
pendingChunkIndices: chunkStates.flatMap(
|
|
5991
|
+
(state) => state.status === "pending" || state.status === "cancelled" || state.status === "failed" ? [state.chunkIndex] : []
|
|
5992
|
+
),
|
|
5993
|
+
failedChunkIndices: [...failedIndices],
|
|
5994
|
+
cancelledChunkIndices: chunkStates.filter((state) => state.status === "cancelled").map((state) => state.chunkIndex),
|
|
5995
|
+
chunkStates,
|
|
5996
|
+
totalChunks: chunks.length
|
|
5997
|
+
};
|
|
5998
|
+
throw error;
|
|
5999
|
+
}
|
|
5577
6000
|
const orderedResults = results.filter((result) => result !== void 0);
|
|
5578
6001
|
return {
|
|
5579
6002
|
ok: true,
|
|
5580
6003
|
success: true,
|
|
5581
6004
|
status: "success",
|
|
5582
|
-
value: mergeSynthesisResults(orderedResults, {
|
|
6005
|
+
value: await mergeSynthesisResults(orderedResults, {
|
|
5583
6006
|
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
|
|
5584
|
-
signal: options.signal
|
|
6007
|
+
signal: jobScope?.signal ?? options.signal,
|
|
6008
|
+
customMerger: options.customMerger,
|
|
6009
|
+
outputMimeType: options.outputMimeType,
|
|
6010
|
+
postMergeValidator: options.postMergeValidator
|
|
5585
6011
|
})
|
|
5586
6012
|
};
|
|
5587
6013
|
} catch (error) {
|
|
5588
6014
|
const synthesisError = toSynthesisError(error);
|
|
5589
|
-
return failure(synthesisError);
|
|
6015
|
+
return failure(synthesisError, partialResultFrom(error));
|
|
6016
|
+
} finally {
|
|
6017
|
+
fallbackJobScope?.dispose();
|
|
5590
6018
|
}
|
|
5591
6019
|
}
|
|
5592
6020
|
function withValidationSignal(options, signal) {
|
|
@@ -5607,14 +6035,23 @@ var AzureTtsClient = class {
|
|
|
5607
6035
|
__privateSet(this, _options, options);
|
|
5608
6036
|
}
|
|
5609
6037
|
async synthesize(ssml) {
|
|
5610
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
6038
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
5611
6039
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
5612
6040
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
5613
|
-
const config = {
|
|
6041
|
+
const config = {
|
|
6042
|
+
endpoint,
|
|
6043
|
+
region,
|
|
6044
|
+
subscriptionKey,
|
|
6045
|
+
outputFormat,
|
|
6046
|
+
signal,
|
|
6047
|
+
timeoutMs,
|
|
6048
|
+
timeouts,
|
|
6049
|
+
retryOptions: __privateGet(this, _options).retryOptions
|
|
6050
|
+
};
|
|
5614
6051
|
return synthesizeSpeech(ssml, config);
|
|
5615
6052
|
}
|
|
5616
6053
|
async synthesizeSsml(ssml, options = {}) {
|
|
5617
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
6054
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
5618
6055
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
5619
6056
|
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
5620
6057
|
return synthesizeSsml(ssml, {
|
|
@@ -5624,13 +6061,20 @@ var AzureTtsClient = class {
|
|
|
5624
6061
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
5625
6062
|
signal: options.signal ?? signal,
|
|
5626
6063
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
6064
|
+
timeouts: options.timeouts ?? timeouts,
|
|
5627
6065
|
sourceNodePath: options.sourceNodePath,
|
|
5628
6066
|
sourceTextSegments: options.sourceTextSegments,
|
|
5629
|
-
sourceMarkers: options.sourceMarkers
|
|
6067
|
+
sourceMarkers: options.sourceMarkers,
|
|
6068
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
6069
|
+
cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
|
|
6070
|
+
customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
|
|
6071
|
+
outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
|
|
6072
|
+
postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
|
|
6073
|
+
resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
|
|
5630
6074
|
});
|
|
5631
6075
|
}
|
|
5632
6076
|
async synthesizeChunks(chunks, options = {}) {
|
|
5633
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
6077
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
|
|
5634
6078
|
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
5635
6079
|
return synthesizeSsmlChunks(chunks, {
|
|
5636
6080
|
endpoint,
|
|
@@ -5639,10 +6083,18 @@ var AzureTtsClient = class {
|
|
|
5639
6083
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
5640
6084
|
signal: options.signal ?? signal,
|
|
5641
6085
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
6086
|
+
timeouts: options.timeouts ?? timeouts,
|
|
5642
6087
|
sourceNodePath: options.sourceNodePath,
|
|
5643
6088
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
5644
6089
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
5645
|
-
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
6090
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
|
|
6091
|
+
cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
|
|
6092
|
+
resumeChunks: options.resumeChunks,
|
|
6093
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
6094
|
+
customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
|
|
6095
|
+
outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
|
|
6096
|
+
postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
|
|
6097
|
+
resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
|
|
5646
6098
|
});
|
|
5647
6099
|
}
|
|
5648
6100
|
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
@@ -5654,6 +6106,7 @@ var AzureTtsClient = class {
|
|
|
5654
6106
|
outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
|
|
5655
6107
|
signal: options.signal ?? __privateGet(this, _options).signal,
|
|
5656
6108
|
timeoutMs: options.timeoutMs ?? __privateGet(this, _options).timeoutMs,
|
|
6109
|
+
timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
|
|
5657
6110
|
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
5658
6111
|
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
5659
6112
|
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
@@ -5747,7 +6200,9 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5747
6200
|
voiceCount: sortedVoices.length,
|
|
5748
6201
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5749
6202
|
apiVersion: AZURE_VOICE_API_VERSION,
|
|
5750
|
-
regions
|
|
6203
|
+
regions,
|
|
6204
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
6205
|
+
regionDiffs: {}
|
|
5751
6206
|
}
|
|
5752
6207
|
};
|
|
5753
6208
|
}
|
|
@@ -5757,6 +6212,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5757
6212
|
AzureTtsClient,
|
|
5758
6213
|
AzureTtsError,
|
|
5759
6214
|
AzureTtsSdkError,
|
|
6215
|
+
BatchChunkValidationError,
|
|
5760
6216
|
ChunkValidationError,
|
|
5761
6217
|
DEFAULT_OUTPUT_FORMAT,
|
|
5762
6218
|
MergeError,
|
|
@@ -5767,6 +6223,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5767
6223
|
buildPartialSsml,
|
|
5768
6224
|
buildSsml,
|
|
5769
6225
|
canMergeAudioFormat,
|
|
6226
|
+
computeChunkFingerprint,
|
|
5770
6227
|
createAzureUrlValidatorRunner,
|
|
5771
6228
|
extractSsmlText,
|
|
5772
6229
|
extractSsmlTranslatableText,
|
|
@@ -5774,6 +6231,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5774
6231
|
fromPlainTextToSsml,
|
|
5775
6232
|
getAzureVoiceCatalogMetadata,
|
|
5776
6233
|
getBuiltInVoiceCatalogMetadata,
|
|
6234
|
+
getRetryAfterDelayMs,
|
|
5777
6235
|
getSsmlSourceMap,
|
|
5778
6236
|
inspectAudioSpecification,
|
|
5779
6237
|
isValidAzureAudioDuration,
|