diff --git a/.gitignore b/.gitignore index 155c816..2ff65d2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ node_modules/ package-lock.json yarn.lock +# 音源脚本(第三方、不可审计,不入库;放入 sources/ 自动加载) +sources/ +lx-music-source-v5.js + # Runtime *.log logs/ diff --git a/README.md b/README.md index 76771b3..70768d9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 音乐服务器(MusicServer) -基于洛雪音乐(lx-music)聚合 SDK 的多平台音乐 API 服务器,纯 Node.js 实现,无需浏览器环境。 +多平台音乐聚合 API 服务器,纯 Node.js 实现,无需浏览器环境。 ## 功能 @@ -25,7 +25,7 @@ pnpm test # 运行冒烟测试(自动启动/关闭本地服务器) | `GET /search?name=xxx&singer=xxx&source=kw&page=1&limit=20` | 搜索歌曲(source 省略则聚合所有平台) | | `GET /find?name=xxx&singer=xxx&albumName=xxx&interval=xxx` | 精确匹配搜索(跨平台去重排序) | | `GET /url/:source/:id?quality=320k` | 获取播放地址(quality: 128k/320k/flac/flac24bit) | -| `GET /lyric/:source/:id` | 获取歌词(返回 lyric/tlyric/lxlyric) | +| `GET /lyric/:source/:id` | 获取歌词(返回 lyric/tlyric/yrc) | | `GET /pic/:source/:id` | 获取封面 | | `GET /boards/:source` | 榜单列表 | | `GET /boards/:source/:boardId` | 榜单详情 | @@ -59,8 +59,8 @@ src/ components.js OpenAPI 共享 schema(与路由校验同源) ui.js ReDoc 页面模板 apis/ 各平台 URL 直连实现(kw/kg/tx/wy/mg/bd) - lx-source.js lx 聚合源懒加载入口 - lx-source-ivm.js isolated-vm 隔离执行器 + source.js 外部音源懒加载入口(sources/ 目录脚本,不入库) + source-ivm.js isolated-vm 隔离执行器 common/ 公共模块 request.js HTTP 请求(可取消、自动 JSON 解析、gzip/deflate 解压) utils.js 工具函数(sizeFormate/formatPlayTime/dateFormat 等) @@ -70,7 +70,7 @@ src/ lyricDecode.js 酷我/QQ 歌词解码(替代原 Electron 主进程处理器) qrcDecode.js QQ 歌词 3DES 解密(纯 JS 移植) lyricUtils/kg.js 酷狗 krc 歌词解码 -musicSdk/ 洛雪音乐聚合 SDK(搜索/歌词/榜单等核心逻辑,TypeScript 源码) +musicSdk/ 音乐聚合 SDK(搜索/歌词/榜单等核心逻辑,TypeScript 源码) index.js 平台聚合入口 request.js SDK 请求模块(转接 src/common/request.js) message.js SDK 消息模块(转接 src/common/message.js) diff --git a/docs/API.md b/docs/API.md index 6ec4009..9161014 100644 --- a/docs/API.md +++ b/docs/API.md @@ -71,7 +71,7 @@ { "code": 0, "data": { "type": "320k", "url": "https://..." } } // GET /lyric/kw/228908 -{ "code": 0, "data": { "lyric": "[00:00.00]...", "tlyric": "", "lxlyric": "" } } +{ "code": 0, "data": { "lyric": "[00:00.00]...", "tlyric": "", "yrc": "" } } // 参数缺失 // 422 diff --git a/docs/lx-source-ivm.md b/docs/lx-source-ivm.md deleted file mode 100644 index 8a92a05..0000000 --- a/docs/lx-source-ivm.md +++ /dev/null @@ -1,48 +0,0 @@ -# lx 聚合源 —— isolated-vm 加载器 - -## 架构 - -``` -调用方 ── lx.kw.getMusicUrl(songInfo, type) ──▶ src/apis/lx-source.js(懒加载单例) - │ - ▼ - src/apis/lx-source-ivm.js - isolated-vm 硬隔离沙箱 - ┌──────────────────────────┐ - │ 原始混淆脚本(升版即换文件) │ - │ ↕ 仅经显式桥接通道 │ - └──────────────────────────┘ - http(白名单) crypto zlib 定时器 console -``` - -- 接口契约与其它平台适配器一致:`getMusicUrl(songInfo, type) → { promise, cancelHttp }`,resolve → `{ type, url }` -- 脚本升版:替换项目根目录脚本文件(或设 `LX_SOURCE_SCRIPT_PATH`)即可,无需改代码 - -## 安全模型 - -| 面 | 措施 | -|---|---| -| 进程 | isolate 无 process/require/fs,内存上限 256MB(可调) | -| HTTP | **默认阻止**;白名单取脚本头部 `SERVER_SCRIPT_CONFIG.apiUrl` 同源,可覆盖 | -| 密钥 | crypto 桥接日志只记操作与长度,不落密钥内容 | -| 观测 | 每个 lx API 调用(on/send/request/utils/dispatch)均有日志,请求含耗时与状态码 | - -HTTP 白名单选项(`createIvmLxSource`): - -- `allowedOrigins: string[]` —— 显式指定白名单(覆盖脚本声明) -- `httpFilter: (url) => boolean` —— 自定义判定,优先于 origin 白名单 -- `allowAllHttp: true` —— 全部放行(仅调试用) - -被拦截的请求会以错误回调返回脚本(`✋` 日志标记),脚本按请求失败自行处理。 - -## 环境变量(经 lx-source.js 封装) - -- `LX_SOURCE_SCRIPT_PATH` —— 脚本文件路径(默认项目根目录 `lx-music-source-v5.js`) -- `LX_SOURCE_LOG=0` —— 关闭日志 -- `LX_SOURCE_ALLOW_ALL_HTTP=1` —— 放行全部 HTTP(调试用) - -## 已知边界 - -- `lx.request` 回调的 resp 不含 `raw`/`bytes`(Buffer 无法跨隔离边界,实测脚本未使用) -- `formData` 选项不支持(底层 httpFetch 不支持 multipart) -- `updateAlert` 事件仅记日志,不弹窗 diff --git a/docs/source-ivm.md b/docs/source-ivm.md new file mode 100644 index 0000000..3850101 --- /dev/null +++ b/docs/source-ivm.md @@ -0,0 +1,57 @@ +# 外部聚合音源 —— isolated-vm 加载器 + +## 架构 + +``` +调用方 ── source.kw.getMusicUrl(songInfo, type) ──▶ src/apis/source.js(懒加载单例) + │ + ▼ + src/apis/source-ivm.js + isolated-vm 硬隔离沙箱 + ┌──────────────────────────┐ + │ 音源脚本(升版即换文件) │ + │ ↕ 仅经显式桥接通道 │ + └──────────────────────────┘ + http(白名单) crypto zlib 定时器 console +``` + +- 接口契约与其它平台适配器一致:`getMusicUrl(songInfo, type) → { promise, cancelHttp }`,resolve → `{ type, url }` +- 脚本来源:默认自动读取 `sources/` 目录下按文件名排序的第一个 `.js`;也可用 `SOURCE_SCRIPT_PATH` 显式指定 +- 脚本升版:替换 `sources/` 目录下的脚本文件即可,无需改代码 +- 脚本文件含第三方混淆内容,不入库(`.gitignore` 已排除 `sources/`) + +## 安全模型 + +| 面 | 措施 | +|---|---| +| 进程 | isolate 无 process/require/fs,内存上限 256MB(可调) | +| HTTP | **默认阻止**;白名单取脚本头部 `SERVER_SCRIPT_CONFIG.apiUrl` 同源,可覆盖 | +| 密钥 | crypto 桥接日志只记操作与长度,不落密钥内容 | +| 观测 | 每个音源 API 调用(on/send/request/utils/dispatch)均有日志,请求含耗时与状态码 | + +HTTP 白名单选项(`createIvmSource`): + +- `allowedOrigins: string[]` —— 显式指定白名单(覆盖脚本声明) +- `httpFilter: (url) => boolean` —— 自定义判定,优先于 origin 白名单 +- `allowAllHttp: true` —— 全部放行(仅调试用) + +被拦截的请求会以错误回调返回脚本(`✋` 日志标记),脚本按请求失败自行处理。 + +## 启用方式 + +设置环境变量 `SOURCE_ENABLED`(任意非 `0`/`false` 值)后启动服务器,取流请求即走音源; +不设置或为空则使用内置直连接口。歌词、封面、榜单等其余能力始终走内置实现。 + +## 环境变量(经 source.js 封装) + +- `SOURCE_ENABLED=1` —— 启用音源(见上) +- `SOURCE_SCRIPT_PATH` —— 脚本文件路径(缺省扫描 `sources/` 目录) +- `SOURCE_LOG=0` —— 关闭日志 +- `SOURCE_ALLOW_ALL_HTTP=1` —— 放行全部 HTTP(调试用) + +## 已知边界 + +- 音源请求回调的 resp 不含 `raw`/`bytes`(Buffer 无法跨隔离边界,实测脚本未使用) +- `formData` 选项不支持(底层 httpFetch 不支持 multipart) +- `updateAlert` 事件仅记日志,不弹窗 +- 沙箱内全局对象名 `lx` 为第三方脚本依赖的 API 契约名,不可改名(见 `source-ivm.js` GLUE 注释) diff --git a/lx-music-source-v5.js b/lx-music-source-v5.js deleted file mode 100644 index f66741c..0000000 --- a/lx-music-source-v5.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * @name 独家音源 - * @description 后续更新,请关注微信公众号: 洛雪科技 - * @version 5 - * @author w - */ - -// ===== 服务端下发配置(自动生成, 请勿修改) ===== -globalThis['SERVER_SCRIPT_CONFIG'] = {"apiUrl":"https:\/\/88.lxmusic.xn--fiqs8s","apiKey":"lxmusic","signSalt":"LxSrv@2026#Sig","fingerprint":"74c52ba24b386a2301983d94596246c2"}; -// ===== 服务端下发配置结束 ===== - - -;(function ﱡﹶ(ﱡﹲ,ﱡﱣ,ﹲ‌,ﱞﹶ,ﹲﹰ,ﹸﹼ,ﹰﱣ,ﹰﹼ,ﹴﱠ,ﹸﹸ,ءﱟ,ﱟﹸ,ﹰﹲ,ﱠﹴ,ﱞﹲ,ﹼﱣ,ﹶﹲ,ﹶﹰ){return ﹶﹰ=function(ﱟﹺ,ﹼ,ﹰﹺ,ءﱟ){return function(ﹼﹶ,ﱡء,ءﱡ,ﱡﹲ,ﹲ‌,ﹲﹰ,ﹸﹼ,ﹰﱣ,ﹴﱠ){if(ﹼﹶ=ﹼﹶ^ﱡء,ﹲﹰ=66666-ﹼﹶ,ﹸﹼ=ﹶﹲ[ﹲﹰ],ﹸﹼ!==void 0)return ﹸﹼ;for(ﹲ‌=ﱡﱣ[ﹼﹶ],ﱡﹲ=[],ﹸﹼ=ﹰﹺ(ﹼﹶ,ﱡء,ﹲ‌.\u006c\u0065\u006e\u0067\u0074\u0068,ﹼ,ﱟﹺ),ﹰﱣ=0,ﹼﹶ=(ﹸﹼ^ﹸﹼ>>>16^ﱡء^ﹼﹶ)&65535,ءﱡ=0;ءﱡ<ﹲ‌.\u006c\u0065\u006e\u0067\u0074\u0068;ءﱡ++){(ءﱡ&1)===0&&(ﹸﹼ=ﹸﹼ+2654435769>>>0,ﹰﱣ=ﹼ(ﹸﹼ,ﱟﹺ));ﱡء=ﹰﱣ>>>((ءﱡ&1)<<4)&65535,ﹴﱠ=ﹲ‌[ءﱡ],ﹰﹼ(ﱡﹲ,(ﹴﱠ^ﱡء^ﹼﹶ)&65535),ﹼﹶ=ﹴﱠ+(ﹰﱣ>>>16&65535)+ءﱡ+40503&65535}return ﹶﹲ[ﹲﹰ]=ﹸﹸ(ﱡﹲ)}}(function(ﹼﹶ,ﱟﹺ,ﱡء,ﹼ){return ﱡء=ﹼﹶ>>>16&65535,ﹼﹶ=ﹼﹶ&65535,ﹼ=ﱟﹺ>>>16&65535,ﱟﹺ=ﱟﹺ&65535,ﹼﹶ*ﱟﹺ+(ﱡء*ﱟﹺ+ﹼﹶ*ﹼ<<16>>>0)|0},function(ﹼﹶ,ﱟﹺ){return ﹼﹶ>>>=0,ﹼﹶ=ﱟﹺ(ﹼﹶ^ﹼﹶ>>>16,569420461),ﹼﹶ=ﱟﹺ(ﹼﹶ^ﹼﹶ>>>15,1935289751),(ﹼﹶ^ﹼﹶ>>>15)>>>0},function(ﹸ‍,ﱡء,ءﱡ,ﹼ,ﱟﹺ){return ﹼ(1327381188>>>0^ﱟﹺ(ﹸ‍+1>>>0,2654435761)^ﱟﹺ(ﱡء+1>>>0,2246822507)^ﱟﹺ(ءﱡ+1>>>0,3266489909),ﱟﹺ)}),function ﹰﹺ(ﹼﹶ,ﱟﹺ,ﱡء,ﹼ,ءﱡ){ﱟﹺ=ﱟﹺ||[],ﱡء=ﱡء||[],ﹼ=ﹼ||[];for(var ﱡﹴ=ﱡء.\u006c\u0065\u006e\u0067\u0074\u0068,ءﹶ,ﹰﹸ;ﹰﹸ=ﱡﹲ[ﹼﹶ++];)if(61<ﹰﹸ)ﹰﹸ<68&&((ﹰﹸ>65||(ﹰﹸ<64&&((ﹰﹸ-->62||(ﱡء[++ﱡﹴ-ﹰﹸ+++60]=function(ﱡء,ﱡﹴ){return function(){return ﹶﹰ(ﱡء,ﱡﹴ)}}(ﱡﹲ[ﹼﹶ++]^-++ﹰﹸ+32881+ﹼﹶ,ﱡﹲ[ﹼﹶ++]^-ﹰﹸ--+49974+ﹼﹶ),!true))&&(ﱡء[ﱡﹴ+++ﹰﹸ---62]=function(ﱡء){return function(){return ﱟﹺ[ﱡء]++}}(ﱡﹲ[ﹼﹶ++]^--ﹰﹸ+57119+ﹼﹶ)),!0)||ﹰﹸ>64&&(ﱡء[ﱡﹴ+++ﹰﹸ-67]()||(ﹼﹶ=ﱡء[ﱡﹴ-ﹰﹸ+++63]()),(ﱡﹴ-=3)+ﹰﹸ+++31,1)||(ﱡء[ﱡﹴ+++ﹰﹸ-64]=function(){return function(){return typeof \u0070\u0072\u006f\u0063\u0065\u0073\u0073}}()),""))&&(66<ﹰﹸ||(ﱡء[(ﱡﹴ+=101)- --ﹰﹸ-38]=function(ﱡء,ﱡﹴ){return function(){return new ﹰﹲ(ﱡء(),ﱡﹴ())}}(ﱡء[++ﱡﹴ-104],ﱡء[(ﱡﹴ-=103)+ﹰﹸ++-65]),void 0))&&(ﱡء[--ﱡﹴ+ﹰﹸ-67]=function(ﱡء){return function(){return!ﱡء()}}(ﱡء[++ﱡﹴ- --ﹰﹸ+65])),!false)||73>ﹰﹸ&&(ﹰﹸ<70&&(ﹰﹸ<69&&(ﱡء[++ﱡﹴ+ ++ﹰﹸ-70]=function(){return function(){return \u0070\u0061\u0072\u0073\u0065\u0049\u006e\u0074}}(),!0)||(ﱡء[ﱡﹴ+++ ++ﹰﹸ-70]=function(){return function(){return \u0050\u0072\u006f\u006d\u0069\u0073\u0065}}()),true)||(71<ﹰﹸ||((71<++ﹰﹸ||(ﱡء[(ﱡﹴ+=--ﹰﹸ)-78]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ,ﹰﱞ,ﹰء,ﹲﹼ,ﹼﱡ){return function(){return ﹰﱣ(ﱡء())(ﱡﹴ(),ﹰﹸ(),ﹼﹶ(),ﹰﱞ(),ﹰء(),ﹲﹼ(),ﹼﱡ())()}}(ﱡء[(ﱡﹴ+=43)-++ﹰﹸ-49],ﱡء[--ﱡﹴ+ﹰﹸ++-191],ﱡء[ﱡﹴ++-118],ﱡء[(ﱡﹴ+=8)+ﹰﹸ---198],ﱡء[--ﱡﹴ-ﹰﹸ-53],ﱡء[(ﱡﹴ+=ﹰﹸ--)-194],ﱡء[ﱡﹴ+ﹰﹸ++-263],ﱡء[(ﱡﹴ-=198)-ﹰﹸ+77]),![]))&&(ﱡء[(ﱡﹴ+=3)-ﹰﹸ--+67]()&&(ﹼﹶ=ﱡء[ﱡﹴ-ﹰﹸ--+67]()),(ﱡﹴ-=5)-ﹰﹸ+++115),null))&&(ﱡء[ﱡﹴ+++ ++ﹰﹸ-73]=function(){return function(){return \u0063\u006f\u006e\u0073\u006f\u006c\u0065}}()),!false)||ﹰﹸ>77&&(ﹰﹸ>81&&(83>ﹰﹸ--&&(ﱡء[ﱡﹴ+++ﹰﹸ---81]=function(ﱡء){return function(){return ﱡء}}(ﹼﱣ),!null)||(ﱡء[(ﱡﹴ-=10)-ﹰﹸ+89]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ){return function(){return ﹸﹼ(ﱡء(),(ﱡﹴ()[0]=ﹰﹸ(),ﹼﹶ()))()}}(ﱡء[ﱡﹴ+ﹰﹸ-74],ﱡء[(ﱡﹴ+=36)+ﹰﹸ-109],ﱡء[(ﱡﹴ+=ﹰﹸ)-111],ﱡء[(ﱡﹴ-=110)+ﹰﹸ---81])),!null)||++ﹰﹸ>81&&(ﱡء[ﱡﹴ-ﹰﹸ+80]=function(ﱡء,ﱡﹴ){return function(){return delete ﱡء()[ﱡﹴ()]}}(ﱡء[(ﱡﹴ+=ﹰﹸ++)-84],ﱡء[(ﱡﹴ-=83)+ﹰﹸ---83]),!null)||ﹰﹸ>80&&(ﱡء[ﱡﹴ+++ ++ﹰﹸ-82]=function(){return function(){return \u0053\u0079\u006d\u0062\u006f\u006c}}(),1)||80>ﹰﹸ&&(ﱡء[ﱡﹴ++]=function(){return function(){return \u004f\u0062\u006a\u0065\u0063\u0074}}(),!0)||(ﱡء[ﱡﹴ++]=function(){return function(){return \u0072\u0065\u0071\u0075\u0069\u0072\u0065}}()),!false)||(ﹰﹸ>76||((ﹰﹸ>75||(ﹰﹸ<75&&((ﹰﹸ<74||(ﱡء[(ﱡﹴ+=92)+ ++ﹰﹸ-168]=function(ﱡء){return function(){return-ﱡء()}}(ﱡء[(ﱡﹴ-=92)-++ﹰﹸ+75]),void 0))&&(ﱡء[ﱡﹴ--+--ﹰﹸ-74]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()+ﱡﹴ()}}(ﱡء[(ﱡﹴ-=--ﹰﹸ)+70],ﱡء[(ﱡﹴ+=71)-ﹰﹸ--+71])),1)||(ﱡء[(ﱡﹴ+=ﹰﹸ)-77]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()*ﱡﹴ()}}(ﱡء[ﱡﹴ++-ﹰﹸ++-2],ﱡء[(ﱡﹴ-=77)+--ﹰﹸ-75])),void 0))&&(ﱡء[++ﱡﹴ-ﹰﹸ--+73]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()<=ﱡﹴ()}}(ﱡء[--ﱡﹴ+ﹰﹸ---77],ﱡء[--ﱡﹴ+--ﹰﹸ-73])),0))&&ﱡء[(ﱡﹴ-=2)-ﹰﹸ--+77]()&&(ﹼﹶ=ﱡء[--ﱡﹴ+ﹰﹸ-74]());else if(ﹰﹸ<22)ﹰﹸ>16&&((++ﹰﹸ>21||((20>ﹰﹸ||((ﹰﹸ>20||(ﱡء[++ﱡﹴ+ﹰﹸ++-21]=function(ﱡء){return function(){return ﱡء}}(void 2),""))&&(ﱡء[ﱡﹴ+++ﹰﹸ-21]=function(ﱡء){return function(){return ﱡء}}([])),""))&&(ﹰﹸ++<19&&(ﱡء[ﱡﹴ+++--ﹰﹸ-18]=function(){return function(){return \u004e\u0075\u006d\u0062\u0065\u0072}}(),true)||(ﱡء[--ﱡﹴ- --ﹰﹸ+18]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()<ﱡﹴ()}}(ﱡء[ﱡﹴ-ﹰﹸ+++18],ﱡء[ﱡﹴ]))),""))&&(ﱡء[++ﱡﹴ-++ﹰﹸ+22]=function(){return function(){return \u0045\u0072\u0072\u006f\u0072}}()),true)||11<ﹰﹸ&&((ﹰﹸ>15||(ﹰﹸ>14&&(ﱡء[ﱡﹴ++-++ﹰﹸ+16]=function(ﱡء){return function(){return ﱡء}}({}),!"")||14<++ﹰﹸ&&(ﱡء[++ﱡﹴ-1]=function(ﱡء){return function(){return ﱡء}}(ﱞﹲ),1)||(ﹰﹸ>13||(function(){throw ﱡء[(ﱡﹴ+=2)-3]()}(),""))&&(ﱡء[++ﱡﹴ+--ﹰﹸ-14]=function(){return function(){return typeof \u0053\u0079\u006d\u0062\u006f\u006c}}()),""))&&(ﱡء[(ﱡﹴ+=76)-++ﹰﹸ-61]=function(ﱡء,ﱡﹴ){return function(){return ﱟﹺ[ﱡء()]=ﱡﹴ()}}(ﱡء[ﱡﹴ++-78],ﱡء[(ﱡﹴ-=78)+ﹰﹸ-17])),true)||ﹰﹸ<7&&((ﹰﹸ>4||(3>ﹰﹸ--&&(ﹰﹸ>0&&(ﱡء[--ﱡﹴ+ﹰﹸ-2]=function(ﱡء,ﱡﹴ){return function(){return new(ﱡء())(ﱡﹴ())}}(ﱡء[--ﱡﹴ+ﹰﹸ---1],ﱡء[ﱡﹴ+++ ++ﹰﹸ]),!"")||(ﱡء[ﱡﹴ++-ﹰﹸ]=function(){return function(){return typeof \u0072\u0065\u0071\u0075\u0069\u0072\u0065}}()),!0)||(2<ﹰﹸ||(ﱡء[ﱡﹴ---ﹰﹸ--+1](),0))&&(ﱡء[++ﱡﹴ+ﹰﹸ---4]=function(ﱡء){return function(){return ﱟﹺ[ﱡء]}}(ﱡﹲ[ﹼﹶ++]^-ﹰﹸ+28941+ﹼﹶ)),void 0))&&(6>ﹰﹸ||(ﱡء[(ﱡﹴ-=94)+ ++ﹰﹸ+80]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ,ﹰﱞ,ﹰء,ﹲﹼ){return function(){return ﹰﱣ(ﱡء(),ﱡﹴ())(ﹰﹸ(),ﹼﹶ(),ﹰﱞ())(ﹰء(),ﹲﹼ())}}(ﱡء[ﱡﹴ---ﹰﹸ+95],ﱡء[ﱡﹴ++-ﹰﹸ+95],ﱡء[(ﱡﹴ+=--ﹰﹸ)+83],ﱡء[(ﱡﹴ-=55)-ﹰﹸ+145],ﱡء[(ﱡﹴ+=65)+ﹰﹸ--+69],ﱡء[ﱡﹴ--- --ﹰﹸ+80],ﱡء[(ﱡﹴ+=73)+ﹰﹸ+1]),void 0))&&(ﱡء[ﱡﹴ++-ﹰﹸ--+5]=ﱡء[(ﱡﹴ-=105)+--ﹰﹸ+100]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ,ﱟﹺ){return ﹰﹸ=function(){return ﱡﹴ},ﱟﹺ=function(){return ﱡﹴ=ﱡء(),ﹼﹶ=ﹰﹸ,ﱡﹴ},ﹼﹶ=ﱟﹺ,function(){return ﹼﹶ()}}(ﱡء[(ﱡﹴ+=105)-ﹰﹸ+1])),!0)||9>ﹰﹸ&&(7<ﹰﹸ++&&(ﱡء[(ﱡﹴ+=++ﹰﹸ)-11]=function(ﱡء){return function(){return ﱡء}}(ﱡء[(ﱡﹴ-=10)+--ﹰﹸ-10]()),!false)||(ﱡء[ﱡﹴ+--ﹰﹸ-9]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()===ﱡﹴ()}}(ﱡء[++ﱡﹴ-3],ﱡء[(ﱡﹴ-=2)+ ++ﹰﹸ-8])),!null)||10>ﹰﹸ&&(ﱡء[ﱡﹴ++-ﹰﹸ+++9]=function(){return function(){return \u0053\u0043\u0052\u0049\u0050\u0054\u005f\u004d\u0044\u0035}}(),!null)||(10<ﹰﹸ||(ﱡء[--ﱡﹴ-ﹰﹸ--+9]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()-ﱡﹴ()}}(ﱡء[--ﱡﹴ+--ﹰﹸ-8],ﱡء[++ﱡﹴ+--ﹰﹸ-7]),!true))&&(ﱡء[--ﱡﹴ+--ﹰﹸ-14]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ,ﹰﱞ){return function(){return ﹰﱣ(ﱡء())(ﱡﹴ(),ﹰﹸ(),ﹼﹶ())(ﹰﱞ())}}(ﱡء[(ﱡﹴ-=++ﹰﹸ)+8],ﱡء[++ﱡﹴ-ﹰﹸ+++17],ﱡء[(ﱡﹴ+=++ﹰﹸ)-5],ﱡء[ﱡﹴ--+ﹰﹸ++-17],ﱡء[(ﱡﹴ-=5)+3]));else if(41<ﹰﹸ--){if(ﹰﹸ>56){if(ﹰﹸ>59)ﹼﹶ=ﱡء[ءﹶ=ﹼﹶ,--ﱡﹴ+ﹰﹸ---60]();else if(59>ﹰﹸ)57<ﹰﹸ--&&(ﱡء[ﱡﹴ++-++ﹰﹸ+58]=function(ﱡء){return function(){return ﱡء}}(null),!"")||(ﱡء[++ﱡﹴ-ﹰﹸ--+53]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()!=ﱡﹴ()}}(ﱡء[++ﱡﹴ+ﹰﹸ-59],ﱡء[(ﱡﹴ-=3)+ﹰﹸ-55]));else return ءﱡ.\ufe7c\ufc60=0;}else if(47>ﹰﹸ){if(43>ﹰﹸ)42>ﹰﹸ++&&(ﱡء[ﱡﹴ---++ﹰﹸ+41]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()%ﱡﹴ()}}(ﱡء[ﱡﹴ--+ ++ﹰﹸ-45],ﱡء[ﱡﹴ+++1]),!0)||(ﱡء[ﱡﹴ--+ﹰﹸ++-45]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()==ﱡﹴ()}}(ﱡء[(ﱡﹴ+=113)-114],ﱡء[(ﱡﹴ-=113)+ﹰﹸ++-44]));else if(--ﹰﹸ<43)ﱡء[(ﱡﹴ-=ﹰﹸ--)+39]=function(ﱡء,ﱡﹴ,ﹰﹸ){return function(){return ﹼ[ﹼ.\u006c\u0065\u006e\u0067\u0074\u0068-ﱡء()][ﱡﹴ()]=ﹰﹸ()}}(ﱡء[ﱡﹴ++-ﹰﹸ+81],ﱡء[(ﱡﹴ-=ﹰﹸ)+79],ﱡء[(ﱡﹴ+=80)+1]);else if(ﹰﹸ<44)return ﱡء[ﱡﹴ---ﹰﹸ--+42]();else(ﹰﹸ>44||(ﱡء[ﱡﹴ++]=function(){return function(){return \u006c\u0078}}(),0))&&(ﱡء[++ﱡﹴ-10]=function(ﱠﹼ,ﹴﹴ,ﹴﹰ,ﱣﱣ,ﱠﹺ,ءﹰ,ﱞﱡ,ﱢﹸ,ﱠﱣ,ﹺﹴ,ﹸﱣ){return ﹺﹴ=ﹴﱠ(ﹼ,[ﱟﹺ]),ﹸﱣ=function ﹸﱣ(){var ﱡ=\u0061\u0072\u0067\u0075\u006d\u0065\u006e\u0074\u0073.\u006c\u0065\u006e\u0067\u0074\u0068>ﱢﹸ().\u006c\u0065\u006e\u0067\u0074\u0068?ﱢﹸ().\u006c\u0065\u006e\u0067\u0074\u0068:\u0061\u0072\u0067\u0075\u006d\u0065\u006e\u0074\u0073.\u006c\u0065\u006e\u0067\u0074\u0068,ﹲﹴ=[];for(var ﹶﱣ=0;ﹶﱣ<ﱡ;ﹶﱣ++)ﹲﹴ[ﱢﹸ()[ﹶﱣ]]=\u0061\u0072\u0067\u0075\u006d\u0065\u006e\u0074\u0073[ﹶﱣ];return ﱠﹺ()&&function(ﱡء,ﱡﹴ,ﹼﹶ){try{for(ﱡﹴ=ﱡ-1,ﹼﹶ=[];ﱡﹴ>=0;ﱡﹴ--)(function(ﱡﹴ,ﹰﹸ){ﹰﹸ in ﹼﹶ||(ءﱟ(ﱡء,ﱡﹴ,{\u0067\u0065\u0074:function(){return ﹲﹴ[ﹰﹸ]},\u0073\u0065\u0074:function(ﱡء){ﹲﹴ[ﹰﹸ]=ﱡء},\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0061\u0062\u006c\u0065:!0}),ﹼﹶ[ﹰﹸ]=1)})(ﱡﹴ,ﱢﹸ()[ﱡﹴ])}catch(ﱣﹰ){}}(ﹲﹴ[ﹴﹰ()]=\u0061\u0072\u0067\u0075\u006d\u0065\u006e\u0074\u0073),ﱠﱣ()&&(ﹲﹴ[ﱞﱡ()]=this),ﱣﱣ()&&function(){try{ءﱟ(ﹲﹴ,ﹴﹴ(),{\u0076\u0061\u006c\u0075\u0065:ﹸﱣ,\u0077\u0072\u0069\u0074\u0061\u0062\u006c\u0065:!1})}catch(ﱣﹰ){}}(),ﹰﹺ(ءﹰ(),ﹲﹴ,[],ﹺﹴ)},function(){try{ءﱟ(ﹸﱣ,"length",{\u0076\u0061\u006c\u0075\u0065:ﱠﹼ(),\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0061\u0062\u006c\u0065:!0})}catch(ﱣﹰ){}}(),function(){return function(){return ﹸﱣ}}()}(ﱡء[ﱡﹴ---ﹰﹸ+++43],ﱡء[ﱡﹴ-ﹰﹸ+44],ﱡء[--ﱡﹴ-ﹰﹸ+41],ﱡء[ﱡﹴ---ﹰﹸ--+44],ﱡء[ﱡﹴ--- --ﹰﹸ+39],ﱡء[++ﱡﹴ+ﹰﹸ++-51],ﱡء[ﱡﹴ--+ﹰﹸ-47],ﱡء[++ﱡﹴ+--ﹰﹸ-50],ﱡء[(ﱡﹴ-=6)+ﹰﹸ---41]));}else(51<ﹰﹸ++||(ﹰﹸ<50&&((ﹰﹸ<49||(ﱡء[ﱡﹴ+ﹰﹸ++-52]=function(ﱡء,ﱡﹴ,ﹰﹸ){return function(){return ﹸﹼ(ﱡء(),[ﱡﹴ(),ﹰﹸ()])()}}(ﱡء[--ﱡﹴ-1],ﱡء[ﱡﹴ---2],ﱡء[ﱡﹴ+--ﹰﹸ-48]),!{}))&&(ﱡء[++ﱡﹴ-1]=function(ﱡء){return function(){return ﱡء}}(!!null)),true)||(51<ﹰﹸ||((--ﹰﹸ<50||(ﱡء[ﱡﹴ++-ﹰﹸ+50]=function(){return function(){return \u004a\u0053\u004f\u004e}}(),""))&&(ﱡء[ﱡﹴ+++--ﹰﹸ-48]=function(){return function(){return \u0053\u0074\u0072\u0069\u006e\u0067}}()),!1))&&(ﱡء[++ﱡﹴ+ ++ﹰﹸ-54]=function(){return function(){return \u0054\u0079\u0070\u0065\u0045\u0072\u0072\u006f\u0072}}()),!1))&&(55>ﹰﹸ--&&((52<ﹰﹸ||(ﱡء[ﱡﹴ++-ﹰﹸ+++52]=function(ﱡء){return function(){return ﱡء}}(true),false))&&(ﱡء[++ﱡﹴ-++ﹰﹸ+53]=function(){return function(){return \u0065\u006e\u0063\u006f\u0064\u0065\u0055\u0052\u0049\u0043\u006f\u006d\u0070\u006f\u006e\u0065\u006e\u0074}}()),1)||(ﹰﹸ++>55||((56>ﹰﹸ||(ﱡء[ﱡﹴ+++ ++ﹰﹸ-57]=function(){return function(){return \u0070\u0072\u006f\u0063\u0065\u0073\u0073}}(),![]))&&(ﱡء[++ﱡﹴ- --ﹰﹸ+53]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]^35843+ﹼﹶ)),0))&&(ﱡء[(ﱡﹴ+=102)-ﹰﹸ-48]=function(ﱡء,ﱡﹴ,ﹰﹸ){return function(){return ﱡء()[ﱡﹴ()]=ﹰﹸ()}}(ﱡء[--ﱡﹴ+ﹰﹸ++-161],ﱡء[ﱡﹴ++-103],ﱡء[(ﱡﹴ-=104)+--ﹰﹸ-56])));}else if(ﹰﹸ>36){if(39<ﹰﹸ)try{var ﹰﱡ=ﱡﹲ[ﹼﹶ++]^--ﹰﹸ+44385+ﹼﹶ,ﱣ=ﱡﹲ[ﹼﹶ++]^-ﹰﹸ+++48647+ﹼﹶ,ﱞﹼ=ﱡﹲ[ﹼﹶ++]^-ﹰﹸ+16051+ﹼﹶ,ﹰﹰ=ﱡﹲ[ﹼﹶ++]^-ﹰﹸ--+46121+ﹼﹶ,ﱞﹸ={\ufe7c\ufc60:1},ﱞ‌=ﹰﹺ(ﹼﹶ,ﱟﹺ,[],ﹼ,ﱞﹸ);if(ﱞﹸ.\ufe7c\ufc60)return ﱞ‌;if(ﱞﹸ.\ufc5e\ufe70!==void 0)if(ﹰﱡ=ﱞﹸ.\ufc5e\ufe70,ﱞﹸ.\ufc63\ufe74>1)return ءﱡ.\ufc5e\ufe70=ﹰﱡ,ءﱡ.\ufc63\ufe74=--ﱞﹸ.\ufc63\ufe74,ءﱡ.\ufe7c\ufc60=0}catch(ﱣﹰ){ﱟﹺ[ﹰﹰ]=ﱣﹰ,ﱞﹸ={\ufe7c\ufc60:1},ﱞ‌=ﹰﹺ(ﱣ,ﱟﹺ,[],ﹼ,ﱞﹸ);if(ﱞﹸ.\ufe7c\ufc60)return ﱞ‌;if(ﱞﹸ.\ufc5e\ufe70!==void 0)if(ﹰﱡ=ﱞﹸ.\ufc5e\ufe70,ﱞﹸ.\ufc63\ufe74>1)return ءﱡ.\ufc5e\ufe70=ﹰﱡ,ءﱡ.\ufc63\ufe74=--ﱞﹸ.\ufc63\ufe74,ءﱡ.\ufe7c\ufc60=0}finally{ﱞﹸ={\ufe7c\ufc60:1},ﱞ‌=ﹰﹺ(ﱞﹼ,ﱟﹺ,[],ﹼ,ﱞﹸ);if(ﱞﹸ.\ufe7c\ufc60)return ءﱡ&&(ءﱡ.\ufe7c\ufc60=1),ﱞ‌;if(ﱞﹸ.\ufc5e\ufe70!==void 0){if(ﹰﱡ=ﱞﹸ.\ufc5e\ufe70,ﱞﹸ.\ufc63\ufe74>1)return ءﱡ.\ufc5e\ufe70=ﹰﱡ,ءﱡ.\ufc63\ufe74=--ﱞﹸ.\ufc63\ufe74,ءﱡ.\ufe7c\ufc60=0;return ﹰﹺ(ﹰﱡ,ﱟﹺ,[],ﹼ,ءﱡ)}ﹼﹶ=ﹰﱡ}else(++ﹰﹸ<40||(ﱡء[(ﱡﹴ+=53)- --ﹰﹸ-16]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()[ﱡﹴ()]}}(ﱡء[--ﱡﹴ-ﹰﹸ-15],ﱡء[ﱡﹴ-=53]),![]))&&(ﹰﹸ>38&&(ﱡء[ﱡﹴ++-ﹰﹸ+++39]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]-(++ﹰﹸ+19983+ﹼﹶ)),!"")||(ﱡء[++ﱡﹴ-1]=function(){return function(){return \u0069\u0073\u004e\u0061\u004e}}()));}else if(ﹰﹸ>31)ﹰﹸ>35&&(ﱡء[++ﱡﹴ-ﹰﹸ+31]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ){return function(){return ﹲﹰ(ﱡء(),ﱡﹴ(),ﹰﹸ())(ﹼﹶ())}}(ﱡء[++ﱡﹴ-ﹰﹸ+31],ﱡء[(ﱡﹴ+=ﹰﹸ--)-42],ﱡء[++ﱡﹴ- --ﹰﹸ-7],ﱡء[(ﱡﹴ-=42)+--ﹰﹸ-31]),!0)||(34>ﹰﹸ||(ﹰﹸ<35&&(ﱡء[(ﱡﹴ-=ﹰﹸ++)+32]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()>ﱡﹴ()}}(ﱡء[ﱡﹴ+ ++ﹰﹸ-4],ﱡء[ﱡﹴ+=33]),true)||(ﱡء[++ﱡﹴ-ﹰﹸ+++34]=function(){return function(){return \u0044\u0061\u0074\u0065}}()),null))&&(32<ﹰﹸ||(ﱡء[(ﱡﹴ+=42)-++ﹰﹸ-11]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()^ﱡﹴ()}}(ﱡء[ﱡﹴ++-ﹰﹸ---11],ﱡء[(ﱡﹴ-=44)-++ﹰﹸ+33]),0))&&(ﱡء[ﱡﹴ++]=function(ﱡء,ﱡﹴ){return function(){return ﹼ[ﹼ.\u006c\u0065\u006e\u0067\u0074\u0068-ﱡء][ﱡﹴ]++}}(ﱡﹲ[ﹼﹶ++]^-ﹰﹸ--+50681+ﹼﹶ,ﱡﹲ[ﹼﹶ++]^ﹰﹸ--+53617+ﹼﹶ));else if(26<ﹰﹸ)(ﹰﹸ>30||((ﹰﹸ<29||((28<--ﹰﹸ||(ﱡء[ﱡﹴ+--ﹰﹸ-28]=function(ﱡء){return function(){return void ﱡء()}}(ﱡء[ﱡﹴ- --ﹰﹸ+25]),!1))&&(ﱡء[ﱡﹴ++-ﹰﹸ+29]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[ﱡﹴ]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[ﱡﹴ+ﹰﹸ-28]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[(ﱡﹴ+=ﹰﹸ--)-27]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[(ﱡﹴ+=24)+ﹰﹸ---78]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[ﱡﹴ++-49]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++]),ﱡء[(ﱡﹴ-=48)+--ﹰﹸ-27]=function(ﱡء){return function(){return ﱡء}}(ﱡﹲ[ﹼﹶ++])),null))&&(27<ﹰﹸ&&(ﱡء[ﱡﹴ+++ﹰﹸ-28]=function(ﱡء,ﱡﹴ){return function(){return ﹼ[ﹼ.\u006c\u0065\u006e\u0067\u0074\u0068-ﱡء][ﱡﹴ]}}(ﱡﹲ[ﹼﹶ++]^62427+ﹼﹶ,ﱡﹲ[ﹼﹶ++]^ﹰﹸ+++42457+ﹼﹶ),true)||(ﱡء[(ﱡﹴ+=ﹰﹸ)-29]=function(ﱡء,ﱡﹴ){return function(){return ﹸﹼ(ﱡء(),[ﱡﹴ()])()}}(ﱡء[ﱡﹴ++-ﹰﹸ++-1],ﱡء[(ﱡﹴ-=29)- --ﹰﹸ+26]))),0))&&(ﱡء[ﱡﹴ+++ﹰﹸ++-31]=function(){return function(){return \u0075\u006e\u0064\u0065\u0066\u0069\u006e\u0065\u0064}}());else if(24<ﹰﹸ)25<ﹰﹸ&&(ﱡء[ﱡﹴ++-ﹰﹸ+++26]=function(){return function(){return \u0067\u006c\u006f\u0062\u0061\u006c\u0054\u0068\u0069\u0073}}(),!0)||(ﱡء[ﱡﹴ++- --ﹰﹸ+22]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()instanceof ﱡﹴ()}}(ﱡء[(ﱡﹴ+=ﹰﹸ)-27],ﱡء[ﱡﹴ-=26]));else if(ﹰﹸ<23){if(ﹰﹸ>21)ﱡء[++ﱡﹴ+--ﹰﹸ-24]=function(ﱡء,ﱡﹴ){return function(){return ﱡء()!==ﱡﹴ()}}(ﱡء[ﱡﹴ--+ ++ﹰﹸ-25],ﱡء[--ﱡﹴ+ﹰﹸ-22]);else return ءﱡ.\ufc5e\ufe70=ﱡء[ﱡﹴ-ﹰﹸ--+20](),ءﱡ.\ufc63\ufe74=ﱡء[++ﱡﹴ+ﹰﹸ-23](),ءﱡ.\ufe7c\ufc60=0;}else 24>ﹰﹸ&&(ﱡء[++ﱡﹴ-1]=function(){return function(){return \u0041\u0072\u0072\u0061\u0079}}(),!0)||(ﱡء[(ﱡﹴ-=++ﹰﹸ)+19]=function(ﱡء,ﱡﹴ,ﹰﹸ,ﹼﹶ,ﹰﱞ,ﹰء){return function(){return ﹰﱣ(ﱡء(),ﱡﹴ(),ﹰﹸ(),ﹼﹶ())(ﹰﱞ())(ﹰء())}}(ﱡء[ﱡﹴ++-ﹰﹸ--+45],ﱡء[--ﱡﹴ+--ﹰﹸ-4],ﱡء[--ﱡﹴ-ﹰﹸ+45],ﱡء[--ﱡﹴ-++ﹰﹸ+48],ﱡء[--ﱡﹴ-ﹰﹸ+++50],ﱡء[(ﱡﹴ+=23)-ﹰﹸ+29]))}(5162)})(function(ﱡﹶ,ﹲ‌,ﹲﹰ,ﹸﹼ,ﹰﱣ,ﹰﹼ){try{return ﹲ‌=function(ﱡﹶ,ﱡﹲ){try{return ﱡﹶ()}catch(ﱣﹰ){return ﱡﹲ()}},ﹲﹰ=function(ﱡﹶ){if(typeof \u0061\u0074\u006f\u0062==="function")return ﹲ‌(function(ﱡﹲ,ﹴﱠ,ﹸﹸ){for(ﱡﹲ=\u0061\u0074\u006f\u0062(ﱡﹶ),ﹴﱠ=[],ﹸﹸ=0;ﹸﹸ<ﱡﹲ.\u006c\u0065\u006e\u0067\u0074\u0068;ﹸﹸ++)ﹴﱠ.\u0070\u0075\u0073\u0068(ﱡﹲ.\u0063\u0068\u0061\u0072\u0043\u006f\u0064\u0065\u0041\u0074(ﹸﹸ)&255);return ﹴﱠ},function(){return ﹰﱣ(ﱡﹶ)});return ﹰﱣ(ﱡﹶ)},ﹸﹼ=function(ﱡﹶ,ﱡﹲ,ﹴﱠ,ﹸﹸ,ءﱟ){ﹴﱠ=0,ﹸﹸ=0;do ءﱟ=ﱡﹶ[ﱡﹲ.\u0069++],ﹴﱠ+=(ءﱟ&127)*\u004d\u0061\u0074\u0068.\u0070\u006f\u0077(2,ﹸﹸ),ﹸﹸ+=7;while(ءﱟ&128);return ﹴﱠ},ﹰﱣ=function(ﱡﹶ,ﱡﹲ,ﹴﱠ,ﹸﹸ,ءﱟ,ﹶﹲ,ﹶﹰ,ﹸﱡ,ﱣﹲ){ﱡﹲ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",ﹴﱠ=[],ﹸﹸ=0;while(ﹸﹸ<ﱡﹶ.\u006c\u0065\u006e\u0067\u0074\u0068)ءﱟ=ﱡﹲ.\u0069\u006e\u0064\u0065\u0078\u004f\u0066(ﱡﹶ.\u0063\u0068\u0061\u0072\u0041\u0074(ﹸﹸ++)),ﹶﹲ=ﱡﹲ.\u0069\u006e\u0064\u0065\u0078\u004f\u0066(ﱡﹶ.\u0063\u0068\u0061\u0072\u0041\u0074(ﹸﹸ++)),ﹶﹰ=ﱡﹲ.\u0069\u006e\u0064\u0065\u0078\u004f\u0066(ﱡﹶ.\u0063\u0068\u0061\u0072\u0041\u0074(ﹸﹸ++)),ﹸﱡ=ﱡﹲ.\u0069\u006e\u0064\u0065\u0078\u004f\u0066(ﱡﹶ.\u0063\u0068\u0061\u0072\u0041\u0074(ﹸﹸ++)),ﱣﹲ=ءﱟ<<18|ﹶﹲ<<12|(ﹶﹰ&63)<<6|ﹸﱡ&63,ﹴﱠ.\u0070\u0075\u0073\u0068(ﱣﹲ>>16&255),ﹶﹰ!==64&&ﹴﱠ.\u0070\u0075\u0073\u0068(ﱣﹲ>>8&255),ﹸﱡ!==64&&ﹴﱠ.\u0070\u0075\u0073\u0068(ﱣﹲ&255);return ﹴﱠ},ﹰﹼ=function(ﱡﹶ,ﱡﹲ,ﹴﱠ,ﹸﹸ){ﱡﹲ=ﹲﹰ(ﱡﹶ),ﹴﱠ=[],ﹸﹸ={\u0069:0};while(ﹸﹸ.\u0069<ﱡﹲ.\u006c\u0065\u006e\u0067\u0074\u0068)ﹴﱠ.\u0070\u0075\u0073\u0068(ﹸﹼ(ﱡﹲ,ﹸﹸ));return ﹴﱠ},ﹰﹼ(ﱡﹶ)}catch(ﱣﹰ){return[]}}("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"),[[56779,33889,57385,33238,2760],[34585,56229,22885],[13633,31959],[1028,44638,35499,22393,15640,58684],[8431,49946],[48899,42302,52007,43410,42837,52164,62605,30028],[18818,6312,35100,10506,30122,13314,32831,8944],[64664,60832,43717],[32800,28616,30454,60756],[11608,31994,37318,53363,15548,9556,51266],[16202,25333,13050,63417,49095,41578,25384],[44054,52144],[10950,52060,26717,2319],[6553,22690,33479,65396,3502,46191],[64779,39730,47494],[16051,60739,12868,12779,10178,20076],[61245],[15166],[47999],[25842],[63716],[16827],[35635,27012],[3584],[4567,28791,29959,12071,10748,14359],[41487,49204],[31489,61270,690,57706,15240,9853,50871,42840,49102,44648,20069,28470,5748,29947,60650,36698,30558,6039,40102,50821,819,57840,54946,40285,37217,12311],[56341,34740,36923],[7314],[42373,17576,1889],[11788,19505,22439,3602,1845,42372,19264,6897,26709,31390,21776,3264,49492,41207,3553,29710,14066,10940,23664,64169,20689,8024],[15644,9394,11152,19051,4645,28674],[54554,40148,11608,21635],[54131,38784,28849],[58896,33996],[25938,2024,63771],[27238,63639,39001],[5118,45648,23861],[31191,63569],[15365,59920],[47723,59526,10510,18281,63995,26686,57135,33762,2209,64274,61303,37880,31221,59559,49364,41307,63848,61396,7046,65079,41328,16441,35577,22926,3514,31867,47888],[23280,14594,57417,34535,18973,6540],[42540,19671,27271,15269,22124,5371,57378],[301,41221,51132],[48649,58785,28897,8047,62892],[35504,22420,37097,24391],[8903,20331,56243,34364,43805,22327,1348,31982,63768,39168,62227,40849],[49423,41432],[28430,5052,18276,26498,8728,49212,48464,48131],[55196,35393,62378,63059],[57056,34143],[2521,55445,15089,26293],[29316,5892,31997,58534,195,24954,36730,62147,33376],[54941,49051,525],[5096,62357,25633,1742,59366,34321,29977,13470,55270,35350],[38199,30145,37530,54059,8988,20235,16466,9896],[59027],[6820,47432,23797,32672,45781,44804,57414,33271,23101,4082,976,58036],[3138,48616,32216,31870,21923,13339,53103,38425],[34695,14902,59149],[20706,5993,30425],[1906,28569,56864,34013,21562,13305,34522],[57186,33513],[54692,29770,19149,26989,56630,36321,62110,38660,37791],[23882,1265],[19773,12275,53874,37140,57817,32910],[20214,4900,45915,21187,31484],[27084,30826,62528,37575,3217],[58481,33748,17509],[64591,42775,6253],[24714,1704,32948,57712,27296,6478,40350,19543,4992,63035,27380,2375,28931,4404,5684,48328],[59567,39241,55719,38996,3126,21919,10977,64184,26422,2808],[32711,57983,28373,3479,3853,21309,18134],[47122,48676,24375,15857,52735],[33916,9501,56165,35749],[21407,3612,29615,13243,43362,63511],[57326,33175],[38770,23979,41076,49887,34215,27724,40933,65121,64466,48741,10585,55755,55649,35022,50458,41784],[56700],[17551],[4948,53707,38021,57127,50985,65227,25982,3085,4792,57191,30976],[29006,16353],[],[3067,23451],[54110,45469,34240,60524,59467],[19380,10885,59895,34909,17964,9227,6800,22831,14155,63199],[18852,59568,43054,24575,19849,11299,7112,47721,62648,65371,11235,22017],[36634,54723,15525,32074,27542,2449,6885,57163],[64004,59441],[60174,35112,25424],[60854],[15336,9811,49504,41453,61639],[31369],[26254,2906,32129,61355],[59080,32100,13994,23368,33018,8609],[59985,47072],[45670,20609],[1538,42027,39809,52118],[26475,6849,57027],[7731,52457,5843,55067,58319,34338,59954,47519,25457,648,24145,15591,5692,47086],[60110,35107,17447,25230,33713,58049,36669,30179,16582,32588,43154,31110],[32181,60541],[65131,64979,22173,3214,47557],[1952,42582,22848],[61011,38025],[28900,12704,54982,35699,2633,55782,41969,20064,36834,12678,22369,7832,10899,18694,31118,10268,45802,54051,38854,19025,39401,14454,47242,47449,16205,25239,60996,37819,45817,57151,31063,30670,3861,54990,38389,15336,12788,53392,47521,43040,52692,39950,15892,12257,27288,30565,7430,17549,48847,25391,41846,17921,59580,39767,25818,1960,16346,58035,23048,6363,17814,9237,58534,33549,50484,44174,36535,13820,7100,51104,20615,12606,63983,43444,9866,19717,22588,14648,8177,17332,65495,58111,15097,15090,21908,7045,18192,59997,22103,3215,29505,4488,14745,59392,47860,47866,2071,27358,63371,48070,46910,47049,43766,22149,36031,64849,63111,40308,26905,6191,11132,51729,7559,17428,48101,55976,62367,44550,43278,18663,43395,47147,13477,27850,42453,56298,36709,60948,31740,11734,14228,10801,50498,41937,53513,61869,10568,18551,48437,25640,55252,39819,11956,13239,32919,8401,12389,22398,51344,59332,47075,31106,40270,50373,36382,24405,28214,65406,46886,59943,8276,53162,35447,23332,9872,32023,5125,54096,15407,11123,9243,17258,6852,17989,45140,45242,56411,40146,8699,16646,17334,9539,19763,27664,18075,10149,6792,23205,30779,14169,36564],[8921,49441,65483,25175,4050,44593,29341,4501,21465,30291],[48562,42080,2120],[43725],[4517,53335],[19487,10910],[21711],[24635,757,31830,9955],[31900],[34525,15697,51976,46854,30447,11561],[52104,64015,64341,43712,29816],[36997,21303,13624,29830,52447],[58500],[21994,5133,49861],[25191,1008],[3849,30127,34814,26168,22169,2825],[6806,19386,18385,10840,28572,4732,36738,53783,35436,10516,57518,33084,28427,64813,33857,8927,21084,13301],[2473,43059],[45218,30546,63835,39833],[47323,55430,53407,37665],[52710,44156],[27862],[30736,6063,25333,955,27160,14373,56781,35940,15689,58827,42592,31945],[13171,28364,23251,63745,26531],[21773,4897,40334],[22676,14994,6557,51248],[5120,21049,12913,20699,39120,52094,23474],[24202,7330,56800,35954,55822,47292],[44417,63398,10528],[48952,45049,56904,33555],[64490,39472,61700,46015,30861,64171,5908,57026,52397],[29954,13759,49333,41792,34716],[23773],[28013,62602,16369,24141],[25327,794,1862,59054,5455,46309],[32678,11843,39083,14155,6018,24233,53322,29422,64386,44592],[63622,40746,33375,8973,30574],[6082,30303,56622],[19967,5305,33172,8245,15275],[8667,49308,2910],[37874],[54793,39972,13143,20950,26082,3147,47357,60194],[17249,25331],[14674,10492,10331,51694,19034,10741,27414],[13817,27699,61835,37301,32244,15755,54305,30216,28824,4406],[23578,3491,211,57726],[33626,10220,14052,13698,16459],[42251,19629,15384,10812,14027,15211,53864,37096,38265],[60147,35099],[39963,18102,576,41162,61311,37609,3065,44551,40604,17628,46706,31970],[56944,33741,18465,6551],[49345],[20620,12577,31360],[32134],[14248,30230,39255],[61891,36945],[15545,9502,41645,49948,59290,39430],[57709,32784],[20823,4595,5917],[3051,43610,7104,50809,56108],[33788],[21600,62418],[46505,44106],[49958,42966,28071,13731,59766,34838,7067,50696,62461,60971],[1431,60507,10266],[40259,24329],[58318,33396,29220],[48264],[10693,51319,13762],[10645,55355,29420,4881,29144,4124,28587],[27966],[62809,29695,37520,53051,17412,8744],[1945,24216,16411],[60155,63357,37395,12432,7908,48418,37938],[43732,55125,29541,29136],[29160,4160,40726,49857],[56029,34462],[64791,58653,59307,34376,13786,60419,22122],[37338,28790,28638,63116],[8849,65444,25106],[49154,59045,4542,61536,14530],[52655],[43914,46594,49539,41005,37745],[63665,63856,8327,49842,400],[33264,24606,15233,59939,56538],[12954,12436,12416,53537,42902,48565],[49353,24901],[37145],[60906,46159],[47041,63096,46522],[53404,37155],[49293,26385],[44234,30562,19425],[48607,42398,9194,24153,43882,51614,55452,31557,54706,48188,7190,19862,64239,43392,42911,50753,36389,60860,27426,31679,45388,53437],[38977,52973,45970,46722,22597],[48565],[24875,205,60588],[26329,1346,46491,21514],[50749,58613,44474,62570,22457,6782,43630,51401,18974,14803],[50013],[63173,27491,5364,19868,24997,69],[52105,43572,61330],[43386,22966],[33523,8840],[35587,10554],[46200,48769,52874],[7907],[35149,26762],[22788,2226,29736],[38593,51985],[41693,49426,3647],[13224,62986,10068],[60802,35956],[16148,15628],[25427,745,33594],[62952,27725],[23532,14923,64658,43782],[6056,23450],[64287,42754,37554,55151,39923],[23729,1323],[5264,55167],[40254,52117,53198,46738],[32322],[29066,4129,20878,4140,37906],[46599,46505,8234],[2,61117,53956],[61050,38782],[44054,31398],[11452,23893,49955,45014],[49228,45025,43730,52111,48303,43853,30196,13319],[18487,9747,65256,59206,57960],[5340,32026],[28684,65444,36500],[48619,44108,54732,62570,39629,55580],[54871,48543,688,24906,25144,1,37664,13015,5180],[10340,56013,49787,41177,21809,63275],[46363,27930,23847,3334,24835,290,29928,7494,55702,34871,23929,7202,9920,51063,41610],[46030,61047,8757,16839,53037,38360,6538],[14952,27078,4004],[2708,47256,31719],[7749,17916,19046,6291],[45255,54119,29295,4554,45129,22223],[61824,36899,51495,43401,43936,22080],[47872,26410,27854,2923,14827,10639],[61745,61619],[29140],[48960,56812,24276,667,10354,16140],[36876,61032],[61374,36442,61275],[13681,62937,64459],[11171,15894],[4598,53326,13932,15287,31855],[28759,12567,56805,33868,20006],[22367,3541],[36221,29850,3193,54963,11168],[5059,62046],[39147,14659,27157,63546,18021,27602,40906],[22834,2244,58123,33032,39964,31381,43109,56971,15891,9227,18775,10125],[31864,9872,64791,58554,6215,51680,64334],[4677,46064,24292,832,30885,10636,33423,24876,18835],[64303,42733,934,41555,17327,8811,25365,471,3059,46758],[43188,18776,48028,26292,43701],[14167,62954,27789,14103,38327,54373,38654,19238],[61679,45375,36497,60708],[5921,57227],[31240],[18855,63563,28167,15413,8278,49912,56065,35567,62824,48574,50954,39722,47975,27101,10034,23476],[49891,41223,3024,54908,36401,60897,49623,41031,40553,17541,1045,56723,4093],[11085,55011,6710,18683,16383,8765,47313,59771,10477,18772],[32526,10020],[62162,37742],[17896,58446],[55402,39552],[21456,12925],[63006,64982,65482],[18768,6347,3241,21839,53591,37109,31810],[39079,16027],[47076,43590],[33627,25026,27748,3832,22367],[35227,10327,60270,46647],[19794,11738,61014,36316,51158],[5605,54336,1750],[10438,51026,7494,17838],[38016,21944],[42811,52670,32002,11493,33836,58886,13109,12694,29423,4441,47889,42684],[47593,43073],[64352,47617],[34304,11448,13184],[40004,17908,18811,6202,11583],[61560,37366],[6547],[41874,49721,30175,11268,25285,867,49336,57697,35843,27561,63049,29845,10357,18891,23121],[42318,17541,53056,38634,24755,285,58381],[56019,35711,14729,10293,51637,38943],[33238,8288],[40999,52893,39888,50795],[18866,6208,52723,38042,48670],[40407,52321,4489,53428,58989,33933],[22665,6447,57608,33702,53996,29373],[43640,22950,54326,37407,25755,3406,56623],[50782,47897,22248,3329,38114,21824,4724,45210,32634,7612],[61951,37013,33392],[64795,26422],[11521,19745,61727,38835,53047],[22602,5869,20922],[8701,16409,37632,53536,57018],[56181,35420,56503,35682,38602,53051,4934,53996,29976,5294,35653,28346,24821,301,48517],[32563,8932,46770,23317,55613,31725,59060,34115,20416,12914,41407,49260,39764,23041,64136,59728,4958,53961,6044],[56272,35430,55097,40370,25130,142,43605,23307,56474,35477,57038,36206,21602,4800,11568,52449],[32202,11368,35862],[10982,22792,10451,19225,50510,25851,19906,5233],[9008,49806],[38448,54518,21740,3349,39196,22921],[12137,30351,15354,10808,14979,59851],[18491,11906,15384],[53438,37246,16075,9590,33984,10107,62102,32522],[29108,12388,62748,29628],[50023,42701],[45839,22463,42338,50126,9733,17456,10680,14444,59981,36817,25658],[7259],[20925,12387,12203,12942,32762,58939,13552,54598,42111,50095,15897],[11004,55596,23371,1783,2872,23000,10601,51346],[28481,13068,54717],[19488,60382,13080,29654,27977,14249,54542,38180,34888,16114,22998,2100,18302,27183],[20408,29301],[47088,61083],[32142,58555,60798],[933,24963],[14916,10479,48652,60580,63765,28421,24408,1489,3474,44069,12826,22411],[54695,29827,5660,21561,25077,84],[30681,13939],[21993,5123,37108,20664,31909,9604,15839,25992,37218,61571,27936,13716,34448,11084,49205,42505,5816],[22212,32542,43781],[8973,49946,63393,27568,35358,26672,64963,40001,65186,58724],[17735],[56541,35684,65119,31993,34991,59263],[37907,62106,65287,58034,11144],[46111,44741,29147],[54594,36326,431],[8655,16490,27843,62821,42812,23194,41130,49500],[978,25209,35837,10923,16,40488,29632,13949,45488],[42193,50040,18411],[27334,6521,24920,230,32060,60373],[46726,54735],[37766,30627],[38885,20046,54901],[55731,63556,32556,9932,57514],[58361,40575,55822,35286,5420,46315,54929,40853,7242,56053,25280,1911,55210],[46188,45006,50132,41614,51048,26120,32775],[21846,3527],[23027,6286,24070,938,21718,4962,505,57529,31966,6993,19960,5046,46570],[1235,25449,1900,64244],[56683,40131,47352],[53148,62034,36655],[21426,5001,29372],[41152,51056],[6622,47496,59491,40865,26098],[23377,2782],[4779,54039,17682,15667,45148,29667,22830,2288],[16105],[2557,43046,58042,34652,22556,6610],[35523,26997,52286,60876,61368,37396],[20346,7903,41914,49553,10129,18984],[40209,17725,9992,56098],[24329,805,52720,31768,25156,247,64317,59038,49659,41011],[14134,23808,3680,44536,30887],[59431,39414,61585,45890,45434],[62055,46988,23859,1809],[50528,44507,315,41208,49875,41331,41940,18032,11493,21780],[25096,21134,33167,41410,55027,53094,35843,55303,26121],[36604,56579],[22948,33756,61271,45785],[43169,14808],[48357,12140,34325,65510,48508],[30805,6650,14494,15164],[59019],[36321,54395,22137],[57076,34636,44984,46659],[9805,50420,29581,11781,63849,30873,54287,45953],[62791,37863],[8040,50831,24511,7805,62320,37284,45269],[65306,59028,33042,25048,9567],[48478,31734,24739,345],[48744,42136,19087,14509],[10081,50893,693],[14132,22262],[14849,27051,41266,50972,7348,17757,20395],[17714,7451,34160,25658,65253],[59076,34168,4966],[56241,35437,5752,55069,14404,59893,30575,5663],[3602,30122,29355,61577,47803],[24330,693],[37912,12848],[26702,2545,38396,21590],[46217,29365],[24369,758,39658],[2268,42817,25223,1955,39575,22849],[35105,63765],[22518,5683,10732,22670,63427,30321,40757,15775],[50984,49118,51163],[6282,18739,30069],[54772,40088,16388,8746],[1814,47923,9456],[13121,20904,55782,38988],[31965,15628,9974,23874,17385,9812,31716,61011,21144],[46611,56367],[56294,40456,60903],[52814,28665,37110],[9290,32755,50286,58072,61330,37505,13704,54355,45693,45522,61280,37574,64106,39092,41167],[29511],[4245,54097,36303,54385,8178],[42984,65109,29212,61443,3136,32502],[1084,32451,1212,25443,34900,27606,64055,40920,37110,28948,23381,3609],[52968,38540,41585,49448,43016,55736,64203],[12209,54859,20377,29267]],function(){try{return this||typeof \u0067\u006c\u006f\u0062\u0061\u006c\u0054\u0068\u0069\u0073==="object"&&\u0067\u006c\u006f\u0062\u0061\u006c\u0054\u0068\u0069\u0073||typeof \u0073\u0065\u006c\u0066==="object"&&\u0073\u0065\u006c\u0066||typeof \u0077\u0069\u006e\u0064\u006f\u0077==="object"&&\u0077\u0069\u006e\u0064\u006f\u0077||typeof \u0067\u006c\u006f\u0062\u0061\u006c==="object"&&\u0067\u006c\u006f\u0062\u0061\u006c||\u0046\u0075\u006e\u0063\u0074\u0069\u006f\u006e("return this")()}catch(ﱣﹰ){return{}}}(),{},\u004f\u0062\u006a\u0065\u0063\u0074.\u0063\u0061\u006c\u006c.\u0062\u0069\u006e\u0064(\u0041\u0072\u0072\u0061\u0079.\u0062\u0069\u006e\u0064),\u0041\u0072\u0072\u0061\u0079.\u0061\u0070\u0070\u006c\u0079.\u0062\u0069\u006e\u0064(\u004e\u0075\u006d\u0062\u0065\u0072.\u0062\u0069\u006e\u0064),\u004e\u0075\u006d\u0062\u0065\u0072.\u0062\u0069\u006e\u0064.\u0062\u0069\u006e\u0064(\u004f\u0062\u006a\u0065\u0063\u0074.\u0062\u0069\u006e\u0064),\u004f\u0062\u006a\u0065\u0063\u0074.\u0063\u0061\u006c\u006c.\u0062\u0069\u006e\u0064([].\u0070\u0075\u0073\u0068),\u0041\u0072\u0072\u0061\u0079.\u0063\u0061\u006c\u006c.\u0062\u0069\u006e\u0064([].\u0063\u006f\u006e\u0063\u0061\u0074),\u004e\u0075\u006d\u0062\u0065\u0072.\u0061\u0070\u0070\u006c\u0079.\u0062\u0069\u006e\u0064(\u0053\u0074\u0072\u0069\u006e\u0067.\u0066\u0072\u006f\u006d\u0043\u0068\u0061\u0072\u0043\u006f\u0064\u0065,null),\u004f\u0062\u006a\u0065\u0063\u0074.\u0064\u0065\u0066\u0069\u006e\u0065\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079.\u0062\u0069\u006e\u0064(\u004f\u0062\u006a\u0065\u0063\u0074),\u004f\u0062\u006a\u0065\u0063\u0074.\u0067\u0065\u0074\u004f\u0077\u006e\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079\u0044\u0065\u0073\u0063\u0072\u0069\u0070\u0074\u006f\u0072.\u0062\u0069\u006e\u0064(\u004f\u0062\u006a\u0065\u0063\u0074),\u0052\u0065\u0067\u0045\u0078\u0070,function(){return typeof \u0042\u0069\u0067\u0049\u006e\u0074!=="undefined"?\u0042\u0069\u0067\u0049\u006e\u0074:void 0}(),\u0053\u0074\u0072\u0069\u006e\u0067.\u0066\u0072\u006f\u006d\u0043\u0068\u0061\u0072\u0043\u006f\u0064\u0065,function(ﱡﹲ,ﱡﱣ,ﱞﹶ,ﹲﹰ){return ﱡﹲ=function(){}.\u0063\u0061\u006c\u006c,ﱡﱣ=ﱡﹲ.\u0062\u0069\u006e\u0064("".\u0073\u0070\u006c\u0069\u0074),ﱞﹶ=ﱡﹲ.\u0062\u0069\u006e\u0064([].\u0072\u0065\u0076\u0065\u0072\u0073\u0065),ﹲﹰ=ﱡﹲ.\u0062\u0069\u006e\u0064([].\u006a\u006f\u0069\u006e),function(ﱡء){return ﹲﹰ(ﱞﹶ(ﱡﱣ(ﱡء,"")),"")}}(),[]); diff --git a/musicSdk/api-source-info.ts b/musicSdk/api-source-info.ts index ffd6433..3831796 100644 --- a/musicSdk/api-source-info.ts +++ b/musicSdk/api-source-info.ts @@ -23,8 +23,8 @@ const sources: ApiSourceInfo[] = [ // }, // }, { - id: 'v5', - name: 'lx-music-source-v5 聚合源', + id: 'source', + name: '外部聚合音源', disabled: false, supportQualitys: { kw: ['128k', '320k', 'flac'], diff --git a/musicSdk/api-source.ts b/musicSdk/api-source.ts index bde68e1..f04fc7f 100644 --- a/musicSdk/api-source.ts +++ b/musicSdk/api-source.ts @@ -1,8 +1,8 @@ -// 接口选择层:按 apiSource 配置把请求路由到 v5 聚合源或服务器内置直连接口 +// 接口选择层:按 apiSource 配置把请求路由到外部聚合音源或服务器内置直连接口 import apiSourceInfo from './api-source-info.ts' import { apiSource, userApi } from '../src/renderer/store.js' import internalApi from '../src/apis/index.js' -import { createApi as createLxSourceApi } from '../src/apis/lx-source.js' +import { createApi as createSourceApi } from '../src/apis/source.js' interface SourceApi { getMusicUrl(songInfo: any, type: string): { promise: Promise; cancelHttp(): void } @@ -10,11 +10,11 @@ interface SourceApi { } const allApi: Record = { - v5_kw: createLxSourceApi('kw'), - v5_kg: createLxSourceApi('kg'), - v5_tx: createLxSourceApi('tx'), - v5_wy: createLxSourceApi('wy'), - v5_mg: createLxSourceApi('mg'), + source_kw: createSourceApi('kw'), + source_kg: createSourceApi('kg'), + source_tx: createSourceApi('tx'), + source_wy: createSourceApi('wy'), + source_mg: createSourceApi('mg'), } const apiList: Record = {} diff --git a/musicSdk/kg/lyric.ts b/musicSdk/kg/lyric.ts index 1069d61..ba61cb8 100644 --- a/musicSdk/kg/lyric.ts +++ b/musicSdk/kg/lyric.ts @@ -82,7 +82,7 @@ export default { lyric: Buffer.from(body.content, 'base64').toString('utf-8'), tlyric: '', rlyric: '', - lxlyric: '', + yrc: '', } default: return Promise.reject(new Error(`未知歌词格式: ${body.fmt}`)) diff --git a/musicSdk/kw/lyric.ts b/musicSdk/kw/lyric.ts index b94ffc2..99278fa 100644 --- a/musicSdk/kw/lyric.ts +++ b/musicSdk/kw/lyric.ts @@ -224,10 +224,10 @@ export default { // return Promise.reject(new Error('Get lyric failed')) // } // if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '') - // lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric) + // lrcInfo.yrc = lrcTools.parse(lrcInfo.lyric) // // console.log(lrcInfo.lyric) // // console.log(lrcInfo.tlyric) - // // console.log(lrcInfo.lxlyric) + // // console.log(lrcInfo.yrc) // // console.log(JSON.stringify(lrcInfo)) // }) // }) @@ -255,9 +255,9 @@ export default { // console.log(lrcInfo) if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '') try { - lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric) + lrcInfo.yrc = lrcTools.parse(lrcInfo.lyric) } catch { - lrcInfo.lxlyric = '' + lrcInfo.yrc = '' } lrcInfo.lyric = lrcInfo.lyric.replace(lrcTools.rxps.wordTimeAll, '') if (!existTimeExp.test(lrcInfo.lyric)) return Promise.reject(new Error('Get lyric failed')) diff --git a/musicSdk/mg/lyric.ts b/musicSdk/mg/lyric.ts index 70638db..1d6c575 100644 --- a/musicSdk/mg/lyric.ts +++ b/musicSdk/mg/lyric.ts @@ -11,7 +11,7 @@ const mrcTools = { parseLyric(str) { str = str.replace(/\r/g, '') const lines = str.split('\n') - const lxlrcLines: string[] = [] + const yrcLines: string[] = [] const lrcLines: string[] = [] for (const line of lines) { @@ -40,11 +40,11 @@ const mrcTools = { }) const wordArr = words.split(this.rxps.wordTime) const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('') - lxlrcLines.push(`[${time}]${newWords}`) + yrcLines.push(`[${time}]${newWords}`) } return { lyric: lrcLines.join('\n'), - lxlyric: lxlrcLines.join('\n'), + yrc: yrcLines.join('\n'), } }, getText(url, tryNum = 0) { @@ -67,7 +67,7 @@ const mrcTools = { }) }, getLrc(url) { - return this.getText(url).then(text => ({ lxlyric: '', lyric: text })) + return this.getText(url).then(text => ({ yrc: '', lyric: text })) }, getTrc(url) { if (!url) return Promise.resolve('') diff --git a/musicSdk/mg/utils/mrc.ts b/musicSdk/mg/utils/mrc.ts index bbb4b58..6498f25 100644 --- a/musicSdk/mg/utils/mrc.ts +++ b/musicSdk/mg/utils/mrc.ts @@ -72,7 +72,7 @@ const toBigintArray = (data: string): bigint[] => { return jArr } -// https://github.com/lyswhut/lx-music-desktop/issues/445#issuecomment-1139338682 +// 基于社区通用的 mrc 逐字歌词解析思路 const MAX = 9223372036854775807n const MIN = -9223372036854775808n const toLong = (str: string | bigint): bigint => { diff --git a/musicSdk/options.ts b/musicSdk/options.ts index d98ac4c..5147d94 100644 --- a/musicSdk/options.ts +++ b/musicSdk/options.ts @@ -2,7 +2,7 @@ export const bHh = '624868746c' export const headers: Record = { - 'User-Agent': 'lx-music request', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', [bHh]: [bHh], } diff --git a/musicSdk/tx/lyric.ts b/musicSdk/tx/lyric.ts index 9920420..1367935 100644 --- a/musicSdk/tx/lyric.ts +++ b/musicSdk/tx/lyric.ts @@ -30,10 +30,10 @@ const parseTools = { parseLyric(lrc) { lrc = lrc.trim() lrc = lrc.replace(/\r/g, '') - if (!lrc) return { lyric: '', lxlyric: '' } + if (!lrc) return { lyric: '', yrc: '' } const lines = lrc.split('\n') - const lxlrcLines: string[] = [] + const yrcLines: string[] = [] const lrcLines: string[] = [] for (let line of lines) { @@ -41,11 +41,11 @@ const parseTools = { let result = this.rxps.lineTime.exec(line) if (!result) { if (line.startsWith('[offset')) { - lxlrcLines.push(line) + yrcLines.push(line) lrcLines.push(line) } if (this.rxps.lineTime2.test(line)) { - // lxlrcLines.push(line) + // yrcLines.push(line) lrcLines.push(line) } continue @@ -67,17 +67,17 @@ const parseTools = { }) const wordArr = words.split(this.rxps.wordTime) const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('') - lxlrcLines.push(`${startTimeStr}${newWords}`) + yrcLines.push(`${startTimeStr}${newWords}`) } return { lyric: lrcLines.join('\n'), - lxlyric: lxlrcLines.join('\n'), + yrc: yrcLines.join('\n'), } }, parseRlyric(lrc) { lrc = lrc.trim() lrc = lrc.replace(/\r/g, '') - if (!lrc) return { lyric: '', lxlyric: '' } + if (!lrc) return { lyric: '', yrc: '' } const lines = lrc.split('\n') const lrcLines: string[] = [] @@ -177,14 +177,14 @@ const parseTools = { lyric: '', tlyric: '', rlyric: '', - lxlyric: '', + yrc: '', } if (lrc) { - let { lyric, lxlyric } = this.parseLyric(this.removeTag(lrc)) + let { lyric, yrc } = this.parseLyric(this.removeTag(lrc)) info.lyric = lyric - info.lxlyric = lxlyric + info.yrc = yrc // console.log(lyric) - // console.log(lxlyric) + // console.log(yrc) } if (rlrc) info.rlyric = this.fixRlrcTimeTag(this.parseRlyric(this.removeTag(rlrc)), info.lyric) if (tlrc) info.tlyric = this.fixTlrcTimeTag(tlrc, info.lyric) diff --git a/musicSdk/types.ts b/musicSdk/types.ts index 10ff0a1..28664e7 100644 --- a/musicSdk/types.ts +++ b/musicSdk/types.ts @@ -82,7 +82,7 @@ export interface LyricInfo { lyric: string tlyric: string rlyric?: string - lxlyric?: string + yrc?: string } /** 搜索结果 */ diff --git a/musicSdk/wy/lyric.ts b/musicSdk/wy/lyric.ts index f5582c9..0ac29cb 100644 --- a/musicSdk/wy/lyric.ts +++ b/musicSdk/wy/lyric.ts @@ -8,7 +8,7 @@ import type { LyricInfo, HttpRequestObject } from '../types.ts' // str = str.replace(/\r/g, '') -// let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => { +// let yrc = str.replace(/\[((\d+),\d+)\].*/g, str => { // let result = str.match(/\[((\d+),\d+)\].*/) // let time = parseInt(result[2]) // let ms = time % 1000 @@ -31,8 +31,8 @@ import type { LyricInfo, HttpRequestObject } from '../types.ts' // return str // }) -// lxlyric = decodeName(lxlyric) -// return lxlyric.trim() +// yrc = decodeName(yrc) +// return yrc.trim() // } const eapiRequest = (url: string, data: object) => { @@ -72,7 +72,7 @@ const parseTools = { return `[${m}:${s}.${ms}]` }, parseLyric(lines) { - const lxlrcLines: string[] = [] + const yrcLines: string[] = [] const lrcLines: string[] = [] for (let line of lines) { @@ -80,7 +80,7 @@ const parseTools = { let result = this.rxps.lineTime.exec(line) if (!result) { if (line.startsWith('[offset')) { - lxlrcLines.push(line) + yrcLines.push(line) lrcLines.push(line) } continue @@ -103,11 +103,11 @@ const parseTools = { const wordArr = words.split(this.rxps.wordTime) wordArr.shift() const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('') - lxlrcLines.push(`${startTimeStr}${newWords}`) + yrcLines.push(`${startTimeStr}${newWords}`) } return { lyric: lrcLines.join('\n'), - lxlyric: lxlrcLines.join('\n'), + yrc: yrcLines.join('\n'), } }, parseHeaderInfo(str) { @@ -170,7 +170,7 @@ const parseTools = { lyric: '', tlyric: '', rlyric: '', - lxlyric: '', + yrc: '', } if (ylrc) { let lines = this.parseHeaderInfo(ylrc) @@ -196,7 +196,7 @@ const parseTools = { const timeRxp = /^\[[\d:.]+\]/ const headers = lines.filter(l => timeRxp.test(l)).join('\n') info.lyric = `${headers}\n${result.lyric}` - info.lxlyric = result.lxlyric + info.yrc = result.yrc return info } } @@ -242,13 +242,13 @@ const parseTools = { // lyric: body.lrc.lyric, // tlyric: body.tlyric?.lyric ?? '', // rlyric: body.romalrc?.lyric ?? '', -// // lxlyric: parseLyric(body.klyric.lyric), +// // yrc: parseLyric(body.klyric.lyric), // } // }) // return requestObj // } -// https://github.com/lyswhut/lx-music-mobile/issues/370 +// 参考社区通用的歌词时间标签修正思路 const fixTimeLabel = (lrc: string, tlrc?: string, romalrc?: string) => { if (lrc) { let newLrc = lrc.replace(/\[(\d{2}:\d{2}):(\d{2})]/g, '[$1.$2]') diff --git a/package.json b/package.json index 02386b6..f423e00 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "music-server", "version": "1.0.0", - "description": "多平台音乐聚合服务器(基于 lx-music 聚合 SDK)", + "description": "多平台音乐聚合服务器", "type": "module", "main": "src/index.js", "packageManager": "pnpm@12.4.1", diff --git a/src/apis/index.js b/src/apis/index.js index ce3e3ed..1e99386 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -1,13 +1,12 @@ // 各平台 URL 直连接口汇总 // 服务器环境固定使用内置直连接口(apiSource.value === 'internal') -// lx 为 lx-music 聚合源:isolated-vm 硬隔离执行原始脚本(签名方案已放弃,见 docs/lx-source-ivm.md) +// 外部聚合音源(sources/ 目录脚本)不走这里,由 musicSdk/api-source.ts 直接接入 source.js import kw from './kw.js'; import kg from './kg.js'; import tx from './tx.js'; import wy from './wy.js'; import mg from './mg.js'; import bd from './bd.js'; -import lx from './lx-source.js'; export default { kw: kw, kg: kg, @@ -15,5 +14,4 @@ export default { wy: wy, mg: mg, bd: bd, - lx: lx, }; diff --git a/src/apis/index.ts b/src/apis/index.ts index 09a8402..63d3a96 100644 --- a/src/apis/index.ts +++ b/src/apis/index.ts @@ -1,6 +1,6 @@ // 各平台 URL 直连接口汇总 // 服务器环境固定使用内置直连接口(apiSource.value === 'internal') -// lx 为 lx-music 聚合源:isolated-vm 硬隔离执行原始脚本(签名方案已放弃,见 docs/lx-source-ivm.md) +// 外部聚合音源(sources/ 目录脚本)不走这里,由 musicSdk/api-source.ts 直接接入 source.js import kw from './kw.js' import kg from './kg.js' @@ -8,7 +8,6 @@ import tx from './tx.js' import wy from './wy.js' import mg from './mg.js' import bd from './bd.js' -import lx from './lx-source.js' interface SongInfo { songmid?: string @@ -34,5 +33,4 @@ export default { wy: wy as ApiModule, mg: mg as ApiModule, bd: bd as ApiModule, - lx: lx as unknown as Record, } diff --git a/src/apis/lx-source.js b/src/apis/lx-source.js deleted file mode 100644 index 526da21..0000000 --- a/src/apis/lx-source.js +++ /dev/null @@ -1,52 +0,0 @@ -// lx 聚合源统一入口(isolated-vm 懒加载封装) -// 内部委托 lx-source-ivm.js 在硬隔离沙箱中执行原始脚本(签名方案已放弃:v5 的 -// to/si 为脚本内部哈希、服务端独立校验,直连复刻不可行,详见 docs/lx-source-ivm.md)。 -// 首次调用 getMusicUrl 时才加载并初始化隔离环境,后续复用同一实例。 -// 统一接口约定:lx.kw.getMusicUrl(songInfo, type) → { promise, cancelHttp },resolve → { type, url } - -import { createIvmLxSource } from './lx-source-ivm.js' - -export const SOURCES = ['kw', 'kg', 'tx', 'wy', 'mg'] - -let sharedPromise = null - -const getShared = () => { - if (!sharedPromise) { - // 配置可经环境变量调整,与项目其它适配器保持一致风格 - sharedPromise = createIvmLxSource({ - scriptPath: process.env.LX_SOURCE_SCRIPT_PATH || undefined, - log: process.env.LX_SOURCE_LOG !== '0', - allowAllHttp: process.env.LX_SOURCE_ALLOW_ALL_HTTP === '1', - }).catch(err => { - sharedPromise = null // 失败允许下次重试 - throw err - }) - } - return sharedPromise -} - -const apiFor = (source) => ({ - getMusicUrl(songInfo, type) { - const requestObj = { cancelHttp() {}, promise: null } - let inner = null - requestObj.cancelHttp = () => inner?.cancelHttp() - requestObj.promise = (async () => { - const lx = await getShared() - if (!lx[source]) throw new Error(`脚本不支持的平台: ${source}`) - inner = lx[source].getMusicUrl(songInfo, type) - return inner.promise - })() - return requestObj - }, -}) - -// 兼容导出:musicSdk/api-source.js 以 createApi(source) 方式接入 v5_* 聚合源 -export const createApi = apiFor - -const lxSource = {} -for (const s of SOURCES) lxSource[s] = apiFor(s) - -// 预热:提前触发脚本加载与初始化(可选调用) -lxSource.init = getShared - -export default lxSource diff --git a/src/apis/lx-source-ivm.js b/src/apis/source-ivm.js similarity index 86% rename from src/apis/lx-source-ivm.js rename to src/apis/source-ivm.js index 7dd7ba6..3c5a191 100644 --- a/src/apis/lx-source-ivm.js +++ b/src/apis/source-ivm.js @@ -1,39 +1,36 @@ -// lx 聚合源加载器(isolated-vm 硬隔离执行第三方脚本) +// 外部聚合音源加载器(isolated-vm 硬隔离执行第三方脚本) // -// 这是 lx 聚合源的唯一接入方式:在 isolated-vm 硬隔离沙箱中运行原始混淆脚本 -// (如 lx-music-source-v5.js),脚本的所有能力都经由本模块显式桥接的通道: +// 这是外部音源的唯一接入方式:在 isolated-vm 硬隔离沙箱中运行原始混淆脚本, +// 脚本的所有能力都经由本模块显式桥接的通道: // - http:默认只放行脚本头部 SERVER_SCRIPT_CONFIG.apiUrl 同源请求,其余一律阻止 // - crypto / zlib / buffer:Node 侧实现,日志只记操作与长度,不记录密钥内容 // - 定时器 / console:桥接到 Node,随 isolate 销毁回收 // 对外接口:getMusicUrl(songInfo, type) → { promise, cancelHttp },与其它平台适配器一致。 -// 脚本升版时只需替换脚本文件,无需改代码。 +// 脚本升版时只需替换 sources/ 目录下的脚本文件,无需改代码。 // // 安全模型: // - isolate 内存上限(memoryLimit),无 process/require/fs/网络直连能力 // - HTTP 出站默认拒绝(deny-all),白名单来自脚本自身声明的 apiUrl,可被 options 收紧或放宽 -// - 每个 lx API 调用(on/send/request/utils/dispatch)都有日志,请求含耗时与状态 +// - 每个音源 API 调用(on/send/request/utils/dispatch)都有日志,请求含耗时与状态 // // 依赖:isolated-vm(未安装时工厂函数抛出明确错误) // 用法: -// import { createIvmLxSource } from './lx-source-ivm.js' -// const lx = await createIvmLxSource() // 默认加载项目根目录的 lx-music-source-v5.js -// const lx = await createIvmLxSource({ scriptPath, http }) // http 可注入 mock 做离线测试 -// await lx.kw.getMusicUrl({ songmid: '123' }, '320k').promise -// await lx.destroy() +// import { createIvmSource } from './source-ivm.js' +// const source = await createIvmSource({ scriptPath }) // scriptPath 必填(sources/ 扫描逻辑见 source.js) +// const source = await createIvmSource({ scriptPath, http }) // http 可注入 mock 做离线测试 +// await source.kw.getMusicUrl({ songmid: '123' }, '320k').promise +// await source.destroy() // // 已知边界(相对桌面端 preload 的差异): -// - lx.request 回调的 resp 不含 raw/bytes(Buffer 无法跨隔离边界,实测脚本未使用) +// - source.request 回调的 resp 不含 raw/bytes(Buffer 无法跨隔离边界,实测脚本未使用) // - formData 选项不支持(httpFetch 不支持 multipart) // - updateAlert 事件仅记录到日志,不弹窗 import fs from 'node:fs' import crypto from 'node:crypto' import zlib from 'node:zlib' -import { fileURLToPath } from 'node:url' import { httpFetch } from '../common/request.js' -const DEFAULT_SCRIPT_PATH = fileURLToPath(new URL('../../lx-music-source-v5.js', import.meta.url)) - // 从脚本头部的 SERVER_SCRIPT_CONFIG 提取 apiUrl 声明,作为 HTTP 白名单缺省值。 // 兼容裸键(apiUrl:)与 JSON 风格("apiUrl":),并处理 \/ 转义 const extractAllowedOrigins = (script) => { @@ -51,7 +48,7 @@ const brief = (v, max = 72) => { return s.length > max ? `${s.slice(0, max)}…(${s.length})` : s } -// 从脚本头部注释解析 @name/@version 等(对齐 lx.currentScriptInfo) +// 从脚本头部注释解析 @name/@version 等(对齐音源脚本 currentScriptInfo) const parseScriptInfo = (script, scriptPath) => { const head = script.slice(0, 2000) const pick = (key) => { @@ -69,7 +66,7 @@ const parseScriptInfo = (script, scriptPath) => { } } -// 注入隔离环境的胶水层:提供 lx API、定时器、console、Bytes 包装。 +// 注入隔离环境的胶水层:提供音源脚本 API(globalThis.lx 为脚本契约名)、定时器、console、Bytes 包装。 // 与 Node 的数据交换一律走 JSON 字符串 / hex 字符串,避免跨隔离传输复杂对象。 const GLUE = ` 'use strict' @@ -188,10 +185,10 @@ function __fromAny(data, enc) { // ---- console / 定时器桥接 ---- globalThis.console = { - log: function () { __log('[lx] ' + Array.prototype.map.call(arguments, String).join(' ')) }, - info: function () { __log('[lx] ' + Array.prototype.map.call(arguments, String).join(' ')) }, - warn: function () { __log('[lx] ' + Array.prototype.map.call(arguments, String).join(' ')) }, - error: function () { __log('[lx] ' + Array.prototype.map.call(arguments, String).join(' ')) }, + log: function () { __log('[source] ' + Array.prototype.map.call(arguments, String).join(' ')) }, + info: function () { __log('[source] ' + Array.prototype.map.call(arguments, String).join(' ')) }, + warn: function () { __log('[source] ' + Array.prototype.map.call(arguments, String).join(' ')) }, + error: function () { __log('[source] ' + Array.prototype.map.call(arguments, String).join(' ')) }, debug: function () {}, } globalThis.setTimeout = function (fn, ms) { @@ -221,7 +218,9 @@ globalThis.__fireTimer = function (id) { try { t.fn.apply(undefined, t.args) } catch (e) { __log('timer error: ' + (e && e.message)) } } -// ---- lx API(对齐 lx-music-desktop preload.js)---- +// ---- 音源脚本 preload API ---- +// 注意:globalThis.lx 是第三方脚本依赖的 API 契约名(脚本内部调用 lx.on/source.request 等), +// 属于对外兼容接口而非命名残留,不可改动,否则脚本初始化直接失败。 var __EVENT_NAMES = { request: 'request', inited: 'inited', updateAlert: 'updateAlert' } var __handlers = {} globalThis.lx = { @@ -242,7 +241,7 @@ globalThis.lx = { var err = __send(String(name), JSON.stringify(data === undefined ? null : data)) return err ? Promise.reject(new Error(err)) : Promise.resolve() }, - request: function (url, options, callback) { return __lxRequest(url, options, callback, __currentDispatch) }, + request: function (url, options, callback) { return __sourceRequest(url, options, callback, __currentDispatch) }, utils: { crypto: { md5: function (str) { return __md5(str) }, @@ -256,11 +255,11 @@ globalThis.lx = { }, buffer: { from: function (data, enc) { - __log('lx.utils.buffer.from(' + (data instanceof __Bytes ? 'Bytes(' + data.length + ')' : typeof data + (data && data.length !== undefined ? '[' + data.length + ']' : '')) + (enc ? ',' + enc : '') + ')') + __log('source.utils.buffer.from(' + (data instanceof __Bytes ? 'Bytes(' + data.length + ')' : typeof data + (data && data.length !== undefined ? '[' + data.length + ']' : '')) + (enc ? ',' + enc : '') + ')') return __fromAny(data, enc) }, bufToString: function (buf, format) { - __log('lx.utils.buffer.bufToString(' + (buf && buf.length !== undefined ? buf.length + 'B' : typeof buf) + ',' + (format || 'utf8') + ')') + __log('source.utils.buffer.bufToString(' + (buf && buf.length !== undefined ? buf.length + 'B' : typeof buf) + ',' + (format || 'utf8') + ')') return buf.toString(format || 'utf8') }, }, @@ -272,7 +271,7 @@ globalThis.lx = { } // 隔离环境内不依赖 ivm 全局:回调统一走「隔离内注册表 + 自增 id」, // Node 侧通过 jail.get 拿到 __completeHttp 等入口反调 -function __lxRequest(url, options, callback, dispatchId) { +function __sourceRequest(url, options, callback, dispatchId) { options = options || {} var payload = { method: options.method || 'get', @@ -343,9 +342,9 @@ globalThis.__dispatch = function (dispatchId, payloadJson) { ` /** - * 创建隔离执行的 lx 聚合源 + * 创建隔离执行的外部音源 * @param {object} [options] - * @param {string} [options.scriptPath] 原始脚本路径,默认项目根目录 lx-music-source-v5.js + * @param {string} options.scriptPath 音源脚本路径(必填,sources/ 目录扫描逻辑见 source.js) * @param {Function} [options.http] http 实现,默认项目统一 httpFetch;测试可注入 mock * @param {number} [options.memoryLimit] 隔离内存上限 MB,默认 256 * @param {number} [options.initTimeout] 等待脚本上报 inited 的超时 ms,默认 10000 @@ -356,8 +355,8 @@ globalThis.__dispatch = function (dispatchId, payloadJson) { * @param {boolean} [options.allowAllHttp] 放行全部 http 请求(调试用,默认 false) * @returns {{ env: string, scriptInfo: object, inited: object|null, apis: object, allowedOrigins: string[], destroy: Function }} */ -export const createIvmLxSource = async({ - scriptPath = DEFAULT_SCRIPT_PATH, +export const createIvmSource = async({ + scriptPath, http: httpImpl = httpFetch, memoryLimit = 256, initTimeout = 10000, @@ -371,8 +370,9 @@ export const createIvmLxSource = async({ try { ivm = (await import('isolated-vm')).default } catch { - throw new Error('未安装 isolated-vm,无法启用隔离执行兜底:npm i isolated-vm') + throw new Error('未安装 isolated-vm,无法启用隔离执行音源:pnpm add isolated-vm') } + if (!scriptPath) throw new Error('缺少音源脚本路径 scriptPath(sources/ 目录扫描逻辑见 source.js)') const script = fs.readFileSync(scriptPath, 'utf8') const scriptInfo = parseScriptInfo(script, scriptPath) @@ -382,7 +382,7 @@ export const createIvmLxSource = async({ ? logOption : logOption === false ? () => {} - : (msg) => console.log('[lx-ivm]', msg) + : (msg) => console.log('[source-ivm]', msg) const scriptOrigins = extractAllowedOrigins(script) const origins = (allowedOrigins ?? scriptOrigins).map(o => String(o).replace(/\/+$/, '')) const httpAllowed = (urlStr) => { @@ -433,12 +433,12 @@ export const createIvmLxSource = async({ await jail.set('__log', new ivm.Callback((msg) => log(String(msg)))) await jail.set('__on', new ivm.Callback((name) => { state.onRegistered = name - log(`lx.on(${String(name)})`) + log(`source.on(${String(name)})`) })) await jail.set('__send', new ivm.Callback((name, dataJson) => { let data = null try { data = JSON.parse(dataJson) } catch {} - log(`lx.send(${String(name)}) ${brief(dataJson ?? '')}`) + log(`source.send(${String(name)}) ${brief(dataJson ?? '')}`) if (name === 'inited') { state.inited = data const waiters = state.initWaiters.splice(0) @@ -458,26 +458,26 @@ export const createIvmLxSource = async({ else p.reject(new Error(parsed.message || '获取失败')) })) await jail.set('__md5', new ivm.Callback((str) => { - log(`lx.utils.crypto.md5(${brief(str)})`) + log(`source.utils.crypto.md5(${brief(str)})`) return crypto.createHash('md5').update(String(str)).digest('hex') })) await jail.set('__randomBytesHex', new ivm.Callback((size) => { - log(`lx.utils.crypto.randomBytes(${size})`) + log(`source.utils.crypto.randomBytes(${size})`) return crypto.randomBytes(size).toString('hex') })) await jail.set('__aesEncryptHex', new ivm.Callback((hexBuf, mode, hexKey, hexIv) => { - log(`lx.utils.crypto.aesEncrypt(${Buffer.from(hexBuf, 'hex').length}B, ${mode})`) + log(`source.utils.crypto.aesEncrypt(${Buffer.from(hexBuf, 'hex').length}B, ${mode})`) const cipher = crypto.createCipheriv(mode, Buffer.from(hexKey, 'hex'), Buffer.from(hexIv, 'hex')) return Buffer.concat([cipher.update(Buffer.from(hexBuf, 'hex')), cipher.final()]).toString('hex') })) await jail.set('__rsaEncryptHex', new ivm.Callback((hexBuf, pemKey) => { - log(`lx.utils.crypto.rsaEncrypt(${Buffer.from(hexBuf, 'hex').length}B, key=${brief(pemKey, 24)})`) + log(`source.utils.crypto.rsaEncrypt(${Buffer.from(hexBuf, 'hex').length}B, key=${brief(pemKey, 24)})`) let buf = Buffer.from(hexBuf, 'hex') buf = Buffer.concat([Buffer.alloc(128 - buf.length), buf]) return crypto.publicEncrypt({ key: pemKey, padding: crypto.constants.RSA_NO_PADDING }, buf).toString('hex') })) await jail.set('__zlib', new ivm.Callback((opId, op, hex) => { - log(`lx.utils.zlib.${op}(${Buffer.from(hex, 'hex').length}B)`) + log(`source.utils.zlib.${op}(${Buffer.from(hex, 'hex').length}B)`) const buf = Buffer.from(hex, 'hex') zlib[op](buf, (err, out) => { safeApply(zlibDoneRef, [opId, err ? String(err.message) : null, err ? '' : out.toString('hex')]) @@ -488,11 +488,11 @@ export const createIvmLxSource = async({ const opts = JSON.parse(optionsJson) const method = String(opts.method || 'get').toUpperCase() if (!httpAllowed(url)) { - log(`✋ lx.request ${method} ${url} → 已阻止(不在白名单)`) + log(`✋ source.request ${method} ${url} → 已阻止(不在白名单)`) safeApply(completeHttpRef, [cbId, `http blocked by allowlist: ${url}`, null, null]) return reqId } - log(`→ lx.request ${method} ${brief(url, 120)}`) + log(`→ source.request ${method} ${brief(url, 120)}`) const startedAt = Date.now() const options = { method: opts.method || 'get', @@ -506,12 +506,12 @@ export const createIvmLxSource = async({ if (!dispatchHttp.has(dispatchId)) dispatchHttp.set(dispatchId, new Set()) dispatchHttp.get(dispatchId).add(requestObj) requestObj.promise.then(({ statusCode, statusMessage, headers, body }) => { - log(`← lx.request ${method} ${brief(url, 80)} → ${statusCode}(${Date.now() - startedAt}ms)`) + log(`← source.request ${method} ${brief(url, 80)} → ${statusCode}(${Date.now() - startedAt}ms)`) forgetHttp(reqId, dispatchId, requestObj) const resp = { statusCode, statusMessage: statusMessage || '', headers: headers || {}, body } return safeApply(completeHttpRef, [cbId, null, JSON.stringify(resp), JSON.stringify(body === undefined ? null : body)]) }).catch(err => { - log(`← lx.request ${method} ${brief(url, 80)} → 失败(${Date.now() - startedAt}ms)${err?.message ? ' ' + err.message : ''}`) + log(`← source.request ${method} ${brief(url, 80)} → 失败(${Date.now() - startedAt}ms)${err?.message ? ' ' + err.message : ''}`) forgetHttp(reqId, dispatchId, requestObj) return safeApply(completeHttpRef, [cbId, String(err && err.message || err), null, null]) }) @@ -639,4 +639,4 @@ export const createIvmLxSource = async({ } } -export default { createIvmLxSource } +export default { createIvmSource } diff --git a/src/apis/source.js b/src/apis/source.js new file mode 100644 index 0000000..453b931 --- /dev/null +++ b/src/apis/source.js @@ -0,0 +1,76 @@ +// 外部聚合音源统一入口(isolated-vm 懒加载封装) +// 内部委托 source-ivm.js 在硬隔离沙箱中执行音源脚本。 +// 首次调用 getMusicUrl 时才加载并初始化隔离环境,后续复用同一实例。 +// 统一接口约定:source.kw.getMusicUrl(songInfo, type) → { promise, cancelHttp },resolve → { type, url } +// +// 脚本来源(优先级): +// 1. 环境变量 SOURCE_SCRIPT_PATH 指定的脚本文件 +// 2. sources/ 目录下按文件名排序的第一个 .js(脚本文件不入库,见 .gitignore) + +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { createIvmSource } from './source-ivm.js' + +export const SOURCES = ['kw', 'kg', 'tx', 'wy', 'mg'] + +const SOURCES_DIR = fileURLToPath(new URL('../../sources/', import.meta.url)) + +const resolveScriptPath = () => { + const custom = process.env.SOURCE_SCRIPT_PATH + if (custom) return custom + if (!fs.existsSync(SOURCES_DIR)) { + throw new Error(`音源目录不存在: ${SOURCES_DIR}(将脚本放入 sources/ 目录,或用 SOURCE_SCRIPT_PATH 指定)`) + } + const files = fs.readdirSync(SOURCES_DIR).filter(f => f.toLowerCase().endsWith('.js')).sort() + if (!files.length) { + throw new Error('sources/ 目录下没有 .js 音源脚本(将脚本放入该目录,或用 SOURCE_SCRIPT_PATH 指定)') + } + if (files.length > 1) { + console.log(`[source] sources/ 下有 ${files.length} 个脚本,使用 ${files[0]}(可用 SOURCE_SCRIPT_PATH 指定)`) + } + return path.join(SOURCES_DIR, files[0]) +} + +let sharedPromise = null + +const getShared = () => { + if (!sharedPromise) { + // 配置可经环境变量调整,与项目其它适配器保持一致风格 + sharedPromise = createIvmSource({ + scriptPath: resolveScriptPath(), + log: process.env.SOURCE_LOG !== '0', + allowAllHttp: process.env.SOURCE_ALLOW_ALL_HTTP === '1', + }).catch(err => { + sharedPromise = null // 失败允许下次重试 + throw err + }) + } + return sharedPromise +} + +const apiFor = (source) => ({ + getMusicUrl(songInfo, type) { + const requestObj = { cancelHttp() {}, promise: null } + let inner = null + requestObj.cancelHttp = () => inner?.cancelHttp() + requestObj.promise = (async () => { + const api = await getShared() + if (!api[source]) throw new Error(`脚本不支持的平台: ${source}`) + inner = api[source].getMusicUrl(songInfo, type) + return inner.promise + })() + return requestObj + }, +}) + +// 兼容导出:musicSdk/api-source.js 以 createApi(source) 方式接入聚合音源 +export const createApi = apiFor + +const sourceApi = {} +for (const s of SOURCES) sourceApi[s] = apiFor(s) + +// 预热:提前触发脚本加载与初始化(可选调用) +sourceApi.init = getShared + +export default sourceApi diff --git a/src/common/ipcNames.js b/src/common/ipcNames.js index bb99d03..c988fd6 100644 --- a/src/common/ipcNames.js +++ b/src/common/ipcNames.js @@ -1,4 +1,4 @@ -// IPC 事件名(对齐原 lx-music src/common/ipcNames.ts) +// IPC 事件名 export const WIN_MAIN_RENDERER_EVENT_NAME = { handle_kw_decode_lyric: 'handle_kw_decode_lyric', diff --git a/src/common/lyricDecode.js b/src/common/lyricDecode.js index 27b4a3a..39f467d 100644 --- a/src/common/lyricDecode.js +++ b/src/common/lyricDecode.js @@ -1,6 +1,6 @@ -// 歌词解码(替代原 lx-music 主进程 IPC 处理器) +// 歌词解码(原主进程 IPC 处理器的纯 JS 实现) // - handle_kw_decode_lyric: 酷我歌词(inflate + yeelion XOR + gb18030) -// 算法来源: lx-music-desktop src/main/modules/winMain/rendererEvent/kw_decodeLyric.ts +// 算法来源: kw 桌面端歌词解码实现 // - handle_tx_decode_lyric: QQ音乐歌词(qrc 解码:hex → XOR 偏移 → deflate) // qrc 算法来源: 开源 qrc 解码器(ZhuYiQing/qrcDecode 纯 JS 版) diff --git a/src/common/lyricUtils/kg.js b/src/common/lyricUtils/kg.js index d75424f..fb3e279 100644 --- a/src/common/lyricUtils/kg.js +++ b/src/common/lyricUtils/kg.js @@ -1,9 +1,9 @@ -// 酷狗 krc 歌词解码(对齐原 lx-music src/common/utils/lyricUtils/kg.js) +// 酷狗 krc 歌词解码 import zlib from 'node:zlib' import { decodeName } from '../utils.js' -// https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784 +// 基于社区通用的 krc 解析思路 const enc_key = Buffer.from([0x40, 0x47, 0x61, 0x77, 0x5e, 0x32, 0x74, 0x47, 0x51, 0x36, 0x31, 0x2d, 0xce, 0xd2, 0x6e, 0x69], 'binary') const decodeLyric = str => new Promise((resolve, reject) => { @@ -42,7 +42,7 @@ const parseLyric = str => { } } let i = 0 - let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => { + let yrc = str.replace(/\[((\d+),\d+)\].*/g, str => { let result = str.match(/\[((\d+),\d+)\].*/) let time = parseInt(result[2]) let ms = time % 1000 @@ -58,16 +58,16 @@ const parseLyric = str => { }) rlyric = rlyric ? rlyric.join('\n') : '' tlyric = tlyric ? tlyric.join('\n') : '' - lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>') - lxlyric = decodeName(lxlyric) - lyric = lxlyric.replace(/<\d+,\d+>/g, '') + yrc = yrc.replace(/<(\d+,\d+),\d+>/g, '<$1>') + yrc = decodeName(yrc) + lyric = yrc.replace(/<\d+,\d+>/g, '') rlyric = decodeName(rlyric) tlyric = decodeName(tlyric) return { lyric, tlyric, rlyric, - lxlyric, + yrc, } } diff --git a/src/common/message.js b/src/common/message.js index 3fb76d5..e3a59a1 100644 --- a/src/common/message.js +++ b/src/common/message.js @@ -1,4 +1,4 @@ -// 请求提示消息(替代原 lx-music 的 message.js) +// 请求提示消息 export const requestMsg = { cancelRequest: '请求已取消', diff --git a/src/common/rendererIpc.js b/src/common/rendererIpc.js index fa311a3..7172012 100644 --- a/src/common/rendererIpc.js +++ b/src/common/rendererIpc.js @@ -1,4 +1,4 @@ -// 替代原 lx-music 的 @common/rendererIpc(Electron ipcRenderer.invoke) +// 渲染器 IPC 桥(替代 Electron ipcRenderer.invoke) // 服务器环境下 IPC 调用直接映射到本地实现: // handle_kw_decode_lyric → 酷我歌词解码(见 lyricDecode.js) // handle_tx_decode_lyric → QQ音乐歌词解码(见 lyricDecode.js) diff --git a/src/common/request.js b/src/common/request.js index cb46f0c..b44e31a 100644 --- a/src/common/request.js +++ b/src/common/request.js @@ -1,4 +1,4 @@ -// 网络请求模块(替代原 lx-music 渲染器 src/renderer/utils/request.js) +// 网络请求模块 // 接口与原版保持一致: // httpFetch(url, { method, headers, body, form, timeout, lookup, family }) // → { promise, cancelHttp } diff --git a/src/common/utils.js b/src/common/utils.js index 364a252..2ae0699 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -1,4 +1,4 @@ -// 通用工具函数(对齐 lx-music 原版 src/common/utils/common.ts 与 src/renderer/utils/index.ts) +// 通用工具函数 import crypto from 'node:crypto' diff --git a/src/index.js b/src/index.js index c058ed1..ac178a6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ // SDK 根模块 —— musicSdk 内 `import xxx from '../../index.js'` 的解析目标 -// 聚合导出通用工具函数(对齐原 lx-music src/renderer/utils/index.ts 的导出) +// 聚合导出通用工具函数 export { sizeFormate, diff --git a/src/openapi/components.js b/src/openapi/components.js index 937fd8f..1a19cb2 100644 --- a/src/openapi/components.js +++ b/src/openapi/components.js @@ -78,7 +78,7 @@ export const COMPONENT_SCHEMAS = { lyric: { type: 'string', description: '原文歌词(LRC)' }, tlyric: { type: 'string', description: '翻译歌词' }, rlyric: { type: 'string', description: '罗马音歌词(部分平台)' }, - lxlyric: { type: 'string', description: '逐字歌词(部分平台)' }, + yrc: { type: 'string', description: '逐字歌词(部分平台)' }, }, additionalProperties: true, required: ['lyric', 'tlyric'], diff --git a/src/renderer/store.js b/src/renderer/store.js index ae0154b..6a7efe0 100644 --- a/src/renderer/store.js +++ b/src/renderer/store.js @@ -1,9 +1,11 @@ -// 替代原 lx-music 渲染器的 @renderer/store +// 替代原渲染器的 @renderer/store 模块(保留导入别名所需的接口形态) // 服务器环境默认使用内置直连接口(apiSource.value === 'internal'), -// 设置 LX_SOURCE_ENABLED=true 可切换至 lx-music-source-v5 聚合源。 +// 设置 SOURCE_ENABLED 环境变量(任意非 0/false 值)即启用外部聚合音源。 + +const enabled = process.env.SOURCE_ENABLED export const apiSource = { - value: process.env.LX_SOURCE_ENABLED === 'true' ? 'v5' : 'internal', + value: enabled && enabled !== '0' && enabled !== 'false' ? 'source' : 'internal', } export const userApi = { diff --git a/src/renderer/utils.js b/src/renderer/utils.js index 887aa35..78fb967 100644 --- a/src/renderer/utils.js +++ b/src/renderer/utils.js @@ -1,3 +1,3 @@ -// 替代原 lx-music 渲染器的 @renderer/utils(SDK 内 `@renderer/utils` 别名目标) +// 渲染器工具模块(SDK 内 `@renderer/utils` 别名目标) export { decodeName, formatPlayTime, sizeFormate, dateFormat, dateFormat2, formatPlayCount } from '../common/utils.js' diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..41c10a7 --- /dev/null +++ b/start.bat @@ -0,0 +1,2 @@ +set LX_SOURCE_ENABLED=true +pnpm run start \ No newline at end of file