package main // gentss:离线生成 TTS 朗读音频(Edge TTS 神经网络语音)并回填各 audio 字段。 // 用法: // // go run ./cmd/gentss --list # 打印待生成清单 // go run ./cmd/gentss --strategy=1 # 只生成第 1 计(8 节点剧本已导入) // go run ./cmd/gentss --only=script|content|scene|option|teach # 只生成指定内容源 // go run ./cmd/gentss --force # 覆盖已生成 mp3 重新合成 // // 幂等:mp3 文件已存在且字段已回填 → 跳过(--force 覆盖);文件在但字段空 → 只补回填。 // 配置在 config.yml 的 tts 段;产物落 ui-src/static/generated/audio/,URL /static/generated/audio/...(与 PNG 素材同挂载)。 import ( "flag" "fmt" "sync" "time" _ "github.com/gogf/gf/contrib/drivers/sqlite/v2" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" ) var staticDir = "ui-src/static/generated" // 打包入客户端的音频目录 type genFlags struct { listOnly bool only string // script | content | scene | option | teach strategy int // 0 = 全部计策 force bool } type ttsConfig struct { voice string rate string version string concurrency int timeout int retries int } func main() { fl := parseFlags() cfg := loadTTSConfig() if fl.listOnly { printInventory(fl) return } client := &ttsClient{ voice: cfg.voice, rate: cfg.rate, version: cfg.version, timeout: time.Duration(cfg.timeout) * time.Second, retries: cfg.retries, } tasks := enumerateTasks(fl) if len(tasks) == 0 { fmt.Println("[gentss] 无待生成任务") return } sem := make(chan struct{}, cfg.concurrency) var wg sync.WaitGroup for _, t := range tasks { t.force = fl.force wg.Add(1) sem <- struct{}{} go func(t task) { defer wg.Done() defer func() { <-sem }() if err := runTask(client, t); err != nil { glog.Errorf(ctx, "[gentss] %s 失败: %v", taskDesc(t), err) } }(t) } wg.Wait() fmt.Println("[gentss] 完成") } func parseFlags() genFlags { var fl genFlags flag.BoolVar(&fl.listOnly, "list", false, "只打印待生成清单") flag.StringVar(&fl.only, "only", "", "script | content | scene | option | teach,默认全部") flag.IntVar(&fl.strategy, "strategy", 0, "只处理指定计策(strategy_id),0=全部") flag.BoolVar(&fl.force, "force", false, "覆盖已生成音频重新合成") flag.Parse() return fl } func loadTTSConfig() ttsConfig { return ttsConfig{ voice: g.Cfg().MustGet(ctx, "tts.voice", "zh-CN-XiaoxiaoNeural").String(), rate: g.Cfg().MustGet(ctx, "tts.rate", "+0%").String(), version: g.Cfg().MustGet(ctx, "tts.version", "1-143.0.3650.75").String(), concurrency: g.Cfg().MustGet(ctx, "tts.concurrency", 2).Int(), timeout: g.Cfg().MustGet(ctx, "tts.timeout", 60).Int(), retries: g.Cfg().MustGet(ctx, "tts.retries", 3).Int(), } } func taskDesc(t task) string { if t.kind == "script" { return fmt.Sprintf("node %d", t.nodeID) } return t.relPath }