dsh-live-speak 1.0.2 → 1.0.3
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/RELEASE-NOTES.md +21 -2
- package/index.js +25 -1
- package/package.json +1 -1
package/RELEASE-NOTES.md
CHANGED
|
@@ -204,6 +204,27 @@ dsh-live-speak
|
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
|
207
|
+
## 1.0.3 更新说明
|
|
208
|
+
|
|
209
|
+
**修掉:Linux 上本机没装语音后端时,会一直无声地重试**
|
|
210
|
+
|
|
211
|
+
本地语音在 Linux 上用的是系统自带的 `espeak-ng`。如果这台机器根本没装,原来的行为是:每一段都判为合成失败,然后一直重试下去——声音始终不出来,日志每 15 秒还写一条,看起来像插件卡死了。
|
|
212
|
+
|
|
213
|
+
现在把「后端根本不存在」和「网络抖了一下」分成两类处理:
|
|
214
|
+
|
|
215
|
+
- **网络类失败**:照旧快速重试(0.9 秒起、封顶 15 秒),恢复后立刻补上,这是原来就有的能力
|
|
216
|
+
- **后端不存在**:重试间隔拉到 60 秒,日志收敛成每 10 次一条。它仍然不会跳过任何一段,而且装好 `espeak-ng` 后最多一分钟就会自己恢复出声,不需要重启 DSH
|
|
217
|
+
|
|
218
|
+
macOS 上如果系统被裁剪到连 `say` 都没有,走同样的处理。**Windows 用户不受影响**——本地档用的是系统语音,不经过这条路径。
|
|
219
|
+
|
|
220
|
+
**怎么验证的**:在 Windows 上以「免安装解包」的方式跑了真实的 `espeak-ng 1.52.0`(同一个程序、同一套命令行),验证了声线清单解析、中文合成、WAV 输出格式和读回时长四件事:223 行声线清单解析出 222 条全部正确,41 字中文合成出 9.52 秒、22050Hz/16bit 的标准 WAVE,读回时长计算一致。测试全程没有在系统里安装任何东西,临时文件已清理。
|
|
221
|
+
|
|
222
|
+
**一条不变的原则**:合成失败不跳过。任何一段合成不出来,就一直重试到成功为止,宁可晚几秒开口,也不让内容缺一段。
|
|
223
|
+
|
|
224
|
+
当前版本:**1.0.3**
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
207
228
|
## 1.0.2 更新说明
|
|
208
229
|
|
|
209
230
|
**修掉:标题和正文连成一句**
|
|
@@ -220,8 +241,6 @@ dsh-live-speak
|
|
|
220
241
|
|
|
221
242
|
**验证**:真实播报一篇 246 字、带一个短行标题的文章,切成 2 段,标题被读成「帘卷西风。」后接正文;两段合成零失败、零重试,播放间隙 2 毫秒,全程无跳段。
|
|
222
243
|
|
|
223
|
-
当前版本:**1.0.2**
|
|
224
|
-
|
|
225
244
|
---
|
|
226
245
|
|
|
227
246
|
## 1.0.1 更新说明
|
package/index.js
CHANGED
|
@@ -1570,6 +1570,12 @@ export function apply(ctx, config = {}) {
|
|
|
1570
1570
|
execFile('say', args, { timeout: 25_000 }, (error) => {
|
|
1571
1571
|
if (error) {
|
|
1572
1572
|
cleanup()
|
|
1573
|
+
// say 是系统自带,正常不会缺;被裁剪的发行版或沙盒里可能没有。
|
|
1574
|
+
// 这是确定性失败:重试多少次都不会出声,标成 fatal 让合成泵放慢重试。
|
|
1575
|
+
if (error.code === 'ENOENT' || error.code === 'EACCES') {
|
|
1576
|
+
error.fatal = true
|
|
1577
|
+
error.message = `local say unavailable (${error.code})`
|
|
1578
|
+
}
|
|
1573
1579
|
reject(error)
|
|
1574
1580
|
return
|
|
1575
1581
|
}
|
|
@@ -1617,6 +1623,13 @@ export function apply(ctx, config = {}) {
|
|
|
1617
1623
|
execFile(bin, args, { timeout: 25_000 }, (error) => {
|
|
1618
1624
|
if (error) {
|
|
1619
1625
|
cleanup()
|
|
1626
|
+
// 二进制根本不在(Linux 上没装 espeak-ng / espeak):确定性失败。
|
|
1627
|
+
// 重试一万次也不会出声,标成 fatal 让合成泵把重试间隔拉到 60 秒,
|
|
1628
|
+
// 而不是每 15 秒刷一条日志、整篇却一直静默。
|
|
1629
|
+
if (error.code === 'ENOENT' || error.code === 'EACCES') {
|
|
1630
|
+
error.fatal = true
|
|
1631
|
+
error.message = `local espeak binary unavailable: ${bin} (${error.code})`
|
|
1632
|
+
}
|
|
1620
1633
|
reject(error)
|
|
1621
1634
|
return
|
|
1622
1635
|
}
|
|
@@ -1899,9 +1912,20 @@ export function apply(ctx, config = {}) {
|
|
|
1899
1912
|
attempt += 1
|
|
1900
1913
|
synthFailCount += 1
|
|
1901
1914
|
lastError = String(error?.message ?? error).split('\n')[0]
|
|
1902
|
-
log(`synth fail id=${id} chars=${text.length} attempt=${attempt} err=${lastError}`)
|
|
1903
1915
|
bump()
|
|
1904
1916
|
if (epoch !== myEpoch) break
|
|
1917
|
+
// 后端二进制不存在(Linux 没装 espeak-ng,或 mac 上的 say 被裁掉)属于确定性失败:
|
|
1918
|
+
// 重试一万次也不会出声,但这一段也不能丢(内容完整性优先)。
|
|
1919
|
+
// 所以照样一直重试——用户装上后端、或切回线上档(epoch 变化)就自动恢复——
|
|
1920
|
+
// 只把间隔拉到 60 秒、日志每 10 次才报一条,避免整篇静默的同时还在刷屏。
|
|
1921
|
+
if (error?.fatal) {
|
|
1922
|
+
if (attempt % 10 === 1) {
|
|
1923
|
+
log(`local backend unavailable id=${id} attempt=${attempt} err=${lastError} retry_in=60s`)
|
|
1924
|
+
}
|
|
1925
|
+
await sleep(60_000)
|
|
1926
|
+
continue
|
|
1927
|
+
}
|
|
1928
|
+
log(`synth fail id=${id} chars=${text.length} attempt=${attempt} err=${lastError}`)
|
|
1905
1929
|
// 退避翻倍、封顶 15 秒:Edge 恢复后这一段一定能补上,只是可能要等一会儿
|
|
1906
1930
|
const backoff = Math.min(SYNTH_RETRY_BASE_MS * 2 ** Math.min(attempt - 1, 4), SYNTH_RETRY_MAX_MS)
|
|
1907
1931
|
log(`synth retry id=${id} attempt=${attempt} retry_in=${backoff}ms`)
|