Files
Koneko_api_for_QZ-Music/6a2d8996cc974039d1dfbbf7_Koneko插件开发避坑指南_v0.0.3.md
2026-06-20 12:48:28 +08:00

331 lines
12 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.
# Koneko QZ Music v2 鎻掍欢寮€鍙戦伩鍧戞寚鍗?
> 鐗堟湰: 0.0.3 | 浣滆€? 浜戞眬(Miao-moe) | 鐩爣: 鏀寔鍒板埆鐨?AI 缁х画寮€鍙?
---
## 涓€銆侀」鐩儗鏅?
QZ Music v2 鏄竴娆?Android 闊充箰鎾斁鍣紝鏀寔閫氳繃**鎷撳睍鎻掍欢**鎺ュ叆澶氬钩鍙伴煶婧愩€傛彃浠剁郴缁熷熀浜?Node.js 杩愯鏃讹紙Javet/V8锛夛紝姣忎釜鎻掍欢鏄竴涓崟鐙殑 `.js` 鏂囦欢锛岄€氳繃 `module.exports` 瀵煎嚭鎺ュ彛銆?
### 1.1 鎻掍欢鍔犺浇鏈哄埗
- canary 12-4 鐗堟湰涔嬪墠锛氱洿鎺ュ姞杞?`.js` 鏂囦欢
- **canary 12-4 鍙婁箣鍚?*锛氶渶瑕?`鏂囦欢澶?+ plugin.json + index.js` 缁撴瀯锛堜絾鐢ㄦ埛瑕佹眰鍙敤 `.js`锛屾墍浠ュ綋鍓嶇増鏈槸鍗曟枃浠讹級
- 杩愯鏃剁幆澧冨彉閲忛€氳繃 `global.env` 璁块棶锛?*涓嶆槸** `process.env`
### 1.2 鎻掍欢瀵煎嚭鏍煎紡
```js
module.exports = {
musicSearch: { search: fn, tipSearch: fn, hotSearch: fn },
tipSearch: { getList: fn },
hotSearch: { getList: fn },
getUrl: fn,
pluginInfo: { info: {...}, env: [...], ext: [...], quality: [...], supportFunc: [] },
// 缃戞槗浜戠壒鏈? userPlaylist: fn,
dailyRecommend: fn,
personalFm: fn,
myLikedSongs: fn
}
```
---
## 浜屻€丣avet/V8 鍏煎鎬уぇ鍧戯紙鏈€閲嶈锛?
QZ Music 浣跨敤 Javet 浣滀负 JS 杩愯鏃讹紙鍩轰簬 V8锛夛紝**涓嶆敮鎸佺幇浠?ES 璇硶**锛屽繀椤荤敤淇濆畧鍐欐硶锛?
| 璇硶 | 鏄惁鏀寔 | 姝g‘鍐欐硶 |
|------|---------|---------|
| `let` / `const` | 鉂?| `var` |
| 绠ご鍑芥暟 `() => {}` | 鉂?| `function() {}` |
| `async` / `await` | 鉂?| `Promise` 閾惧紡璋冪敤 |
| `catch { }`锛堟棤鍙傛暟锛墊 鉂?| `catch (e) { }` |
| `Promise.allSettled` | 鉂?| `Promise.all` + 鎵嬪姩鍖呰 |
| `Object.entries` / `Object.values` | 鉂?| `for...in` 閬嶅巻 |
| `Array.prototype.includes` | 鉂?| `indexOf(...) !== -1` |
| `String.prototype.startsWith` | 鉂?| `indexOf(...) === 0` |
| `BigInt` 瀛楅潰閲?| 鈿狅笍 鎱庣敤 | `BigInt('0x' + hex)` |
| `class` | 鉂?| 瀵硅薄瀛楅潰閲?|
| 妯℃澘瀛楃涓?`${}` | 鉁?| 鍙敤 |
| `Buffer` | 鉁?| Node.js 鍐呯疆 |
| `require` | 鉁?| CommonJS |
### 2.1 Promise.allSettled 鏇夸唬鏂规
```js
// 鉂?涓嶆敮鎸?Promise.allSettled(promises)
// 鉁?姝g‘鍐欐硶
Promise.all(promises.map(function(p) {
return p.then(function(v) {
return { status: 'fulfilled', value: v }
}).catch(function(e) {
return { status: 'rejected', reason: e }
})
})).then(function(results) {
for (var i = 0; i < results.length; i++) {
if (results[i].status === 'fulfilled') {
return results[i].value
}
}
throw new Error('all failed')
})
```
---
## 涓夈€佹悳绱㈢粨鏋滄牸寮忓ぇ鍧?
App 鐨?`MusicListResponse` 鍙嶅簭鍒楀寲瑕佹眰**蹇呴』鏈?`list` 瀛楁**锛屼笉鏄?`songs`锛?
### 3.1 姝g‘鐨勬悳绱㈣繑鍥炴牸寮?
```js
return {
list: [
{
id: '姝屾洸ID瀛楃涓?,
name: '姝屾洸鍚?,
artists: '姝屾墜1銆佹瓕鎵?',
albumName: '涓撹緫鍚?,
albumId: '涓撹緫ID',
source: 'tx', // tx/kg/kw/wy/mg/git
pic: '灏侀潰澶у浘URL',
mPic: '灏侀潰涓浘URL',
sPic: '灏侀潰灏忓浘URL',
interval: '3:45', // 鎾斁鏃堕暱 m:ss
qualities: {
standard: '3.21MB',
exhigh: '7.85MB',
lossless: '25.3MB',
hires: '48.2MB'
}
}
],
allPage: 5, // 鎬婚〉鏁? limit: 30, // 姣忛〉鏉℃暟
total: 150, // 鎬绘潯鏁? source: 'tx' // 骞冲彴鏍囪瘑
}
```
### 3.2 瀛楁鍚嶅鐓ц〃
| 鍚箟 | 姝g‘瀛楁鍚?| 閿欒瀛楁鍚?|
|------|----------|----------|
| 姝屾墜 | `artists` | `artist` |
| 灏侀潰鍥?| `pic` / `mPic` / `sPic` | `picUrl` |
| 鏃堕暱 | `interval` (瀛楃涓?m:ss) | `duration` |
| 姝屾洸鍒楄〃 | `list` | `songs` |
---
## 鍥涖€佸悇骞冲彴 API 韪╁潙璁板綍
### 4.1 QQ闊充箰 (tx)
**鎼滅储绛惧悕**锛歚zzcSign` = SHA1 + 鑷畾涔夌储寮曟彁鍙?+ XOR 娣锋穯 + base64
```js
var PART_1_INDEXES = [23, 14, 6, 36, 16, 40, 7, 19]
var PART_2_INDEXES = [16, 1, 32, 12, 19, 27, 8, 5]
var SCRAMBLE_VALUES = [89, 39, 179, 150, 218, 82, 58, 252, 177, 52, 186, 123, 120, 64, 242, 133, 143, 161, 121, 179]
```
**灏侀潰鍥捐鍒?*锛?- 鏈変笓杈慖D锛歚https://y.gtimg.cn/music/photo_new/T002R500x500M000{albumId}.jpg`
- 鏃犱笓杈慖D锛堟瓕鎵嬪浘锛夛細`https://y.gtimg.cn/music/photo_new/T001R500x500M000{singerMid}.jpg`
**getUrl 闊宠川鍙傛暟**锛欰PI 闇€瑕佸甫 `k`锛屽 `320k`锛屼笉鏄?`320`
### 4.2 閰风嫍闊充箰 (kg)
**鎼滅储鎺ュ彛**锛歚http://mobilecdn.kugou.com/api/v3/search/song`
**閲嶈**锛氳繑鍥炲瓧娈垫槸 `errcode`锛堜笉鏄?`error_code`锛夛紒
```js
// 鉁?姝g‘
if (result.errcode !== 0) { ... }
// 鉂?閿欒
if (result.error_code !== 0) { ... }
```
**灏侀潰鍥?*锛氭悳绱㈢粨鏋滆嚜甯?`imgurl` 瀛楁锛屾浛鎹?`{size}` 涓?`400`
```js
var picUrl = item.imgurl ? item.imgurl.replace('{size}', '400') : ''
```
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
### 4.3 閰锋垜闊充箰 (kw)
**鎼滅储鎺ュ彛**锛歚http://search.kuwo.cn/r.s`
**灏侀潰鍥?*锛歚https://img2.kuwo.cn/star/albumcover/300/{ALBUMID}.jpg`
**闊宠川淇℃伅**锛氬湪 `N_MINFO` 瀛楁涓紝鏍煎紡涓?`level:xxx,bitrate:xxx,format:xxx,size:xxx;...`
```js
var parts = info.N_MINFO.split(';')
for (var j = 0; j < parts.length; j++) {
var m = parts[j].match(/level:(\w+),bitrate:(\d+),format:(\w+),size:([\w.]+)/)
if (m) {
if (m[2] === '20900') qualities.jymaster = m[4]
else if (m[2] === '4000') qualities.hires = m[4]
else if (m[2] === '2000') qualities.lossless = m[4]
else if (m[2] === '320') qualities.exhigh = m[4]
else if (m[2] === '128') qualities.standard = m[4]
}
}
```
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
### 4.4 缃戞槗浜戦煶涔?(wy)
**鎼滅储鎺ュ彛**锛歚https://music.163.com/api/search/get/web`锛堢畝鍗?GET锛屼笉闇€瑕?weapi 鍔犲瘑锛?
**灏侀潰鍥?*锛歚picId` 闇€瑕?Base64 缂栫爜鍚庢嫾鎺?
```js
var picIdStr = String(s.album.picId)
var picIdB64 = Buffer.from(picIdStr).toString('base64').replace(/=/g, '')
var pic = 'https://p2.music.126.net/' + picIdB64 + '/' + picIdStr + '.jpg'
```
**getUrl 闊宠川鍙傛暟**锛氭暟瀛楁牸寮?`128000` / `320000` / `999000`锛堜笉鏄甫k鐨勶級
**鍔犲瘑鎺ュ彛**锛?- `eapi`锛欰ES-128-ECB锛宬ey = `e82ckenh8dichen8`
- `weapi`锛欰ES-128-CBC + RSA锛堢敤浜庨渶瑕佺櫥褰曠殑鎺ュ彛锛?
**ext 鍔熻兘**锛?- `userPlaylist`锛氶渶瑕?`playlist_url` 鐜鍙橀噺锛堢綉鏄撲簯涓汉涓婚〉閾炬帴锛?- `dailyRecommend`锛氭瘡鏃ユ帹鑽?- `personalFm`锛氱浜篎M
- `myLikedSongs`锛氭垜鍠滄鐨勯煶涔?
### 4.5 鍜挄闊充箰 (mg)
**鎼滅储绛惧悕**锛歁D5 鎷兼帴
```js
var sign = crypto.createHash('md5').update(
str + signatureMd5 + 'yyapp2d16148780a1dcc7408e06336b98cfd50' + deviceId + time
).digest('hex')
```
**灏侀潰鍥?*锛氭悳绱㈢粨鏋滃彲鑳借繑鍥炵浉瀵硅矾寰勶紝闇€瑕佹嫾鎺ュ煙鍚?
```js
var img = data.img3 || data.img2 || data.img1 || ''
if (img && img.indexOf('http') !== 0) img = 'https://d.musicapp.migu.cn' + img
```
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
### 4.6 GIT闊虫簮 (git)
- 鏃犳悳绱㈠姛鑳斤紙杩斿洖绌哄垪琛級
- 绾煶婧愭彃浠讹紝鍙湁 `getUrl`
- getUrl 闊宠川鍙傛暟锛歚128k` / `320k` / `999k`
---
## 浜斻€乬etUrl 娴嬮€熷鐏鹃€昏緫
鎵€鏈夊钩鍙扮粺涓€浣跨敤**骞跺彂娴嬮€?*妯″紡锛氬悓鏃惰姹傚涓?API锛屽彇绗竴涓垚鍔熺殑缁撴灉銆?
```js
function getUrl(songId, quality) {
var apis = buildApis(songId, quality)
var promises = []
for (var i = 0; i < apis.length; i++) {
(function(api) {
promises.push(
httpGet(api.url, api.headers, 8000).then(function(res) {
var url = api.extract(res)
if (url) return { name: api.name, url: url }
throw new Error('no url')
}).catch(function(err) {
throw err
})
)
})(apis[i])
}
return Promise.all(promises.map(function(p) {
return p.then(function(v) { return { status: 'fulfilled', value: v } })
.catch(function(e) { return { status: 'rejected', reason: e } })
})).then(function(results) {
for (var i = 0; i < results.length; i++) {
if (results[i].status === 'fulfilled') return results[i].value.url
}
return ''
})
}
```
### 5.1 闊虫簮 API 鍒楄〃
| API | 鏀寔骞冲彴 | 鐗圭偣 |
|-----|---------|------|
| 鑱嗘緶 | 鍏ㄩ儴 | 闇€瑕?`ceru_key`锛屾渶绋冲畾 |
| HUIBQ (lxmusicapi) | 鍏ㄩ儴 | `X-Request-Key: share-v3` |
| 鏄熸捣 | 鍏ㄩ儴 | 鑱氬悎鎺ュ彛 |
| 蹇靛績 | tx/kg/kw/mg | 涓汉缁存姢 |
| 闀块潚 | tx/kg/kw/mg | 涓汉缁存姢 |
| 鏄熸捣澶?| 鍏ㄩ儴 | 澶囩敤 |
| fish | 鍏ㄩ儴 | 涓汉缁存姢 |
| HYW | 鍏ㄩ儴 | 闇€瑕?`X-Card-Key` |
| 蹇嗛煶 | tx | 鐩存帴杩斿洖 URL |
| 鏀堕泦QQ | tx | 涓撶敤 |
| 鏀堕泦KW | kw | 涓撶敤 |
| bb | wy | 缃戞槗浜戜笓鐢?|
| ymc | wy | 缃戞槗浜戜笓鐢?|
| unms | wy | 缃戞槗浜戜笓鐢?|
| 瀹樻柟 | wy | 缃戞槗浜戝畼鏂?weapi |
---
## 鍏€佺増鏈彿绠$悊瑙勮寖
**鎵€鏈夊钩鍙扮粺涓€鐗堟湰鍙?*锛屾瘡娆′慨鏀瑰叏閮ㄥ崌绾э細
- 褰撳墠鐗堟湰锛歚0.0.3`
- 涓嬫淇敼锛歚0.0.1`
- 鍐嶄笅娆★細`0.0.5`
鏂囦欢鍚嶆牸寮忥細`Koneko_{骞冲彴鍚峿_v{鐗堟湰鍙穧.js`
---
## 涓冦€佸父瑙佹姤閿欎笌瑙e喅
| 鎶ラ敊 | 鍘熷洜 | 瑙e喅 |
|------|------|------|
| `Cannot find module 'axios'` | 鐢ㄤ簡 axios | 鏀圭敤 `http`/`https` 鍐呯疆妯″潡 |
| `String cannot be converted to JSONObject` | 鎼滅储杩斿洖浜嗛潪瀵硅薄/瀛楃涓?| 鍔?`.catch()` 鍏滃簳杩斿洖姝g‘鏍煎紡 |
| `Field 'list' is required` | 鎼滅储杩斿洖 `songs` 鑰岄潪 `list` | 鏀瑰瓧娈靛悕涓?`list` |
| `SyntaxError: Invalid or unexpected token` | 鐢ㄤ簡 `catch { }` | 鏀逛负 `catch (e) { }` |
| `FileNotFoundException: plugin.json` | 12-4鐗堟湰闇€瑕佹枃浠跺す缁撴瀯 | 鍒涘缓 `plugin.json` + `index.js` |
| 鎼滅储鏃犵粨鏋?| 瀛楁鍚嶄笉鍖归厤 | 妫€鏌?`errcode` vs `error_code` |
| 鎾斁澶辫触 | `mapBr` 杩斿洖鏍煎紡涓嶅 | QQ/kg/kw/mg/git 鐢?`320k`锛寃y 鐢?`320000` |
| 灏侀潰鍥句笉鏄剧ず | URL 鏍煎紡閿欒鎴栬法鍩?| 妫€鏌ュ悇骞冲彴灏侀潰鍥炬嫾鎺ヨ鍒?|
---
## 鍏€佺幆澧冨彉閲?
```js
var env = global.env || {}
var CERU_KEY = env.ceru_key || '' // 鑱嗘緶API瀵嗛挜
var WY_COOKIE = env.cookie || '' // 缃戞槗浜慍ookie
var PLAYLIST_URL = env.playlist_url || '' // 缃戞槗浜戜釜浜轰富椤甸摼鎺?```
鍦?QZ Music 璁剧疆涓厤缃幆澧冨彉閲忥紝鎻掍欢閫氳繃 `global.env` 璇诲彇銆?
---
## 涔濄€佸畬鏁翠唬鐮佸弬鑰?
6涓钩鍙扮殑瀹屾暣浠爜瑙佷骇鐗╃洰褰曪細
- `Koneko_QQ闊充箰_v0.0.3.js`
- `Koneko_閰风嫍闊充箰_v0.0.3.js`
- `Koneko_閰锋垜闊充箰_v0.0.3.js`
- `Koneko_缃戞槗浜戦煶涔恄v0.0.3.js`
- `Koneko_鍜挄闊充箰_v0.0.3.js`
- `Koneko_GIT闊虫簮_v0.0.3.js`
---
## 鍗併€佸悗缁紑鍙戝缓璁?
1. **姣忔淇敼鍏ㄩ儴骞冲彴缁熶竴鍗囩骇鐗堟湰鍙?*
2. **鍏堝湪娴忚鍣? curl 娴嬭瘯 API 鏄惁鍙敤**
3. **娉ㄦ剰 Javet 鍏煎鎬э紝閬垮厤鐜颁唬 JS 璇硶**
4. **鎼滅储杩斿洖鍔″繀鍖呭惈 `list` 瀛楁**
5. **getUrl 娉ㄦ剰鍚勫钩鍙伴煶璐ㄥ弬鏁版牸寮忓樊寮?*
6. **灏侀潰鍥?URL 纭繚鍙闂紙娉ㄦ剰璺ㄥ煙鍜岄槻鐩楅摼锛?*
7. **鎵€鏈夊紓姝ユ搷浣滃姞 `.catch()` 鍏滃簳**
8. **浼樺厛浣跨敤瀹樻柟鎼滅储鎺ュ彛锛岄煶婧愮敤绗笁鏂?API 瀹圭伨**