Files
AskaEth 6906d274fc feat(voice): M47 后端启动后探活 emotionTTS,未起则异步拉起一键启动脚本
主人 2026-10-24:「tts启动脚本在这 ...\一键启动.bat,以后启动后端可以顺便检测
一下如果TTS没起就顺带启一下」。本片只做「启动时顺带探一下」,**默认关**(主人
早前说过「开机自启先不急 后面做」),未开开关时连探活都不发。

- 新包 internal/runtime/voice/ttslaunch:探 http://127.0.0.1:9880(毫秒级超时),
  已在跑什么都不做;没起则异步起脚本(cmd.exe /c 传参,不拼命令行字符串)
- 防重复:in-flight 单飞 + 一次尝试后的冷却(失败也算一次,重启风暴不会变成重试风暴)
- 失败只 WARN:探活超时/脚本不存在/起进程失败都只记带 source 的日志,
  组合根那条路径**没有 error 通道**
- 配置走面板文件 $DATA_DIR/config/voice-tts-launch.json(零新 .env 键)
- 接线在 cmd/cyrene 的网关 Listen 之后,整段在后台 goroutine 里,不占启动时长
  (用例以「挂载耗时 < 150ms 而探活超时 800ms」证明)
- 不新开端点、不加帧族;不碰 gateway 包
2026-10-26 18:47:36 +08:00

49 lines
1.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ttslaunch
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
)
// scriptSpawner 把"起那个脚本"包成 spawnFunc(缺省实现)。
func scriptSpawner(path string) spawnFunc {
return func(ctx context.Context) error {
return LaunchScript(ctx, path)
}
}
// LaunchScript 起一个启动脚本并**立即返回**,不等它结束。
//
// 细节与代价:
// - 脚本不存在 / 是目录 ⇒ 返回错误(调用方只 WARN:这台机器上没装 emotionTTS
// 是正常形态,不是后端的病)。
// - `cmd.Dir` 设为脚本所在目录:`一键启动.bat` 里的相对路径按它自己的习惯解析,
// 而不是按后端的进程工作目录。
// - **刻意不使用 ctx**:`exec.CommandContext` 会在 ctx 取消时杀掉子进程,而后端
// 优雅退出**不该带走** TTS —— 服务是独立的,主人的后端重启与它是两件事。
// - 起完由后台 goroutine `Wait` 回收(脚本通常活到 TTS 退出为止,这个 goroutine
// 跟着后端一起活,代价可忽略)。
func LaunchScript(_ context.Context, path string) error {
path = strings.TrimSpace(path)
if path == "" {
return fmt.Errorf("ttslaunch: empty script path")
}
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("ttslaunch: script %s: %w", path, err)
}
if info.IsDir() {
return fmt.Errorf("ttslaunch: script %s is a directory", path)
}
cmd := shellCommand(path)
cmd.Dir = filepath.Dir(path)
if err := startDetached(cmd); err != nil {
return fmt.Errorf("ttslaunch: start %s: %w", path, err)
}
go func() { _ = cmd.Wait() }()
return nil
}