1. 新增BYTEDANCE_TTS_FORMAT和BYTEDANCE_TTS_SAMPLE_RATE环境变量配置 2. 实现OpenAI TTS格式到火山API格式的映射转换 3. 自动处理wav格式:内部用pcm请求后封装标准WAV头 4. 动态设置响应Content-Type,适配不同输出格式 5. 更新文档与配置示例,完善日志与错误处理
123 lines
3.6 KiB
Go
123 lines
3.6 KiB
Go
package setting
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/volcano-tts/tts-api/common"
|
|
"github.com/volcano-tts/tts-api/dto"
|
|
)
|
|
|
|
var (
|
|
TTSConfig dto.ByteDanceTTSConfig
|
|
TTSConfigErr error
|
|
)
|
|
|
|
func InitTTSConfig() error {
|
|
apiKey := os.Getenv("BYTEDANCE_TTS_API_KEY")
|
|
resourceId := os.Getenv("BYTEDANCE_TTS_RESOURCE_ID")
|
|
speaker := os.Getenv("BYTEDANCE_TTS_SPEAKER")
|
|
model := os.Getenv("BYTEDANCE_TTS_MODEL")
|
|
if model == "" {
|
|
model = "seed-tts-2.0-standard" // 文档默认值 复刻音色可设为 seed-tts-2.0-expressive
|
|
}
|
|
|
|
missingVars := []string{}
|
|
if apiKey == "" {
|
|
missingVars = append(missingVars, "BYTEDANCE_TTS_API_KEY")
|
|
}
|
|
if resourceId == "" {
|
|
missingVars = append(missingVars, "BYTEDANCE_TTS_RESOURCE_ID")
|
|
}
|
|
if speaker == "" {
|
|
missingVars = append(missingVars, "BYTEDANCE_TTS_SPEAKER")
|
|
}
|
|
|
|
if len(missingVars) > 0 {
|
|
return fmt.Errorf("缺少必需的环境变量: %v", missingVars)
|
|
}
|
|
|
|
url := "https://openspeech.bytedance.com/api/v3/tts/unidirectional"
|
|
|
|
timeout := common.DefaultTimeout
|
|
if timeoutStr := os.Getenv("BYTEDANCE_TTS_TIMEOUT"); timeoutStr != "" {
|
|
if parsedTimeout, err := time.ParseDuration(timeoutStr); err == nil {
|
|
timeout = parsedTimeout
|
|
} else {
|
|
log.Printf("无效的超时设置 '%s',使用默认值: %v", timeoutStr, timeout)
|
|
}
|
|
}
|
|
|
|
// 音频格式,默认 mp3(文档默认值,流式场景下 wav 会多次返回 header,不推荐)
|
|
format := os.Getenv("BYTEDANCE_TTS_FORMAT")
|
|
if format == "" {
|
|
format = "mp3"
|
|
}
|
|
|
|
// 采样率,默认 24000
|
|
sampleRate := 24000
|
|
if srStr := os.Getenv("BYTEDANCE_TTS_SAMPLE_RATE"); srStr != "" {
|
|
if sr, err := fmt.Sscanf(srStr, "%d", &sampleRate); err != nil || sr != 1 {
|
|
log.Printf("无效的采样率设置 '%s',使用默认值: 24000", srStr)
|
|
sampleRate = 24000
|
|
}
|
|
validRates := map[int]bool{8000: true, 16000: true, 22050: true, 24000: true, 32000: true, 44100: true, 48000: true}
|
|
if !validRates[sampleRate] {
|
|
log.Printf("不支持的采样率 %d,使用默认值: 24000", sampleRate)
|
|
sampleRate = 24000
|
|
}
|
|
}
|
|
|
|
TTSConfig = dto.ByteDanceTTSConfig{
|
|
ApiKey: apiKey,
|
|
ResourceId: resourceId,
|
|
Speaker: speaker,
|
|
Model: model,
|
|
URL: url,
|
|
Timeout: timeout,
|
|
Format: format,
|
|
SampleRate: sampleRate,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CheckEnvironmentVariables() map[string]interface{} {
|
|
requiredVars := map[string]bool{
|
|
"BYTEDANCE_TTS_API_KEY": os.Getenv("BYTEDANCE_TTS_API_KEY") != "",
|
|
"BYTEDANCE_TTS_RESOURCE_ID": os.Getenv("BYTEDANCE_TTS_RESOURCE_ID") != "",
|
|
"BYTEDANCE_TTS_SPEAKER": os.Getenv("BYTEDANCE_TTS_SPEAKER") != "",
|
|
}
|
|
|
|
missingVars := []string{}
|
|
for varName, isSet := range requiredVars {
|
|
if !isSet {
|
|
missingVars = append(missingVars, varName)
|
|
}
|
|
}
|
|
|
|
optionalVars := map[string]bool{
|
|
"BYTEDANCE_TTS_TIMEOUT": os.Getenv("BYTEDANCE_TTS_TIMEOUT") != "",
|
|
"BYTEDANCE_TTS_MODEL": os.Getenv("BYTEDANCE_TTS_MODEL") != "",
|
|
"BYTEDANCE_TTS_FORMAT": os.Getenv("BYTEDANCE_TTS_FORMAT") != "",
|
|
"BYTEDANCE_TTS_SAMPLE_RATE": os.Getenv("BYTEDANCE_TTS_SAMPLE_RATE") != "",
|
|
"OPENAI_TTS_API_KEY": os.Getenv("OPENAI_TTS_API_KEY") != "",
|
|
"ALLOWED_ORIGINS": os.Getenv("ALLOWED_ORIGINS") != "",
|
|
"PORT": os.Getenv("PORT") != "",
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"all_required_vars_set": len(missingVars) == 0,
|
|
"missing_required_vars": missingVars,
|
|
"required_vars_set": requiredVars,
|
|
"optional_vars_set": optionalVars,
|
|
}
|
|
}
|
|
|
|
func CheckStaticFiles() {
|
|
if _, err := os.Stat("health.html"); os.IsNotExist(err) {
|
|
log.Println("警告: health.html 不存在,/dashboard 路由将返回 404")
|
|
}
|
|
}
|