package handler import ( "encoding/json" "log" "net/http" "github.com/yourname/cyrene-ai/voice-service/internal/service" ) // TTSHandler TTS HTTP API 处理器 type TTSHandler struct { svc *service.TTSService } // NewTTSHandler 创建 TTS 处理器 func NewTTSHandler(svc *service.TTSService) *TTSHandler { return &TTSHandler{svc: svc} } // RegisterRoutes 注册 TTS 路由 func (h *TTSHandler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("/api/v1/tts/synthesize", h.handleSynthesize) mux.HandleFunc("/api/v1/tts/voices", h.handleVoices) mux.HandleFunc("/api/v1/tts/status", h.handleStatus) } // TTSSynthesizeRequest TTS 合成请求体 type TTSSynthesizeRequest struct { Text string `json:"text"` Voice string `json:"voice"` Rate string `json:"rate"` } // handleSynthesize POST /api/v1/tts/synthesize func (h *TTSHandler) handleSynthesize(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } // 解析 JSON 请求体 var req TTSSynthesizeRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "请求体解析失败: "+err.Error()) return } if req.Text == "" { writeError(w, http.StatusBadRequest, "text 字段不能为空") return } // 检查 TTS 引擎是否可用 if !h.svc.IsAvailable() { log.Printf("[tts-handler] TTS 引擎不可用") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) json.NewEncoder(w).Encode(map[string]interface{}{ "error": "TTS 引擎不可用,请安装 edge-tts (pip install edge-tts) 或 espeak-ng", "code": "TTS_UNAVAILABLE", "install": "pip install edge-tts", }) return } // 调用合成 audioData, format, err := h.svc.Synthesize(req.Text, req.Voice, req.Rate) if err != nil { log.Printf("[tts-handler] TTS 合成失败: %v", err) writeError(w, http.StatusInternalServerError, "TTS 合成失败: "+err.Error()) return } // 返回音频流 contentType := "audio/mpeg" if format == "wav" { contentType = "audio/wav" } w.Header().Set("Content-Type", contentType) w.Header().Set("Content-Disposition", "inline; filename=synthesized."+format) w.WriteHeader(http.StatusOK) w.Write(audioData) } // handleVoices GET /api/v1/tts/voices func (h *TTSHandler) handleVoices(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } voices := h.svc.GetVoices() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "voices": voices, "count": len(voices), }) } // handleStatus GET /api/v1/tts/status func (h *TTSHandler) handleStatus(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } status := h.svc.GetEngineStatus() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "service": "voice-service", "tts": status, }) }