# Museek 音乐平台内容获取实现解析 本文档解析 Museek 如何通过各音乐平台(网易云 wy、酷狗 kg、酷我 kw、QQ 音乐 tx、咪咕 mg)的公开接口获取**搜索、歌单、专辑、排行榜、热搜**等内容。所有实现均移植自 lx-music-desktop 的 `musicSdk//`,代码位于 `src/lib/` 下按功能分目录组织。 > 平台缩写对照:`wy` = 网易云音乐,`kg` = 酷狗音乐,`kw` = 酷我音乐,`tx` = QQ 音乐(腾讯),`mg` = 咪咕音乐。 --- ## 目录 1. [通用基础设施](#1-通用基础设施) 2. [平台 SDK 桶与签名算法](#2-平台-sdk-桶与签名算法) 3. [歌曲搜索(5 平台)](#3-歌曲搜索) 4. [热搜关键词(5 平台)](#4-热搜关键词) 5. [排行榜(5 平台)](#5-排行榜) 6. [歌单(5 平台)](#6-歌单) 7. [歌单搜索](#7-歌单搜索) 8. [歌单链接解析](#8-歌单链接解析) 9. [专辑(5 平台)](#9-专辑) 10. [专辑搜索](#10-专辑搜索) 11. [附:播放 URL / 歌词获取](#11-附播放-url--歌词获取) --- ## 1. 通用基础设施 ### 1.1 HTTP 层 — `src/lib/http.ts` 所有第三方请求统一经过 `httpFetch(input, init)`: - **Tauri 环境**(`__TAURI_INTERNALS__ in window`):走 `@tauri-apps/plugin-http` 原生 HTTP,绕过浏览器 CORS 限制。 - **浏览器/预览环境**:路由到 Vite 开发代理 `/__proxy?target=`,并将浏览器禁止 JS 设置的请求头(`user-agent`、`referer`、`origin`、`cookie`、`host`)通过 `x-pxy-*` 前缀中继给代理还原。 ### 1.2 缓存层 — `src/lib/cache.ts` `createAsyncCache(ttlMs, max)` 提供进程内 TTL 缓存 + Promise 级去重(并发相同请求共享一次网络调用),失败不缓存。各模块用它包裹只读接口。 | 模块 | 缓存键 | TTL | |------|--------|-----| | 歌曲搜索 | `platform:query:page` | 3 min | | 歌单搜索 | `source:query:page` | 3 min | | 专辑搜索 | `v4:source:query:page:limit` | 3 min | | 歌单(热门/标签/详情) | `source:page:tag` / `source:tags` / `source:id:page` | 5 min | | 专辑(热门/标签/详情) | `v2:source:page:tag` 等 | 5–10 min | | 排行榜歌曲 | `source:boardId:page` | 5 min | | 热搜 | `source` | 10 min | | 歌词 | `source:songId:version` | 30 min | ### 1.3 统一数据模型 — `src/types/music.ts` 所有平台返回统一归一化为 `MusicInfo`: ```ts interface MusicInfo { id: string; // "_",如 "wy_12345" name: string; singer: string; source: Source; // "kw" | "kg" | "tx" | "wy" | "mg" | "local" interval: string; // 时长 "m:ss" albumName: string; meta: MusicInfoMeta; // 平台特有字段 + 音质列表 } ``` - `MusicInfoMeta.songId` 是平台原始歌曲 ID(网易云是数字 id,QQ 是 songmid,酷狗是 album_audio_id 等)。 - `MusicInfoMeta.qualitys` / `_qualitys` 由 `src/lib/quality.ts` 的 `indexQualitySizes` 归一化,音质枚举:`128k` / `320k` / `flac` / `flac24bit`。 - 各平台特有字段:`hash`(酷狗 FileHash)、`strMediaMid`(QQ media_mid)、`copyrightId`(咪咕)。 --- ## 2. 平台 SDK 桶与签名算法 `src/lib/platforms/index.ts` 是各平台的 SDK 聚合桶(`export * as wy/kg/tx/mg/kw`),各平台 index 仅做 re-export,签名/加密逻辑集中在各自文件内。 ### 2.1 网易云 eapi 签名 — `src/lib/platforms/wy/eapi.ts` ```ts eapi(url, object) → { params: string } eapiParams(url, object) → string ``` 算法(移植自 wy/utils/crypto.js): 1. `text = JSON.stringify(object)`; 2. `message = "nobody" + url + "use" + text + "md5forencrypt"`; 3. `digest = md5(message)`; 4. `data = url + "-36cd479b6b5-" + text + "-36cd479b6b5-" + digest`; 5. 用密钥 `e82ckenh8dichen8` 做 **AES-128-ECB + PKCS7** 加密,输出大写十六进制。 请求体为 `application/x-www-form-urlencoded` 的 `params=...`,POST 到 `http://interface.music.163.com/eapi/batch`(或用 `eapi` 直连网关)。 ### 2.2 QQ 音乐 zzcSign 签名 — `src/lib/search/txDesktop.ts` 用于 `musics.fcg` 桌面搜索(search_type 0/2/3): 1. 对请求体 JSON 求 **SHA-1**; 2. 按 `PART_1_INDEXES=[23,14,6,36,16,40,7,19]`、`PART_2_INDEXES=[16,1,32,12,19,27,8,5]` 抽取散列字符; 3. `SCRAMBLE_VALUES`(20 个固定字节)与散列逐字节异或后 Base64(去掉 `/+`); 4. 签名 = `zzc + part1 + b64 + part2`(小写); 5. URL 形如 `https://u.y.qq.com/cgi-bin/musics.fcg?sign=`,body 为 `comm` + `music.search.SearchCgiService.DoSearchForQQMusicDesktop` 协议包,含随机 `searchid`(32 位 hex + 5 位数字)。 ### 2.3 QQ 音乐 musicu.fcg(无 zzcSign) 排行榜/热搜/歌单/专辑使用的 `u.y.qq.com/cgi-bin/musicu.fcg` 使用传统 `comm` 块(`uin/format/ct/cv`),**不需要** zzcSign。请求体 `data` 参数为 `encodeURIComponent(JSON.stringify(body))`,或 POST JSON。 ### 2.4 酷我 wbdCrypto — `src/lib/charts/kw.ts` 排行榜请求签名: - AES key 固定 16 字节 `[112,87,39,61,199,250,41,191,57,68,45,114,221,94,140,228]`; - `appId = "y67sprxhhpws"`; - `encodeData = base64(AES-128-ECB(JSON))`;`sign = MD5(appId + encodeData + time).toUpperCase()`; - 最终 query:`data=&time=