@leiboss/dsh-tts 0.0.0-stage → 0.3.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 +111 -0
- package/LICENSE +21 -0
- package/README.md +374 -2
- package/cordis.patch.yml +4 -0
- package/dist/audio/mp3-duration.d.ts +23 -0
- package/dist/audio/mp3-duration.d.ts.map +1 -0
- package/dist/audio/mp3-duration.js +140 -0
- package/dist/audio/mp3-duration.js.map +1 -0
- package/dist/audio-route.d.ts +122 -0
- package/dist/audio-route.d.ts.map +1 -0
- package/dist/audio-route.js +408 -0
- package/dist/audio-route.js.map +1 -0
- package/dist/cache.d.ts +50 -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 +174 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +186 -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 +161 -0
- package/dist/edge-tts.js.map +1 -0
- package/dist/engine-health.d.ts +108 -0
- package/dist/engine-health.d.ts.map +1 -0
- package/dist/engine-health.js +170 -0
- package/dist/engine-health.js.map +1 -0
- package/dist/engines/edge-js.d.ts +51 -0
- package/dist/engines/edge-js.d.ts.map +1 -0
- package/dist/engines/edge-js.js +98 -0
- package/dist/engines/edge-js.js.map +1 -0
- package/dist/errors.d.ts +66 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +110 -0
- package/dist/errors.js.map +1 -0
- package/dist/fallback.d.ts +69 -0
- package/dist/fallback.d.ts.map +1 -0
- package/dist/fallback.js +89 -0
- package/dist/fallback.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +723 -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/register-service.d.ts +25 -0
- package/dist/register-service.d.ts.map +1 -0
- package/dist/register-service.js +33 -0
- package/dist/register-service.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 +32 -0
- package/dist/self-check.d.ts.map +1 -0
- package/dist/self-check.js +75 -0
- package/dist/self-check.js.map +1 -0
- package/dist/service.d.ts +119 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +404 -0
- package/dist/service.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 +93 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ConcurrencyLimiter } from './concurrency.ts';
|
|
2
|
+
import type { ResolvedConfig } from './config.ts';
|
|
3
|
+
import { type SpawnFunction, type SynthesizedAudio } from './edge-tts.ts';
|
|
4
|
+
import { type FallbackAttempt } from './fallback.ts';
|
|
5
|
+
import { type ProbeConnector } from './probe.ts';
|
|
6
|
+
import { type SelfCheckResult } from './self-check.ts';
|
|
7
|
+
import { type SapiOptions } from './sapi-tts.ts';
|
|
8
|
+
/** What a caller can rely on without probing the network. */
|
|
9
|
+
export interface TtsCapabilities {
|
|
10
|
+
/** Word-level timings, which only the pure-JS backend produces. */
|
|
11
|
+
readonly wordBoundaries: boolean;
|
|
12
|
+
readonly formats: readonly ('mp3' | 'wav')[];
|
|
13
|
+
/** A fully local engine exists (SAPI on Windows). */
|
|
14
|
+
readonly offline: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** One synthesis request. */
|
|
17
|
+
export interface TtsRequest {
|
|
18
|
+
text: string;
|
|
19
|
+
voice?: string;
|
|
20
|
+
rate?: string;
|
|
21
|
+
pitch?: string;
|
|
22
|
+
volume?: string;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
/** Audio actually produced on this host. */
|
|
26
|
+
export interface TtsAudioResult {
|
|
27
|
+
kind: 'audio';
|
|
28
|
+
filePath: string;
|
|
29
|
+
format: string;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
timingQuality: 'exact' | 'estimated';
|
|
32
|
+
engine: string;
|
|
33
|
+
fromCache: boolean;
|
|
34
|
+
/** Present when the backend reported word boundaries. */
|
|
35
|
+
words?: {
|
|
36
|
+
text: string;
|
|
37
|
+
offsetMs: number;
|
|
38
|
+
durationMs: number;
|
|
39
|
+
}[];
|
|
40
|
+
attempts?: FallbackAttempt[];
|
|
41
|
+
}
|
|
42
|
+
/** No engine could produce a file; the caller should speak `text` itself. */
|
|
43
|
+
export interface TtsSpeechResult {
|
|
44
|
+
kind: 'speechSynthesis';
|
|
45
|
+
text: string;
|
|
46
|
+
voice: string;
|
|
47
|
+
rate: string;
|
|
48
|
+
reason: string;
|
|
49
|
+
}
|
|
50
|
+
export type TtsResult = TtsAudioResult | TtsSpeechResult;
|
|
51
|
+
/** Offline/online snapshot used by the voiceover capability matrix. */
|
|
52
|
+
export interface TtsProbeReport {
|
|
53
|
+
host: string;
|
|
54
|
+
port: number;
|
|
55
|
+
reachable: boolean;
|
|
56
|
+
elapsedMs: number;
|
|
57
|
+
viaProxy: boolean;
|
|
58
|
+
offlineEngine: boolean;
|
|
59
|
+
}
|
|
60
|
+
/** Environment report with exactly one suggested next action per problem. */
|
|
61
|
+
export interface TtsDoctorReport {
|
|
62
|
+
selfCheck: SelfCheckResult;
|
|
63
|
+
probe: TtsProbeReport;
|
|
64
|
+
capabilities: TtsCapabilities;
|
|
65
|
+
suggestions: string[];
|
|
66
|
+
}
|
|
67
|
+
/** Injection points; production uses the real binaries and network. */
|
|
68
|
+
export interface TtsServiceDeps {
|
|
69
|
+
spawnImpl?: SpawnFunction;
|
|
70
|
+
probeConnect?: ProbeConnector;
|
|
71
|
+
now?: () => Date;
|
|
72
|
+
limiter?: ConcurrencyLimiter;
|
|
73
|
+
/** Backoff sleep; injected so retry tests never wait. */
|
|
74
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
75
|
+
/** Platform override for the SAPI chain (tests run on any OS). */
|
|
76
|
+
platform?: NodeJS.Platform;
|
|
77
|
+
powershellBinary?: string;
|
|
78
|
+
ffmpegBinary?: string;
|
|
79
|
+
/** Injected SAPI implementation, so tests never launch PowerShell. */
|
|
80
|
+
synthesizeSapiImpl?: (text: string, options: SapiOptions) => Promise<SynthesizedAudio>;
|
|
81
|
+
log?: (message: string) => void;
|
|
82
|
+
}
|
|
83
|
+
/** One service instance owns one capacity pool and one cache namespace. */
|
|
84
|
+
export declare class TtsService {
|
|
85
|
+
private readonly config;
|
|
86
|
+
private readonly deps;
|
|
87
|
+
private readonly limiter;
|
|
88
|
+
private readonly log;
|
|
89
|
+
private engineKind?;
|
|
90
|
+
constructor(config: ResolvedConfig, deps?: TtsServiceDeps);
|
|
91
|
+
/** Static capabilities; never touches the network. */
|
|
92
|
+
get capabilities(): TtsCapabilities;
|
|
93
|
+
/** Synthesize, preferring a cache hit and falling back across engines. */
|
|
94
|
+
synthesize(request: TtsRequest): Promise<TtsResult>;
|
|
95
|
+
/** Resolve the backend once per instance; `auto` prefers the pure-JS engine. */
|
|
96
|
+
private resolveEngine;
|
|
97
|
+
/** Produce audio without consulting the cache. */
|
|
98
|
+
private produce;
|
|
99
|
+
/** Local engine: no network, no Edge CLI, straight to the SAPI chain. */
|
|
100
|
+
private produceWithSapi;
|
|
101
|
+
/** Wait between throttle retries; injected in tests so they never sleep. */
|
|
102
|
+
private sleep;
|
|
103
|
+
/** CLI backend: one process per chunk, no word boundaries. */
|
|
104
|
+
private produceWithCli;
|
|
105
|
+
/** Pure-JS backend: one connection per chunk, word boundaries preserved. */
|
|
106
|
+
private produceWithEdgeJs;
|
|
107
|
+
/** Try the configured chain after the primary backend failed. */
|
|
108
|
+
private fallbackChain;
|
|
109
|
+
/**
|
|
110
|
+
* Measure the produced file and record which engine wrote it. `override`
|
|
111
|
+
* carries facts the backend already knows (word boundaries, exact duration).
|
|
112
|
+
*/
|
|
113
|
+
private finish;
|
|
114
|
+
/** One TCP probe; never synthesizes and never consumes service quota. */
|
|
115
|
+
probe(): Promise<TtsProbeReport>;
|
|
116
|
+
/** Self-check plus probe plus one actionable suggestion per failure. */
|
|
117
|
+
doctor(): Promise<TtsDoctorReport>;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The injectable `ctx.tts` service.
|
|
3
|
+
*
|
|
4
|
+
* The tool layer (`tts_generate`) and other plugins share one implementation:
|
|
5
|
+
* cache lookup, bounded concurrency, chunked long text, fallback, and duration
|
|
6
|
+
* measurement. The service deliberately returns structured audio facts
|
|
7
|
+
* (duration, timing quality, engine, word boundaries) rather than a rendered
|
|
8
|
+
* string, so a consumer plugin can build its own UX.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
import { measureMp3 } from "./audio/mp3-duration.js";
|
|
13
|
+
import { AUDIO_FORMAT, cacheKey, findCachedFile, outputFilePath, readCacheMetadata, writeCacheMetadata } from "./cache.js";
|
|
14
|
+
import { splitText } from "./chunker.js";
|
|
15
|
+
import { synthesizeChunked } from "./chunked-tts.js";
|
|
16
|
+
import { ConcurrencyLimiter } from "./concurrency.js";
|
|
17
|
+
import { synthesize } from "./edge-tts.js";
|
|
18
|
+
import { isEdgeJsAvailable, synthesizeWithEdgeJs } from "./engines/edge-js.js";
|
|
19
|
+
import { ErrorCode, looksLikeThrottle, TtsError } from "./errors.js";
|
|
20
|
+
import { isFallbackEligible, runFallback } from "./fallback.js";
|
|
21
|
+
import { probeEndpoint } from "./probe.js";
|
|
22
|
+
import { selfCheck } from "./self-check.js";
|
|
23
|
+
import { isSapiSupported } from "./sapi-tts.js";
|
|
24
|
+
/** Voice names are `<lang>-<REGION>-<Name>Neural` with an optional sub-region. */
|
|
25
|
+
const VOICE_PATTERN = /^[a-z]{2,3}-[A-Z]{2,3}(?:-[a-z0-9]+)?-[A-Za-z0-9]+Neural$/;
|
|
26
|
+
const RATE_PATTERN = /^[+-]\d{1,3}%$/;
|
|
27
|
+
const PITCH_PATTERN = /^[+-]\d{1,3}Hz$/;
|
|
28
|
+
const VOLUME_PATTERN = /^[+-]\d{1,3}%$/;
|
|
29
|
+
/** Average spoken characters per second for Chinese at `+0%`. */
|
|
30
|
+
const CHARS_PER_SECOND = 4.6;
|
|
31
|
+
/** Backoff schedule for upstream throttling, tried before any fallback. */
|
|
32
|
+
const THROTTLE_BACKOFF_MS = [2000, 6000, 18000];
|
|
33
|
+
/** Whether a failure is transient throttling worth backing off on. */
|
|
34
|
+
function isThrottleError(error) {
|
|
35
|
+
if (!(error instanceof TtsError))
|
|
36
|
+
return false;
|
|
37
|
+
return error.code === ErrorCode.UpstreamThrottled || looksLikeThrottle(error.detail);
|
|
38
|
+
}
|
|
39
|
+
/** Parse a signed percentage such as `+10%` into a speed factor. */
|
|
40
|
+
function rateFactor(rate) {
|
|
41
|
+
const match = /^([+-])(\d{1,3})%$/.exec(rate);
|
|
42
|
+
if (match === null)
|
|
43
|
+
return 1;
|
|
44
|
+
const magnitude = Number(match[2]);
|
|
45
|
+
return match[1] === '-' ? Math.max(0.1, 1 - magnitude / 100) : 1 + magnitude / 100;
|
|
46
|
+
}
|
|
47
|
+
/** Coarse duration for engines that cannot report their own timing. */
|
|
48
|
+
function estimateDurationMs(text, rate) {
|
|
49
|
+
const seconds = text.length / (CHARS_PER_SECOND * rateFactor(rate));
|
|
50
|
+
return Math.max(200, Math.round(seconds * 1000));
|
|
51
|
+
}
|
|
52
|
+
async function readAudioFacts(filePath, text, rate) {
|
|
53
|
+
const buffer = await readFile(filePath);
|
|
54
|
+
const info = measureMp3(buffer);
|
|
55
|
+
if (info !== undefined) {
|
|
56
|
+
return { bytes: buffer.length, durationMs: info.durationMs, timingQuality: 'exact' };
|
|
57
|
+
}
|
|
58
|
+
return { bytes: buffer.length, durationMs: estimateDurationMs(text, rate), timingQuality: 'estimated' };
|
|
59
|
+
}
|
|
60
|
+
/** Absolute end of the last reported word, used to advance a chunk offset. */
|
|
61
|
+
function wordsEndMs(words) {
|
|
62
|
+
let end = 0;
|
|
63
|
+
for (const word of words)
|
|
64
|
+
end = Math.max(end, word.offsetMs + word.durationMs);
|
|
65
|
+
return end;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Cache key for one synthesis. The published `cacheKey(text, voice, rate)` is
|
|
69
|
+
* kept for compatibility, but prosody now participates: two calls that differ
|
|
70
|
+
* only in pitch or volume must not share a file.
|
|
71
|
+
*/
|
|
72
|
+
function synthesisKey(text, voice, rate, pitch, volume) {
|
|
73
|
+
return cacheKey(`${text}\u0000pitch=${pitch}\u0000volume=${volume}`, voice, rate);
|
|
74
|
+
}
|
|
75
|
+
/** One service instance owns one capacity pool and one cache namespace. */
|
|
76
|
+
export class TtsService {
|
|
77
|
+
config;
|
|
78
|
+
deps;
|
|
79
|
+
limiter;
|
|
80
|
+
log;
|
|
81
|
+
engineKind;
|
|
82
|
+
constructor(config, deps = {}) {
|
|
83
|
+
this.config = config;
|
|
84
|
+
this.deps = deps;
|
|
85
|
+
this.limiter =
|
|
86
|
+
deps.limiter ??
|
|
87
|
+
new ConcurrencyLimiter({
|
|
88
|
+
maxConcurrent: config.maxConcurrent,
|
|
89
|
+
queueSize: config.queueSize,
|
|
90
|
+
queueTimeoutMs: config.queueTimeoutMs,
|
|
91
|
+
});
|
|
92
|
+
this.log = deps.log ?? (() => { });
|
|
93
|
+
}
|
|
94
|
+
/** Static capabilities; never touches the network. */
|
|
95
|
+
get capabilities() {
|
|
96
|
+
return {
|
|
97
|
+
// Conservative until the backend is resolved: only the pure-JS engine
|
|
98
|
+
// reports word boundaries, and `auto` may land on the CLI backend.
|
|
99
|
+
wordBoundaries: this.engineKind === 'edge-js',
|
|
100
|
+
formats: ['mp3'],
|
|
101
|
+
offline: isSapiSupported(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Synthesize, preferring a cache hit and falling back across engines. */
|
|
105
|
+
async synthesize(request) {
|
|
106
|
+
const text = request.text ?? '';
|
|
107
|
+
if (text.trim().length === 0) {
|
|
108
|
+
throw new TtsError(ErrorCode.EmptyText, 'The text to synthesize is empty.');
|
|
109
|
+
}
|
|
110
|
+
const voice = request.voice ?? this.config.defaultVoice;
|
|
111
|
+
const rate = request.rate ?? '+0%';
|
|
112
|
+
const pitch = request.pitch ?? '+0Hz';
|
|
113
|
+
const volume = request.volume ?? '+0%';
|
|
114
|
+
if (!VOICE_PATTERN.test(voice)) {
|
|
115
|
+
throw new TtsError(ErrorCode.InvalidVoice, `Invalid voice name: ${voice}`, 'Use an Edge-TTS voice such as zh-CN-XiaoxiaoNeural.');
|
|
116
|
+
}
|
|
117
|
+
if (!RATE_PATTERN.test(rate)) {
|
|
118
|
+
throw new TtsError(ErrorCode.InvalidRate, `Invalid rate: ${rate}`, 'Use +0% or -10%.');
|
|
119
|
+
}
|
|
120
|
+
if (!PITCH_PATTERN.test(pitch)) {
|
|
121
|
+
throw new TtsError(ErrorCode.InvalidPitch, `Invalid pitch: ${pitch}`, 'Use +0Hz or +10Hz.');
|
|
122
|
+
}
|
|
123
|
+
if (!VOLUME_PATTERN.test(volume)) {
|
|
124
|
+
throw new TtsError(ErrorCode.InvalidVolume, `Invalid volume: ${volume}`, 'Use +0% or -20%.');
|
|
125
|
+
}
|
|
126
|
+
if (this.config.outputDir.length === 0) {
|
|
127
|
+
throw new TtsError(ErrorCode.DshHomeNotSet, 'No output directory is available.', 'Set DSH_HOME or configure outputDir.');
|
|
128
|
+
}
|
|
129
|
+
const hash = synthesisKey(text, voice, rate, pitch, volume);
|
|
130
|
+
const cached = await findCachedFile(this.config.outputDir, hash);
|
|
131
|
+
if (cached !== undefined) {
|
|
132
|
+
const facts = await readAudioFacts(cached, text, rate);
|
|
133
|
+
const metadata = await readCacheMetadata(cached);
|
|
134
|
+
return {
|
|
135
|
+
kind: 'audio',
|
|
136
|
+
filePath: cached,
|
|
137
|
+
format: AUDIO_FORMAT,
|
|
138
|
+
durationMs: facts.durationMs,
|
|
139
|
+
timingQuality: facts.timingQuality,
|
|
140
|
+
engine: metadata?.engine ?? 'edge-tts',
|
|
141
|
+
fromCache: true,
|
|
142
|
+
...(metadata?.words === undefined ? {} : { words: metadata.words }),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return this.limiter.run(async () => this.produce({
|
|
146
|
+
text,
|
|
147
|
+
voice,
|
|
148
|
+
rate,
|
|
149
|
+
pitch,
|
|
150
|
+
volume,
|
|
151
|
+
hash,
|
|
152
|
+
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
153
|
+
}), request.signal);
|
|
154
|
+
}
|
|
155
|
+
/** Resolve the backend once per instance; `auto` prefers the pure-JS engine. */
|
|
156
|
+
async resolveEngine() {
|
|
157
|
+
if (this.engineKind !== undefined)
|
|
158
|
+
return this.engineKind;
|
|
159
|
+
const configured = this.config.engine;
|
|
160
|
+
this.engineKind =
|
|
161
|
+
configured === 'auto' ? ((await isEdgeJsAvailable()) ? 'edge-js' : 'edge-py') : configured;
|
|
162
|
+
return this.engineKind;
|
|
163
|
+
}
|
|
164
|
+
/** Produce audio without consulting the cache. */
|
|
165
|
+
async produce(input) {
|
|
166
|
+
const engine = await this.resolveEngine();
|
|
167
|
+
// `engine=sapi` must not silently run the Edge CLI: the local engine is a
|
|
168
|
+
// different code path with different failure modes.
|
|
169
|
+
if (engine === 'sapi')
|
|
170
|
+
return this.produceWithSapi(input);
|
|
171
|
+
const attempt = () => engine === 'edge-js' ? this.produceWithEdgeJs(input) : this.produceWithCli(input);
|
|
172
|
+
// Throttling is transient: back off and retry the good engine before
|
|
173
|
+
// falling back. A fallback would hide the load problem and produce worse
|
|
174
|
+
// audio, and a 429 from Edge usually clears within seconds.
|
|
175
|
+
let lastError;
|
|
176
|
+
for (let index = 0; index <= THROTTLE_BACKOFF_MS.length; index += 1) {
|
|
177
|
+
try {
|
|
178
|
+
return await attempt();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
lastError = error;
|
|
182
|
+
const delay = THROTTLE_BACKOFF_MS[index];
|
|
183
|
+
if (delay === undefined || !isThrottleError(error))
|
|
184
|
+
break;
|
|
185
|
+
this.log(`upstream throttled; retrying in ${delay}ms`);
|
|
186
|
+
await this.sleep(delay, input.signal);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return this.fallbackChain(input, lastError);
|
|
190
|
+
}
|
|
191
|
+
/** Local engine: no network, no Edge CLI, straight to the SAPI chain. */
|
|
192
|
+
async produceWithSapi(input) {
|
|
193
|
+
const order = this.config.fallbackOrder.filter((name) => name === 'sapi');
|
|
194
|
+
if (order.length === 0) {
|
|
195
|
+
throw new TtsError(ErrorCode.EngineUnavailable, 'engine=sapi, but "sapi" is not in fallbackOrder.', 'Add "sapi" to fallbackOrder, or set engine to auto.');
|
|
196
|
+
}
|
|
197
|
+
const outcome = await runFallback({
|
|
198
|
+
text: input.text,
|
|
199
|
+
rate: input.rate,
|
|
200
|
+
outputDir: this.config.outputDir,
|
|
201
|
+
hash: input.hash,
|
|
202
|
+
timeoutMs: this.config.timeoutMs,
|
|
203
|
+
fallbackOrder: order,
|
|
204
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
205
|
+
}, {
|
|
206
|
+
...(this.deps.platform === undefined ? {} : { platform: this.deps.platform }),
|
|
207
|
+
...(this.deps.spawnImpl === undefined ? {} : { spawnImpl: this.deps.spawnImpl }),
|
|
208
|
+
...(this.deps.powershellBinary === undefined ? {} : { powershellBinary: this.deps.powershellBinary }),
|
|
209
|
+
...(this.deps.ffmpegBinary === undefined ? {} : { ffmpegBinary: this.deps.ffmpegBinary }),
|
|
210
|
+
...(this.deps.synthesizeSapiImpl === undefined ? {} : { synthesizeSapiImpl: this.deps.synthesizeSapiImpl }),
|
|
211
|
+
});
|
|
212
|
+
if (outcome.kind === 'audio') {
|
|
213
|
+
return this.finish(outcome.audio, { ...input, engine: 'sapi' }, { attempts: outcome.attempts });
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
kind: 'speechSynthesis',
|
|
217
|
+
text: input.text,
|
|
218
|
+
voice: input.voice,
|
|
219
|
+
rate: input.rate,
|
|
220
|
+
reason: outcome.attempts.map((attempt) => `${attempt.engine}: ${attempt.reason}`).join('; '),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Wait between throttle retries; injected in tests so they never sleep. */
|
|
224
|
+
async sleep(ms, signal) {
|
|
225
|
+
if (this.deps.sleep !== undefined) {
|
|
226
|
+
await this.deps.sleep(ms, signal);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
await new Promise((resolve, reject) => {
|
|
230
|
+
const timer = setTimeout(resolve, ms);
|
|
231
|
+
signal?.addEventListener('abort', () => {
|
|
232
|
+
clearTimeout(timer);
|
|
233
|
+
reject(new TtsError(ErrorCode.Aborted, 'Synthesis was aborted during a throttle backoff.'));
|
|
234
|
+
}, { once: true });
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/** CLI backend: one process per chunk, no word boundaries. */
|
|
238
|
+
async produceWithCli(input) {
|
|
239
|
+
const { text, voice, rate, pitch, volume, hash, signal } = input;
|
|
240
|
+
const chunked = this.config.chunkingEnabled && text.length > this.config.chunkSize;
|
|
241
|
+
const audio = chunked
|
|
242
|
+
? await synthesizeChunked(text, {
|
|
243
|
+
voice,
|
|
244
|
+
rate,
|
|
245
|
+
pitch,
|
|
246
|
+
volume,
|
|
247
|
+
proxy: this.config.proxy,
|
|
248
|
+
binary: this.config.edgeTtsBinary,
|
|
249
|
+
outputDir: this.config.outputDir,
|
|
250
|
+
hash,
|
|
251
|
+
timeoutMs: this.config.timeoutMs,
|
|
252
|
+
totalTimeoutMs: this.config.totalTimeoutMs,
|
|
253
|
+
chunkSize: this.config.chunkSize,
|
|
254
|
+
signal,
|
|
255
|
+
spawnImpl: this.deps.spawnImpl,
|
|
256
|
+
now: this.deps.now?.(),
|
|
257
|
+
})
|
|
258
|
+
: await synthesize(text, {
|
|
259
|
+
voice,
|
|
260
|
+
rate,
|
|
261
|
+
pitch,
|
|
262
|
+
volume,
|
|
263
|
+
proxy: this.config.proxy,
|
|
264
|
+
binary: this.config.edgeTtsBinary,
|
|
265
|
+
outputDir: this.config.outputDir,
|
|
266
|
+
hash,
|
|
267
|
+
timeoutMs: this.config.timeoutMs,
|
|
268
|
+
signal,
|
|
269
|
+
spawnImpl: this.deps.spawnImpl,
|
|
270
|
+
now: this.deps.now?.(),
|
|
271
|
+
});
|
|
272
|
+
return this.finish(audio, { text, voice, rate, pitch, volume, engine: 'edge-py' });
|
|
273
|
+
}
|
|
274
|
+
/** Pure-JS backend: one connection per chunk, word boundaries preserved. */
|
|
275
|
+
async produceWithEdgeJs(input) {
|
|
276
|
+
const { text, voice, rate, pitch, volume, hash, signal } = input;
|
|
277
|
+
const chunks = this.config.chunkingEnabled && text.length > this.config.chunkSize
|
|
278
|
+
? splitText(text, this.config.chunkSize)
|
|
279
|
+
: [text];
|
|
280
|
+
const buffers = [];
|
|
281
|
+
const words = [];
|
|
282
|
+
let offsetMs = 0;
|
|
283
|
+
for (const chunk of chunks) {
|
|
284
|
+
if (signal?.aborted) {
|
|
285
|
+
throw new TtsError(ErrorCode.Aborted, 'Synthesis was aborted between chunks.');
|
|
286
|
+
}
|
|
287
|
+
const result = await synthesizeWithEdgeJs({
|
|
288
|
+
text: chunk,
|
|
289
|
+
voice,
|
|
290
|
+
rate,
|
|
291
|
+
pitch,
|
|
292
|
+
volume,
|
|
293
|
+
...(this.config.proxy === undefined ? {} : { proxy: this.config.proxy }),
|
|
294
|
+
timeoutMs: this.config.timeoutMs,
|
|
295
|
+
});
|
|
296
|
+
buffers.push(result.audio);
|
|
297
|
+
for (const word of result.words) {
|
|
298
|
+
words.push({ ...word, offsetMs: word.offsetMs + offsetMs });
|
|
299
|
+
}
|
|
300
|
+
const measured = measureMp3(result.audio);
|
|
301
|
+
offsetMs += measured?.durationMs ?? wordsEndMs(result.words);
|
|
302
|
+
}
|
|
303
|
+
const audio = Buffer.concat(buffers);
|
|
304
|
+
const filePath = outputFilePath(this.config.outputDir, hash, this.deps.now?.());
|
|
305
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
306
|
+
await writeFile(filePath, audio);
|
|
307
|
+
return this.finish({ filePath, format: AUDIO_FORMAT }, { text, voice, rate, pitch, volume, engine: 'edge-js' }, {
|
|
308
|
+
...(words.length === 0 ? {} : { words }),
|
|
309
|
+
...(offsetMs > 0 ? { durationMs: offsetMs, timingQuality: 'exact' } : {}),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/** Try the configured chain after the primary backend failed. */
|
|
313
|
+
async fallbackChain(input, error) {
|
|
314
|
+
if (!this.config.fallbackEnabled || !isFallbackEligible(error))
|
|
315
|
+
throw error;
|
|
316
|
+
this.log(`primary backend failed, trying the fallback chain: ${String(error)}`);
|
|
317
|
+
const outcome = await runFallback({
|
|
318
|
+
text: input.text,
|
|
319
|
+
rate: input.rate,
|
|
320
|
+
outputDir: this.config.outputDir,
|
|
321
|
+
hash: input.hash,
|
|
322
|
+
timeoutMs: this.config.timeoutMs,
|
|
323
|
+
fallbackOrder: this.config.fallbackOrder,
|
|
324
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
325
|
+
primaryError: error,
|
|
326
|
+
}, {
|
|
327
|
+
...(this.deps.platform === undefined ? {} : { platform: this.deps.platform }),
|
|
328
|
+
...(this.deps.spawnImpl === undefined ? {} : { spawnImpl: this.deps.spawnImpl }),
|
|
329
|
+
...(this.deps.powershellBinary === undefined ? {} : { powershellBinary: this.deps.powershellBinary }),
|
|
330
|
+
...(this.deps.ffmpegBinary === undefined ? {} : { ffmpegBinary: this.deps.ffmpegBinary }),
|
|
331
|
+
...(this.deps.synthesizeSapiImpl === undefined ? {} : { synthesizeSapiImpl: this.deps.synthesizeSapiImpl }),
|
|
332
|
+
});
|
|
333
|
+
if (outcome.kind === 'audio') {
|
|
334
|
+
return this.finish(outcome.audio, { ...input, engine: outcome.engine }, { attempts: outcome.attempts });
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
kind: 'speechSynthesis',
|
|
338
|
+
text: input.text,
|
|
339
|
+
voice: input.voice,
|
|
340
|
+
rate: input.rate,
|
|
341
|
+
reason: outcome.attempts.map((attempt) => `${attempt.engine}: ${attempt.reason}`).join('; '),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Measure the produced file and record which engine wrote it. `override`
|
|
346
|
+
* carries facts the backend already knows (word boundaries, exact duration).
|
|
347
|
+
*/
|
|
348
|
+
async finish(audio, identity, override = {}) {
|
|
349
|
+
const facts = await readAudioFacts(audio.filePath, identity.text, identity.rate);
|
|
350
|
+
await writeCacheMetadata(audio.filePath, {
|
|
351
|
+
engine: identity.engine,
|
|
352
|
+
voice: identity.voice,
|
|
353
|
+
rate: identity.rate,
|
|
354
|
+
pitch: identity.pitch,
|
|
355
|
+
volume: identity.volume,
|
|
356
|
+
createdAt: (this.deps.now?.() ?? new Date()).toISOString(),
|
|
357
|
+
...(override.words === undefined ? {} : { words: override.words }),
|
|
358
|
+
});
|
|
359
|
+
return {
|
|
360
|
+
kind: 'audio',
|
|
361
|
+
filePath: audio.filePath,
|
|
362
|
+
format: audio.format,
|
|
363
|
+
durationMs: override.durationMs ?? facts.durationMs,
|
|
364
|
+
timingQuality: override.timingQuality ?? facts.timingQuality,
|
|
365
|
+
engine: identity.engine,
|
|
366
|
+
fromCache: false,
|
|
367
|
+
...(override.words === undefined ? {} : { words: override.words }),
|
|
368
|
+
...(override.attempts === undefined ? {} : { attempts: override.attempts }),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/** One TCP probe; never synthesizes and never consumes service quota. */
|
|
372
|
+
async probe() {
|
|
373
|
+
const result = await probeEndpoint({ connectImpl: this.deps.probeConnect });
|
|
374
|
+
return {
|
|
375
|
+
host: result.host,
|
|
376
|
+
port: result.port,
|
|
377
|
+
reachable: result.reachable,
|
|
378
|
+
elapsedMs: result.elapsedMs,
|
|
379
|
+
viaProxy: this.config.proxy !== undefined,
|
|
380
|
+
offlineEngine: isSapiSupported(),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/** Self-check plus probe plus one actionable suggestion per failure. */
|
|
384
|
+
async doctor() {
|
|
385
|
+
const check = await selfCheck({ config: this.config, spawnImpl: this.deps.spawnImpl });
|
|
386
|
+
const probe = await this.probe();
|
|
387
|
+
const suggestions = [];
|
|
388
|
+
if (!check.edgeTtsAvailable) {
|
|
389
|
+
suggestions.push('Install Edge-TTS (`pip install edge-tts`) or point `edgeTtsBinary` at the executable.');
|
|
390
|
+
}
|
|
391
|
+
if (!probe.reachable) {
|
|
392
|
+
suggestions.push(probe.viaProxy
|
|
393
|
+
? 'The proxy is configured but the endpoint is still unreachable: verify the proxy allows WSS to speech.platform.bing.com.'
|
|
394
|
+
: 'The endpoint is unreachable: configure `proxy`, or rely on the offline engine (`sapi` on Windows).');
|
|
395
|
+
}
|
|
396
|
+
if (!check.outputDirWritable) {
|
|
397
|
+
suggestions.push('The output directory is not writable: change `outputDir` or fix its permissions.');
|
|
398
|
+
}
|
|
399
|
+
if (suggestions.length === 0)
|
|
400
|
+
suggestions.push('No action needed.');
|
|
401
|
+
return { selfCheck: check, probe, capabilities: this.capabilities, suggestions };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/dist/voices.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static catalogue of Edge-TTS voices the plugin documents.
|
|
3
|
+
*
|
|
4
|
+
* The list is deliberately offline: `tts_self_check` must not query the
|
|
5
|
+
* network, so it reports this constant instead of Edge-TTS's live catalogue.
|
|
6
|
+
* Synthesis still accepts any voice name Edge-TTS understands; this catalogue
|
|
7
|
+
* is metadata, not a whitelist.
|
|
8
|
+
*/
|
|
9
|
+
/** One documented voice. */
|
|
10
|
+
export interface VoiceInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
gender: 'Female' | 'Male';
|
|
13
|
+
locale: string;
|
|
14
|
+
/** Short human-facing description used by the README. */
|
|
15
|
+
description: string;
|
|
16
|
+
}
|
|
17
|
+
/** Voices surfaced by `tts_self_check` and documented in the README. */
|
|
18
|
+
export declare const VOICES: readonly VoiceInfo[];
|
|
19
|
+
/** Voice names only, in catalogue order. */
|
|
20
|
+
export declare const AVAILABLE_VOICES: readonly string[];
|
|
21
|
+
//# sourceMappingURL=voices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voices.d.ts","sourceRoot":"","sources":["../src/voices.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,4BAA4B;AAC5B,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,wEAAwE;AACxE,eAAO,MAAM,MAAM,EAAE,SAAS,SAAS,EA6DtC,CAAA;AAED,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAsC,CAAA"}
|
package/dist/voices.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static catalogue of Edge-TTS voices the plugin documents.
|
|
3
|
+
*
|
|
4
|
+
* The list is deliberately offline: `tts_self_check` must not query the
|
|
5
|
+
* network, so it reports this constant instead of Edge-TTS's live catalogue.
|
|
6
|
+
* Synthesis still accepts any voice name Edge-TTS understands; this catalogue
|
|
7
|
+
* is metadata, not a whitelist.
|
|
8
|
+
*/
|
|
9
|
+
/** Voices surfaced by `tts_self_check` and documented in the README. */
|
|
10
|
+
export const VOICES = [
|
|
11
|
+
{
|
|
12
|
+
name: 'zh-CN-XiaoxiaoNeural',
|
|
13
|
+
gender: 'Female',
|
|
14
|
+
locale: 'zh-CN',
|
|
15
|
+
description: '默认女声,自然通用',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'zh-CN-YunxiNeural',
|
|
19
|
+
gender: 'Male',
|
|
20
|
+
locale: 'zh-CN',
|
|
21
|
+
description: '男声,年轻自然',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'zh-CN-XiaoyiNeural',
|
|
25
|
+
gender: 'Female',
|
|
26
|
+
locale: 'zh-CN',
|
|
27
|
+
description: '女声,活泼',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'zh-CN-YunyangNeural',
|
|
31
|
+
gender: 'Male',
|
|
32
|
+
locale: 'zh-CN',
|
|
33
|
+
description: '男声,播音腔',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'zh-CN-YunjianNeural',
|
|
37
|
+
gender: 'Male',
|
|
38
|
+
locale: 'zh-CN',
|
|
39
|
+
description: '男声,沉稳',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'zh-CN-liaoning-XiaobeiNeural',
|
|
43
|
+
gender: 'Female',
|
|
44
|
+
locale: 'zh-CN-liaoning',
|
|
45
|
+
description: '女声,东北话',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'zh-CN-shaanxi-XiaoniNeural',
|
|
49
|
+
gender: 'Female',
|
|
50
|
+
locale: 'zh-CN-shaanxi',
|
|
51
|
+
description: '女声,陕西话',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'en-US-AriaNeural',
|
|
55
|
+
gender: 'Female',
|
|
56
|
+
locale: 'en-US',
|
|
57
|
+
description: '英文女声,自然通用',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'en-US-GuyNeural',
|
|
61
|
+
gender: 'Male',
|
|
62
|
+
locale: 'en-US',
|
|
63
|
+
description: '英文男声',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'en-US-JennyNeural',
|
|
67
|
+
gender: 'Female',
|
|
68
|
+
locale: 'en-US',
|
|
69
|
+
description: '英文女声,对话感',
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
/** Voice names only, in catalogue order. */
|
|
73
|
+
export const AVAILABLE_VOICES = VOICES.map((voice) => voice.name);
|
|
74
|
+
//# sourceMappingURL=voices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voices.js","sourceRoot":"","sources":["../src/voices.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,wEAAwE;AACxE,MAAM,CAAC,MAAM,MAAM,GAAyB;IAC1C;QACE,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,WAAW;KACzB;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,SAAS;KACvB;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,OAAO;KACrB;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,OAAO;KACrB;IACD;QACE,IAAI,EAAE,8BAA8B;QACpC,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,gBAAgB;QACxB,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,eAAe;QACvB,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,WAAW;KACzB;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,MAAM;KACpB;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,UAAU;KACxB;CACF,CAAA;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAsB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA"}
|