package handlers import ( "encoding/json" "fanqie/internal/fqapi" "fmt" "io" "net/http" "strings" "time" ) // HandleAudio 音频处理 func HandleAudio(w http.ResponseWriter, r *http.Request) { incrementStat("total", "audio") w.Header().Set("Content-Type", "application/json; charset=utf-8") params, err := validateParams(r, "item_ids") if err != nil { respondError(w, err.Error(), true) return } toneID := r.URL.Query().Get("tone_id") output := r.URL.Query().Get("output") result, err := fqapi.HandleAudio(params["item_ids"], toneID, output) if err != nil { respondError(w, "获取音频失败: "+err.Error(), false) return } fmt.Fprint(w, result) } // HandleAudioProxy 音频代理处理(流式转发,音频不全量驻留内存) func HandleAudioProxy(w http.ResponseWriter, r *http.Request) { incrementStat("total", "audio") // 从URL路径提取key: /audio/proxy/{key} path := r.URL.Path key := strings.TrimPrefix(path, "/audio/proxy/") if key == "" || key == path { http.Error(w, "缺少音频key", http.StatusBadRequest) return } // 调用代理处理函数 body, contentType, err := fqapi.HandleAudioProxy(key) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } defer body.Close() // 设置正确的Content-Type if contentType != "" { w.Header().Set("Content-Type", contentType) } else { w.Header().Set("Content-Type", "audio/mpeg") } // 直接播放,不强制下载 // 浏览器会根据Content-Type决定是播放还是下载 io.Copy(w, body) } // HandleVideo 视频处理 func HandleVideo(w http.ResponseWriter, r *http.Request) { incrementStat("total", "video") w.Header().Set("Content-Type", "application/json; charset=utf-8") params, err := validateParams(r, "item_ids") if err != nil { respondError(w, err.Error(), true) return } output := r.URL.Query().Get("output") quality := r.URL.Query().Get("quality") result, err := fqapi.HandleVideo(params["item_ids"], output, quality) if err != nil { respondError(w, "获取视频失败: "+err.Error(), false) return } fmt.Fprint(w, result) } // HandleVideoProxy 视频解密代理处理(磁盘缓存优先;http.ServeContent 支持 Range/seek/HEAD, // 播放器拖动进度条时按需分片返回) func HandleVideoProxy(w http.ResponseWriter, r *http.Request) { incrementStat("total", "video") path := r.URL.Path key := strings.TrimPrefix(path, "/video/proxy/") if key == "" || key == path { http.Error(w, "缺少视频key", http.StatusBadRequest) return } content, contentType, err := fqapi.HandleVideoProxy(key) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if closer, ok := content.(io.Closer); ok { defer closer.Close() } w.Header().Set("Content-Type", contentType) http.ServeContent(w, r, key+".mp4", time.Time{}, content) } // HandleManga 漫画处理 func HandleManga(w http.ResponseWriter, r *http.Request) { incrementStat("total", "manga") itemID := r.URL.Query().Get("item_ids") if itemID == "" { itemID = r.URL.Query().Get("item_id") } if itemID == "" { respondError(w, "缺少 item_ids 参数", true) return } // 检查是否要返回HTML格式 showHTML := r.URL.Query().Get("show_html") == "1" // 检查输出模式参数(优先级高于配置文件) // 支持:output=url / output=proxy / output=data_uri outputMode := r.URL.Query().Get("output") result, err := fqapi.HandleGetManga(itemID, showHTML, outputMode) if err != nil { respondError(w, "获取漫画失败: "+err.Error(), false) return } if showHTML { // 返回HTML格式 w.Header().Set("Content-Type", "text/html; charset=utf-8") if contentMap, ok := result.(map[string]interface{}); ok { if content, ok := contentMap["content"].(string); ok { fmt.Fprint(w, content) return } } fmt.Fprint(w, `

未找到漫画内容

`) } else { // 返回JSON格式 jsonData, err := json.Marshal(map[string]interface{}{ "code": 0, "msg": "success", "data": result, }) if err != nil { respondError(w, "JSON编码失败: "+err.Error(), false) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") fmt.Fprint(w, string(jsonData)) } }