@leiboss/dsh-tts 0.0.0-stage → 0.2.1
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 +74 -0
- package/LICENSE +21 -0
- package/README.md +352 -2
- package/cordis.patch.yml +4 -0
- package/dist/audio-route.d.ts +116 -0
- package/dist/audio-route.d.ts.map +1 -0
- package/dist/audio-route.js +406 -0
- package/dist/audio-route.js.map +1 -0
- package/dist/cache.d.ts +41 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +105 -0
- package/dist/cache.js.map +1 -0
- package/dist/chunked-tts.d.ts +34 -0
- package/dist/chunked-tts.d.ts.map +1 -0
- package/dist/chunked-tts.js +121 -0
- package/dist/chunked-tts.js.map +1 -0
- package/dist/chunker.d.ts +19 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +98 -0
- package/dist/chunker.js.map +1 -0
- package/dist/client.js +90 -0
- package/dist/concurrency.d.ts +33 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +95 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config.d.ts +133 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +143 -0
- package/dist/config.js.map +1 -0
- package/dist/edge-tts.d.ts +74 -0
- package/dist/edge-tts.d.ts.map +1 -0
- package/dist/edge-tts.js +154 -0
- package/dist/edge-tts.js.map +1 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +68 -0
- package/dist/errors.js.map +1 -0
- package/dist/fallback.d.ts +55 -0
- package/dist/fallback.d.ts.map +1 -0
- package/dist/fallback.js +79 -0
- package/dist/fallback.js.map +1 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +617 -0
- package/dist/index.js.map +1 -0
- package/dist/probe.d.ts +42 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +63 -0
- package/dist/probe.js.map +1 -0
- package/dist/provider.d.ts +33 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +7 -0
- package/dist/provider.js.map +1 -0
- package/dist/sapi-tts.d.ts +29 -0
- package/dist/sapi-tts.d.ts.map +1 -0
- package/dist/sapi-tts.js +131 -0
- package/dist/sapi-tts.js.map +1 -0
- package/dist/self-check.d.ts +24 -0
- package/dist/self-check.d.ts.map +1 -0
- package/dist/self-check.js +72 -0
- package/dist/self-check.js.map +1 -0
- package/dist/voices.d.ts +21 -0
- package/dist/voices.d.ts.map +1 -0
- package/dist/voices.js +74 -0
- package/dist/voices.js.map +1 -0
- package/icon.svg +6 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +88 -4
package/dist/config.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { ErrorCode, TtsError } from "./errors.js";
|
|
5
|
+
/** Voice Edge-TTS uses when neither config nor the call specifies one. */
|
|
6
|
+
export const DEFAULT_VOICE = 'zh-CN-XiaoxiaoNeural';
|
|
7
|
+
/** Rate Edge-TTS uses when the call omits one. */
|
|
8
|
+
export const DEFAULT_RATE = '+0%';
|
|
9
|
+
/** Per-synthesis subprocess deadline. */
|
|
10
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
11
|
+
/** CLI name resolved through PATH unless overridden. */
|
|
12
|
+
export const DEFAULT_EDGE_TTS_BINARY = 'edge-tts';
|
|
13
|
+
/** Subdirectory of `DSH_HOME` that owns every generated audio file. */
|
|
14
|
+
export const OUTPUT_SUBDIR = 'tts-output';
|
|
15
|
+
/** Target characters per chunk, and the one-shot threshold. */
|
|
16
|
+
export const DEFAULT_CHUNK_SIZE = 1_200;
|
|
17
|
+
/** Long texts are chunked by default. */
|
|
18
|
+
export const DEFAULT_CHUNKING_ENABLED = true;
|
|
19
|
+
/** Engines the fallback chain understands, in default priority order. */
|
|
20
|
+
export const DEFAULT_FALLBACK_ORDER = [
|
|
21
|
+
'edge-tts',
|
|
22
|
+
'sapi',
|
|
23
|
+
'speechSynthesis',
|
|
24
|
+
];
|
|
25
|
+
/** Fallback is enabled unless configured otherwise. */
|
|
26
|
+
export const DEFAULT_FALLBACK_ENABLED = true;
|
|
27
|
+
/** Syntheses allowed to run at the same time. */
|
|
28
|
+
export const DEFAULT_MAX_CONCURRENT = 2;
|
|
29
|
+
/** Callers allowed to wait for a synthesis slot. */
|
|
30
|
+
export const DEFAULT_QUEUE_SIZE = 4;
|
|
31
|
+
/** How long a caller may wait for a slot before TTS_BUSY. */
|
|
32
|
+
export const DEFAULT_QUEUE_TIMEOUT_MS = 10_000;
|
|
33
|
+
/** Upper bound for one complete `tts_generate` call, across all chunks. */
|
|
34
|
+
export const DEFAULT_TOTAL_TIMEOUT_MS = 180_000;
|
|
35
|
+
/** Extra Host names accepted by the audio route (reverse proxies). */
|
|
36
|
+
export const DEFAULT_TRUSTED_HOSTS = [];
|
|
37
|
+
/** Narrow an arbitrary config string to a known engine. */
|
|
38
|
+
export function isTtsEngine(value) {
|
|
39
|
+
return DEFAULT_FALLBACK_ORDER.includes(value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Config schema exposed to the DSH configuration layer. Defaults live here (not
|
|
43
|
+
* in call sites) so the settings surface and runtime behaviour agree.
|
|
44
|
+
*/
|
|
45
|
+
export const Config = z.object({
|
|
46
|
+
outputDir: z.string(),
|
|
47
|
+
defaultVoice: z.string().default(DEFAULT_VOICE),
|
|
48
|
+
timeoutMs: z.number().default(DEFAULT_TIMEOUT_MS),
|
|
49
|
+
edgeTtsBinary: z.string().default(DEFAULT_EDGE_TTS_BINARY),
|
|
50
|
+
chunkSize: z.number().default(DEFAULT_CHUNK_SIZE),
|
|
51
|
+
chunkingEnabled: z.boolean().default(DEFAULT_CHUNKING_ENABLED),
|
|
52
|
+
fallbackEnabled: z.boolean().default(DEFAULT_FALLBACK_ENABLED),
|
|
53
|
+
fallbackOrder: z.array(z.string()).default([...DEFAULT_FALLBACK_ORDER]),
|
|
54
|
+
maxConcurrent: z.number().default(DEFAULT_MAX_CONCURRENT),
|
|
55
|
+
queueSize: z.number().default(DEFAULT_QUEUE_SIZE),
|
|
56
|
+
queueTimeoutMs: z.number().default(DEFAULT_QUEUE_TIMEOUT_MS),
|
|
57
|
+
totalTimeoutMs: z.number().default(DEFAULT_TOTAL_TIMEOUT_MS),
|
|
58
|
+
trustedHosts: z.array(z.string()).default([...DEFAULT_TRUSTED_HOSTS]),
|
|
59
|
+
proxy: z.string(),
|
|
60
|
+
});
|
|
61
|
+
function nonEmpty(value) {
|
|
62
|
+
const trimmed = value?.trim();
|
|
63
|
+
return trimmed ? trimmed : undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve config defaults without touching the filesystem. A missing
|
|
67
|
+
* `DSH_HOME` is not fatal here: explicit `outputDir` still works, and the
|
|
68
|
+
* self-check needs to report the missing value instead of failing to load.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveConfig(raw, env = process.env) {
|
|
71
|
+
const dshHome = nonEmpty(env.DSH_HOME) ?? '';
|
|
72
|
+
const configuredDir = nonEmpty(raw?.outputDir);
|
|
73
|
+
const outputDir = configuredDir
|
|
74
|
+
? resolve(configuredDir)
|
|
75
|
+
: dshHome
|
|
76
|
+
? resolve(dshHome, OUTPUT_SUBDIR)
|
|
77
|
+
: join(homedir(), '.dsh', OUTPUT_SUBDIR);
|
|
78
|
+
return {
|
|
79
|
+
dshHome,
|
|
80
|
+
outputDir,
|
|
81
|
+
defaultVoice: nonEmpty(raw?.defaultVoice) ?? DEFAULT_VOICE,
|
|
82
|
+
timeoutMs: raw?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
83
|
+
edgeTtsBinary: nonEmpty(raw?.edgeTtsBinary) ?? DEFAULT_EDGE_TTS_BINARY,
|
|
84
|
+
chunkSize: raw?.chunkSize ?? DEFAULT_CHUNK_SIZE,
|
|
85
|
+
chunkingEnabled: raw?.chunkingEnabled ?? DEFAULT_CHUNKING_ENABLED,
|
|
86
|
+
fallbackEnabled: raw?.fallbackEnabled ?? DEFAULT_FALLBACK_ENABLED,
|
|
87
|
+
fallbackOrder: raw?.fallbackOrder === undefined
|
|
88
|
+
? [...DEFAULT_FALLBACK_ORDER]
|
|
89
|
+
: raw.fallbackOrder.filter(isTtsEngine),
|
|
90
|
+
maxConcurrent: raw?.maxConcurrent ?? DEFAULT_MAX_CONCURRENT,
|
|
91
|
+
queueSize: raw?.queueSize ?? DEFAULT_QUEUE_SIZE,
|
|
92
|
+
queueTimeoutMs: raw?.queueTimeoutMs ?? DEFAULT_QUEUE_TIMEOUT_MS,
|
|
93
|
+
totalTimeoutMs: raw?.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
|
94
|
+
trustedHosts: (raw?.trustedHosts ?? [...DEFAULT_TRUSTED_HOSTS])
|
|
95
|
+
.map((host) => host.trim().toLowerCase())
|
|
96
|
+
.filter((host) => host.length > 0),
|
|
97
|
+
proxy: nonEmpty(raw?.proxy),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Validate the settings a synthesis actually needs. Kept separate from
|
|
102
|
+
* {@link resolveConfig} so loading the plugin never fails at mount time.
|
|
103
|
+
*/
|
|
104
|
+
export function requireSynthesisConfig(config) {
|
|
105
|
+
if (!config.outputDir) {
|
|
106
|
+
throw new TtsError(ErrorCode.DshHomeNotSet, 'No output directory is available: DSH_HOME is not set and `outputDir` is not configured.', 'Set DSH_HOME in the DSH runtime, or set the plugin config field `outputDir`.');
|
|
107
|
+
}
|
|
108
|
+
if (!Number.isFinite(config.timeoutMs) || config.timeoutMs <= 0) {
|
|
109
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: timeoutMs must be a positive number, got ${JSON.stringify(config.timeoutMs)}.`, 'Set `timeoutMs` to a positive millisecond value (default 30000).');
|
|
110
|
+
}
|
|
111
|
+
if (config.chunkingEnabled &&
|
|
112
|
+
(!Number.isInteger(config.chunkSize) || config.chunkSize <= 0)) {
|
|
113
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: chunkSize must be a positive integer, got ${JSON.stringify(config.chunkSize)}.`, 'Set `chunkSize` to a positive character count (default 1200), or disable chunking.');
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isInteger(config.maxConcurrent) ||
|
|
116
|
+
config.maxConcurrent < 1) {
|
|
117
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: maxConcurrent must be a positive integer, got ${JSON.stringify(config.maxConcurrent)}.`, 'Set `maxConcurrent` to at least 1 (default 2).');
|
|
118
|
+
}
|
|
119
|
+
if (!Number.isInteger(config.queueSize) || config.queueSize < 0) {
|
|
120
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: queueSize must be a non-negative integer, got ${JSON.stringify(config.queueSize)}.`, 'Set `queueSize` to 0 or more (default 4).');
|
|
121
|
+
}
|
|
122
|
+
if (!Number.isFinite(config.queueTimeoutMs) || config.queueTimeoutMs <= 0) {
|
|
123
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: queueTimeoutMs must be a positive number, got ${JSON.stringify(config.queueTimeoutMs)}.`, 'Set `queueTimeoutMs` to a positive millisecond value (default 10000).');
|
|
124
|
+
}
|
|
125
|
+
if (!Number.isFinite(config.totalTimeoutMs) || config.totalTimeoutMs <= 0) {
|
|
126
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: totalTimeoutMs must be a positive number, got ${JSON.stringify(config.totalTimeoutMs)}.`, 'Set `totalTimeoutMs` to a positive millisecond value (default 180000).');
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
outputDir: config.outputDir,
|
|
130
|
+
timeoutMs: config.timeoutMs,
|
|
131
|
+
chunkSize: config.chunkSize,
|
|
132
|
+
chunkingEnabled: config.chunkingEnabled,
|
|
133
|
+
fallbackEnabled: config.fallbackEnabled,
|
|
134
|
+
fallbackOrder: config.fallbackOrder,
|
|
135
|
+
maxConcurrent: config.maxConcurrent,
|
|
136
|
+
queueSize: config.queueSize,
|
|
137
|
+
queueTimeoutMs: config.queueTimeoutMs,
|
|
138
|
+
totalTimeoutMs: config.totalTimeoutMs,
|
|
139
|
+
trustedHosts: config.trustedHosts,
|
|
140
|
+
proxy: config.proxy,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Minimal child-process surface used by the runner, kept injectable for tests. */
|
|
2
|
+
export interface ChildStream {
|
|
3
|
+
on(event: 'data', listener: (chunk: Buffer | string) => void): unknown;
|
|
4
|
+
}
|
|
5
|
+
/** Structural view of the parts of `ChildProcess` this module relies on. */
|
|
6
|
+
export interface ChildProcessLike {
|
|
7
|
+
stdout: ChildStream | null;
|
|
8
|
+
stderr: ChildStream | null;
|
|
9
|
+
on(event: 'error', listener: (error: Error) => void): unknown;
|
|
10
|
+
on(event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
|
|
11
|
+
kill(signal?: NodeJS.Signals | number): boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Options passed to the injectable spawn implementation. */
|
|
14
|
+
export interface SpawnOptions {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'];
|
|
16
|
+
windowsHide: boolean;
|
|
17
|
+
env: NodeJS.ProcessEnv;
|
|
18
|
+
}
|
|
19
|
+
/** Injection point for the real `child_process.spawn` (or a test double). */
|
|
20
|
+
export type SpawnFunction = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcessLike;
|
|
21
|
+
/** Captured outcome of one child process. A non-zero exit is not an error here. */
|
|
22
|
+
export interface ProcessResult {
|
|
23
|
+
code: number | null;
|
|
24
|
+
signal: NodeJS.Signals | null;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
}
|
|
28
|
+
/** Controls shared by every child-process call. */
|
|
29
|
+
export interface RunProcessOptions {
|
|
30
|
+
timeoutMs: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
spawnImpl?: SpawnFunction;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Run one child process, consuming stdout and stderr concurrently so neither
|
|
36
|
+
* pipe can deadlock. Resolves for any completed exit status; throws structured
|
|
37
|
+
* errors for spawn denial, cancellation, and timeout (killing the child).
|
|
38
|
+
*/
|
|
39
|
+
export declare function runProcess(command: string, args: readonly string[], options: RunProcessOptions): Promise<ProcessResult>;
|
|
40
|
+
/** Settings for one synthesis call. */
|
|
41
|
+
export interface SynthesizeOptions {
|
|
42
|
+
voice: string;
|
|
43
|
+
rate: string;
|
|
44
|
+
/** Pitch adjustment such as `+10Hz`; omitted for Edge-TTS's own default. */
|
|
45
|
+
pitch?: string;
|
|
46
|
+
/** Volume adjustment such as `-10%`; omitted for Edge-TTS's own default. */
|
|
47
|
+
volume?: string;
|
|
48
|
+
/**
|
|
49
|
+
* HTTP proxy for the WSS connection, forwarded as `--proxy`. Edge-TTS does
|
|
50
|
+
* not honour `HTTPS_PROXY`, so this is the only way to route it.
|
|
51
|
+
*/
|
|
52
|
+
proxy?: string;
|
|
53
|
+
binary: string;
|
|
54
|
+
outputDir: string;
|
|
55
|
+
/** Stable cache key; also names the output file. */
|
|
56
|
+
hash: string;
|
|
57
|
+
timeoutMs: number;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
spawnImpl?: SpawnFunction;
|
|
60
|
+
/** Injectable clock for deterministic tests. */
|
|
61
|
+
now?: Date;
|
|
62
|
+
}
|
|
63
|
+
/** Canonical audio description returned to the tool layer. */
|
|
64
|
+
export interface SynthesizedAudio {
|
|
65
|
+
filePath: string;
|
|
66
|
+
format: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Invoke the Edge-TTS CLI for `text` and return the finished file. Writes to a
|
|
70
|
+
* `.partial` sibling and renames on success, so a crashed or concurrent run
|
|
71
|
+
* never leaves a file that the cache would treat as complete.
|
|
72
|
+
*/
|
|
73
|
+
export declare function synthesize(text: string, options: SynthesizeOptions): Promise<SynthesizedAudio>;
|
|
74
|
+
//# sourceMappingURL=edge-tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-tts.d.ts","sourceRoot":"","sources":["../src/edge-tts.ts"],"names":[],"mappings":"AASA,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO,CAAA;CACvE;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IAC1B,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAA;IAC7D,EAAE,CACA,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,IAAI,GACrE,OAAO,CAAA;IACV,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;CAChD;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,WAAW,EAAE,OAAO,CAAA;IACpB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CACvB;AAED,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,YAAY,KAClB,gBAAgB,CAAA;AAKrB,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,aAAa,CAAA;CAC1B;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CAqFxB;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,gDAAgD;IAChD,GAAG,CAAC,EAAE,IAAI,CAAA;CACX;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAiBD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAyD3B"}
|
package/dist/edge-tts.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdir, rename, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { AUDIO_FORMAT, outputFilePath } from "./cache.js";
|
|
5
|
+
import { ErrorCode, TtsError, spawnFailure } from "./errors.js";
|
|
6
|
+
/** Upper bound on captured stderr/stdout text, keeping the tail. */
|
|
7
|
+
const MAX_CAPTURE = 8_000;
|
|
8
|
+
const defaultSpawn = (command, args, options) => spawn(command, [...args], options);
|
|
9
|
+
function appendTail(current, chunk) {
|
|
10
|
+
const next = current + chunk.toString();
|
|
11
|
+
return next.length > MAX_CAPTURE ? next.slice(next.length - MAX_CAPTURE) : next;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Run one child process, consuming stdout and stderr concurrently so neither
|
|
15
|
+
* pipe can deadlock. Resolves for any completed exit status; throws structured
|
|
16
|
+
* errors for spawn denial, cancellation, and timeout (killing the child).
|
|
17
|
+
*/
|
|
18
|
+
export function runProcess(command, args, options) {
|
|
19
|
+
const spawnImpl = options.spawnImpl ?? defaultSpawn;
|
|
20
|
+
const signal = options.signal;
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let child;
|
|
23
|
+
try {
|
|
24
|
+
child = spawnImpl(command, args, {
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
env: process.env,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
reject(spawnFailure(error));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let settled = false;
|
|
35
|
+
let timer;
|
|
36
|
+
let stdout = '';
|
|
37
|
+
let stderr = '';
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
if (timer !== undefined)
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
signal?.removeEventListener('abort', onAbort);
|
|
42
|
+
};
|
|
43
|
+
const fail = (error) => {
|
|
44
|
+
if (settled)
|
|
45
|
+
return;
|
|
46
|
+
settled = true;
|
|
47
|
+
cleanup();
|
|
48
|
+
reject(error);
|
|
49
|
+
};
|
|
50
|
+
const settle = (code, exitSignal) => {
|
|
51
|
+
if (settled)
|
|
52
|
+
return;
|
|
53
|
+
settled = true;
|
|
54
|
+
cleanup();
|
|
55
|
+
resolve({ code, signal: exitSignal, stdout, stderr });
|
|
56
|
+
};
|
|
57
|
+
const stop = () => {
|
|
58
|
+
try {
|
|
59
|
+
child.kill('SIGTERM');
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// The process already exited; nothing to stop.
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const onAbort = () => {
|
|
66
|
+
fail(new TtsError(ErrorCode.Aborted, 'Edge-TTS synthesis was aborted.'));
|
|
67
|
+
stop();
|
|
68
|
+
};
|
|
69
|
+
child.stdout?.on('data', (chunk) => {
|
|
70
|
+
stdout = appendTail(stdout, chunk);
|
|
71
|
+
});
|
|
72
|
+
child.stderr?.on('data', (chunk) => {
|
|
73
|
+
stderr = appendTail(stderr, chunk);
|
|
74
|
+
});
|
|
75
|
+
child.on('error', (error) => fail(spawnFailure(error)));
|
|
76
|
+
child.on('close', (code, exitSignal) => {
|
|
77
|
+
if (signal?.aborted) {
|
|
78
|
+
fail(new TtsError(ErrorCode.Aborted, 'Edge-TTS synthesis was aborted.'));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
settle(code, exitSignal);
|
|
82
|
+
});
|
|
83
|
+
timer = setTimeout(() => {
|
|
84
|
+
fail(new TtsError(ErrorCode.SynthesisTimeout, `Edge-TTS did not finish within ${options.timeoutMs} ms.`, 'Increase `timeoutMs` or check network connectivity to the Edge-TTS service. 若网络正常但持续超时,请检查是否需要对 speech.platform.bing.com:443 配置代理。注意:环境变量 HTTPS_PROXY 对 edge-tts 的 WSS 连接无效,必须通过本插件的 `proxy` 配置项透传 `--proxy` 参数。详见 README 的「已知限制」章节。'));
|
|
85
|
+
stop();
|
|
86
|
+
}, options.timeoutMs);
|
|
87
|
+
if (signal) {
|
|
88
|
+
if (signal.aborted)
|
|
89
|
+
onAbort();
|
|
90
|
+
else
|
|
91
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function outputFailure(target, error) {
|
|
96
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
97
|
+
return new TtsError(ErrorCode.OutputDirNotWritable, `Cannot write the audio file ${target}: ${detail}`, 'Check the DSH sandbox file policy and the output directory permissions. This is a filesystem denial, not a spawn EPERM.', { cause: error });
|
|
98
|
+
}
|
|
99
|
+
function stderrSuffix(stderr) {
|
|
100
|
+
const trimmed = stderr.trim();
|
|
101
|
+
return trimmed ? `\nstderr:\n${trimmed}` : '';
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Invoke the Edge-TTS CLI for `text` and return the finished file. Writes to a
|
|
105
|
+
* `.partial` sibling and renames on success, so a crashed or concurrent run
|
|
106
|
+
* never leaves a file that the cache would treat as complete.
|
|
107
|
+
*/
|
|
108
|
+
export async function synthesize(text, options) {
|
|
109
|
+
const filePath = outputFilePath(options.outputDir, options.hash, options.now);
|
|
110
|
+
const partialPath = `${filePath}.partial`;
|
|
111
|
+
try {
|
|
112
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw outputFailure(dirname(filePath), error);
|
|
116
|
+
}
|
|
117
|
+
const args = [
|
|
118
|
+
`--text=${text}`,
|
|
119
|
+
`--voice=${options.voice}`,
|
|
120
|
+
`--rate=${options.rate}`,
|
|
121
|
+
...(options.pitch === undefined ? [] : [`--pitch=${options.pitch}`]),
|
|
122
|
+
...(options.volume === undefined ? [] : [`--volume=${options.volume}`]),
|
|
123
|
+
...(options.proxy === undefined ? [] : [`--proxy=${options.proxy}`]),
|
|
124
|
+
`--write-media=${partialPath}`,
|
|
125
|
+
];
|
|
126
|
+
try {
|
|
127
|
+
const result = await runProcess(options.binary, args, {
|
|
128
|
+
timeoutMs: options.timeoutMs,
|
|
129
|
+
signal: options.signal,
|
|
130
|
+
spawnImpl: options.spawnImpl,
|
|
131
|
+
});
|
|
132
|
+
if (result.code !== 0) {
|
|
133
|
+
const status = result.code === null ? 'null' : String(result.code);
|
|
134
|
+
const signal = result.signal ? ` (signal ${result.signal})` : '';
|
|
135
|
+
throw new TtsError(ErrorCode.EdgeTtsFailed, `Edge-TTS exited with code ${status}${signal}.${stderrSuffix(result.stderr)}`, 'Check the voice and rate values and network connectivity, then run `tts_self_check`. 若网络正常但持续超时,请检查是否需要对 speech.platform.bing.com:443 配置代理。注意:环境变量 HTTPS_PROXY 对 edge-tts 的 WSS 连接无效,必须通过本插件的 `proxy` 配置项透传 `--proxy` 参数。详见 README 的「已知限制」章节。');
|
|
136
|
+
}
|
|
137
|
+
const info = await stat(partialPath).catch(() => undefined);
|
|
138
|
+
if (!info?.isFile() || info.size === 0) {
|
|
139
|
+
throw new TtsError(ErrorCode.EmptyAudio, 'Edge-TTS reported success but produced no audio bytes.', 'Retry the call; if it persists, run `tts_self_check` and inspect Edge-TTS output.');
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
await rename(partialPath, filePath);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
throw outputFailure(filePath, error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
await rm(partialPath, { force: true }).catch(() => { });
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
return { filePath, format: AUDIO_FORMAT };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=edge-tts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-tts.js","sourceRoot":"","sources":["../src/edge-tts.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { HarnessError } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
/**
|
|
3
|
+
* Stable, machine-readable failure codes. Callers route on these codes instead
|
|
4
|
+
* of parsing human-facing messages.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ErrorCode: {
|
|
7
|
+
readonly EmptyText: "EMPTY_TEXT";
|
|
8
|
+
readonly InvalidVoice: "INVALID_VOICE";
|
|
9
|
+
readonly InvalidRate: "INVALID_RATE";
|
|
10
|
+
readonly InvalidPitch: "INVALID_PITCH";
|
|
11
|
+
readonly InvalidVolume: "INVALID_VOLUME";
|
|
12
|
+
readonly InvalidConfig: "INVALID_CONFIG";
|
|
13
|
+
readonly DshHomeNotSet: "DSH_HOME_NOT_SET";
|
|
14
|
+
readonly OutputDirNotWritable: "OUTPUT_DIR_NOT_WRITABLE";
|
|
15
|
+
readonly EdgeTtsNotFound: "EDGE_TTS_NOT_FOUND";
|
|
16
|
+
readonly SpawnEperm: "SPAWN_EPERM";
|
|
17
|
+
readonly SpawnFailed: "SPAWN_FAILED";
|
|
18
|
+
readonly EdgeTtsFailed: "EDGE_TTS_FAILED";
|
|
19
|
+
readonly SynthesisTimeout: "SYNTHESIS_TIMEOUT";
|
|
20
|
+
readonly EmptyAudio: "EMPTY_AUDIO";
|
|
21
|
+
readonly Aborted: "ABORTED";
|
|
22
|
+
readonly TtsBusy: "TTS_BUSY";
|
|
23
|
+
readonly AllFallbacksFailed: "ALL_FALLBACKS_FAILED";
|
|
24
|
+
readonly SpeechSynthesisRequired: "SPEECH_SYNTHESIS_REQUIRED";
|
|
25
|
+
};
|
|
26
|
+
export type TtsErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
27
|
+
/**
|
|
28
|
+
* Structured plugin error: a routable `code`, a human-readable message, and an
|
|
29
|
+
* actionable suggestion. Extends the harness error base so the tool registry
|
|
30
|
+
* can retain the code in its failure metadata.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TtsError extends HarnessError {
|
|
33
|
+
/** Pre-suggestion message, kept verbatim for structured consumers. */
|
|
34
|
+
readonly detail: string;
|
|
35
|
+
/** Action the caller (or the user) can take to resolve the failure. */
|
|
36
|
+
readonly suggestion?: string;
|
|
37
|
+
constructor(code: TtsErrorCode, message: string, suggestion?: string, options?: ErrorOptions);
|
|
38
|
+
}
|
|
39
|
+
/** Read a Node `SystemError` code without assuming the thrown value's shape. */
|
|
40
|
+
export declare function errnoCode(error: unknown): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Translate a failed `spawn()` attempt into a structured error. `EPERM` is
|
|
43
|
+
* deliberately distinct: it means the sandbox refused process creation, not
|
|
44
|
+
* that the output directory is unwritable.
|
|
45
|
+
*/
|
|
46
|
+
export declare function spawnFailure(error: unknown): TtsError;
|
|
47
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;CAmBZ,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAA;AAErE;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,YAAY;IACxC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;gBAEhB,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAM7F;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAM5D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CA4BrD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { HarnessError } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
/**
|
|
3
|
+
* Stable, machine-readable failure codes. Callers route on these codes instead
|
|
4
|
+
* of parsing human-facing messages.
|
|
5
|
+
*/
|
|
6
|
+
export const ErrorCode = {
|
|
7
|
+
EmptyText: 'EMPTY_TEXT',
|
|
8
|
+
InvalidVoice: 'INVALID_VOICE',
|
|
9
|
+
InvalidRate: 'INVALID_RATE',
|
|
10
|
+
InvalidPitch: 'INVALID_PITCH',
|
|
11
|
+
InvalidVolume: 'INVALID_VOLUME',
|
|
12
|
+
InvalidConfig: 'INVALID_CONFIG',
|
|
13
|
+
DshHomeNotSet: 'DSH_HOME_NOT_SET',
|
|
14
|
+
OutputDirNotWritable: 'OUTPUT_DIR_NOT_WRITABLE',
|
|
15
|
+
EdgeTtsNotFound: 'EDGE_TTS_NOT_FOUND',
|
|
16
|
+
SpawnEperm: 'SPAWN_EPERM',
|
|
17
|
+
SpawnFailed: 'SPAWN_FAILED',
|
|
18
|
+
EdgeTtsFailed: 'EDGE_TTS_FAILED',
|
|
19
|
+
SynthesisTimeout: 'SYNTHESIS_TIMEOUT',
|
|
20
|
+
EmptyAudio: 'EMPTY_AUDIO',
|
|
21
|
+
Aborted: 'ABORTED',
|
|
22
|
+
TtsBusy: 'TTS_BUSY',
|
|
23
|
+
AllFallbacksFailed: 'ALL_FALLBACKS_FAILED',
|
|
24
|
+
SpeechSynthesisRequired: 'SPEECH_SYNTHESIS_REQUIRED',
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Structured plugin error: a routable `code`, a human-readable message, and an
|
|
28
|
+
* actionable suggestion. Extends the harness error base so the tool registry
|
|
29
|
+
* can retain the code in its failure metadata.
|
|
30
|
+
*/
|
|
31
|
+
export class TtsError extends HarnessError {
|
|
32
|
+
/** Pre-suggestion message, kept verbatim for structured consumers. */
|
|
33
|
+
detail;
|
|
34
|
+
/** Action the caller (or the user) can take to resolve the failure. */
|
|
35
|
+
suggestion;
|
|
36
|
+
constructor(code, message, suggestion, options) {
|
|
37
|
+
super(suggestion ? `${message}\nSuggestion: ${suggestion}` : message, code, options);
|
|
38
|
+
this.name = 'TtsError';
|
|
39
|
+
this.detail = message;
|
|
40
|
+
this.suggestion = suggestion;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Read a Node `SystemError` code without assuming the thrown value's shape. */
|
|
44
|
+
export function errnoCode(error) {
|
|
45
|
+
if (typeof error === 'object' && error !== null && 'code' in error) {
|
|
46
|
+
const code = error.code;
|
|
47
|
+
if (typeof code === 'string')
|
|
48
|
+
return code;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Translate a failed `spawn()` attempt into a structured error. `EPERM` is
|
|
54
|
+
* deliberately distinct: it means the sandbox refused process creation, not
|
|
55
|
+
* that the output directory is unwritable.
|
|
56
|
+
*/
|
|
57
|
+
export function spawnFailure(error) {
|
|
58
|
+
const code = errnoCode(error);
|
|
59
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
60
|
+
if (code === 'ENOENT') {
|
|
61
|
+
return new TtsError(ErrorCode.EdgeTtsNotFound, `Cannot start Edge-TTS: ${detail}`, 'Install it with `pip install edge-tts`, or point `edgeTtsBinary` at the executable.', { cause: error });
|
|
62
|
+
}
|
|
63
|
+
if (code === 'EPERM') {
|
|
64
|
+
return new TtsError(ErrorCode.SpawnEperm, `The DSH sandbox denied process creation for Edge-TTS (spawn EPERM): ${detail}`, 'Raise the DSH sandbox process/file policy and retry. This is a sandbox policy denial, not a missing or unwritable output directory.', { cause: error });
|
|
65
|
+
}
|
|
66
|
+
return new TtsError(ErrorCode.SpawnFailed, `Failed to spawn Edge-TTS (${code ?? 'unknown error'}): ${detail}`, 'Check that `edgeTtsBinary` names an existing, executable Edge-TTS CLI.', { cause: error });
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,YAAY;IACvB,YAAY,EAAE,eAAe;IAC7B,WAAW,EAAE,cAAc;IAC3B,YAAY,EAAE,eAAe;IAC7B,aAAa,EAAE,gBAAgB;IAC/B,aAAa,EAAE,gBAAgB;IAC/B,aAAa,EAAE,kBAAkB;IACjC,oBAAoB,EAAE,yBAAyB;IAC/C,eAAe,EAAE,oBAAoB;IACrC,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,iBAAiB;IAChC,gBAAgB,EAAE,mBAAmB;IACrC,UAAU,EAAE,aAAa;IACzB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,UAAU;IACnB,kBAAkB,EAAE,sBAAsB;IAC1C,uBAAuB,EAAE,2BAA2B;CAC5C,CAAA;AAIV;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,YAAY;IACxC,sEAAsE;IAC7D,MAAM,CAAQ;IACvB,uEAAuE;IAC9D,UAAU,CAAS;IAE5B,YAAY,IAAkB,EAAE,OAAe,EAAE,UAAmB,EAAE,OAAsB;QAC1F,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,iBAAiB,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QACpF,IAAI,CAAC,IAAI,GAAG,UAAU,CAAA;QACtB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAA;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;CACF;AAED,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACnE,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAA;QAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;IAC3C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAErE,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,IAAI,QAAQ,CACjB,SAAS,CAAC,eAAe,EACzB,0BAA0B,MAAM,EAAE,EAClC,qFAAqF,EACrF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;IACH,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,QAAQ,CACjB,SAAS,CAAC,UAAU,EACpB,uEAAuE,MAAM,EAAE,EAC/E,qIAAqI,EACrI,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;IACH,CAAC;IAED,OAAO,IAAI,QAAQ,CACjB,SAAS,CAAC,WAAW,EACrB,6BAA6B,IAAI,IAAI,eAAe,MAAM,MAAM,EAAE,EAClE,wEAAwE,EACxE,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback chain orchestration: Edge-TTS is the primary engine, and on an
|
|
3
|
+
* eligible failure the configured chain is tried in order. SAPI is Windows-only;
|
|
4
|
+
* the browser `speechSynthesis` entry produces a marker instead of a file (the
|
|
5
|
+
* host cannot produce audio without an engine). A chain with no reachable
|
|
6
|
+
* engine ends as `ALL_FALLBACKS_FAILED` listing every engine and reason.
|
|
7
|
+
*/
|
|
8
|
+
import type { TtsEngine } from './config.ts';
|
|
9
|
+
import type { SpawnFunction, SynthesizedAudio } from './edge-tts.ts';
|
|
10
|
+
import { TtsError } from './errors.ts';
|
|
11
|
+
import { type SapiOptions } from './sapi-tts.ts';
|
|
12
|
+
/** Whether a failed Edge-TTS attempt is worth retrying with another engine. */
|
|
13
|
+
export declare function isFallbackEligible(error: unknown): error is TtsError;
|
|
14
|
+
/** One engine that was skipped or failed, with a human-readable reason. */
|
|
15
|
+
export interface FallbackAttempt {
|
|
16
|
+
engine: TtsEngine;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
/** Either a produced file or a request for the client to speak the text. */
|
|
20
|
+
export type FallbackOutcome = {
|
|
21
|
+
kind: 'audio';
|
|
22
|
+
audio: SynthesizedAudio;
|
|
23
|
+
engine: TtsEngine;
|
|
24
|
+
attempts: FallbackAttempt[];
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'speechSynthesis';
|
|
27
|
+
engine: 'speechSynthesis';
|
|
28
|
+
attempts: FallbackAttempt[];
|
|
29
|
+
};
|
|
30
|
+
export interface FallbackOptions {
|
|
31
|
+
text: string;
|
|
32
|
+
rate: string;
|
|
33
|
+
outputDir: string;
|
|
34
|
+
hash: string;
|
|
35
|
+
timeoutMs: number;
|
|
36
|
+
fallbackOrder: readonly TtsEngine[];
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
now?: Date;
|
|
39
|
+
/** Error thrown by the primary Edge-TTS attempt, kept for the final message. */
|
|
40
|
+
primaryError?: unknown;
|
|
41
|
+
}
|
|
42
|
+
/** Injection points; production uses the real Windows/ffmpeg binaries. */
|
|
43
|
+
export interface FallbackDependencies {
|
|
44
|
+
platform?: NodeJS.Platform;
|
|
45
|
+
spawnImpl?: SpawnFunction;
|
|
46
|
+
powershellBinary?: string;
|
|
47
|
+
ffmpegBinary?: string;
|
|
48
|
+
synthesizeSapiImpl?: (text: string, options: SapiOptions) => Promise<SynthesizedAudio>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Run the configured engines after Edge-TTS already failed. `edge-tts` in
|
|
52
|
+
* `fallbackOrder` marks the primary attempt rather than a retry.
|
|
53
|
+
*/
|
|
54
|
+
export declare function runFallback(options: FallbackOptions, deps?: FallbackDependencies): Promise<FallbackOutcome>;
|
|
55
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACpE,OAAO,EAAa,QAAQ,EAAqB,MAAM,aAAa,CAAA;AACpE,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,eAAe,CAAA;AAYtB,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAEpE;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,4EAA4E;AAC5E,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B,GACD;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B,CAAA;AAEL,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,SAAS,SAAS,EAAE,CAAA;IACnC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,gFAAgF;IAChF,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kBAAkB,CAAC,EAAE,CACnB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,gBAAgB,CAAC,CAAA;CAC/B;AAuBD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,eAAe,EACxB,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAiD1B"}
|