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 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`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-live-speak",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "用高保真神经语音实时播报模型输出(边生成边念),自带播放器,可随时暂停/恢复/停止。",
5
5
  "type": "module",
6
6
  "main": "./index.js",