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

12 KiB
Raw Blame History

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 鎻掍欢瀵煎嚭鏍煎紡

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 鏇夸唬鏂规

// 鉂?涓嶆敮鎸?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‘鐨勬悳绱㈣繑鍥炴牸寮?

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

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锛夛紒

// 鉁?姝g‘
if (result.errcode !== 0) { ... }

// 鉂?閿欒
if (result.error_code !== 0) { ... }

**灏侀潰鍥?*锛氭悳绱㈢粨鏋滆嚜甯?imgurl 瀛楁锛屾浛鎹?{size} 涓?400

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;...

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 缂栫爜鍚庢嫾鎺?

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 鎷兼帴

var sign = crypto.createHash('md5').update(
  str + signatureMd5 + 'yyapp2d16148780a1dcc7408e06336b98cfd50' + deviceId + time
).digest('hex')

**灏侀潰鍥?*锛氭悳绱㈢粨鏋滃彲鑳借繑鍥炵浉瀵硅矾寰勶紝闇€瑕佹嫾鎺ュ煙鍚?

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锛屽彇绗竴涓垚鍔熺殑缁撴灉銆?

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 鏍煎紡閿欒鎴栬法鍩? 妫€鏌ュ悇骞冲彴灏侀潰鍥炬嫾鎺ヨ鍒?

鍏€佺幆澧冨彉閲?

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钩鍙扮殑瀹屾暣浠g爜瑙佷骇鐗洰褰曪細

- `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 瀹圭伨**