@ssml-builder-js/azure-tts-client 2.15.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 +22 -0
- package/dist/index.d.mts +121 -7
- package/dist/index.d.ts +121 -7
- package/dist/index.js +786 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +786 -171
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +17 -4
- package/src/errors.ts +66 -2
- package/src/index.ts +26 -2
- package/src/safe.ts +414 -118
- package/src/synthesis.ts +514 -69
- package/src/types.ts +89 -0
- package/src/voiceCatalog.ts +4 -0
- package/test/v216-pipeline.test.ts +110 -0
- package/test/v217-pipeline.test.ts +125 -0
package/src/client.ts
CHANGED
|
@@ -19,16 +19,16 @@ export class AzureTtsClient {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async synthesize(ssml: string): Promise<ArrayBuffer> {
|
|
22
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
|
|
22
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
|
|
23
23
|
const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
24
24
|
this.#options.logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
25
25
|
|
|
26
|
-
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
26
|
+
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts };
|
|
27
27
|
return synthesizeSpeech(ssml, config);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async synthesizeSsml(ssml: string, options: Partial<TtsConfig> = {}): Promise<SsmlSynthesisResult> {
|
|
31
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
|
|
31
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
|
|
32
32
|
const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
33
33
|
this.#options.logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
34
34
|
|
|
@@ -39,6 +39,7 @@ export class AzureTtsClient {
|
|
|
39
39
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
40
40
|
signal: options.signal ?? signal,
|
|
41
41
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
42
|
+
timeouts: options.timeouts ?? timeouts,
|
|
42
43
|
sourceNodePath: options.sourceNodePath,
|
|
43
44
|
sourceTextSegments: options.sourceTextSegments,
|
|
44
45
|
sourceMarkers: options.sourceMarkers,
|
|
@@ -49,7 +50,7 @@ export class AzureTtsClient {
|
|
|
49
50
|
chunks: readonly (SsmlSynthesisChunk | string)[],
|
|
50
51
|
options: SynthesizeChunksOptions = {},
|
|
51
52
|
): Promise<SsmlSynthesisResult> {
|
|
52
|
-
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
|
|
53
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
|
|
53
54
|
const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
54
55
|
return synthesizeSsmlChunks(chunks, {
|
|
55
56
|
endpoint,
|
|
@@ -58,8 +59,17 @@ export class AzureTtsClient {
|
|
|
58
59
|
outputFormat: options.outputFormat ?? outputFormat,
|
|
59
60
|
signal: options.signal ?? signal,
|
|
60
61
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
62
|
+
timeouts: options.timeouts ?? timeouts,
|
|
61
63
|
sourceNodePath: options.sourceNodePath,
|
|
62
64
|
onProgress: options.onProgress ?? this.#options.onProgress,
|
|
65
|
+
concurrency: options.concurrency ?? this.#options.concurrency,
|
|
66
|
+
retryOptions: options.retryOptions ?? this.#options.retryOptions,
|
|
67
|
+
cancelOnFailure: options.cancelOnFailure,
|
|
68
|
+
resumeChunks: options.resumeChunks,
|
|
69
|
+
resumeChunkIndices: options.resumeChunkIndices,
|
|
70
|
+
customMerger: options.customMerger,
|
|
71
|
+
outputMimeType: options.outputMimeType,
|
|
72
|
+
postMergeValidator: options.postMergeValidator,
|
|
63
73
|
});
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -76,7 +86,10 @@ export class AzureTtsClient {
|
|
|
76
86
|
outputFormat: options.outputFormat ?? this.#options.outputFormat,
|
|
77
87
|
signal: options.signal ?? this.#options.signal,
|
|
78
88
|
timeoutMs: options.timeoutMs ?? this.#options.timeoutMs,
|
|
89
|
+
timeouts: options.timeouts ?? this.#options.timeouts,
|
|
79
90
|
onProgress: options.onProgress ?? this.#options.onProgress,
|
|
91
|
+
concurrency: options.concurrency ?? this.#options.concurrency,
|
|
92
|
+
retryOptions: options.retryOptions ?? this.#options.retryOptions,
|
|
80
93
|
});
|
|
81
94
|
}
|
|
82
95
|
|
package/src/errors.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type SynthesisErrorKind =
|
|
|
2
2
|
| "validation-error"
|
|
3
3
|
| "azure-api-error"
|
|
4
4
|
| "merge-error"
|
|
5
|
+
| "audio-format-mismatch"
|
|
5
6
|
| "unsupported-format-error"
|
|
6
7
|
| "cancelled"
|
|
7
8
|
| "timeout";
|
|
@@ -12,17 +13,59 @@ export class AzureTtsError extends Error {
|
|
|
12
13
|
readonly statusText: string;
|
|
13
14
|
readonly responseBody: string;
|
|
14
15
|
readonly requestId: string | null;
|
|
16
|
+
readonly retryAfterMs?: number;
|
|
15
17
|
|
|
16
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
status: number,
|
|
20
|
+
statusText: string,
|
|
21
|
+
responseBody: string,
|
|
22
|
+
requestId: string | null,
|
|
23
|
+
responseHeaders?: Headers | Readonly<Record<string, string>>,
|
|
24
|
+
) {
|
|
17
25
|
super(`Azure TTS request failed: ${status} ${statusText}`);
|
|
18
26
|
this.name = "AzureTtsError";
|
|
19
27
|
this.status = status;
|
|
20
28
|
this.statusText = statusText;
|
|
21
29
|
this.responseBody = responseBody;
|
|
22
30
|
this.requestId = requestId;
|
|
31
|
+
const value =
|
|
32
|
+
responseHeaders instanceof Headers
|
|
33
|
+
? responseHeaders.get("retry-after")
|
|
34
|
+
: (responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"]);
|
|
35
|
+
const seconds = value ? Number(value.trim()) : NaN;
|
|
36
|
+
const date = value ? Date.parse(value) : NaN;
|
|
37
|
+
if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1000;
|
|
38
|
+
else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
|
|
23
39
|
}
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
/** Reads Retry-After from an error-like value, returning milliseconds when present. */
|
|
43
|
+
export function getRetryAfterDelayMs(error: unknown): number | undefined {
|
|
44
|
+
if (error instanceof AzureTtsError && error.retryAfterMs !== undefined) return error.retryAfterMs;
|
|
45
|
+
if (!error || typeof error !== "object") return undefined;
|
|
46
|
+
const candidate = error as { retryAfterMs?: unknown; headers?: unknown; response?: unknown };
|
|
47
|
+
if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
|
|
48
|
+
const headers = candidate.headers ?? (candidate.response as { headers?: unknown } | undefined)?.headers;
|
|
49
|
+
if (headers instanceof Headers) {
|
|
50
|
+
const value = headers.get("retry-after");
|
|
51
|
+
if (!value) return undefined;
|
|
52
|
+
const seconds = Number(value.trim());
|
|
53
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
|
|
54
|
+
const date = Date.parse(value);
|
|
55
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
|
|
56
|
+
}
|
|
57
|
+
if (headers && typeof headers === "object") {
|
|
58
|
+
const value =
|
|
59
|
+
(headers as Record<string, unknown>)["retry-after"] ?? (headers as Record<string, unknown>)["Retry-After"];
|
|
60
|
+
if (typeof value !== "string") return undefined;
|
|
61
|
+
const seconds = Number(value.trim());
|
|
62
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
|
|
63
|
+
const date = Date.parse(value);
|
|
64
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
26
69
|
export class AzureTtsSdkError extends AzureTtsError {
|
|
27
70
|
readonly errorDetails: string;
|
|
28
71
|
|
|
@@ -63,6 +106,18 @@ export class MergeError extends Error {
|
|
|
63
106
|
}
|
|
64
107
|
}
|
|
65
108
|
|
|
109
|
+
/** Thrown when chunk headers describe incompatible audio streams. */
|
|
110
|
+
export class AudioFormatMismatchError extends Error {
|
|
111
|
+
readonly kind = "audio-format-mismatch" as const;
|
|
112
|
+
readonly inputSpecs: readonly AudioSpecification[];
|
|
113
|
+
|
|
114
|
+
constructor(message: string, inputSpecs: readonly AudioSpecification[] = []) {
|
|
115
|
+
super(message);
|
|
116
|
+
this.name = "AudioFormatMismatchError";
|
|
117
|
+
this.inputSpecs = inputSpecs;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
66
121
|
/** Thrown when audio buffers require container re-multiplexing before they can be merged. */
|
|
67
122
|
export class UnsupportedMergeFormatError extends Error {
|
|
68
123
|
readonly kind = "unsupported-format-error" as const;
|
|
@@ -78,16 +133,24 @@ export class UnsupportedMergeFormatError extends Error {
|
|
|
78
133
|
export type AzureTtsSynthesisError =
|
|
79
134
|
| AzureTtsError
|
|
80
135
|
| MergeError
|
|
136
|
+
| AudioFormatMismatchError
|
|
81
137
|
| UnsupportedMergeFormatError
|
|
82
138
|
| SynthesisCancelledError
|
|
83
139
|
| SynthesisTimeoutError;
|
|
84
140
|
|
|
85
141
|
export function toSynthesisError(
|
|
86
142
|
error: unknown,
|
|
87
|
-
):
|
|
143
|
+
):
|
|
144
|
+
| AzureTtsError
|
|
145
|
+
| MergeError
|
|
146
|
+
| AudioFormatMismatchError
|
|
147
|
+
| UnsupportedMergeFormatError
|
|
148
|
+
| SynthesisCancelledError
|
|
149
|
+
| SynthesisTimeoutError {
|
|
88
150
|
if (
|
|
89
151
|
error instanceof AzureTtsError ||
|
|
90
152
|
error instanceof MergeError ||
|
|
153
|
+
error instanceof AudioFormatMismatchError ||
|
|
91
154
|
error instanceof UnsupportedMergeFormatError ||
|
|
92
155
|
error instanceof SynthesisCancelledError ||
|
|
93
156
|
error instanceof SynthesisTimeoutError
|
|
@@ -103,3 +166,4 @@ export function createSpeechSdkError(error: unknown): AzureTtsSdkError {
|
|
|
103
166
|
const message = error instanceof Error ? error.message : String(error);
|
|
104
167
|
return new AzureTtsSdkError(message);
|
|
105
168
|
}
|
|
169
|
+
import type { AudioSpecification } from "./types.ts";
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export type {
|
|
6
6
|
AzureTtsClientOptions,
|
|
7
7
|
AzureTtsLogger,
|
|
8
|
+
AudioSpecification,
|
|
9
|
+
MappingStatus,
|
|
8
10
|
MergedSynthesisResult,
|
|
9
11
|
SsmlSynthesisBookmark,
|
|
10
12
|
SsmlSynthesisBoundary,
|
|
@@ -14,15 +16,25 @@ export type {
|
|
|
14
16
|
SynthesisProgressEvent,
|
|
15
17
|
SynthesizeChunksOptions,
|
|
16
18
|
SynthesisChunkStatus,
|
|
19
|
+
RetryOptions,
|
|
20
|
+
SynthesisTimeouts,
|
|
21
|
+
SynthesizedChunk,
|
|
22
|
+
PartialChunkSynthesisResult,
|
|
23
|
+
PartialSynthesisResult,
|
|
24
|
+
CustomMergerContext,
|
|
25
|
+
CustomAudioMerger,
|
|
26
|
+
PostMergeValidator,
|
|
17
27
|
TtsConfig,
|
|
18
28
|
} from "./types.ts";
|
|
19
29
|
export {
|
|
20
30
|
AzureTtsError,
|
|
21
31
|
AzureTtsSdkError,
|
|
32
|
+
AudioFormatMismatchError,
|
|
22
33
|
MergeError,
|
|
23
34
|
SynthesisCancelledError,
|
|
24
35
|
SynthesisTimeoutError,
|
|
25
36
|
UnsupportedMergeFormatError,
|
|
37
|
+
getRetryAfterDelayMs,
|
|
26
38
|
} from "./errors.ts";
|
|
27
39
|
export type { AzureTtsSynthesisError, SynthesisErrorKind } from "./errors.ts";
|
|
28
40
|
export { AzureTtsClient } from "./client.ts";
|
|
@@ -32,13 +44,24 @@ export {
|
|
|
32
44
|
canMergeAudioFormat,
|
|
33
45
|
mergeAudioBuffers,
|
|
34
46
|
mergeSynthesisResults,
|
|
47
|
+
inspectAudioSpecification,
|
|
35
48
|
resolveMergeAudioFormat,
|
|
36
49
|
synthesizeSsmlChunks,
|
|
37
50
|
} from "./synthesis.ts";
|
|
38
|
-
export type {
|
|
51
|
+
export type {
|
|
52
|
+
InputAudioSpecs,
|
|
53
|
+
MergeAudioFormat,
|
|
54
|
+
MergeAudioOptions,
|
|
55
|
+
MergeSynthesisOptions,
|
|
56
|
+
} from "./synthesis.ts";
|
|
39
57
|
export { DEFAULT_OUTPUT_FORMAT, resolveMimeType } from "./outputFormats.ts";
|
|
40
58
|
export type { AzureTtsOutputFormat } from "./outputFormats.ts";
|
|
41
|
-
export {
|
|
59
|
+
export {
|
|
60
|
+
BatchChunkValidationError,
|
|
61
|
+
ChunkValidationError,
|
|
62
|
+
synthesizeSsmlChunksSafe,
|
|
63
|
+
synthesizeSsmlSafe,
|
|
64
|
+
} from "./safe.ts";
|
|
42
65
|
export type {
|
|
43
66
|
AzureApiErrorResult,
|
|
44
67
|
Result,
|
|
@@ -51,6 +74,7 @@ export type {
|
|
|
51
74
|
SsmlSynthesisChunksSafeResult,
|
|
52
75
|
SynthesizeSsmlChunksSafeOptions,
|
|
53
76
|
ValidationErrorResult,
|
|
77
|
+
ChunkDiagnostics,
|
|
54
78
|
} from "./safe.ts";
|
|
55
79
|
export { fetchAzureVoiceCatalog } from "./voiceCatalog.ts";
|
|
56
80
|
export type {
|