import { useState, useRef, useCallback } from "react"; import { decodeCustomBase64 } from "@/lib/luxsinApi"; /* ── Module-level cache: survives route switches ── */ const rawCurveCache = new Map(); export function useRawCurve() { const [currentRawCurve, setCurrentRawCurve] = useState(null); const [rawCurveLoading, setRawCurveLoading] = useState(false); const rawCurveCacheRef = useRef(rawCurveCache); const getModelCurve = async (brand: string, name: string) => { try { console.log("[modelCurve] request", { brand, name }); const resp = await fetch( `//api.luxsin.com.cn/audio/modelCurve?brand=${encodeURIComponent(brand)}&name=${encodeURIComponent(name)}` ); const data = await resp.text(); // 使用自定义 Base64 解码 const decoded = decodeCustomBase64(data); const parsed = JSON.parse(decoded); console.log("[modelCurve] decoded", { hasFrRaw: !!(parsed && typeof parsed === "object" && "fr" in (parsed as Record) && (parsed as { fr?: unknown }).fr && typeof (parsed as { fr?: unknown }).fr === "object" && "raw" in ((parsed as { fr?: Record }).fr ?? {})), }); console.log("[modelCurve] fr.raw", (parsed as { fr?: { raw?: unknown } }).fr?.raw); return parsed; } catch (error) { console.log("[modelCurve] request failed", error); return null; } }; const loadRawCurveForPeq = useCallback( async (peq: { brand?: string; model?: string; name?: string } | undefined): Promise => { const brand = peq?.brand?.trim() ?? ""; const model = peq?.model?.trim() ?? ""; if (!brand || !model) return null; const cacheKey = `${brand}|${model}`; if (rawCurveCacheRef.current.has(cacheKey)) { const cached = rawCurveCacheRef.current.get(cacheKey); if (cached) { console.log("[modelCurve] cache hit", { brand, model, rawLength: cached.length }); } else { console.log("[modelCurve] cache hit (no raw)", { brand, model }); } return cached ?? null; } const modelCurve = await getModelCurve(brand, model); if (modelCurve && typeof modelCurve === "object") { const payload = modelCurve as { fr?: { raw?: unknown } | unknown; }; const rawCandidate = payload.fr && typeof payload.fr === "object" && Array.isArray((payload.fr as { raw?: unknown }).raw) ? (payload.fr as { raw?: unknown }).raw : null; const raw = Array.isArray(rawCandidate) ? rawCandidate .map((point) => { if (typeof point === "number" && Number.isFinite(point)) return point; if (Array.isArray(point) && point.length >= 2 && typeof point[1] === "number") return point[1]; if (point && typeof point === "object") { const y = (point as Record).y ?? (point as Record).value ?? (point as Record).db ?? (point as Record).gain; if (typeof y === "number" && Number.isFinite(y)) return y; } return NaN; }) : null; const cleanedRaw = raw && raw.every((v) => Number.isFinite(v)) ? (raw as number[]) : null; rawCurveCacheRef.current.set(cacheKey, cleanedRaw); console.log("[modelCurve] raw check", { brand, model, hasRaw: !!cleanedRaw, rawLength: cleanedRaw?.length ?? 0, }); return cleanedRaw; } return null; }, [], ); return { currentRawCurve, setCurrentRawCurve, rawCurveLoading, setRawCurveLoading, loadRawCurveForPeq, }; }