150 lines
3.5 KiB
Go
150 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// 配置结构体
|
|
type Config struct {
|
|
APIBaseURL string `json:"api_base_url"`
|
|
Port string `json:"port"`
|
|
}
|
|
|
|
// 从配置文件加载配置
|
|
func loadConfig() Config {
|
|
config := Config{
|
|
APIBaseURL: "https://api.openai.com", // 默认API地址
|
|
Port: "8080", // 默认端口
|
|
}
|
|
|
|
// 尝试读取配置文件
|
|
if _, err := os.Stat("config.json"); err == nil {
|
|
data, err := os.ReadFile("config.json")
|
|
if err == nil {
|
|
json.Unmarshal(data, &config)
|
|
}
|
|
}
|
|
|
|
// 环境变量覆盖配置文件
|
|
if envURL := os.Getenv("OPENAI_API_BASE_URL"); envURL != "" {
|
|
config.APIBaseURL = envURL
|
|
}
|
|
if envPort := os.Getenv("PORT"); envPort != "" {
|
|
config.Port = envPort
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
// TTS请求结构体
|
|
type TTSRequest struct {
|
|
APIKey string `json:"api_key"`
|
|
Text string `json:"text"`
|
|
Voice string `json:"voice"`
|
|
Model string `json:"model"`
|
|
APIBase string `json:"api_base"`
|
|
}
|
|
|
|
// OpenAI TTS API请求体
|
|
type openAITTSRequest struct {
|
|
Model string `json:"model"`
|
|
Input string `json:"input"`
|
|
Voice string `json:"voice"`
|
|
}
|
|
|
|
func main() {
|
|
// 命令行参数
|
|
var configPath string
|
|
flag.StringVar(&configPath, "config", "config.json", "配置文件路径")
|
|
flag.Parse()
|
|
|
|
config := loadConfig()
|
|
|
|
// 静态文件服务
|
|
http.Handle("/", http.FileServer(http.Dir(".")))
|
|
|
|
// TTS API端点
|
|
http.HandleFunc("/api/tts", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// 解析请求体
|
|
var req TTSRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// 使用用户提供的API地址,如果没有则使用配置中的
|
|
apiBaseURL := config.APIBaseURL
|
|
if req.APIBase != "" {
|
|
apiBaseURL = req.APIBase
|
|
}
|
|
|
|
// 构建OpenAI TTS API请求
|
|
openAIReq := openAITTSRequest{
|
|
Model: req.Model,
|
|
Input: req.Text,
|
|
Voice: req.Voice,
|
|
}
|
|
|
|
reqBody, err := json.Marshal(openAIReq)
|
|
if err != nil {
|
|
http.Error(w, "Failed to marshal request", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// 创建HTTP客户端
|
|
client := &http.Client{}
|
|
openAIReq, err := http.NewRequest("POST", apiBaseURL+"/v1/audio/speech", bytes.NewBuffer(reqBody))
|
|
if err != nil {
|
|
http.Error(w, "Failed to create request", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// 设置请求头
|
|
openAIReq.Header.Set("Content-Type", "application/json")
|
|
openAIReq.Header.Set("Authorization", "Bearer "+req.APIKey)
|
|
|
|
// 发送请求
|
|
resp, err := client.Do(openAIReq)
|
|
if err != nil {
|
|
http.Error(w, "Failed to call OpenAI API: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// 检查响应状态
|
|
if resp.StatusCode != http.StatusOK {
|
|
errBody, _ := io.ReadAll(resp.Body)
|
|
http.Error(w, "OpenAI API error: "+string(errBody), resp.StatusCode)
|
|
return
|
|
}
|
|
|
|
// 设置响应头
|
|
w.Header().Set("Content-Type", "audio/mpeg")
|
|
w.Header().Set("Content-Disposition", "attachment; filename=speech.mp3")
|
|
|
|
// 将响应体写入客户端
|
|
io.Copy(w, resp.Body)
|
|
})
|
|
|
|
log.Printf("Server starting on port %s...", config.Port)
|
|
log.Printf("Using API base URL: %s", config.APIBaseURL)
|
|
log.Printf("Open http://localhost:%s in your browser", config.Port)
|
|
|
|
// 启动服务器
|
|
if err := http.ListenAndServe(":"+config.Port, nil); err != nil {
|
|
log.Fatal("Server failed to start: ", err)
|
|
}
|
|
}
|