修复 curve 接口的一些问题

This commit is contained in:
eafonyang
2026-06-04 19:39:08 +08:00
parent c4cd8b6f5d
commit 4f31cd563f
12 changed files with 202 additions and 82 deletions
+88 -44
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
@@ -18,6 +19,7 @@ import (
"github.com/luxsin/app-api/internal/config"
"github.com/luxsin/app-api/internal/repository"
"github.com/luxsin/app-api/internal/response"
"github.com/luxsin/app-api/pkg/encode"
"go.uber.org/zap"
)
@@ -33,12 +35,12 @@ func NewCurveHandler(repo *repository.CurveRepository, curveCache *cache.CurveCa
}
// GetCurve 获取目标曲线
// GET /audio/getCurve?brand=xxx&name=xxx&target=xxx&includeRaw=false
// GET /audio/getCurve?brand=xxx&name=xxx&target=xxx&base64Resp=true
func (h *CurveHandler) GetCurve(c *gin.Context) {
brand := strings.TrimSpace(c.Query("brand"))
name := strings.TrimSpace(c.Query("name"))
target := strings.TrimSpace(c.Query("target"))
includeRaw := c.Query("includeRaw") == "true" || c.Query("includeRaw") == "1"
brand := strings.TrimSpace(queryParam(c, "brand"))
name := strings.TrimSpace(queryParam(c, "name"))
target := strings.TrimSpace(queryParam(c, "target"))
base64Resp := encode.ParseBase64Param(c)
if brand == "" || name == "" || target == "" {
response.BadRequest(c, "brand、name和target参数不能为空")
@@ -59,22 +61,33 @@ func (h *CurveHandler) GetCurve(c *gin.Context) {
return
}
// 解析 JSON 结果
// 解析 JSON 结果,只提取 parametric_eq
var resp map[string]any
if err := json.Unmarshal([]byte(result), &resp); err != nil {
response.InternalError(c, "解析曲线数据失败")
return
}
// 如果 includeRaw 为 false 且 code != 200,移除 fr 字段
if !includeRaw {
codeVal, _ := resp["code"]
if code, ok := codeVal.(float64); ok && code != 200 {
delete(resp, "fr")
}
parametricEq, _ := resp["parametric_eq"]
resultData := gin.H{
"code": 200,
"msg": "操作成功",
"parametric_eq": parametricEq,
}
response.OK(c, resp)
if base64Resp {
encoded, err := encode.EncodeJSON(resultData)
if err != nil {
h.log.Error("encode response failed", zap.Error(err))
response.InternalError(c, "编码响应失败")
return
}
c.String(http.StatusOK, encoded)
return
}
c.JSON(http.StatusOK, resultData)
}
// getCurvePoint 获取曲线数据:先查缓存,缓存不存在则请求 EQ 接口并缓存结果
@@ -92,6 +105,7 @@ func (h *CurveHandler) getCurvePoint(ctx context.Context, brand, name, target st
// 获取了锁,缓存仍然为空,需要请求 EQ 接口
if acquired {
h.log.Info("curve cache miss, requesting eq api", zap.String("brand", brand), zap.String("name", name), zap.String("target", target))
result, eqErr := h.getCurvePointFromPEQ(ctx, brand, name, target)
if eqErr != nil {
// 释放锁
@@ -193,35 +207,35 @@ func (h *CurveHandler) reqEqualize(headPhone string, measurement map[string]any,
q := floatVal(bassBoost, "q", 0.7)
reqBody := map[string]any{
"target": target,
"sound_signature": nil,
"target": target,
"sound_signature": nil,
"sound_signature_smoothing_window_size": 1,
"bass_boost_gain": gain,
"bass_boost_fc": fc,
"bass_boost_q": q,
"treble_boost_gain": 0,
"treble_boost_fc": 10000,
"treble_boost_q": 0.7,
"tilt": 0,
"fs": 48000,
"bit_depth": 16,
"phase": "minimum",
"f_res": 16,
"preamp": 0,
"max_gain": 12,
"max_slope": 18,
"window_size": 0.08,
"treble_window_size": 2,
"treble_f_lower": 6000,
"treble_f_upper": 8000,
"treble_gain_k": 1,
"graphic_eq": false,
"parametric_eq": true,
"fixed_band_eq": false,
"convolution_eq": false,
"source": source,
"rig": rig,
"parametric_eq_config": "MINIDSP_IL_DSP",
"bass_boost_gain": gain,
"bass_boost_fc": fc,
"bass_boost_q": q,
"treble_boost_gain": 0,
"treble_boost_fc": 10000,
"treble_boost_q": 0.7,
"tilt": 0,
"fs": 48000,
"bit_depth": 16,
"phase": "minimum",
"f_res": 16,
"preamp": 0,
"max_gain": 12,
"max_slope": 18,
"window_size": 0.08,
"treble_window_size": 2,
"treble_f_lower": 6000,
"treble_f_upper": 8000,
"treble_gain_k": 1,
"graphic_eq": false,
"parametric_eq": true,
"fixed_band_eq": false,
"convolution_eq": false,
"source": source,
"rig": rig,
"parametric_eq_config": "MINIDSP_IL_DSP",
"response": map[string]any{
"fr_f_step": 1.02,
"base64fp16": false,
@@ -247,9 +261,17 @@ func (h *CurveHandler) reqEqualize(headPhone string, measurement map[string]any,
}
httpReq.Header.Set("Content-Type", "application/json")
start := time.Now()
client := &http.Client{Timeout: 10 * time.Second}
httpResp, err := client.Do(httpReq)
elapsed := time.Since(start)
h.log.Info("eq api response",
zap.String("headPhone", headPhone),
zap.Duration("latency", elapsed),
zap.String("url", h.cfg.APIURL),
)
if err != nil {
h.log.Error("eq api request failed", zap.Duration("latency", elapsed), zap.Error(err))
return "", fmt.Errorf("call eq api: %w", err)
}
defer httpResp.Body.Close()
@@ -272,8 +294,8 @@ func (h *CurveHandler) reqEqualize(headPhone string, measurement map[string]any,
parametricEq, _ := eqResp["parametric_eq"].(map[string]any)
if parametricEq == nil {
result := map[string]any{
"code": 200,
"msg": "req param error",
"code": 200,
"msg": "req param error",
"param": reqBody,
}
resultJSON, _ := json.Marshal(result)
@@ -389,4 +411,26 @@ func intVal(m map[string]any, key string, defaultVal int) int {
default:
return defaultVal
}
}
}
// queryParam 从 URL 原始 query 中获取参数,保留 + 为字面量而非空格
func queryParam(c *gin.Context, key string) string {
vals, ok := c.Request.URL.Query()[key]
if !ok || len(vals) == 0 {
return ""
}
// c.Query() 会把 + 解码为空格,这里从原始 query 手动解码,+ 保留为 +
if strings.Contains(vals[0], " ") {
rawQuery := c.Request.URL.RawQuery
for _, pair := range strings.Split(rawQuery, "&") {
kv := strings.SplitN(pair, "=", 2)
if len(kv) == 2 && kv[0] == key {
decoded, err := url.PathUnescape(strings.ReplaceAll(kv[1], "+", "%2B"))
if err == nil {
return decoded
}
}
}
}
return vals[0]
}