Files
Koneko_api_for_QZ-Music/6a2d8996cc974039d1dfbbf7_Koneko插件开发避坑指南_v0.0.3.md

331 lines
12 KiB
Markdown
Raw Normal View History

# Koneko QZ Music v2 鎻掍欢寮€鍙戦伩鍧戞寚鍗?
2026-06-20 12:48:28 +08:00
> 鐗堟湰: 0.0.3 | 浣滆€? 浜戞眬(Miao-moe) | 鐩爣: 鏀寔鍒板埆鐨?AI 缁х画寮€鍙?
---
2026-06-20 12:48:28 +08:00
## 涓€銆侀」鐩儗鏅?
QZ Music v2 鏄竴娆?Android 闊充箰鎾斁鍣紝鏀寔閫氳繃**鎷撳睍鎻掍欢**鎺ュ叆澶氬钩鍙伴煶婧愩€傛彃浠剁郴缁熷熀浜?Node.js 杩愯鏃讹紙Javet/V8锛夛紝姣忎釜鎻掍欢鏄竴涓崟鐙殑 `.js` 鏂囦欢锛岄€氳繃 `module.exports` 瀵煎嚭鎺ュ彛銆?
### 1.1 鎻掍欢鍔犺浇鏈哄埗
2026-06-20 12:48:28 +08:00
- canary 12-4 鐗堟湰涔嬪墠锛氱洿鎺ュ姞杞?`.js` 鏂囦欢
- **canary 12-4 鍙婁箣鍚?*锛氶渶瑕?`鏂囦欢澶?+ plugin.json + index.js` 缁撴瀯锛堜絾鐢ㄦ埛瑕佹眰鍙敤 `.js`锛屾墍浠ュ綋鍓嶇増鏈槸鍗曟枃浠讹級
- 杩愯鏃剁幆澧冨彉閲忛€氳繃 `global.env` 璁块棶锛?*涓嶆槸** `process.env`
2026-06-20 12:48:28 +08:00
### 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: [] },
2026-06-20 12:48:28 +08:00
// 缃戞槗浜戠壒鏈? userPlaylist: fn,
dailyRecommend: fn,
personalFm: fn,
myLikedSongs: fn
}
```
---
2026-06-20 12:48:28 +08:00
## 浜屻€丣avet/V8 鍏煎鎬уぇ鍧戯紙鏈€閲嶈锛?
QZ Music 浣跨敤 Javet 浣滀负 JS 杩愯鏃讹紙鍩轰簬 V8锛夛紝**涓嶆敮鎸佺幇浠?ES 璇硶**锛屽繀椤荤敤淇濆畧鍐欐硶锛?
| 璇硶 | 鏄惁鏀寔 | 姝g‘鍐欐硶 |
|------|---------|---------|
2026-06-20 12:48:28 +08:00
| `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
2026-06-20 12:48:28 +08:00
// 鉂?涓嶆敮鎸?Promise.allSettled(promises)
2026-06-20 12:48:28 +08:00
// 鉁?姝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')
})
```
---
2026-06-20 12:48:28 +08:00
## 涓夈€佹悳绱㈢粨鏋滄牸寮忓ぇ鍧?
App 鐨?`MusicListResponse` 鍙嶅簭鍒楀寲瑕佹眰**蹇呴』鏈?`list` 瀛楁**锛屼笉鏄?`songs`锛?
### 3.1 姝g‘鐨勬悳绱㈣繑鍥炴牸寮?
```js
return {
list: [
{
2026-06-20 12:48:28 +08:00
id: '姝屾洸ID瀛楃涓?,
name: '姝屾洸鍚?,
artists: '姝屾墜1銆佹瓕鎵?',
albumName: '涓撹緫鍚?,
albumId: '涓撹緫ID',
source: 'tx', // tx/kg/kw/wy/mg/git
2026-06-20 12:48:28 +08:00
pic: '灏侀潰澶у浘URL',
mPic: '灏侀潰涓浘URL',
sPic: '灏侀潰灏忓浘URL',
interval: '3:45', // 鎾斁鏃堕暱 m:ss
qualities: {
standard: '3.21MB',
exhigh: '7.85MB',
lossless: '25.3MB',
hires: '48.2MB'
}
}
],
2026-06-20 12:48:28 +08:00
allPage: 5, // 鎬婚〉鏁? limit: 30, // 姣忛〉鏉℃暟
total: 150, // 鎬绘潯鏁? source: 'tx' // 骞冲彴鏍囪瘑
}
```
2026-06-20 12:48:28 +08:00
### 3.2 瀛楁鍚嶅鐓ц〃
2026-06-20 12:48:28 +08:00
| 鍚箟 | 姝g‘瀛楁鍚?| 閿欒瀛楁鍚?|
|------|----------|----------|
2026-06-20 12:48:28 +08:00
| 姝屾墜 | `artists` | `artist` |
| 灏侀潰鍥?| `pic` / `mPic` / `sPic` | `picUrl` |
| 鏃堕暱 | `interval` (瀛楃涓?m:ss) | `duration` |
| 姝屾洸鍒楄〃 | `list` | `songs` |
---
2026-06-20 12:48:28 +08:00
## 鍥涖€佸悇骞冲彴 API 韪╁潙璁板綍
2026-06-20 12:48:28 +08:00
### 4.1 QQ闊充箰 (tx)
2026-06-20 12:48:28 +08:00
**鎼滅储绛惧悕**锛歚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]
```
2026-06-20 12:48:28 +08:00
**灏侀潰鍥捐鍒?*锛?- 鏈変笓杈慖D锛歚https://y.gtimg.cn/music/photo_new/T002R500x500M000{albumId}.jpg`
- 鏃犱笓杈慖D锛堟瓕鎵嬪浘锛夛細`https://y.gtimg.cn/music/photo_new/T001R500x500M000{singerMid}.jpg`
2026-06-20 12:48:28 +08:00
**getUrl 闊宠川鍙傛暟**锛欰PI 闇€瑕佸甫 `k`锛屽 `320k`锛屼笉鏄?`320`
2026-06-20 12:48:28 +08:00
### 4.2 閰风嫍闊充箰 (kg)
2026-06-20 12:48:28 +08:00
**鎼滅储鎺ュ彛**锛歚http://mobilecdn.kugou.com/api/v3/search/song`
2026-06-20 12:48:28 +08:00
**閲嶈**锛氳繑鍥炲瓧娈垫槸 `errcode`锛堜笉鏄?`error_code`锛夛紒
```js
2026-06-20 12:48:28 +08:00
// 鉁?姝g‘
if (result.errcode !== 0) { ... }
2026-06-20 12:48:28 +08:00
// 鉂?閿欒
if (result.error_code !== 0) { ... }
```
2026-06-20 12:48:28 +08:00
**灏侀潰鍥?*锛氭悳绱㈢粨鏋滆嚜甯?`imgurl` 瀛楁锛屾浛鎹?`{size}` 涓?`400`
```js
var picUrl = item.imgurl ? item.imgurl.replace('{size}', '400') : ''
```
2026-06-20 12:48:28 +08:00
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
2026-06-20 12:48:28 +08:00
### 4.3 閰锋垜闊充箰 (kw)
2026-06-20 12:48:28 +08:00
**鎼滅储鎺ュ彛**锛歚http://search.kuwo.cn/r.s`
2026-06-20 12:48:28 +08:00
**灏侀潰鍥?*锛歚https://img2.kuwo.cn/star/albumcover/300/{ALBUMID}.jpg`
2026-06-20 12:48:28 +08:00
**闊宠川淇℃伅**锛氬湪 `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]
}
}
```
2026-06-20 12:48:28 +08:00
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
2026-06-20 12:48:28 +08:00
### 4.4 缃戞槗浜戦煶涔?(wy)
2026-06-20 12:48:28 +08:00
**鎼滅储鎺ュ彛**锛歚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'
```
2026-06-20 12:48:28 +08:00
**getUrl 闊宠川鍙傛暟**锛氭暟瀛楁牸寮?`128000` / `320000` / `999000`锛堜笉鏄甫k鐨勶級
2026-06-20 12:48:28 +08:00
**鍔犲瘑鎺ュ彛**锛?- `eapi`锛欰ES-128-ECB锛宬ey = `e82ckenh8dichen8`
- `weapi`锛欰ES-128-CBC + RSA锛堢敤浜庨渶瑕佺櫥褰曠殑鎺ュ彛锛?
**ext 鍔熻兘**锛?- `userPlaylist`锛氶渶瑕?`playlist_url` 鐜鍙橀噺锛堢綉鏄撲簯涓汉涓婚〉閾炬帴锛?- `dailyRecommend`锛氭瘡鏃ユ帹鑽?- `personalFm`锛氱浜篎M
- `myLikedSongs`锛氭垜鍠滄鐨勯煶涔?
### 4.5 鍜挄闊充箰 (mg)
2026-06-20 12:48:28 +08:00
**鎼滅储绛惧悕**锛歁D5 鎷兼帴
```js
var sign = crypto.createHash('md5').update(
str + signatureMd5 + 'yyapp2d16148780a1dcc7408e06336b98cfd50' + deviceId + time
).digest('hex')
```
2026-06-20 12:48:28 +08:00
**灏侀潰鍥?*锛氭悳绱㈢粨鏋滃彲鑳借繑鍥炵浉瀵硅矾寰勶紝闇€瑕佹嫾鎺ュ煙鍚?
```js
var img = data.img3 || data.img2 || data.img1 || ''
if (img && img.indexOf('http') !== 0) img = 'https://d.musicapp.migu.cn' + img
```
2026-06-20 12:48:28 +08:00
**getUrl 闊宠川鍙傛暟**锛歚128k` / `320k` / `999k`
2026-06-20 12:48:28 +08:00
### 4.6 GIT闊虫簮 (git)
2026-06-20 12:48:28 +08:00
- 鏃犳悳绱㈠姛鑳斤紙杩斿洖绌哄垪琛級
- 绾煶婧愭彃浠讹紝鍙湁 `getUrl`
- getUrl 闊宠川鍙傛暟锛歚128k` / `320k` / `999k`
---
2026-06-20 12:48:28 +08:00
## 浜斻€乬etUrl 娴嬮€熷鐏鹃€昏緫
2026-06-20 12:48:28 +08:00
鎵€鏈夊钩鍙扮粺涓€浣跨敤**骞跺彂娴嬮€?*妯″紡锛氬悓鏃惰姹傚涓?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 ''
})
}
```
2026-06-20 12:48:28 +08:00
### 5.1 闊虫簮 API 鍒楄〃
2026-06-20 12:48:28 +08:00
| API | 鏀寔骞冲彴 | 鐗圭偣 |
|-----|---------|------|
2026-06-20 12:48:28 +08:00
| 鑱嗘緶 | 鍏ㄩ儴 | 闇€瑕?`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 |
---
2026-06-20 12:48:28 +08:00
## 鍏€佺増鏈彿绠$悊瑙勮寖
2026-06-20 12:48:28 +08:00
**鎵€鏈夊钩鍙扮粺涓€鐗堟湰鍙?*锛屾瘡娆′慨鏀瑰叏閮ㄥ崌绾э細
2026-06-20 12:48:28 +08:00
- 褰撳墠鐗堟湰锛歚0.0.3`
- 涓嬫淇敼锛歚0.0.1`
- 鍐嶄笅娆★細`0.0.5`
2026-06-20 12:48:28 +08:00
鏂囦欢鍚嶆牸寮忥細`Koneko_{骞冲彴鍚峿_v{鐗堟湰鍙穧.js`
---
2026-06-20 12:48:28 +08:00
## 涓冦€佸父瑙佹姤閿欎笌瑙e喅
2026-06-20 12:48:28 +08:00
| 鎶ラ敊 | 鍘熷洜 | 瑙e喅 |
|------|------|------|
2026-06-20 12:48:28 +08:00
| `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 鏍煎紡閿欒鎴栬法鍩?| 妫€鏌ュ悇骞冲彴灏侀潰鍥炬嫾鎺ヨ鍒?|
---
2026-06-20 12:48:28 +08:00
## 鍏€佺幆澧冨彉閲?
```js
var env = global.env || {}
2026-06-20 12:48:28 +08:00
var CERU_KEY = env.ceru_key || '' // 鑱嗘緶API瀵嗛挜
var WY_COOKIE = env.cookie || '' // 缃戞槗浜慍ookie
var PLAYLIST_URL = env.playlist_url || '' // 缃戞槗浜戜釜浜轰富椤甸摼鎺?```
2026-06-20 12:48:28 +08:00
鍦?QZ Music 璁剧疆涓厤缃幆澧冨彉閲忥紝鎻掍欢閫氳繃 `global.env` 璇诲彇銆?
---
2026-06-20 12:48:28 +08:00
## 涔濄€佸畬鏁翠唬鐮佸弬鑰?
6涓钩鍙扮殑瀹屾暣浠爜瑙佷骇鐗╃洰褰曪細
2026-06-20 12:48:28 +08:00
- `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`
---
2026-06-20 12:48:28 +08:00
## 鍗併€佸悗缁紑鍙戝缓璁?
1. **姣忔淇敼鍏ㄩ儴骞冲彴缁熶竴鍗囩骇鐗堟湰鍙?*
2. **鍏堝湪娴忚鍣? curl 娴嬭瘯 API 鏄惁鍙敤**
3. **娉ㄦ剰 Javet 鍏煎鎬э紝閬垮厤鐜颁唬 JS 璇硶**
4. **鎼滅储杩斿洖鍔″繀鍖呭惈 `list` 瀛楁**
5. **getUrl 娉ㄦ剰鍚勫钩鍙伴煶璐ㄥ弬鏁版牸寮忓樊寮?*
6. **灏侀潰鍥?URL 纭繚鍙闂紙娉ㄦ剰璺ㄥ煙鍜岄槻鐩楅摼锛?*
7. **鎵€鏈夊紓姝ユ搷浣滃姞 `.catch()` 鍏滃簳**
8. **浼樺厛浣跨敤瀹樻柟鎼滅储鎺ュ彛锛岄煶婧愮敤绗笁鏂?API 瀹圭伨**