YwYMusic/docs/music-platform-apis.md

414 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` 等 | 510 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网易云是数字 idQQ 是 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>`zzcSignsearch_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`
主路径 + 兜底(见 decisionsjadeite 可能 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`UADalvik/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` |