414 lines
26 KiB
Markdown
414 lines
26 KiB
Markdown
# Museek 音乐平台内容获取实现解析
|
||
|
||
本文档解析 Museek 如何通过各音乐平台(网易云 wy、酷狗 kg、酷我 kw、QQ 音乐 tx、咪咕 mg)的公开接口获取**搜索、歌单、专辑、排行榜、热搜**等内容。所有实现均移植自 lx-music-desktop 的 `musicSdk/<platform>/`,代码位于 `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=<url>`,并将浏览器禁止 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; // "<source>_<songId>",如 "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<path>` 直连网关)。
|
||
|
||
### 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=<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=<encodeData>&time=<time>&appId=<appId>&sign=<sign>`;
|
||
- 响应体同样用该 AES key 解密。
|
||
|
||
### 2.5 酷狗签名 — `src/lib/playlists/kg.ts`
|
||
|
||
`signatureParams(params, platform, body)`:`MD5(key + sorted(params) + body + key)`。
|
||
- web key = `NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt`;
|
||
- android key = `OIlwieks28dk2k092lksi2UIkp`。
|
||
|
||
### 2.6 咪咕签名 — `src/lib/search/mg.ts` / `albums/search.ts` / `playlists/search.ts`
|
||
|
||
```ts
|
||
deviceId = "963B7AA0D21511ED807EE5846EC87D20" // 固定
|
||
signatureMd5 = "6cdc72a439cef99a3418d2a78aa28c73"
|
||
sign = md5(str + signatureMd5 + "yyapp2d16148780a1dcc7408e06336b98cfd50" + deviceId + time)
|
||
```
|
||
通过请求头 `deviceId / timestamp / sign / uiVersion / channel` 携带。
|
||
|
||
---
|
||
|
||
## 3. 歌曲搜索
|
||
|
||
入口:`src/stores/searchStore.ts` 的 `searchFns` 分发;`scope = "song"`。各实现返回 `SearchResult { list, total, page, allPage, limit }`。
|
||
|
||
### 3.1 网易云 `searchWangyi` — `src/lib/search/wy.ts`
|
||
|
||
- **接口**:`POST http://interface.music.163.com/eapi/batch`
|
||
- **apiPath**:`/api/search/song/list/page`
|
||
- **签名**:eapi(见 2.1)
|
||
- **请求 payload**:`{ keyword, needCorrect:"1", channel:"typing", offset, scene:"normal", total: page===1, limit }`
|
||
- **响应解析**:`data.resources[].baseInfo.simpleSongData` → `normalizeWySong`(音质由 `privilege.maxBrLevel/maxbr` 与 `hr/sq/h/l` 文件对象判定)。
|
||
- **songId**:数字 id。
|
||
|
||
### 3.2 酷狗 `searchKugou` — `src/lib/search/kg.ts`
|
||
|
||
- **接口**:`GET https://songsearch.kugou.com/song_search_v2`
|
||
- **query**:`keyword,page,pagesize,userid=0,platform=WebFilter,filter=2,iscorrection=1,privilege_filter=0,area_code=1`
|
||
- **headers**:`Referer: https://www.kugou.com/`
|
||
- **无签名**
|
||
- **响应解析**:`data.lists[]`(含 `Grp[]` 子组展开去重)→ 音质由 `FileSize/HQFileSize/SQFileSize/ResFileSize` 判定。
|
||
- **songId**:`Audioid`(album_audio_id);`hash` = `FileHash`。
|
||
|
||
### 3.3 酷我 `searchKuwo` — `src/lib/search/kuwo.ts`
|
||
|
||
- **接口**:`GET http://search.kuwo.cn/r.s`
|
||
- **query**:`client=kt,all=<q>,pn,rn,uid=794762570,ver=kwplayer_ar_9.2.2.1,vipver=1,show_copyright_off=1,newver=1,ft=music,cluster=0,strategy=2012,encoding=utf8,rformat=json,mobi=1`
|
||
- **无签名**
|
||
- **响应解析**:`abslist[]`;音质由 `N_MINFO` 正则 `level:\w+,bitrate:(\d+),format:\w+,size:([\w.]+)` 解析(bitrate 4000/2000/320/128 → flac24bit/flac/320k/128k)。
|
||
- **songId**:`MUSICRID` 去掉 `MUSIC_` 前缀。
|
||
|
||
### 3.4 QQ `searchTx` — `src/lib/search/tx.ts` + `txDesktop.ts`
|
||
|
||
- **接口**:`POST https://u.y.qq.com/cgi-bin/musics.fcg?sign=<sign>`(zzcSign,search_type=0)
|
||
- **响应解析**:`body.song.list`(桌面)或 `body.item_song`(旧移动端);`meta.sum/estimate_sum` 为总数。
|
||
- **音质**:`file.size_128mp3/size_320mp3/size_flac/size_hires`。
|
||
- **songId**:`mid`(songmid);`strMediaMid` = `file.media_mid`。
|
||
- 带重试(最多 5 次,处理风控/异常信封)。
|
||
|
||
### 3.5 咪咕 `searchMigu` — `src/lib/search/mg.ts`
|
||
|
||
主路径 + 兜底(见 decisions:jadeite 可能 403,退回旧接口)。
|
||
|
||
- **主接口**:`GET https://jadeite.migu.cn/music_search/v3/search/searchAll`
|
||
- query:`isCorrect=0,isCopyright=1,searchSwitch=<song=1...>,pageSize,text,pageNo,sort=0,sid=USS`
|
||
- 签名头:`uiVersion/deviceId/timestamp/sign/channel`(2.6)
|
||
- **兜底接口**:`GET https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do`
|
||
- query:`isCopyright=1,isCorrect=1,pageNo,pageSize,searchSwitch,sort=0,text`
|
||
- **响应解析**:`songResultData.resultList[][]`,按 `copyrightId` 去重;音质由 `audioFormats[].formatType`(PQ/HQ/SQ/ZQ/ZQ24)判定。
|
||
- **songId**:`songId`;`copyrightId` 独立字段。
|
||
|
||
---
|
||
|
||
## 4. 热搜关键词
|
||
|
||
入口:`src/lib/hotSearch/index.ts` 的 `getHotSearch(source)`,缓存 10 min,最多 30 条,返回 `HotKeyword[] { keyword, rank }`。
|
||
|
||
### 4.1 网易云 `getWyHotSearch` — `hotSearch/wy.ts`
|
||
|
||
- `POST http://interface.music.163.com/eapi/batch`,apiPath `/api/search/chart/detail`,payload `{ id: "HOT_SEARCH_SONG#@#" }`(eapi 签名)。
|
||
- 解析 `data.itemList[].searchWord`。
|
||
|
||
### 4.2 酷狗 `getKgHotSearch` — `hotSearch/kg.ts`
|
||
|
||
- `GET http://gateway.kugou.com/api/v3/search/hot_tab?signature=ee44edb9d7155821412d220bcaf509dd&appid=1005&clientver=10026&plat=0`
|
||
- 自定义头:`dfid/mid/clienttime/x-router: msearch.kugou.com/user-agent(Android)/kg-rc:1`
|
||
- 解析 `data.list[].keywords[].keyword`。
|
||
|
||
### 4.3 酷我 `getKwHotSearch` — `hotSearch/kw.ts`
|
||
|
||
- `GET http://hotword.kuwo.cn/hotword.s?prod=kwplayer_ar_9.3.0.1&corp=kuwo&newver=2&...&tabid=1`(UA:Dalvik/Android)
|
||
- 解析 `tagvalue[].key`。
|
||
|
||
### 4.4 QQ `getTxHotSearch` — `hotSearch/tx.ts`
|
||
|
||
- `POST https://u.y.qq.com/cgi-bin/musicu.fcg`
|
||
- 协议:`comm` + `hotkey: tencent_musicsoso_hotkey.HotkeyService.GetHotkeyForQQMusicPC`
|
||
- 解析 `hotkey.data.vec_hotkey[].query`。
|
||
|
||
### 4.5 咪咕 `getMgHotSearch` — `hotSearch/mg.ts`
|
||
|
||
- 主 `https://jadeite.migu.cn/music_search/v3/search/hotword`,兜底 `http://jadeite.migu.cn:7090/music_search/v3/search/hotword`(头:channel/uiVersion)。
|
||
- 解析 `data.hotwords[].hotwordList[]`,优先 `resourceType === "song"`。
|
||
|
||
---
|
||
|
||
## 5. 排行榜
|
||
|
||
入口:`src/lib/charts/index.ts`。静态榜单元数据 `ALL_BOARDS`(每个平台 `xxBoards: ChartBoard[] { id: "<source>__<bangid>", name }`),`getBoardSongs(source, boardId, page)` 缓存 5 min 并分发到 `getXxBoardSongs`。
|
||
|
||
### 5.1 网易云 `getWyBoardSongs` — `charts/wy.ts`
|
||
|
||
- **接口**:`POST http://interface.music.163.com/eapi/batch`,apiPath `/api/v3/playlist/detail`,payload `{ id: bangid, n: 100000, s: 0 }`(eapi)。
|
||
- 榜单本质是网易云官方歌单,`playlist.tracks` 一次性返回全量(`page` 忽略)。
|
||
- 榜单列表:飙升榜/新歌榜/原创榜/热歌榜/说唱榜/古典榜/电音榜/黑胶VIP爱听榜/ACG榜/韩语榜/国电榜/欧美热歌榜。
|
||
|
||
### 5.2 酷狗 `getKgBoardSongs` — `charts/kg.ts`
|
||
|
||
- **接口**:`GET http://mobilecdnbj.kugou.com/api/v3/rank/song`
|
||
- query:`version=9108,ranktype=1,plat=0,pagesize=100,area_code=1,page,rankid,with_res_tag=0,show_portrait_mv=1`
|
||
- 无签名;解析 `data.info[]`(音质 `filesize/320filesize/sqfilesize/filesize_high`)。
|
||
- 榜单列表:TOP500/酷狗飙升榜/网络红歌榜/抖音热歌榜/分享榜/内地榜/粤语金曲榜/欧美金曲榜/电音榜/DJ热歌榜/古风新歌榜。
|
||
|
||
### 5.3 酷我 `getKwBoardSongs` — `charts/kw.ts`
|
||
|
||
- **接口**:`GET https://wbd.kuwo.cn/api/bd/bang/bang_info?<wbdCrypto 签名>`
|
||
- 请求体 `{ uid:"", devId:"", sFrom:"kuwo_sdk", user_type:"AP", carSource, id: bangid, pn: page-1, rn: 100 }`(wbdCrypto 加密,见 2.4)。
|
||
- **响应体 AES 解密后解析** `data.musiclist[]`(音质 `n_minfo`)。
|
||
- 榜单列表:飙升榜/热歌榜/新歌榜/抖音热歌榜/会员畅听榜/流行趋势榜/经典怀旧榜/华语榜/粤语榜/欧美榜/韩语榜/日语榜。
|
||
|
||
### 5.4 QQ `getTxBoardSongs` — `charts/tx.ts`
|
||
|
||
- **接口**:`POST https://u.y.qq.com/cgi-bin/musicu.fcg`
|
||
- 协议:`toplist: musicToplist.ToplistInfoServer.GetDetail`,param `{ topid, num: 300 }`(省略 `period` 取最新一期)。
|
||
- 解析 `toplist.data.songInfoList[]`(`file` 音质)。
|
||
- 榜单列表:热歌榜/流行指数榜/新歌榜/飙升榜/网络歌曲榜/抖快榜/内地榜/香港地区榜/台湾地区榜/欧美榜/韩国榜/日本榜。
|
||
|
||
### 5.5 咪咕 `getMgBoardSongs` — `charts/mg.ts`
|
||
|
||
- **接口**:`GET https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/querycontentbyId.do?columnId=<bangid>&needAll=0`
|
||
- 无签名;解析 `columnInfo.contents[].objectInfo`(音质 `newRateFormats[].formatType`,ZQ→flac24bit),`length` 尾部 `mm:ss` 取时长。
|
||
- 榜单列表:新歌榜/热歌榜/原创榜/音乐风向榜/彩铃分贝榜/会员臻爱榜/港台榜/内地榜/欧美榜/国风金曲榜。
|
||
|
||
---
|
||
|
||
## 6. 歌单
|
||
|
||
入口:`src/lib/playlists/index.ts`。三组接口:热门歌单 `getHotPlaylists(source, page, tagId?)`、分类标签 `getPlaylistTags(source)`、歌单详情 `getPlaylistDetail(source, id, page)`,均缓存 5 min。`Playlist { id, name, img, playCount?, author?, publishTime?, songCount?, source, kind? }`,`PlaylistDetail { info, list: MusicInfo[] }`。
|
||
|
||
### 6.1 网易云 — `playlists/wy.ts`
|
||
|
||
- **标签** `getWyPlaylistTags`:eapi `/api/playlist/hottags`(payload `{}`),解析 `tags[].name`。
|
||
- **热门** `getWyHotPlaylists`:eapi `/api/playlist/list`,payload `{ cat, order:"hot", limit:30, offset, total:true }`。
|
||
- **详情** `getWyPlaylistDetail`:eapi `/api/v3/playlist/detail`(`{ id, n:100000, s:0 }`)。
|
||
- `playlist.tracks` 仅内联前 ~10 首,剩余通过 `/api/v3/song/detail`(payload `{ c: JSON.stringify([{id}...]) }`,每 500 首分块,最多 1000 首)扩展并合并 `privileges` 音质信息。
|
||
|
||
### 6.2 酷狗 — `playlists/kg.ts`(多路径,最复杂)
|
||
|
||
- **标签** `getKgPlaylistTags`:`GET http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&cdn=cdn`,解析 `data.hotTag.data{}`。
|
||
- **热门** `getKgHotPlaylists`:`GET http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=5&c=<tag>&p=<page>`,解析 `special_db[]`。
|
||
- **详情** `getKgPlaylistDetail(id)`:先 `parseKgPlaylistId` 判定 id 形态,再分流:
|
||
1. **link**(http 链接)→ `getDetailFromLink`(解析 rank / global_collection_id / gcid / chain,必要时抓 HTML)。
|
||
2. **rank** → `getDetailByRankId`(复用榜单 `getKgBoardSongs`)。
|
||
3. **gcid** → `decodeGcid`(`POST https://t.kugou.com/v1/songlist/batch_decode`,android 签名)→ `getDetailByGlobalId`。
|
||
4. **global**(`collection_`/`global_collection_id`)→ `getDetailByGlobalId`:`GET https://mobiles.kugou.com/api/v5/special/info_v2`(web 签名)取元信息 + `GET https://mobiles.kugou.com/api/v5/special/song_v2`(web 签名,300 首/页分页)取歌曲。
|
||
5. **chain** → `getDetailFromShareChain`(`GET http://m.kugou.com/schain/transfer`)。
|
||
6. **code**(酷狗码/纯数字)→ `getDetailByCode`(`POST http://t.kugou.com/command/`,JSON `{appid,clientver,mid,clienttime,key,data}`);type=2 且无 global id 时回退 `special/song`。
|
||
7. **special**(`id_<n>`)→ `getDetailBySpecialIdWithFallback`:`GET http://mobilecdn.kugou.com/api/v3/special/song`(`plat=0&pagesize=-1`)+ `special/info` → 失败则 `special/info` 取 `global_specialid` 走 global → 再失败抓 HTML(`yueku/v9/special/single/<id>-5-9999.html`)解析 `global.data = [...]` 后 `POST http://gateway.kugou.com/v2/album_audio/audio`(`KG-THash/KG-RC/KG-Fake/KG-RF/x-router` 头)批量 hash→歌曲。
|
||
- **songId**:`album_audio_id/audio_id`;hash 来自 `hash`。
|
||
|
||
### 6.3 酷我 — `playlists/kw.ts`
|
||
|
||
- **标签** `getKwPlaylistTags`:`GET http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmTagList?loginUid=0&loginSid=0&appUid=76039576`,筛 `digest === "10000"`。
|
||
- **热门** `getKwHotPlaylists`:
|
||
- 有 tag:`GET .../getTagPlayList?...&id=<tag>&order=hot`
|
||
- 无 tag:`GET .../getRcmPlayList?...&order=hot`(rn=36)
|
||
- **详情** `getKwPlaylistDetail`:`GET http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=<id>&pn=<page-1>&rn=1000&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1`,解析 `musiclist[]`(`N_MINFO` 音质)。
|
||
|
||
### 6.4 QQ — `playlists/tx.ts`
|
||
|
||
- **标签** `getTxPlaylistTags`:`GET musicu.fcg`,协议 `tags: playlist.PlaylistAllCategoriesServer.get_all_categories`。
|
||
- **热门** `getTxHotPlaylists`:
|
||
- 有 tag:`musicu.fcg` 协议 `playlist.PlayListCategoryServer.get_category_content`。
|
||
- 无 tag:`musicu.fcg` 协议 `playlist.PlayListPlazaServer.get_playlist_by_tag`(`id:10000000, order:5 最热`)。
|
||
- **详情** `getTxPlaylistDetail`:`GET https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&new_format=1&disstid=<id>&...`,解析 `cdlist[0].songlist[]`;非 0 code 时重试最多 3 次(退避)。
|
||
|
||
### 6.5 咪咕 — `playlists/mg.ts`
|
||
|
||
- **标签** `getMgPlaylistTags`:`GET https://app.c.nf.migu.cn/pc/v1.0/template/musiclistplaza-taglist/release`,解析 `data[0].content[].texts[]`。
|
||
- **热门** `getMgHotPlaylists`:
|
||
- 有 tag:`GET .../pc/v1.0/template/musiclistplaza-listbytag/release?pageNumber=&templateVersion=2&tagId=`
|
||
- 无 tag:`GET https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=30&start=<page>&templateVersion=5&type=1`(解析 `contentItemList[].itemList[]`,含 `barList[0].title` 播放量;回退 `contents[]` 树 `resType==='2021'`)。
|
||
- **详情** `getMgPlaylistDetail`:`GET https://app.c.nf.migu.cn/MIGUM3.0/resource/playlist/song/v2.0?pageNo=&pageSize=50&playlistId=` + 并行 `GET https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=`(取 title/imgItem/ownerName)。
|
||
|
||
---
|
||
|
||
## 7. 歌单搜索
|
||
|
||
入口:`src/lib/playlists/search.ts` 的 `searchPlaylists(source, query, page, limit)`(缓存 3 min,软失败返回 `[]`)。所有平台走统一签名/端点,归一化为 `Playlist[]`。
|
||
|
||
| 平台 | 端点 / 方法 |
|
||
|------|-------------|
|
||
| wy | eapi `/api/cloudsearch/pc`,`type:1000`(歌单);额外 `type:1002` 搜用户昵称,命中精确昵称时再 `eapi /api/user/playlist`(`{ uid, limit:50, includeVideo:true }`)拉该用户歌单(应对"我喜欢的音乐"这种通用名) |
|
||
| tx | `qqDesktopSearch(query, page, limit, 3)`(search_type=3),解析 `body.songlist.list[]` |
|
||
| kw | `GET http://search.kuwo.cn/r.s?ft=playlist&...`,解析 `abslist[]` |
|
||
| kg | `GET http://msearchretry.kugou.com/api/v3/search/special?keyword=&page=&pagesize=&showtype=10&filter=0&version=7910&sver=2`,解析 `data.info[]`,id 前缀 `id_` |
|
||
| mg | 签名 `GET https://jadeite.migu.cn/music_search/v3/search/searchAll`(`searchSwitch songlist:1`),解析 `songListResultData.result[]` |
|
||
|
||
---
|
||
|
||
## 8. 歌单链接解析
|
||
|
||
`src/lib/playlists/openLink.ts` 的 `parsePlaylistLink(source, raw)` 把用户粘贴的歌单 URL / 纯 ID 转成各平台 `getXxPlaylistDetail` 可识别的 id 形态:
|
||
|
||
- **tx**:`/playlist/(\d+)` 或 `[?&]id=(\d+)`。
|
||
- **kw**:`/playlist(?:_detail)?/(\d+)`。
|
||
- **kg**:`/special/single/(\d+)`(前缀 `id_`);额外 `matchKgPlaylistId` 识别 `rank_`/`global_collection_id`/`gcid_`/`chain_`/songlist/share URL;纯数字视作酷狗码;短链先 `resolveRedirect`(手动 redirect,最多 3 跳)再匹配。
|
||
- **wy**:`[?&]id=(\d+)` 或 `/playlist/(\d+)/\d+`。
|
||
- **mg**:`/playlist/(\d+)`。
|
||
|
||
---
|
||
|
||
## 9. 专辑
|
||
|
||
入口:`src/lib/albums/index.ts`。四组接口:专辑搜索 `searchAlbums`、详情 `getAlbumDetail(source, id, page)`、新碟/热门 `getHotAlbums(source, page, tagId?)`、分类 `getAlbumTags(source)`。`Album { id, name, img, author?, publishTime?, songCount?, source }`。
|
||
|
||
### 9.1 网易云 — `albums/wy.ts`
|
||
|
||
- **详情** `getWyAlbumDetail`:
|
||
- 主:eapi `/api/v1/album/{id}`(payload `{}`,code 200/502 且含 `songs||album` 即用);
|
||
- 兜底:eapi `/api/album/v3/detail`(`{ id }`)。
|
||
- 封面 `https` 化并补 `param=240y240`。
|
||
- **标签** `getWyAlbumTags`:静态地区 `[华语 ZH, 欧美 EA, 韩国 KR, 日本 JP]`。
|
||
- **热门** `getWyHotAlbums`:eapi `/api/album/new`,payload `{ area, limit:30, offset, total:true }`。
|
||
|
||
### 9.2 酷狗 — `albums/kg.ts`
|
||
|
||
- **详情** `getKgAlbumDetail`:
|
||
- 歌曲:`GET http://mobiles.kugou.com/api/v3/album/song?version=9108&albumid=&plat=0&pagesize=200&area_code=0&page=&with_res_tag=0`;
|
||
- 若仅返回 hash,则 `POST http://gateway.kugou.com/v2/album_audio/audio`(同 6.2)批量解析;
|
||
- 信息:`POST http://kmrserviceretry.kugou.com/container/v1/album`(JSON 含 `data:[{album_id}]` 与固定 `key/mid`),解析 `album_name/sizable_cover/author_name`。
|
||
- **标签** `getKgAlbumTags`:静态语言 `[华语 1, 欧美 2, 日语 3, 韩语 4, 其他 5]`。
|
||
- **热门** `getKgHotAlbums`:`GET http://www2.kugou.kugou.com/yueku/v9/album/index?is_ajax=1&cdn=cdn&p=&s=30&l=<lang>&c=&t=0`,解析 `data.data/data.info[]`。
|
||
|
||
### 9.3 酷我 — `albums/kw.ts`
|
||
|
||
- **详情** `getKwAlbumDetail`:`GET http://search.kuwo.cn/r.s?pn=&rn=1000&stype=albuminfo&albumid=&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`(重试最多 3 次;body 可能是单引号 JSON 字符串,用 `objStr2JSON` 修复;音质 `formats` 字段 `MP3128/MP3H/ALFLAC/HIRFLAC`)。
|
||
- **标签** `getKwAlbumTags`:静态榜单 `[新歌榜 17, 热歌榜 16, 飙升榜 93]`。
|
||
- **热门** `getKwHotAlbums`:酷我没有稳定专辑广场,**由榜单歌曲反推去重专辑**(`getKwBoardSongs` → 按 `meta.albumId` 去重)。
|
||
|
||
### 9.4 QQ — `albums/tx.ts`
|
||
|
||
- **详情** `getTxAlbumDetail`:`GET https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg?albummid=&platform=yqq&format=json&...`(重试 3 次),兼容 `data`/顶层、`songlist`/`list` 两种形态。
|
||
- **标签** `getTxAlbumTags`:`GET musicu.fcg` 协议 `req_1: music.web_album_library.get_album_by_tags`(`get_tags:1`),解析 `req_1.data.tags.area`;失败用静态地区兜底。
|
||
- **热门** `getTxHotAlbums`:`musicu.fcg` 协议 `get_album_by_tags`(`sort:2, area, start, num:30`),解析 `req_1.data.list/albumlist`。
|
||
|
||
### 9.5 咪咕 — `albums/mg.ts`
|
||
|
||
- **详情** `getMgAlbumDetail`:
|
||
- 歌曲:`GET http://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/queryAlbumSong?albumId=&pageNo=`(`newRateFormats/artists/length` 形态);
|
||
- 信息:`GET https://app.c.nf.migu.cn/MIGUM3.0/resource/album/v2.0?albumId=`(title/imgItems/singer)。
|
||
- **标签** `getMgAlbumTags`:`GET https://app.c.nf.migu.cn/pc/v1.0/template/get-new-cd-list-header`,从 `actionUrl` 提取 `columnId`;失败用静态兜底。
|
||
- **热门** `getMgHotAlbums`:`GET https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/get-new-cd-list-data?templateVersion=1&columnId=&start=&count=30`,解析 `contentItemList[].itemList[]`(`template === "disk_grid"`),从 `actionUrl` 提取 `album-info?id=`。
|
||
|
||
---
|
||
|
||
## 10. 专辑搜索
|
||
|
||
入口:`src/lib/albums/search.ts` 的 `searchAlbums(source, query, page, limit)`(缓存 3 min,软失败返回 `[]`)。
|
||
|
||
| 平台 | 端点 / 方法 | 说明 |
|
||
|------|-------------|------|
|
||
| wy | eapi `/api/cloudsearch/pc`,`type:10` | 解析 `result.albums[]` |
|
||
| tx | `qqDesktopSearch(query, page, limit, 2)`(search_type=2) | 解析 `body.album.list[]` |
|
||
| kw | `GET http://search.kuwo.cn/r.s?ft=album&...` | 解析 `albumlist/abslist[]`(小写字段) |
|
||
| kg | `GET http://msearchretry.kugou.com/api/v3/search/album?keyword=&page=&pagesize=&sver=2&with_res_tag=0` | 解析 `data.info[]` |
|
||
| mg | 签名 `GET .../searchAll`(`searchSwitch song:1 album:1`) | **合并歌曲命中与官方专辑结果**,按标题/歌曲名相关度打分排序:歌曲命中 `1000*歌名匹配 + 专辑名加成 + 顺序加成 - live/remix 惩罚`;官方专辑 `200*标题匹配 + ...`;过滤"单曲发行"/id=2 的垃圾专辑 |
|
||
|
||
---
|
||
|
||
## 11. 附:播放 URL / 歌词获取
|
||
|
||
(不属于歌单/专辑/榜单/搜索,但同属"内容获取",简述链路。)
|
||
|
||
### 11.1 播放 URL
|
||
|
||
- **内置网易云**:`src/lib/playlists/wyUrl.ts` `getWyBuiltinMusicUrl(songId, quality)` → `GET https://music.163.com/api/song/enhance/player/url?id=&ids=[id]&br=<bitrate>`,作为导入 lx 源失败时的兜底。
|
||
- **主路径**:`src/lib/sourceRunner.ts` 调用用户导入的 lx-music 兼容源脚本(`musicUrl` 动作),Museek 不内置分发内容(见 decisions/constraints)。
|
||
|
||
### 11.2 歌词
|
||
|
||
`src/lib/lyric/loadLyric.ts` `loadLyric(song)` 链路:磁盘/内存缓存 → 平台内置歌词(`src/lib/lyric/`)→ 源脚本 `lyric` 动作 → 解析(`lxlyric`/`lyric`/`tlyric`)并应用逐词卡拉 OK 时序(`src/lib/lyrics/timing.ts`,支持 wy YRC / tx QRC / kw lyricx / kg KRC / mg MRC 及内联 `lxlyric`)。
|
||
|
||
---
|
||
|
||
## 附:平台 → 代码文件速查
|
||
|
||
| 平台 | 搜索 | 热搜 | 排行榜 | 歌单 | 歌单搜索 | 专辑 | 专辑搜索 |
|
||
|------|------|------|--------|------|----------|------|----------|
|
||
| 网易云 wy | `search/wy.ts` | `hotSearch/wy.ts` | `charts/wy.ts` | `playlists/wy.ts` | `playlists/search.ts` | `albums/wy.ts` | `albums/search.ts` |
|
||
| 酷狗 kg | `search/kg.ts` | `hotSearch/kg.ts` | `charts/kg.ts` | `playlists/kg.ts` | `playlists/search.ts` | `albums/kg.ts` | `albums/search.ts` |
|
||
| 酷我 kw | `search/kuwo.ts` | `hotSearch/kw.ts` | `charts/kw.ts` | `playlists/kw.ts` | `playlists/search.ts` | `albums/kw.ts` | `albums/search.ts` |
|
||
| QQ tx | `search/tx.ts` + `search/txDesktop.ts` | `hotSearch/tx.ts` | `charts/tx.ts` | `playlists/tx.ts` | `playlists/search.ts` | `albums/tx.ts` | `albums/search.ts` |
|
||
| 咪咕 mg | `search/mg.ts` | `hotSearch/mg.ts` | `charts/mg.ts` | `playlists/mg.ts` | `playlists/search.ts` | `albums/mg.ts` | `albums/search.ts` |
|