# Koneko QZ Music v2/v3 插件开发避坑指南 > 版本: 0.0.2 | 作者: 云汀(Miao-moe) | 目标: 支持迁移到其他 AI 继续开发 --- ## 一、项目背景 QZ Music v2/v3 是一款 Android/PC 音乐播放器,支持通过**拓展插件**接入多平台音源。插件系统基于 Node.js 运行时(Javet/V8),每个插件是一个单独的 `.js` 文件,通过 `module.exports` 导出接口。 ### 插件加载机制 - 运行时环境变量通过 `global.env` 访问,**不是** `process.env` - 插件为单 `.js` 文件格式 ## 二、Javet/V8 兼容性大坑(最重要) QZ Music 使用 Javet 作为 JS 运行时(基于 V8),**不支持现代 ES 语法**: | 语法 | 状态 | 正确写法 | |------|------|---------| | `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` | | `class` | ❌ | 对象字面量 | | 模板字符串 `${}` | ✅ | 可用 | | `Buffer` | ✅ | Node.js 内置 | ### Promise.allSettled 替代方案 ```js 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`! ```js // ✅ 正确 return { list: [...], allPage: N, limit: N, total: N, source: 'tx' } // ❌ 错误 return { songs: [...], total: N } ``` ### 字段名对照 | 含义 | 正确字段 | 错误字段 | |------|---------|---------| | 歌手 | `artists` | `artist` | | 封面图 | `pic`/`mPic`/`sPic` | `picUrl` | | 时长 | `interval` (m:ss) | `duration` | | 列表 | `list` | `songs` | ## 四、各平台踩坑点 ### QQ音乐 (tx) - 搜索签名:`zzcSign` = SHA1 + 索引提取 + XOR + base64 - 封面图:有专辑ID用 T002,无专辑ID用 T001 - getUrl 音质参数:带 `k` (128k/320k/999k) ### 酷狗音乐 (kg) - 搜索返回值字段是 `errcode`(不是 `error_code`) - 封面图:替换 `{size}` 为 `400` ### 酷我音乐 (kw) - 音质信息在 `N_MINFO` 字段解析 - 封面图:`https://img2.kuwo.cn/star/albumcover/300/{ALBUMID}.jpg` ### 网易云音乐 (wy) - 搜索用 GET:`/api/search/get/web`,不需要 weapi - 封面图:`picId` 需 Base64 编码 - getUrl 音质参数:数字格式 `128000`/`320000`/`999000` - Cookie 用于 weapi 加密接口(每日推荐、私人FM、喜欢歌曲、歌单、专辑、歌词) ### 咪咕音乐 (mg) - 搜索需 MD5 签名 - 封面图:相对路径需拼接 `https://d.musicapp.migu.cn` ### GIT音源 (git) - 无搜索,纯音源 - getUrl 音质参数:`128k`/`320k`/`999k` ## 五、getUrl 容灾逻辑 所有平台使用**并发测速**模式:同时请求多个 API,取第一个成功结果。 API 调用顺序: 1. 聆澜(需 `ceru_key` 环境变量) 2. HUIBQ (lxmusicapi) 3. 星海 / 忆音 / 念心 / 长青 / 星海备 / fish / HYW 网易云额外有: 4. bb / lx / ymc / unms / 官方 weapi ## 六、环境变量说明 | key | 用途 | 适用平台 | 必填 | |-----|------|---------|------| | `ceru_key` | 聆澜音源 API 密钥 | 全部 | 否 | | `playlist_url` | 网易云个人主页链接 | 网易云 | 否 | | `cookie` | 网易云 Cookie | 网易云 | 否 | ## 七、版本管理 - 所有平台统一版本号 - 当前版本:**0.0.2** - 文件名格式:`Koneko_{平台名}_v{版本号}.js` ## 八、常见报错 | 报错 | 原因 | 解决 | |------|------|------| | `Cannot find module 'axios'` | 用了 axios | 改用 `http`/`https` 内置模块 | | `Field 'list' is required` | 搜索返回 `songs` 而非 `list` | 改字段名为 `list` | | `SyntaxError: Invalid or unexpected token` | 用了 `catch { }` | 改为 `catch (e) { }` | | 搜索无结果 | 字段名不匹配 | 检查 `errcode` vs `error_code` | | 播放失败 | `mapBr` 返回格式不对 | QQ/kg/kw/mg/git 用 `320k`,wy 用 `320000` | | 封面图不显示 | URL 格式错误或跨域 | 检查各平台封面图拼接规则 | ## 九、后续开发建议 1. 每次修改全部平台统一升级版本号 2. 先在浏览器/curl 测试 API 是否可用 3. 注意 Javet 兼容性,避免现代 JS 语法 4. 搜索返回务必包含 `list` 字段 5. getUrl 注意各平台音质参数格式差异 6. 所有异步操作加 `.catch()` 兜底 7. 优先使用官方搜索接口,音源用第三方 API 容灾