From bb04cec4508ef2bbdab18d4d5ec964d57ebc0a26 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 15 Mar 2026 18:05:21 +0800 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=BF=85?= =?UTF-8?q?=E9=9B=B7=E7=BD=91=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/updateRecord.md | 3 +- drpy-node-bundle/libs/localDsCore.bundled.js | 687 ++++++++++++++- ...76\347\275\256\344\270\255\345\277\203.js" | 11 +- libs/drpyS.js | 4 +- libs/dsGlobal.js | 4 + spider/js/push_agent.js | 42 +- ...76\347\275\256\344\270\255\345\277\203.js" | 1 + utils/pan/xun.js | 805 ++++++++++++++++++ utils/pans.js | 3 +- 9 files changed, 1542 insertions(+), 18 deletions(-) create mode 100644 utils/pan/xun.js diff --git a/docs/updateRecord.md b/docs/updateRecord.md index 8935d995..227d570e 100644 --- a/docs/updateRecord.md +++ b/docs/updateRecord.md @@ -7,8 +7,7 @@ 1. 彻底解决ds代码问题导致的阻止程序退出 2. 完善全局require处理,支持cjs文件里引入 `axios` 等对象不报错 3. 彻底优化本地bundle包 - -注意: 此版本为纯框架更新,不涉及源,只关注源的用户可以不用升级 +4. 更新迅雷网盘解析支持,设置中心推送里可以推送播放迅雷链接 ### 20260314 diff --git a/drpy-node-bundle/libs/localDsCore.bundled.js b/drpy-node-bundle/libs/localDsCore.bundled.js index e3706a67..e5540372 100644 --- a/drpy-node-bundle/libs/localDsCore.bundled.js +++ b/drpy-node-bundle/libs/localDsCore.bundled.js @@ -116425,11 +116425,688 @@ var YunDrive = class { }; const Yun$1 = new YunDrive(); //#endregion +//#region ../utils/pan/xun.js +var XunDriver = class { + constructor() { + this.regex = /https:\/\/pan.xunlei.com\/s\/(.*)\?.*?pwd=([^&]+)/; + this.headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Connection": "keep-alive", + "Accept": "application/json, text/plain, */*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6" + }; + this.api = "https://xluser-ssl.xunlei.com/"; + this.xun_api = "https://api-pan.xunlei.com/"; + this.captcha_token = ""; + this.parent_id = ""; + this.share_id = ""; + this.pass_code = ""; + this.pass_code_token = ""; + this.filename = "ds"; + this.fileId = ""; + this.vodID = ""; + this.client_id = "XW5SkOhLDjnOZP7J"; + this.x_client_id = "XW-G4v1H72tgfJym"; + this.device_id = "652c6bb3cacdb4b80e852dfc3cb3cca4"; + this.i = 0; + } + async init() { + if (this.auth) if (JSON.parse(import_crypto_js.default.enc.Base64.parse(this.auth.split(".")[1]).toString(import_crypto_js.default.enc.Utf8)).exp > Math.floor(Date.now() / 1e3)) console.log("登录成功"); + else console.log("登录过期,重新登录"); + else await this.getAuth(); + } + get username() { + return ENV.get("xun_username"); + } + get password() { + return ENV.get("xun_password"); + } + get auth() { + return ENV.get("xun_auth"); + } + get app_auth() { + return ENV.get("xun_app_auth"); + } + get refresh_token() { + return ENV.get("xun_refresh_token"); + } + get userId() { + return ENV.get("xun_user_id"); + } + /** + * 延时函数 + * + * 创建一个Promise,在指定毫秒数后resolve,用于控制请求频率。 + * + * @param {number} ms - 延时毫秒数 + * @returns {Promise} 延时Promise + * + * @example + * await delay(1000); // 延时1秒 + */ + delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + async execUrl(url) { + this.link = url; + const matches = this.regex.exec(url); + if (matches && matches[1]) { + this.share_id = matches[1]; + this.pass_code = matches[2] || ""; + } + } + async login() { + let data = JSON.stringify({ + "protocolVersion": "301", + "sequenceNo": "1000001", + "platformVersion": "10", + "isCompressed": "0", + "appid": "40", + "clientVersion": "8.03.0.9067", + "peerID": "c9b076a446517969dff638cd37fa9ff1", + "appName": "ANDROID-com.xunlei.downloadprovider", + "sdkVersion": "231500", + "devicesign": "div101.b71a923eb0e2239842599a3c016b4098612f6cf6d6e9fd1925845ec59285716c", + "netWorkType": "2G", + "providerName": "NONE", + "deviceModel": "22021211RC", + "deviceName": "Xiaomi_22021211Rc", + "OSVersion": "12", + "creditkey": "", + "hl": "zh-CN", + "userName": this.username, + "passWord": this.password, + "verifyKey": "", + "verifyCode": "", + "isMd5Pwd": "0" + }); + let config = { + method: "POST", + url: `${this.api}xluser.core.login/v3/login`, + headers: { "Content-Type": "application/json" }, + data + }; + let login_data = await axios$1.request(config); + if (login_data.status === 200) { + console.log("登录成功"); + return login_data.data.sessionID; + } + } + async getSignCaptcha() { + let data = JSON.stringify({ + "client_id": this.x_client_id, + "action": "POST:/v1/auth/signin", + "device_id": this.device_id, + "captcha_token": "", + "meta": { "phone_number": `+86 ${this.username}` } + }); + let config = { + method: "POST", + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json" + }, + data + }; + let signin = await axios$1.request(config); + if (signin.status === 200) return signin.data.captcha_token; + } + async getAuth() { + let need_auth = 1; + if (this.auth) { + if (JSON.parse(import_crypto_js.default.enc.Base64.parse(this.auth.split(".")[1]).toString(import_crypto_js.default.enc.Utf8)).exp > Math.floor(Date.now() / 1e3)) { + console.log("登录成功"); + need_auth = 0; + } + } + if (need_auth) { + console.log("登录过期,重新登录"); + let captcha_token = await this.getSignCaptcha(); + let data = JSON.stringify({ + "username": `+86 ${this.username}`, + "password": this.password, + "client_id": this.x_client_id + }); + let config = { + method: "POST", + url: `${this.api}v1/auth/signin`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json", + "accept-language": "zh-cn", + "x-captcha-token": captcha_token, + "x-client-id": this.x_client_id, + "x-device-id": this.device_id + }, + data + }; + let auth_data = await axios$1.request(config); + if (auth_data.status === 200) { + ENV.set("xun_auth", auth_data.data.token_type + " " + auth_data.data.access_token); + ENV.set("xun_user_id", auth_data.data.user_id); + } + await this.safecaptcha(); + } + } + async safecaptcha() { + let data = JSON.stringify({ + "client_id": this.x_client_id, + "action": "get:/drive/v1/privilege/USER_SECURITY_TOKEN", + "device_id": this.device_id, + "captcha_token": "", + "meta": { + "username": "", + "phone_number": "", + "email": "", + "package_name": "pan.xunlei.com", + "client_version": "1.92.9", + "captcha_sign": "1.98cda33124387df2c03dea12799af2af", + "timestamp": "1757397551603", + "user_id": this.userId + } + }); + let config = { + method: "POST", + url: "https://xluser-ssl.xunlei.com/v1/shield/captcha/init", + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "content-type": "text/plain;charset=UTF-8" + }, + data + }; + let captcha_token = (await axios$1.request(config)).data.captcha_token; + let safe_data = (await axios$1({ + method: "GET", + url: "https://api-pan.xunlei.com/drive/v1/privilege/USER_SECURITY_TOKEN", + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "content-type": "application/json", + "x-captcha-token": captcha_token, + "x-client-id": this.x_client_id, + "x-device-id": this.device_id + } + })).data; + } + async getCaptcha_token(path, mth) { + let action = `${mth}:${path}`; + let data = JSON.stringify({ + "client_id": "Xqp0kJBXWhwaTpB6", + "action": action, + "device_id": "1bf91caf40093318e8040916eb7ad16a", + "captcha_token": "", + "meta": { + "username": "", + "phone_number": "", + "email": "", + "package_name": "pan.xunlei.com", + "client_version": "1.92.9", + "captcha_sign": "1.cbc20fd633c54023baab5b816228bf90", + "timestamp": "1757383155459", + "user_id": this.userId + } + }); + let config = { + method: "POST", + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json" + }, + data + }; + let captcha_data = await axios$1.request(config); + if (captcha_data.status === 200) return captcha_data.data.captcha_token; + } + async getAppCaptcha_token(path, mth) { + let action = `${mth}:${path}`; + let data = JSON.stringify({ + "client_id": "XW-G4v1H72tgfJym", + "action": action, + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { + "package_name": "ThunderPanPlugin", + "client_version": "3.1.5", + "captcha_sign": "1.ee2cef5f061a7cdf4374df81b370d2ec", + "timestamp": "1773293019719", + "user_id": this.userId + } + }); + let config = { + method: "POST", + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json" + }, + data + }; + let captcha_data = await axios$1.request(config); + if (captcha_data.status === 200) return captcha_data.data.captcha_token; + } + sign() { + let x = [ + "QG3/GhopO+5+T", + "1Sv94+ANND3lDmmw", + "q2eTxRva8b3B5d", + "m2", + "VIc5CZRBMU71ENfbOh0+RgWIuzLy", + "66M8Wpw6nkBEekOtL6e", + "N0rucK7S8W/vrRkfPto5urIJJS8dVY0S", + "oLAR7pdUVUAp9xcuHWzrU057aUhdCJrt", + "6lxcykBSsfI//GR9", + "r50cz+1I4gbU/fk8", + "tdwzrTc4SNFC4marNGTgf05flC85A", + "qvNVUDFjfsOMqvdi2gB8gCvtaJAIqxXs" + ]; + const c = { + ClientID: "Xqp0kJBXWhwaTpB6", + ClientVersion: "1.92.9", + PackageName: "pan.xunlei.com", + DeviceID: "1bf91caf40093318e8040916eb7ad16a" + }; + const timestamp = Date.now(); + let w = c.ClientID + c.ClientVersion + c.PackageName + c.DeviceID + "1757338961011"; + for (let i = 0; i < x.length; i++) w = import_crypto_js.default.MD5(w + x[i]).toString(); + return "1." + w; + } + async getShareList() { + let captcha_data = await this.getCaptcha_token("/drive/v1/share", "get"); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/share?share_id=${this.share_id}&pass_code=${this.pass_code}&limit=100&page_token=&thumbnail_size=SIZE_SMALL`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": "", + "x-captcha-token": captcha_data, + "x-client-id": "Xqp0kJBXWhwaTpB6", + "x-device-id": "1bf91caf40093318e8040916eb7ad16a" + } + }; + let sharelist = await axios$1.request(config); + if (sharelist.status === 200) { + let file = {}; + let dirs = []; + let videos = []; + this.pass_code_token = sharelist.data.pass_code_token; + sharelist.data.files.map((it) => { + if (it.mime_type === "") dirs.push(it.id); + else { + let text = /[#|'"\[\]&<>]/g; + let name = text.test(it.name) ? it.name.replace(text, "") : it.name; + videos.push({ + name, + fileId: it.id, + share_id: this.share_id, + parent_id: it.parent_id, + pass_code_token: encodeURIComponent(this.pass_code_token) + }); + } + }); + if (!(sharelist.data.title in file) && sharelist.data.title !== void 0) file[sharelist.data.title] = []; + if (videos.length > 0 && sharelist.data.title !== void 0) file[sharelist.data.title] = [...videos]; + let result = await Promise.all(dirs.map(async (id) => this.getShareDetail(id))); + result = result.filter((item) => item !== void 0 && item !== null).flat(); + if (result.length >= 0) file[sharelist.data.title].push(...result); + return file; + } + } + async getShareDetail(id) { + let captcha_data = await this.getCaptcha_token("/drive/v1/share", "get"); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/share/detail?share_id=${this.share_id}&parent_id=${id}&pass_code_token=${encodeURIComponent(this.pass_code_token)}&limit=100&page_token=&thumbnail_size=SIZE_SMALL`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": "", + "content-type": "application/json", + "x-captcha-token": captcha_data, + "x-client-id": "Xqp0kJBXWhwaTpB6", + "x-device-id": "1bf91caf40093318e8040916eb7ad16a" + } + }; + let detail_data = await axios$1.request(config); + if (detail_data.status === 200) { + let dirs = []; + let videos = []; + detail_data.data.files.map((it) => { + if (it.mime_type === "") dirs.push(it.id); + else { + let text = /[#|'"\[\]&<>]/g; + let name = text.test(it.name) ? it.name.replace(text, "") : it.name; + videos.push({ + name, + fileId: it.id, + share_id: this.share_id, + parent_id: it.parent_id, + pass_code_token: encodeURIComponent(this.pass_code_token) + }); + } + }); + let result = await Promise.all(dirs.map(async (id) => this.getShareDetail(id))); + result = result.filter((item) => item !== void 0 && item !== null); + return [...videos, ...result.flat()]; + } + } + async getShareUrl(fileId, share_id, pass_code_token) { + let captcha_data = await this.getCaptcha_token("/drive/v1/share", "get"); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/share/file_info?pass_code_token=${encodeURIComponent(pass_code_token)}&space=&file_id=${fileId}&share_id=${share_id}&&pass_code=${this.pass_code}`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": "", + "cache-control": "no-cache", + "content-type": "application/json", + "x-captcha-token": captcha_data, + "x-client-id": "Xqp0kJBXWhwaTpB6", + "x-device-id": "1bf91caf40093318e8040916eb7ad16a" + } + }; + let url_list = await axios$1.request(config); + if (url_list.status === 200) { + let urls = []; + url_list.data.file_info.medias.map((it) => { + if (it.link !== null) { + urls.push(it.media_name, it.link.url + "#isVideo=true##fastPlayMode##threads=20#"); + urls.push("猫画" + it.media_name, `http://127.0.0.1:5575/proxy?thread=${ENV.get("thread") || 6}&chunkSize=256&url=` + encodeURIComponent(it.link.url)); + } + }); + return urls; + } + } + async getShareData(url) { + if (url.startsWith("https://")) { + await this.execUrl(url); + return await this.getShareList(); + } + } + async saveResult(fileId, share_id, pass_code_token) { + let captcha_data = await this.getAppCaptcha_token("drive/v1/files", "get"); + let data = JSON.stringify({ + "parent_id": this.fileId, + "share_id": share_id, + "pass_code_token": decodeURIComponent(pass_code_token), + "ancestor_ids": [], + "file_ids": [fileId], + "specify_parent_id": true + }); + let config = { + method: "POST", + url: `${this.xun_api}drive/v1/share/restore`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "x-captcha-token": captcha_data, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data + }; + let file_data = await axios$1.request(config).catch((e) => e.response); + if (file_data.status === 200) console.log("转存文件成功"); + if (file_data.status === 404) { + this.i++; + if (this.i < 3) await this.saveResult(fileId, share_id, pass_code_token); + else { + this.i = 0; + console.log("转存失败:" + file_data.data); + } + } + } + async saveFile(fileId, share_id, pass_code_token) { + await this.createFile(); + if (this.fileId !== "" && this.fileId !== void 0) { + await this.delay(5e3); + await this.saveResult(fileId, share_id, pass_code_token); + } + } + async getFile() { + if (this.auth === void 0 || this.auth === "") await this.getAuth(); + let captcha_data = await this.getAppCaptcha_token("drive/v1/files", "get"); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/files?parent_id=&filters=%7B%22phase%22%3A%7B%22eq%22%3A%22PHASE_TYPE_COMPLETE%22%7D%2C%22trashed%22%3A%7B%22eq%22%3Afalse%7D%7D&with_audit=true&thumbnail_size=SIZE_SMALL&limit=50`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "content-type": "application/json", + "x-captcha-token": captcha_data, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + } + }; + let file_data = await axios$1.request(config); + if (file_data.status === 200) file_data.data.files.map((it) => { + if (it.name === this.filename) this.fileId = it.id; + }); + } + async createFile() { + await this.getFile(); + let captcha_data = await this.getAppCaptcha_token("drive/v1/files", "get"); + let data = JSON.stringify({ + "parent_id": "", + "name": this.filename, + "kind": "drive#folder", + "space": "" + }); + let config = { + method: "POST", + url: `${this.xun_api}drive/v1/files`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "x-captcha-token": captcha_data, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data + }; + let file_data = await axios$1.request(config).catch((e) => e.response); + if (file_data.status === 200) this.fileId = file_data.data.file.id; + } + async getVodId() { + let captcha_data = await this.getAppCaptcha_token("drive/v1/files", "get"); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/files?parent_id=${this.fileId}&filters=%7B%22phase%22%3A%7B%22eq%22%3A%22PHASE_TYPE_COMPLETE%22%7D%2C%22trashed%22%3A%7B%22eq%22%3Afalse%7D%7D&with_audit=true&thumbnail_size=SIZE_SMALL&limit=50`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "content-type": "application/json", + "x-captcha-token": captcha_data, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + } + }; + let file_data = await axios$1.request(config); + if (file_data.status === 200) file_data.data.files.map((it) => { + this.vodID = it.id; + }); + return this.vodID; + } + async deleteFile() { + await this.getFile(); + if (this.fileId !== "" && this.fileId !== void 0) { + let captcha_data = await this.getAppCaptcha_token("drive/v1/files", "get"); + let data = JSON.stringify({ + "ids": [this.fileId], + "space": "" + }); + let config = { + method: "POST", + url: `${this.xun_api}drive/v1/files:batchDelete`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json", + "accept-language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "authorization": this.auth, + "x-captcha-token": captcha_data, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data + }; + let delete_data = await axios$1.request(config).catch((e) => e.response); + if (delete_data.status === 200) console.log("删除文件成功"); + else if (delete_data.status === 404) console.log("文件未找到,删除失败"); + } + } + async getDownload_CAPTCHA_TOKEN() { + let data = { + "client_id": "XW-G4v1H72tgfJym", + "action": "GET:CAPTCHA_TOKEN", + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { + "package_name": "ThunderPanPlugin", + "client_version": "3.1.1", + "captcha_sign": "1.0eada0deeeaac52a6376f2e167fa9f29", + "timestamp": "1757378771289", + "user_id": this.userId + } + }; + let config = { + method: "POST", + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Accept-Encoding": "gzip, deflate, br", + "Content-Type": "application/json" + }, + data + }; + let captcha_data = await axios$1.request(config); + if (captcha_data.status === 200) return captcha_data.data.captcha_token; + } + async getAppCaptcha() { + let data = JSON.stringify({ + "client_id": "Xqp0kJBXWhwaTpB6", + "action": "POST:/v1/auth/signin", + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { "phone_number": `+86 ${this.username}` } + }); + let config = { + method: "POST", + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Content-Type": "application/json" + }, + data + }; + let signin = await axios$1.request(config); + if (signin.status === 200) return signin.data.captcha_token; + } + async AppAuth() { + if (this.app_auth) if (JSON.parse(import_crypto_js.default.enc.Base64.parse(this.app_auth.split(".")[1]).toString(import_crypto_js.default.enc.Utf8)).exp > Math.floor(Date.now() / 1e3)) console.log("登录成功"); + else { + console.log("登录过期,重新登录"); + let captcha_token = await this.getAppCaptcha(); + let data = JSON.stringify({ + "username": `+86 ${this.username}`, + "password": this.password, + "client_id": "XW-G4v1H72tgfJym" + }); + let config = { + method: "POST", + url: `${this.api}v1/auth/signin`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Content-Type": "application/json", + "x-captcha-token": captcha_token, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data + }; + let auth_data = await axios$1.request(config); + if (auth_data.status === 200) { + ENV.set("xun_refresh_token", auth_data.data.refresh_token); + ENV.set("xun_app_auth", auth_data.data.token_type + " " + auth_data.data.access_token); + } + } + } + async getDownload_auth() { + await this.AppAuth(); + let data = JSON.stringify({ + "client_id": "XW-G4v1H72tgfJym", + "client_secret": "Qbaferw2knfQKqxa25EYJGtZ2_6755CMwzXBN3ctW54", + "grant_type": "refresh_token", + "refresh_token": this.refresh_token + }); + let dwon_data = await axios$1({ + method: "POST", + url: "https://xluser-ssl.xunlei.com/v1/auth/token", + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Content-Type": "application/json", + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data + }).catch((e) => e.response); + if (dwon_data.status === 200) { + ENV.set("xun_retoken", dwon_data.data.refresh_token); + ENV.set("xun_App_auth", dwon_data.data.token_type + " " + dwon_data.data.access_token); + } + } + async getDownloadUrl(fileId, share_id, pass_code_token) { + try { + await this.getDownload_auth(); + await this.deleteFile(); + await this.delay(1e3); + await this.saveFile(fileId, share_id, pass_code_token); + let vodID = await this.getVodId(); + let x_captcha_token = await this.getDownload_CAPTCHA_TOKEN(); + let config = { + method: "GET", + url: `${this.xun_api}drive/v1/files/${vodID}?space=&with[0]=public_share_tag&usage=FETCH`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "x-captcha-token": x_captcha_token, + "authorization": this.app_auth, + "content-type": "application/json", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + } + }; + let file_data = await axios$1.request(config); + if (file_data.status === 200) { + console.log("下载地址:" + file_data.data.links["application/octet-stream"].url || file_data.data["web_content_link"]); + return file_data.data.links["application/octet-stream"].url || file_data.data["web_content_link"]; + } + } catch (e) { + console.log(e); + return ""; + } + } +}; +const Xun$1 = new XunDriver(); +//#endregion //#region ../utils/pans.js /** * 网盘服务模块集合 * 统一导入和导出各种网盘服务提供商的实现 -* +* * 支持的网盘服务: * - Ali: 阿里云盘服务 * - Baidu: 百度网盘服务 @@ -116439,7 +117116,7 @@ const Yun$1 = new YunDrive(); * - Quark: 夸克网盘服务 * - UC: UC网盘服务 * - Yun: 115网盘服务 -* +* * @example * import pans from './pans.js'; * const aliPan = new pans.Ali(config); @@ -116453,7 +117130,8 @@ var pans_default = { Pan: Pan$1, Quark: Quark$1, UC: UC$1, - Yun: Yun$1 + Yun: Yun$1, + Xun: Xun$1 }; //#endregion //#region ../node_modules/cheerio/dist/esm/options.js @@ -392699,7 +393377,7 @@ globalThis._fetch = fetch; globalThis.JsonBig = (0, import_json_bigint.default)({ storeAsString: true }); globalThis.require = rootRequire; initializeGlobalDollar(); -const { Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun } = pans_default; +const { Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun, Xun } = pans_default; const { sleep, sleepSync, getNowTime, computeHash, deepCopy, urljoin: urljoin$1, urljoin2, joinUrl, keysToLowerCase, naturalSort, $js, createBasicAuthHeaders, get_size } = utils_exports$1; const CACHE_OPTIONS = { max: 100, @@ -392899,6 +393577,7 @@ const STATIC_LIBS_SANDBOX = { Cloud, Yun, Pan, + Xun, createWebDAVClient, createFTPClient, require: rootRequire, diff --git "a/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" "b/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" index 1db8bd51..d082198e 100644 --- "a/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" +++ "b/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" @@ -65,6 +65,7 @@ let quick_data = { 阿里: 'https://www.alipan.com/s/vgXMcowK8pQ', 天翼: 'https://cloud.189.cn/web/share?code=INJbU3NbqyUj', 百度: 'https://pan.baidu.com/s/1L0UIv4p0X0QrbbKErJuc_w?pwd=2pwj', + 迅雷: 'https://pan.xunlei.com/s/VOkBwLBNoXN8eO9WrcVbXdTcA1?pwd=8tvj#', 移动1: 'https://yun.139.com/shareweb/#/w/i/0i5CLQ7BpV7Ai', 移动2: 'https://caiyun.139.com/m/i?2jexC1gcjeN7q', 移动3: 'https://yun.139.com/shareweb/#/w/i/2i2MoE9ZHn9p1', @@ -481,8 +482,7 @@ var rule = { vod_name: '测试代理流', vod_play_from: 'drpyS本地流代理', // vod_play_url: '测试播放流$' + getProxyUrl().split('?')[0] + media_url + '#不代理直接播$' + media_url + '#8k播放$' + m3u8_url, - // vod_play_url: '测试播放流$' + getProxyUrl().split('?')[0] + media_url + '#不代理直接播$' + media_url - vod_play_url: '测试播放流$' + getProxyUrl() + '&url=' + media_url + '#不代理直接播$' + media_url + vod_play_url: '测试播放流$' + getProxyUrl().split('?')[0] + media_url + '#不代理直接播$' + media_url } } }, @@ -491,16 +491,15 @@ var rule = { let {input} = this; return {parse: 0, url: input} }, - proxy_rule: async function (params) { + proxy_rule: async function () { let {input, proxyPath} = this; - const url = proxyPath || params.url; + const url = proxyPath; log('start proxy:', url); try { const headers = { 'user-agent': PC_UA, } - // return [200, null, url, headers, 2] - return [302, null, '', {location: url}] + return [200, null, url, headers, 2] } catch (e) { log('proxy error:', e.message); return [500, 'text/plain', e.message] diff --git a/libs/drpyS.js b/libs/drpyS.js index 5701bb1b..dee69db4 100644 --- a/libs/drpyS.js +++ b/libs/drpyS.js @@ -63,7 +63,7 @@ globalThis.JsonBig = JSONbig({storeAsString: true}); globalThis.require = rootRequire; initializeGlobalDollar(); -const {Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun} = PanS; +const {Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun, Xun} = PanS; const { sleep, sleepSync, getNowTime, computeHash, deepCopy, urljoin, urljoin2, joinUrl, keysToLowerCase, naturalSort, $js, @@ -178,7 +178,7 @@ const STATIC_LIBS_SANDBOX = { matchesAll, cut, gbkTool, CryptoJS, JSEncrypt, NODERSA, pako, JSON5, jinja, template, batchExecute, atob, btoa, base64Encode, base64Decode, md5, rc4Encrypt, rc4Decrypt, rc4, rc4_decode, randomUa, jsonpath, hlsParser, axios, axiosX, URL, pathLib, executeParse, qs, Buffer, URLSearchParams, COOKIE, - ENV, _ENV, Quark, Baidu, Baidu2, UC, Ali, Cloud, Yun, Pan, createWebDAVClient, createFTPClient, + ENV, _ENV, Quark, Baidu, Baidu2, UC, Ali, Cloud, Yun, Pan, Xun, createWebDAVClient, createFTPClient, require: rootRequire, WebSocket, WebSocketServer, zlib, JSONbig, JsonBig, minizlib, iconv: globalThis.iconv, cheerio: globalThis.cheerio, }; diff --git a/libs/dsGlobal.js b/libs/dsGlobal.js index 731c99a4..556ee370 100644 --- a/libs/dsGlobal.js +++ b/libs/dsGlobal.js @@ -71,6 +71,10 @@ globalThis.Baidu = Baidu; // 百度网盘接口2 - 百度网盘的另一个实现版本 globalThis.Baidu2 = Baidu2; + +// 迅雷网盘 +globalThis.Xun = Xun; + // webdav globalThis.createWebDAVClient = createWebDAVClient; // ftp diff --git a/spider/js/push_agent.js b/spider/js/push_agent.js index 5afb3951..5e8ae9a8 100644 --- a/spider/js/push_agent.js +++ b/spider/js/push_agent.js @@ -78,7 +78,7 @@ var rule = { let list = input.split('@'); // log(list); for (let i = 0; i < list.length; i++) { - if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|caiyun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com|pan.baidu.com/.test(list[i])) { + if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|caiyun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com|pan.baidu.com|pan.xunlei.com/.test(list[i])) { if (/pan.quark.cn/.test(list[i])) { playPans.push(list[i]); const shareData = Quark.getShareData(list[i]); @@ -189,12 +189,21 @@ var rule = { }) vod.vod_content = vod_content_add.join('\n'); } + if (/pan.xunlei.com/.test(input)) { + const data = await Xun.getShareData(input) + Object.keys(data).forEach(it => { + playform.push('Xun-' + it) + const urls = data[it].map(item => item.name + "$" + [item.fileId, item.share_id, item.parent_id, item.pass_code_token].join('*')).join('#'); + playurls.push(urls); + }) + } + } else { playform.push('推送'); playurls.push("推送" + '$' + list[i]) } } - } else if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|caiyun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com|pan.baidu.com/.test(input)) { + } else if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|caiyun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com|pan.baidu.com|pan.xunlei.com/.test(input)) { if (/pan.quark.cn/.test(input)) { playPans.push(input); const shareData = Quark.getShareData(input); @@ -305,6 +314,15 @@ var rule = { }) vod.vod_content = vod_content_add.join('\n'); } + if (/pan.xunlei.com/.test(input)) { + const data = await Xun.getShareData(input) + Object.keys(data).forEach(it => { + playform.push('Xun-' + it) + const urls = data[it].map(item => item.name + "$" + [item.fileId, item.share_id, item.parent_id, item.pass_code_token].join('*')).join('#'); + playurls.push(urls); + }) + } + } else { playform.push('推送'); playurls.push("推送" + '$' + input) @@ -333,7 +351,7 @@ var rule = { // 确保返回parse: 0和正确的URL格式,避免前端创建新的播放列表 return {parse: 0, url: ["原画", input]} } - } else if (/Quark-|UC-|Ali-|Cloud-|Yun-|Pan123-|Baidu-/.test(flag)) { + } else if (/Quark-|UC-|Ali-|Cloud-|Yun-|Pan123-|Baidu-|Xun-/.test(flag)) { const ids = id.split('*'); let UCDownloadingCache = {}; let downUrl = '' @@ -463,6 +481,24 @@ var rule = { } } } + if (flag.startsWith('Xun-')) { + log('迅雷云盘开始解析') + //转码和下载的第二个值不同 + let urls = await Xun.getShareUrl(ids[0], ids[1], ids[3]) + // let url = await Xun.getDownloadUrl(ids[0],ids[2],ids[3]) + // if(url!==''){ + // urls.push('原画',url+ "#isVideo=true##fastPlayMode##threads=20#") + // urls.push("猫画", `http://127.0.0.1:5575/proxy?thread=${ENV.get('thread') || 6}&chunkSize=256&url=` + encodeURIComponent(url)); + // } + return { + parse: 0, + url: urls, + header: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" + } + } + } + } else { return input } diff --git "a/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" "b/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" index 17af5d19..d082198e 100644 --- "a/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" +++ "b/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" @@ -65,6 +65,7 @@ let quick_data = { 阿里: 'https://www.alipan.com/s/vgXMcowK8pQ', 天翼: 'https://cloud.189.cn/web/share?code=INJbU3NbqyUj', 百度: 'https://pan.baidu.com/s/1L0UIv4p0X0QrbbKErJuc_w?pwd=2pwj', + 迅雷: 'https://pan.xunlei.com/s/VOkBwLBNoXN8eO9WrcVbXdTcA1?pwd=8tvj#', 移动1: 'https://yun.139.com/shareweb/#/w/i/0i5CLQ7BpV7Ai', 移动2: 'https://caiyun.139.com/m/i?2jexC1gcjeN7q', 移动3: 'https://yun.139.com/shareweb/#/w/i/2i2MoE9ZHn9p1', diff --git a/utils/pan/xun.js b/utils/pan/xun.js new file mode 100644 index 00000000..6b029e49 --- /dev/null +++ b/utils/pan/xun.js @@ -0,0 +1,805 @@ +import axios from "axios"; +import {ENV} from "../env.js"; +import CryptoJS from "crypto-js"; + +class XunDriver { + constructor() { + this.regex = /https:\/\/pan.xunlei.com\/s\/(.*)\?.*?pwd=([^&]+)/;//https://pan.baidu.com/s/1kbM0KWLDpeS8I49tmwS6lQ?pwd=74j5 + this.headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", + "Connection": "keep-alive", + "Accept": "application/json, text/plain, */*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6" + }; + this.api = 'https://xluser-ssl.xunlei.com/'; + this.xun_api = 'https://api-pan.xunlei.com/' + this.captcha_token = '' + this.parent_id = '' + this.share_id = '' + this.pass_code = '' + this.pass_code_token = '' + this.filename = 'ds' + this.fileId = '' + this.vodID = '' + this.client_id = 'XW5SkOhLDjnOZP7J' + this.x_client_id = 'XW-G4v1H72tgfJym' + this.device_id = '652c6bb3cacdb4b80e852dfc3cb3cca4' + this.i = 0 + } + + // 初始化方法,加载本地配置 + async init() { + if (this.auth) { + let info = JSON.parse(CryptoJS.enc.Base64.parse(this.auth.split('.')[1]).toString(CryptoJS.enc.Utf8)) + if (info.exp > Math.floor(Date.now() / 1000)) { + console.log("登录成功") + } else { + console.log("登录过期,重新登录") + } + } else { + await this.getAuth() + } + } + + get username() { + return ENV.get('xun_username') + } + + get password() { + return ENV.get('xun_password') + } + + get auth() { + return ENV.get('xun_auth') + } + + get app_auth() { + return ENV.get('xun_app_auth') + } + + get refresh_token() { + return ENV.get('xun_refresh_token') + } + + get userId() { + return ENV.get('xun_user_id') + } + + /** + * 延时函数 + * + * 创建一个Promise,在指定毫秒数后resolve,用于控制请求频率。 + * + * @param {number} ms - 延时毫秒数 + * @returns {Promise} 延时Promise + * + * @example + * await delay(1000); // 延时1秒 + */ + delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + async execUrl(url) { + this.link = url + const matches = this.regex.exec(url); + if (matches && matches[1]) { + this.share_id = matches[1]; + this.pass_code = matches[2] || ''; + } + } + + async login() { + let data = JSON.stringify({ + "protocolVersion": "301", + "sequenceNo": "1000001", + "platformVersion": "10", + "isCompressed": "0", + "appid": "40", + "clientVersion": "8.03.0.9067", + "peerID": "c9b076a446517969dff638cd37fa9ff1", + "appName": "ANDROID-com.xunlei.downloadprovider", + "sdkVersion": "231500", + "devicesign": "div101.b71a923eb0e2239842599a3c016b4098612f6cf6d6e9fd1925845ec59285716c", + "netWorkType": "2G", + "providerName": "NONE", + "deviceModel": "22021211RC", + "deviceName": "Xiaomi_22021211Rc", + "OSVersion": "12", + "creditkey": "", + "hl": "zh-CN", + "userName": this.username, + "passWord": this.password, + "verifyKey": "", + "verifyCode": "", + "isMd5Pwd": "0" + }); + let config = { + method: 'POST', + url: `${this.api}xluser.core.login/v3/login`, + headers: { + 'Content-Type': 'application/json' + }, + data: data + }; + let login_data = (await axios.request(config)) + if (login_data.status === 200) { + console.log('登录成功') + return login_data.data.sessionID; + } + } + + async getSignCaptcha() { + let data = JSON.stringify({ + "client_id": this.x_client_id, + "action": "POST:/v1/auth/signin", + "device_id": this.device_id, + "captcha_token": "", + "meta": { + "phone_number": `+86 ${this.username}` + } + }); + + let config = { + method: 'POST', + url: `${this.api}v1/shield/captcha/init`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json' + }, + data: data + }; + let signin = await axios.request(config) + if (signin.status === 200) { + return signin.data.captcha_token + } + } + + async getAuth() { + let need_auth = 1; + if (this.auth) { + let info = JSON.parse(CryptoJS.enc.Base64.parse(this.auth.split('.')[1]).toString(CryptoJS.enc.Utf8)) + if (info.exp > Math.floor(Date.now() / 1000)) { + console.log("登录成功"); + need_auth = 0; + } + } + if (need_auth) { + console.log("登录过期,重新登录") + let captcha_token = await this.getSignCaptcha(); + let data = JSON.stringify({ + "username": `+86 ${this.username}`, + "password": this.password, + "client_id": this.x_client_id + }); + let config = { + method: 'POST', + url: `${this.api}v1/auth/signin`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'accept-language': 'zh-cn', + 'x-captcha-token': captcha_token, + 'x-client-id': this.x_client_id, + 'x-device-id': this.device_id + }, + data: data + }; + let auth_data = (await axios.request(config)) + if (auth_data.status === 200) { + ENV.set('xun_auth', auth_data.data.token_type + " " + auth_data.data.access_token) + ENV.set('xun_user_id', auth_data.data.user_id) + } + await this.safecaptcha() + } + + } + + async safecaptcha() { + let data = JSON.stringify({ + "client_id": this.x_client_id, + "action": "get:/drive/v1/privilege/USER_SECURITY_TOKEN", + "device_id": this.device_id, + "captcha_token": "", + "meta": { + "username": "", + "phone_number": "", + "email": "", + "package_name": "pan.xunlei.com", + "client_version": "1.92.9", + "captcha_sign": "1.98cda33124387df2c03dea12799af2af", + "timestamp": "1757397551603", + "user_id": this.userId + } + }); + let config = { + method: 'POST', + url: 'https://xluser-ssl.xunlei.com/v1/shield/captcha/init', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'content-type': 'text/plain;charset=UTF-8' + }, + data: data + }; + let safe = (await axios.request(config)).data + let captcha_token = safe.captcha_token + let cfg = { + method: 'GET', + url: 'https://api-pan.xunlei.com/drive/v1/privilege/USER_SECURITY_TOKEN', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'content-type': 'application/json', + 'x-captcha-token': captcha_token, + 'x-client-id': this.x_client_id, + 'x-device-id': this.device_id + } + }; + let safe_data = (await axios(cfg)).data + } + + async getCaptcha_token(path, mth) { + let action = `${mth}:${path}` + let data = JSON.stringify({ + "client_id": 'Xqp0kJBXWhwaTpB6', + "action": action, + "device_id": "1bf91caf40093318e8040916eb7ad16a", + "captcha_token": "", + "meta": { + "username": "", + "phone_number": "", + "email": "", + "package_name": "pan.xunlei.com", + "client_version": "1.92.9", + "captcha_sign": "1.cbc20fd633c54023baab5b816228bf90", + "timestamp": "1757383155459", + "user_id": this.userId//this.user_id + } + }); + let config = { + method: 'POST', + url: `${this.api}v1/shield/captcha/init`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + }, + data: data + }; + let captcha_data = (await axios.request(config)) + if (captcha_data.status === 200) { + return captcha_data.data.captcha_token; + } + } + + async getAppCaptcha_token(path, mth) { + let action = `${mth}:${path}` + let data = JSON.stringify({ + "client_id": "XW-G4v1H72tgfJym", + "action": action, + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { + "package_name": "ThunderPanPlugin", + "client_version": "3.1.5", + "captcha_sign": "1.ee2cef5f061a7cdf4374df81b370d2ec", + "timestamp": "1773293019719", + "user_id": this.userId + } + }); + let config = { + method: 'POST', + url: `${this.api}v1/shield/captcha/init`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + }, + data: data + }; + let captcha_data = (await axios.request(config)) + if (captcha_data.status === 200) { + return captcha_data.data.captcha_token; + } + } + + sign() { + let x = [ + "QG3/GhopO+5+T", + "1Sv94+ANND3lDmmw", + "q2eTxRva8b3B5d", + "m2", + "VIc5CZRBMU71ENfbOh0+RgWIuzLy", + "66M8Wpw6nkBEekOtL6e", + "N0rucK7S8W/vrRkfPto5urIJJS8dVY0S", + "oLAR7pdUVUAp9xcuHWzrU057aUhdCJrt", + "6lxcykBSsfI//GR9", + "r50cz+1I4gbU/fk8", + "tdwzrTc4SNFC4marNGTgf05flC85A", + "qvNVUDFjfsOMqvdi2gB8gCvtaJAIqxXs" + ] + const c = { + ClientID: 'Xqp0kJBXWhwaTpB6', + ClientVersion: '1.92.9', + PackageName: 'pan.xunlei.com', + DeviceID: '1bf91caf40093318e8040916eb7ad16a' + } + const timestamp = Date.now() + let w = c.ClientID + c.ClientVersion + c.PackageName + c.DeviceID + '1757338961011' + for (let i = 0; i < x.length; i++) { + w = CryptoJS.MD5(w + x[i]).toString(); + } + return "1." + w + } + + async getShareList() { + let path = '/drive/v1/share' + let mth = 'get' + let captcha_data = await this.getCaptcha_token(path, mth) + let config = { + method: 'GET', + url: `${this.xun_api}drive/v1/share?share_id=${this.share_id}&pass_code=${this.pass_code}&limit=100&page_token=&thumbnail_size=SIZE_SMALL`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': '', + 'x-captcha-token': captcha_data, + 'x-client-id': 'Xqp0kJBXWhwaTpB6', + 'x-device-id': '1bf91caf40093318e8040916eb7ad16a' + } + }; + let sharelist = await axios.request(config) + if (sharelist.status === 200) { + let file = {} + let dirs = [] + let videos = [] + this.pass_code_token = sharelist.data.pass_code_token + sharelist.data.files.map(it => { + if (it.mime_type === '') { + dirs.push(it.id) + } else { + let text = /[#|'"\[\]&<>]/g + let name = text.test(it.name) ? it.name.replace(text, '') : it.name + videos.push({ + name: name, + fileId: it.id, + share_id: this.share_id, + parent_id: it.parent_id, + pass_code_token: encodeURIComponent(this.pass_code_token) + }) + } + }) + if (!(sharelist.data.title in file) && sharelist.data.title !== undefined) { + file[sharelist.data.title] = []; + } + if (videos.length > 0 && sharelist.data.title !== undefined) { + file[sharelist.data.title] = [...videos] + } + let result = await Promise.all(dirs.map(async (id) => this.getShareDetail(id))) + result = result.filter(item => item !== undefined && item !== null).flat() + if (result.length >= 0) { + file[sharelist.data.title].push(...result); + } + return file + } + } + + async getShareDetail(id) { + let path = '/drive/v1/share' + let mth = 'get' + let captcha_data = await this.getCaptcha_token(path, mth) + let config = { + method: 'GET', + url: `${this.xun_api}drive/v1/share/detail?share_id=${this.share_id}&parent_id=${id}&pass_code_token=${encodeURIComponent(this.pass_code_token)}&limit=100&page_token=&thumbnail_size=SIZE_SMALL`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': '', + 'content-type': 'application/json', + 'x-captcha-token': captcha_data, + 'x-client-id': 'Xqp0kJBXWhwaTpB6', + 'x-device-id': '1bf91caf40093318e8040916eb7ad16a' + } + }; + let detail_data = await axios.request(config) + if (detail_data.status === 200) { + let dirs = [] + let videos = [] + detail_data.data.files.map(it => { + if (it.mime_type === '') { + dirs.push(it.id) + } else { + let text = /[#|'"\[\]&<>]/g + let name = text.test(it.name) ? it.name.replace(text, '') : it.name + videos.push({ + name: name, + fileId: it.id, + share_id: this.share_id, + parent_id: it.parent_id, + pass_code_token: encodeURIComponent(this.pass_code_token) + }) + } + }) + let result = await Promise.all(dirs.map(async (id) => this.getShareDetail(id))) + result = result.filter(item => item !== undefined && item !== null) + return [...videos, ...result.flat()]; + } + } + + async getShareUrl(fileId, share_id, pass_code_token) { + let path = '/drive/v1/share' + let mth = 'get' + let captcha_data = await this.getCaptcha_token(path, mth) + let config = { + method: 'GET', + url: `${this.xun_api}drive/v1/share/file_info?pass_code_token=${encodeURIComponent(pass_code_token)}&space=&file_id=${fileId}&share_id=${share_id}&&pass_code=${this.pass_code}`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': '', + 'cache-control': 'no-cache', + 'content-type': 'application/json', + 'x-captcha-token': captcha_data, + 'x-client-id': 'Xqp0kJBXWhwaTpB6', + 'x-device-id': '1bf91caf40093318e8040916eb7ad16a' + } + }; + let url_list = await axios.request(config) + if (url_list.status === 200) { + let urls = [] + url_list.data.file_info.medias.map(it => { + if (it.link !== null) { + urls.push(it.media_name, it.link.url + "#isVideo=true##fastPlayMode##threads=20#") + urls.push("猫画" + it.media_name, `http://127.0.0.1:5575/proxy?thread=${ENV.get('thread') || 6}&chunkSize=256&url=` + encodeURIComponent(it.link.url)) + } + }) + return urls + } + } + + //获取分享文件列表 + async getShareData(url) { + if (url.startsWith('https://')) { + await this.execUrl(url) + return await this.getShareList() + } + // if(url.startsWith('magnet:')){ + // return await this.getMagnetData(url) + // } + } + + async saveResult(fileId, share_id, pass_code_token) { + let path = 'drive/v1/files' + let mth = 'get' + let captcha_data = await this.getAppCaptcha_token(path, mth) + let data = JSON.stringify({ + "parent_id": this.fileId, + "share_id": share_id, + "pass_code_token": decodeURIComponent(pass_code_token), + "ancestor_ids": [], + "file_ids": [ + fileId + ], + "specify_parent_id": true + }); + let config = { + method: 'POST', + url: `${this.xun_api}drive/v1/share/restore`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'x-captcha-token': captcha_data, + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4' + }, + data: data + }; + let file_data = await axios.request(config).catch(e => e.response) + if (file_data.status === 200) { + console.log("转存文件成功") + } + if (file_data.status === 404) { + this.i++ + if (this.i < 3) { + await this.saveResult(fileId, share_id, pass_code_token) + } else { + this.i = 0 + console.log("转存失败:" + file_data.data) + } + } + } + + //转存文件 + async saveFile(fileId, share_id, pass_code_token) { + await this.createFile() + if (this.fileId !== '' && this.fileId !== undefined) { + await this.delay(5000) + await this.saveResult(fileId, share_id, pass_code_token) + } + } + + //查询存储文件是否存在 + async getFile() { + if (this.auth === undefined || this.auth === '') { + await this.getAuth() + } + let path = 'drive/v1/files' + let mth = 'get' + let captcha_data = await this.getAppCaptcha_token(path, mth) + let url = `${this.xun_api}drive/v1/files?parent_id=&filters=%7B%22phase%22%3A%7B%22eq%22%3A%22PHASE_TYPE_COMPLETE%22%7D%2C%22trashed%22%3A%7B%22eq%22%3Afalse%7D%7D&with_audit=true&thumbnail_size=SIZE_SMALL&limit=50` + let config = { + method: 'GET', + url: url, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'content-type': 'application/json', + 'x-captcha-token': captcha_data, + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4' + } + }; + let file_data = await axios.request(config) + if (file_data.status === 200) { + file_data.data.files.map(it => { + if (it.name === this.filename) { + this.fileId = it.id; + } + }) + } + } + + //创建存储文件并写入文件夹ID + async createFile() { + await this.getFile() + let path = 'drive/v1/files' + let mth = 'get' + let captcha_data = await this.getAppCaptcha_token(path, mth) + let data = JSON.stringify({ + "parent_id": "", + "name": this.filename, + "kind": "drive#folder", + "space": "" + }); + let config = { + method: 'POST', + url: `${this.xun_api}drive/v1/files`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'x-captcha-token': captcha_data, + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4' + }, + data: data + }; + let file_data = await axios.request(config).catch(e => e.response) + if (file_data.status === 200) { + this.fileId = file_data.data.file.id + } + } + + //查询存储文件内容并获取文件ID + async getVodId() { + let path = 'drive/v1/files' + let mth = 'get' + let captcha_data = await this.getAppCaptcha_token(path, mth) + let url = `${this.xun_api}drive/v1/files?parent_id=${this.fileId}&filters=%7B%22phase%22%3A%7B%22eq%22%3A%22PHASE_TYPE_COMPLETE%22%7D%2C%22trashed%22%3A%7B%22eq%22%3Afalse%7D%7D&with_audit=true&thumbnail_size=SIZE_SMALL&limit=50` + let config = { + method: 'GET', + url: url, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'content-type': 'application/json', + 'x-captcha-token': captcha_data, + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4' + } + }; + let file_data = await axios.request(config) + if (file_data.status === 200) { + file_data.data.files.map(it => { + this.vodID = it.id + }) + } + return this.vodID + } + + //删除文件夹所有内容 + async deleteFile() { + await this.getFile() + if (this.fileId !== '' && this.fileId !== undefined) { + let path = 'drive/v1/files' + let mth = 'get' + let captcha_data = await this.getAppCaptcha_token(path, mth) + let data = JSON.stringify({ + "ids": [ + this.fileId + ], + "space": "" + }); + let config = { + method: 'POST', + url: `${this.xun_api}drive/v1/files:batchDelete`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'accept-language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6', + 'authorization': this.auth, + 'x-captcha-token': captcha_data, + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4' + }, + data: data + }; + let delete_data = await axios.request(config).catch(e => e.response) + if (delete_data.status === 200) { + console.log("删除文件成功") + } else if (delete_data.status === 404) { + console.log("文件未找到,删除失败") + } + } + } + + async getDownload_CAPTCHA_TOKEN() { + let data = { + "client_id": "XW-G4v1H72tgfJym", + "action": "GET:CAPTCHA_TOKEN", + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { + "package_name": "ThunderPanPlugin", + "client_version": "3.1.1", + "captcha_sign": "1.0eada0deeeaac52a6376f2e167fa9f29", + "timestamp": "1757378771289", + "user_id": this.userId + } + }; + let config = { + method: 'POST', + url: `${this.api}v1/shield/captcha/init`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Accept-Encoding": "gzip, deflate, br", + "Content-Type": "application/json" + }, + data: data + }; + let captcha_data = (await axios.request(config)) + if (captcha_data.status === 200) { + return captcha_data.data.captcha_token; + } + } + + async getAppCaptcha() { + let data = JSON.stringify({ + "client_id": 'Xqp0kJBXWhwaTpB6', + "action": "POST:/v1/auth/signin", + "device_id": "652c6bb3cacdb4b80e852dfc3cb3cca4", + "captcha_token": "", + "meta": { + "phone_number": `+86 ${this.username}` + } + }); + + let config = { + method: 'POST', + url: `${this.api}v1/shield/captcha/init`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Content-Type': 'application/json' + }, + data: data + }; + let signin = await axios.request(config) + if (signin.status === 200) { + return signin.data.captcha_token + } + } + + async AppAuth() { + if (this.app_auth) { + let info = JSON.parse(CryptoJS.enc.Base64.parse(this.app_auth.split('.')[1]).toString(CryptoJS.enc.Utf8)) + if (info.exp > Math.floor(Date.now() / 1000)) { + console.log("登录成功") + } else { + console.log("登录过期,重新登录") + let captcha_token = await this.getAppCaptcha(); + let data = JSON.stringify({ + "username": `+86 ${this.username}`, + "password": this.password, + "client_id": 'XW-G4v1H72tgfJym' + }); + let config = { + method: 'POST', + url: `${this.api}v1/auth/signin`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Content-Type": "application/json", + "x-captcha-token": captcha_token, + "x-client-id": "XW-G4v1H72tgfJym", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + }, + data: data + }; + let auth_data = (await axios.request(config)) + if (auth_data.status === 200) { + ENV.set('xun_refresh_token', auth_data.data.refresh_token) + ENV.set('xun_app_auth', auth_data.data.token_type + " " + auth_data.data.access_token) + } + } + } + } + + async getDownload_auth() { + await this.AppAuth() + let data = JSON.stringify({ + "client_id": "XW-G4v1H72tgfJym", + "client_secret": "Qbaferw2knfQKqxa25EYJGtZ2_6755CMwzXBN3ctW54", + "grant_type": "refresh_token", + "refresh_token": this.refresh_token + }); + let config = { + method: 'POST', + url: 'https://xluser-ssl.xunlei.com/v1/auth/token', + headers: { + 'User-Agent': 'thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36', + 'Content-Type': 'application/json', + 'x-client-id': 'XW-G4v1H72tgfJym', + 'x-device-id': '652c6bb3cacdb4b80e852dfc3cb3cca4', + }, + data: data + }; + let dwon_data = await axios(config).catch(e => e.response); + if (dwon_data.status === 200) { + ENV.set('xun_retoken', dwon_data.data.refresh_token); + ENV.set('xun_App_auth', dwon_data.data.token_type + " " + dwon_data.data.access_token) + } + } + + //下载 + async getDownloadUrl(fileId, share_id, pass_code_token) { + try { + await this.getDownload_auth() + await this.deleteFile() + await this.delay(1000) + await this.saveFile(fileId, share_id, pass_code_token) + let vodID = await this.getVodId() + let x_captcha_token = await this.getDownload_CAPTCHA_TOKEN() + let config = { + method: 'GET', + url: `${this.xun_api}drive/v1/files/${vodID}?space=&with[0]=public_share_tag&usage=FETCH`, + headers: { + "User-Agent": "thunder/12.4.4.3740 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 XDASKernel/22.3.27 Safari/537.36", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "x-captcha-token": x_captcha_token, + "authorization": this.app_auth, + "content-type": "application/json", + "x-device-id": "652c6bb3cacdb4b80e852dfc3cb3cca4" + } + }; + let file_data = await axios.request(config) + if (file_data.status === 200) { + console.log("下载地址:" + file_data.data.links['application/octet-stream'].url || file_data.data['web_content_link']) + return file_data.data.links['application/octet-stream'].url || file_data.data['web_content_link'] + } + } catch (e) { + console.log(e) + return '' + } + + } +} + +export const Xun = new XunDriver() \ No newline at end of file diff --git a/utils/pans.js b/utils/pans.js index f65e92e5..0c4d1241 100644 --- a/utils/pans.js +++ b/utils/pans.js @@ -27,6 +27,7 @@ import {Pan} from "./pan/pan123.js"; // 123网盘服务 import {Quark} from "./pan/quark.js"; // 夸克网盘服务 import {UC} from "./pan/uc.js"; // UC网盘服务 import {Yun} from "./pan/yun.js"; // 115网盘服务 +import {Xun} from "./pan/xun.js"; // 迅雷网盘服务 // 统一导出所有网盘服务 -export default {Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun} \ No newline at end of file +export default {Ali, Baidu, Baidu2, Cloud, Pan, Quark, UC, Yun, Xun} \ No newline at end of file From 5d1067177b88e518221ced527828354173c98132 Mon Sep 17 00:00:00 2001 From: Taois Date: Mon, 16 Mar 2026 18:59:37 +0800 Subject: [PATCH 02/17] =?UTF-8?q?fix=EF=BC=9A=20=E4=BF=AE=E5=A4=8Dheader?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E5=AF=B9=E4=BA=8E=E6=9F=90=E4=BA=9B=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E8=AF=86=E5=88=AB=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drpy-node-bundle/libs/localDsCore.bundled.js | 81 ++++++++++++--- .../spider/js/30wMV[\345\220\254].js" | 2 +- ...\345\244\247\345\205\250[\345\256\230].js" | 22 ++++- ...\346\216\250\345\233\276[\347\224\273].js" | 13 --- ...76\347\275\256\344\270\255\345\277\203.js" | 2 + package-bundle.js | 40 ++++++-- scripts/test/test_fileHeaderManager.js | 55 +++++++++++ ...\345\244\247\345\205\250[\345\256\230].js" | 22 ++++- ...76\347\275\256\344\270\255\345\277\203.js" | 2 + utils/fileHeaderManager.js | 98 ++++++++++++++++--- 10 files changed, 281 insertions(+), 56 deletions(-) diff --git a/drpy-node-bundle/libs/localDsCore.bundled.js b/drpy-node-bundle/libs/localDsCore.bundled.js index e5540372..55309ac8 100644 --- a/drpy-node-bundle/libs/localDsCore.bundled.js +++ b/drpy-node-bundle/libs/localDsCore.bundled.js @@ -117106,7 +117106,7 @@ const Xun$1 = new XunDriver(); /** * 网盘服务模块集合 * 统一导入和导出各种网盘服务提供商的实现 -* +* * 支持的网盘服务: * - Ali: 阿里云盘服务 * - Baidu: 百度网盘服务 @@ -117116,7 +117116,7 @@ const Xun$1 = new XunDriver(); * - Quark: 夸克网盘服务 * - UC: UC网盘服务 * - Yun: 115网盘服务 -* +* * @example * import pans from './pans.js'; * const aliPan = new pans.Ali(config); @@ -383621,6 +383621,52 @@ var FileHeaderManager = class { } }; /** + * Find the @header(...) block in the comment text + * @param {string} text Comment text + * @param {string} ext File extension (.js or .py) + * @returns {Object|null} { start, end, content } + */ + static findHeaderBlock(text, ext) { + const startIndex = text.indexOf("@header("); + if (startIndex === -1) return null; + let index = startIndex + 8; + let balance = 1; + let inString = false; + let stringChar = ""; + let escape = false; + let inLineComment = false; + for (; index < text.length; index++) { + const char = text[index]; + if (inLineComment) { + if (char === "\n") inLineComment = false; + continue; + } + if (inString) { + if (escape) escape = false; + else if (char === "\\") escape = true; + else if (char === stringChar) inString = false; + continue; + } + if (char === "/" && text[index + 1] === "/") { + inLineComment = true; + index++; + } else if (ext === ".py" && char === "#") inLineComment = true; + else if (char === "\"" || char === "'") { + inString = true; + stringChar = char; + } else if (char === "(") balance++; + else if (char === ")") { + balance--; + if (balance === 0) return { + start: startIndex, + end: index + 1, + content: text.substring(startIndex + 8, index) + }; + } + } + return null; + } + /** * 解析JavaScript对象字面量(支持无引号属性名) * @param {string} str 对象字符串 * @returns {Object} 解析后的对象 @@ -383649,10 +383695,10 @@ var FileHeaderManager = class { if (!config) throw new Error(`Unsupported file type: ${ext}`); const match = content.match(config.regex); if (!match) return null; - const headerMatch = match[0].match(config.headerRegex); - if (!headerMatch) return null; + const headerBlock = this.findHeaderBlock(match[0], ext); + if (!headerBlock) return null; try { - return this.parseObjectLiteral(headerMatch[1].trim()); + return this.parseObjectLiteral(headerBlock.content.trim()); } catch { return null; } @@ -383715,12 +383761,15 @@ var FileHeaderManager = class { const commentStartIndex = content.indexOf(fullComment); const commentEndIndex = commentStartIndex + fullComment.length; if (content.substring(0, commentStartIndex).trim() !== "") newContent = config.createComment(headerStr) + "\n\n" + content; - else if (config.headerRegex.test(fullComment)) { - const updatedComment = fullComment.replace(config.headerRegex, headerStr); - newContent = content.substring(0, commentStartIndex) + updatedComment + content.substring(commentEndIndex); - } else { - const updatedComment = fullComment.replace(config.end, "").trim() + `\n${headerStr}\n${config.end}`; - newContent = content.substring(0, commentStartIndex) + updatedComment + content.substring(commentEndIndex); + else { + const headerBlock = this.findHeaderBlock(fullComment, ext); + if (headerBlock) { + const updatedComment = fullComment.substring(0, headerBlock.start) + headerStr + fullComment.substring(headerBlock.end); + newContent = content.substring(0, commentStartIndex) + updatedComment + content.substring(commentEndIndex); + } else { + const updatedComment = fullComment.replace(config.end, "").trim() + `\n${headerStr}\n${config.end}`; + newContent = content.substring(0, commentStartIndex) + updatedComment + content.substring(commentEndIndex); + } } } else newContent = config.createComment(headerStr) + "\n\n" + content; if (!newContent.replace(config.regex, "").trim()) throw new Error("写入失败:内容不能只包含文件头而无原始内容"); @@ -383788,13 +383837,13 @@ var FileHeaderManager = class { } const match = content.match(config.regex); if (!match) return content.trim(); - let [fullComment, innerContent] = match; - if (config.headerRegex.test(innerContent)) { - innerContent = innerContent.replace(config.headerRegex, ""); - const cleanedInner = innerContent.split("\n").filter((line) => line.trim().length > 0).join("\n"); + let [fullComment] = match; + const headerBlock = this.findHeaderBlock(fullComment, ext); + if (headerBlock) { + let cleanedInner = (fullComment.substring(0, headerBlock.start) + fullComment.substring(headerBlock.end)).replace(config.start, "").replace(config.end, "").split("\n").filter((line) => line.trim().length > 0).join("\n"); if (!cleanedInner.trim()) content = content.replace(fullComment, ""); else { - const newComment = `${config.start}${cleanedInner}${config.end}`; + const newComment = `${config.start}\n${cleanedInner}\n${config.end}`; content = content.replace(fullComment, newComment); } } diff --git "a/drpy-node-bundle/spider/js/30wMV[\345\220\254].js" "b/drpy-node-bundle/spider/js/30wMV[\345\220\254].js" index dd83c31b..54a0ef2f 100644 --- "a/drpy-node-bundle/spider/js/30wMV[\345\220\254].js" +++ "b/drpy-node-bundle/spider/js/30wMV[\345\220\254].js" @@ -90,7 +90,7 @@ var rule = { lazy: async function (flag, id, flags) { let {requestHost} = this; const parseObj = executeParse('_30wmv', requestHost, id); - console.log('parseObj:', parseObj); + // console.log('parseObj:', parseObj); if (parseObj[0]) { const _data = await req(parseObj[1]); const data = JSON.parse(_data.content); diff --git "a/drpy-node-bundle/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" "b/drpy-node-bundle/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" index 1c11ebbb..49b61343 100644 --- "a/drpy-node-bundle/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" +++ "b/drpy-node-bundle/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" @@ -4,7 +4,25 @@ filterable: 1, quickSearch: 0, title: '央视大全', - lang: 'ds' + lang: 'ds', + isProxyPath: true, + more: { + parseApi: [ + { + host: 'cctv://(.+)', + flag: 'CCTV' + }, + { + host: 'cctv4k://(.+)', + flag: 'CCTV4K' + }, + { + host: 'cctvlive://(.+)', + flag: 'CCTV直播' + }, + ] + }, + }) */ @@ -34,7 +52,7 @@ var rule = { play_parse: true, lazy: async function () { let {input, flag, getProxyUrl} = this; - // log(input); + log(input); // log(flag); let guid = ''; let url = ''; diff --git "a/drpy-node-bundle/spider/js/\347\210\261\346\216\250\345\233\276[\347\224\273].js" "b/drpy-node-bundle/spider/js/\347\210\261\346\216\250\345\233\276[\347\224\273].js" index 4665d881..1f224ec1 100644 --- "a/drpy-node-bundle/spider/js/\347\210\261\346\216\250\345\233\276[\347\224\273].js" +++ "b/drpy-node-bundle/spider/js/\347\210\261\346\216\250\345\233\276[\347\224\273].js" @@ -26,15 +26,6 @@ var rule = { searchable: 2, quickSearch: 0, filterable: 0, - 预处理: async function () { - // console.log(encodeStr('你好')); - // console.log(typeof JSEncrypt); - // console.log(typeof JSON5); - // console.log(typeof gbkTool); - // console.log(typeof NODERSA); - // console.log(typeof atob); - // console.log(typeof btoa); - }, headers: { 'User-Agent': 'MOBILE_UA', }, @@ -64,10 +55,6 @@ var rule = { 推荐: '*', searchUrl: '/sou-**-fypage.html', 一级: '#content&&article;h2&&Text;img&&data-src;;a&&href', - // 一级: async function () { - // let {getProxyUrl} = this; - // console.log('本地代理地址:' + getProxyUrl()); - // }, 二级: '*', 搜索: '*', } \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" "b/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" index d082198e..2ba0699f 100644 --- "a/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" +++ "b/drpy-node-bundle/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" @@ -4,6 +4,7 @@ filterable: 0, quickSearch: 0, title: '设置中心', + isProxyPath: true, logo: 'https://avatars.githubusercontent.com/u/49803097?v=4', more: { sourceTag: '设置,动作', @@ -73,6 +74,7 @@ let quick_data = { 直链1: 'https://vdse.bdstatic.com//628ca08719cef5987ea2ae3c6f0d2386.mp4', 嗅探1: 'https://www.6080kk.cc/haokanplay/178120-1-1.html', 嗅探2: 'https://www.hahads.com/play/537106-3-1.html', + 央视跨源:'cctv://3c18e2b3bba249b78b831e910608cfec', 多集: 'https://v.qq.com/x/cover/m441e3rjq9kwpsc/m00253deqqo.html@https://pan.quark.cn/s/6c8158e258f3@https://pan.baidu.com/s/1TdbgcwaMG1dK7B5pQ1LbBg?pwd=1234', 海阔二级单线路: gzip(JSON.stringify({ "actor": "剧集", diff --git a/package-bundle.js b/package-bundle.js index 27cbd552..ed7e1824 100644 --- a/package-bundle.js +++ b/package-bundle.js @@ -12,16 +12,24 @@ const INCLUDE_ITEMS = [ 'localDsCoreTest.js' ]; +// 需要额外复制的特定文件列表 (文件名,相对于 spider/js) +const EXTRA_FILES = [ + '30wMV[听].js', + '爱推图[画].js', + '央视大全[官].js', + '设置中心.js' +]; + // 获取脚本所在目录 (e:\gitwork\drpy-node) const getScriptDir = () => dirname(resolve(url.fileURLToPath(import.meta.url))); -// 复制 _lib 开头的文件到 bundle 目录 -const copyLibFiles = (scriptDir) => { +// 复制 _lib 开头的文件及额外指定的文件到 bundle 目录 +const copyFiles = (scriptDir) => { const sourceDir = join(scriptDir, 'spider', 'js'); const targetDir = join(scriptDir, 'drpy-node-bundle', 'spider', 'js'); if (!existsSync(sourceDir)) { - console.warn(`警告: 源目录 ${sourceDir} 不存在,跳过复制 lib 文件。`); + console.warn(`警告: 源目录 ${sourceDir} 不存在,跳过复制文件。`); return; } @@ -30,23 +38,37 @@ const copyLibFiles = (scriptDir) => { mkdirSync(targetDir, {recursive: true}); } - console.log(`正在从 ${sourceDir} 复制 lib 文件到 ${targetDir}...`); + console.log(`正在从 ${sourceDir} 复制文件到 ${targetDir}...`); try { const files = readdirSync(sourceDir); - let count = 0; + let libCount = 0; + let extraCount = 0; + for (const file of files) { + let shouldCopy = false; + + // 检查是否是 _lib 文件 if (file.startsWith('_lib') && (file.endsWith('.js') || file.endsWith('.cjs'))) { + shouldCopy = true; + libCount++; + } + // 检查是否在额外文件列表中 + else if (EXTRA_FILES.includes(file)) { + shouldCopy = true; + extraCount++; + } + + if (shouldCopy) { const srcPath = join(sourceDir, file); const destPath = join(targetDir, file); copyFileSync(srcPath, destPath); // console.log(`已复制: ${file}`); - count++; } } - console.log(`成功复制了 ${count} 个 lib 文件。`); + console.log(`成功复制了 ${libCount} 个 lib 文件和 ${extraCount} 个额外文件。`); } catch (error) { - console.error(`复制 lib 文件失败: ${error.message}`); + console.error(`复制文件失败: ${error.message}`); } }; @@ -114,7 +136,7 @@ const compressBundle = (scriptDir) => { // 主程序入口 const main = () => { const scriptDir = getScriptDir(); - copyLibFiles(scriptDir); + copyFiles(scriptDir); compressBundle(scriptDir); }; diff --git a/scripts/test/test_fileHeaderManager.js b/scripts/test/test_fileHeaderManager.js index 9a797483..3371e5c2 100644 --- a/scripts/test/test_fileHeaderManager.js +++ b/scripts/test/test_fileHeaderManager.js @@ -87,6 +87,61 @@ function test() { await FileHeaderManager.writeHeader(jsWithCommentFile, headerObj); console.log('\n添加文件头后:'); console.log(await fs.readFile(jsWithCommentFile, 'utf8')); + + // 测试3.5: 复杂嵌套头信息 + console.log('\n\n测试3.5: 复杂嵌套头信息'); + const complexFile = path.join(testDir, 'complex.js'); + const complexContent = `/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '央视大全', + lang: 'ds', + isProxyPath: true, + more: { + parseApi: [ + { + host: 'cctv://(.+)', + flag: 'CCTV' + }, + { + host: 'cctv4k://(.+)', + flag: 'CCTV4K' + }, + { + host: 'cctvlive://(.+)', + flag: 'CCTV直播' + }, + ] + }, + +}) +*/ +var rule = {};`; + await createTestFile(complexFile, complexContent); + + // 读取并验证 + const complexHeader = await FileHeaderManager.readHeader(complexFile); + console.log('读取复杂头信息:', complexHeader.title); + if (complexHeader.more && complexHeader.more.parseApi && complexHeader.more.parseApi.length === 3) { + console.log('✓ 复杂结构解析正确'); + } else { + console.error('✗ 复杂结构解析失败'); + } + + // 写入并验证不损坏 + await FileHeaderManager.writeHeader(complexFile, complexHeader); + const newComplexContent = await fs.readFile(complexFile, 'utf8'); + // Check for cleanliness (no trailing garbage like in the bug) + // The bug caused "})'," or similar garbage at the end of the comment block + // The correct content should end the comment cleanly. + if (newComplexContent.includes("host: 'cctv://(.+)'") && !newComplexContent.includes("})',")) { + console.log('✓ 写入复杂头信息正确 (无乱码残留)'); + } else { + console.error('✗ 写入复杂头信息失败 (可能有残留)'); + console.log(newComplexContent); + } // 测试4: 测试内容完整性检查 console.log('\n\n测试4: 内容完整性检查'); diff --git "a/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" "b/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" index 1c11ebbb..49b61343 100644 --- "a/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" +++ "b/spider/js/\345\244\256\350\247\206\345\244\247\345\205\250[\345\256\230].js" @@ -4,7 +4,25 @@ filterable: 1, quickSearch: 0, title: '央视大全', - lang: 'ds' + lang: 'ds', + isProxyPath: true, + more: { + parseApi: [ + { + host: 'cctv://(.+)', + flag: 'CCTV' + }, + { + host: 'cctv4k://(.+)', + flag: 'CCTV4K' + }, + { + host: 'cctvlive://(.+)', + flag: 'CCTV直播' + }, + ] + }, + }) */ @@ -34,7 +52,7 @@ var rule = { play_parse: true, lazy: async function () { let {input, flag, getProxyUrl} = this; - // log(input); + log(input); // log(flag); let guid = ''; let url = ''; diff --git "a/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" "b/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" index d082198e..2ba0699f 100644 --- "a/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" +++ "b/spider/js/\350\256\276\347\275\256\344\270\255\345\277\203.js" @@ -4,6 +4,7 @@ filterable: 0, quickSearch: 0, title: '设置中心', + isProxyPath: true, logo: 'https://avatars.githubusercontent.com/u/49803097?v=4', more: { sourceTag: '设置,动作', @@ -73,6 +74,7 @@ let quick_data = { 直链1: 'https://vdse.bdstatic.com//628ca08719cef5987ea2ae3c6f0d2386.mp4', 嗅探1: 'https://www.6080kk.cc/haokanplay/178120-1-1.html', 嗅探2: 'https://www.hahads.com/play/537106-3-1.html', + 央视跨源:'cctv://3c18e2b3bba249b78b831e910608cfec', 多集: 'https://v.qq.com/x/cover/m441e3rjq9kwpsc/m00253deqqo.html@https://pan.quark.cn/s/6c8158e258f3@https://pan.baidu.com/s/1TdbgcwaMG1dK7B5pQ1LbBg?pwd=1234', 海阔二级单线路: gzip(JSON.stringify({ "actor": "剧集", diff --git a/utils/fileHeaderManager.js b/utils/fileHeaderManager.js index c13a9901..534ce3e4 100644 --- a/utils/fileHeaderManager.js +++ b/utils/fileHeaderManager.js @@ -24,6 +24,68 @@ class FileHeaderManager { } }; + /** + * Find the @header(...) block in the comment text + * @param {string} text Comment text + * @param {string} ext File extension (.js or .py) + * @returns {Object|null} { start, end, content } + */ + static findHeaderBlock(text, ext) { + const startMarker = '@header('; + const startIndex = text.indexOf(startMarker); + if (startIndex === -1) return null; + + let index = startIndex + startMarker.length; + let balance = 1; + let inString = false; + let stringChar = ''; + let escape = false; + let inLineComment = false; + + for (; index < text.length; index++) { + const char = text[index]; + + if (inLineComment) { + if (char === '\n') inLineComment = false; + continue; + } + + if (inString) { + if (escape) { + escape = false; + } else if (char === '\\') { + escape = true; + } else if (char === stringChar) { + inString = false; + } + continue; + } + + // Start of comment + if (char === '/' && text[index + 1] === '/') { + inLineComment = true; + index++; + } else if (ext === '.py' && char === '#') { + inLineComment = true; + } else if (char === '"' || char === "'") { + inString = true; + stringChar = char; + } else if (char === '(') { + balance++; + } else if (char === ')') { + balance--; + if (balance === 0) { + return { + start: startIndex, + end: index + 1, + content: text.substring(startIndex + startMarker.length, index) + }; + } + } + } + return null; + } + /** * 解析JavaScript对象字面量(支持无引号属性名) * @param {string} str 对象字符串 @@ -60,11 +122,11 @@ class FileHeaderManager { const match = content.match(config.regex); if (!match) return null; - const headerMatch = match[0].match(config.headerRegex); - if (!headerMatch) return null; + const headerBlock = this.findHeaderBlock(match[0], ext); + if (!headerBlock) return null; try { - return this.parseObjectLiteral(headerMatch[1].trim()); + return this.parseObjectLiteral(headerBlock.content.trim()); } catch { return null; } @@ -153,9 +215,13 @@ class FileHeaderManager { newContent = newComment + content; } else { // 这是文件头注释,进行更新 - if (config.headerRegex.test(fullComment)) { + const headerBlock = this.findHeaderBlock(fullComment, ext); + + if (headerBlock) { // 已存在@header,替换它 - const updatedComment = fullComment.replace(config.headerRegex, headerStr); + const updatedComment = fullComment.substring(0, headerBlock.start) + + headerStr + + fullComment.substring(headerBlock.end); newContent = content.substring(0, commentStartIndex) + updatedComment + content.substring(commentEndIndex); @@ -290,12 +356,18 @@ class FileHeaderManager { const match = content.match(config.regex); if (!match) return content.trim(); - let [fullComment, innerContent] = match; - - if (config.headerRegex.test(innerContent)) { - innerContent = innerContent.replace(config.headerRegex, ''); - - const cleanedInner = innerContent + let [fullComment] = match; + + const headerBlock = this.findHeaderBlock(fullComment, ext); + + if (headerBlock) { + const innerContent = fullComment.substring(0, headerBlock.start) + + fullComment.substring(headerBlock.end); + + // Clean up inner content + let cleanedInner = innerContent + .replace(config.start, '') + .replace(config.end, '') .split('\n') .filter(line => line.trim().length > 0) .join('\n'); @@ -303,7 +375,7 @@ class FileHeaderManager { if (!cleanedInner.trim()) { content = content.replace(fullComment, ''); } else { - const newComment = `${config.start}${cleanedInner}${config.end}`; + const newComment = `${config.start}\n${cleanedInner}\n${config.end}`; content = content.replace(fullComment, newComment); } } @@ -348,4 +420,4 @@ class FileHeaderManager { } } -export default FileHeaderManager; \ No newline at end of file +export default FileHeaderManager; From 29b0d66ca63d9c132559d5b2b52c813401b83b6e Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 10:59:14 +0800 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=A4=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drpy-node-bundle/libs/localDsCore.bundled.js | 74 +++++++-- scripts/test/test_cctv_header.js | 102 ++++++++++++ utils/fileHeaderManager.js | 154 +++++++++++++++---- 3 files changed, 283 insertions(+), 47 deletions(-) create mode 100644 scripts/test/test_cctv_header.js diff --git a/drpy-node-bundle/libs/localDsCore.bundled.js b/drpy-node-bundle/libs/localDsCore.bundled.js index 55309ac8..e67e3f1d 100644 --- a/drpy-node-bundle/libs/localDsCore.bundled.js +++ b/drpy-node-bundle/libs/localDsCore.bundled.js @@ -383618,6 +383618,14 @@ var FileHeaderManager = class { headerRegex: /@header\(([\s\S]*?)\)/, createComment: (content) => `"""\n${content}\n"""`, topCommentsRegex: /^(\s*(#[^\n]*\n|'''[\s\S]*?'''|"""[\s\S]*?""")\s*)+/ + }, + ".php": { + start: "/*", + end: "*/", + regex: /(<\?php\s*)?(\s*\/\*[\s\S]*?\*\/)/, + headerRegex: /@header\(([\s\S]*?)\)/, + createComment: (content) => `/*\n${content}\n*/`, + topCommentsRegex: /(<\?php\s*)?(\s*(\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)\s*)+/ } }; /** @@ -383635,12 +383643,20 @@ var FileHeaderManager = class { let stringChar = ""; let escape = false; let inLineComment = false; + let inBlockComment = false; for (; index < text.length; index++) { const char = text[index]; if (inLineComment) { if (char === "\n") inLineComment = false; continue; } + if (inBlockComment) { + if (char === "*" && text[index + 1] === "/") { + inBlockComment = false; + index++; + } + continue; + } if (inString) { if (escape) escape = false; else if (char === "\\") escape = true; @@ -383650,6 +383666,9 @@ var FileHeaderManager = class { if (char === "/" && text[index + 1] === "/") { inLineComment = true; index++; + } else if (char === "/" && text[index + 1] === "*") { + inBlockComment = true; + index++; } else if (ext === ".py" && char === "#") inLineComment = true; else if (char === "\"" || char === "'") { inString = true; @@ -383667,19 +383686,18 @@ var FileHeaderManager = class { return null; } /** - * 解析JavaScript对象字面量(支持无引号属性名) + * 解析对象字符串 * @param {string} str 对象字符串 * @returns {Object} 解析后的对象 */ static parseObjectLiteral(str) { - const normalized = str.replace(/([{,]\s*)([a-zA-Z_$][\w$]*)(\s*:)/g, "$1\"$2\"$3").replace(/'([^']+)'/g, "\"$1\""); try { - return JSON.parse(normalized); + return JSON5.parse(str); } catch (e) { try { return new Function(`return ${str}`)(); - } catch { - throw new Error(`Invalid header object: ${str}`); + } catch (evalError) { + throw new Error(`Invalid header object: ${str}. Error: ${evalError.message}`); } } } @@ -383753,34 +383771,51 @@ var FileHeaderManager = class { const ext = path.extname(filePath); const config = this.COMMENT_CONFIG[ext]; if (!config) throw new Error(`Unsupported file type: ${ext}`); - const headerStr = `@header(${JSON.stringify(headerObj, null, 2).replace(/"([a-zA-Z_$][\w$]*)":/g, "$1:").replace(/"/g, "'")})`; + const headerStr = `@header(${JSON5.stringify(headerObj, null, 2)})`; const match = content.match(config.regex); let newContent; if (match) { const [fullComment] = match; const commentStartIndex = content.indexOf(fullComment); const commentEndIndex = commentStartIndex + fullComment.length; - if (content.substring(0, commentStartIndex).trim() !== "") newContent = config.createComment(headerStr) + "\n\n" + content; - else { + const beforeComment = content.substring(0, commentStartIndex); + if (!(beforeComment.trim() === "" || ext === ".php" && beforeComment.trim() === " { const trimmed = line.trim(); - return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''"); + return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''") && !trimmed.startsWith(" { const trimmed = line.trim(); - return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''") && !trimmed.includes("@header("); + return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''") && !trimmed.startsWith(" 5 && newCodeLines.length < originalCodeLines.length * .8) throw new Error("Content integrity check failed: significant code loss detected, operation aborted"); let backupPath = null; @@ -383840,11 +383875,18 @@ var FileHeaderManager = class { let [fullComment] = match; const headerBlock = this.findHeaderBlock(fullComment, ext); if (headerBlock) { - let cleanedInner = (fullComment.substring(0, headerBlock.start) + fullComment.substring(headerBlock.end)).replace(config.start, "").replace(config.end, "").split("\n").filter((line) => line.trim().length > 0).join("\n"); - if (!cleanedInner.trim()) content = content.replace(fullComment, ""); + let beforeHeader = fullComment.substring(0, headerBlock.start); + let afterHeader = fullComment.substring(headerBlock.end); + beforeHeader = beforeHeader.replace(config.start, ""); + afterHeader = afterHeader.replace(config.end, ""); + if (ext === ".php") beforeHeader = beforeHeader.replace(/<\?php\s*/, ""); + let cleanedInner = (beforeHeader + "\n" + afterHeader).split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n"); + if (!cleanedInner) if (ext === ".php" && fullComment.trim().startsWith(" newComment); } } return content.trim(); diff --git a/scripts/test/test_cctv_header.js b/scripts/test/test_cctv_header.js new file mode 100644 index 00000000..3ecf0a17 --- /dev/null +++ b/scripts/test/test_cctv_header.js @@ -0,0 +1,102 @@ +import fs from 'fs/promises'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import FileHeaderManager from '../../utils/fileHeaderManager.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// 源文件路径 +const sourceFile = path.resolve(__dirname, '../../spider/js/央视大全[官].js'); +// 测试文件路径 +const testFile = path.resolve(__dirname, 'test_cctv_header_temp.js'); + +async function runTest() { + console.log('=== 开始测试文件头操作 ==='); + console.log(`源文件: ${sourceFile}`); + console.log(`测试文件: ${testFile}`); + + try { + // 1. 复制文件 + console.log('\n[1/6] 正在复制源文件到测试文件...'); + try { + await fs.copyFile(sourceFile, testFile); + console.log('✅ 复制完成。'); + } catch (e) { + console.error('❌ 复制失败:', e.message); + return; + } + + // 2. 读取文件头 + console.log('\n[2/6] 正在读取文件头...'); + let header = await FileHeaderManager.readHeader(testFile); + if (header) { + console.log('✅ 读取到的文件头:', JSON.stringify(header, null, 2)); + } else { + console.log('⚠️ 未找到文件头,将尝试创建新文件头。'); + header = {}; + } + + // 3. 修改文件头 + console.log('\n[3/6] 正在修改并写入文件头...'); + const timestamp = Date.now(); + const originalTitle = header.title || 'Unknown'; + header.title = `${originalTitle}_TEST_${timestamp}`; + header.test_timestamp = timestamp; + + try { + await FileHeaderManager.writeHeader(testFile, header); + console.log('✅ 写入操作完成。'); + } catch (e) { + console.error('❌ 写入失败:', e.message); + throw e; + } + + // 4. 再次读取验证 + console.log('\n[4/6] 再次读取以验证写入...'); + const newHeader = await FileHeaderManager.readHeader(testFile); + console.log('读取到的新文件头:', JSON.stringify(newHeader, null, 2)); + + if (newHeader && newHeader.test_timestamp === timestamp) { + console.log('✅ 验证成功:新字段已存在且值正确。'); + } else { + console.error('❌ 验证失败:新字段未找到或值不匹配。'); + throw new Error('Verification failed'); + } + + // 5. 移除文件头 + console.log('\n[5/6] 正在移除文件头...'); + // 注意:removeHeader 返回处理后的内容字符串,不自动写入文件 + const contentWithoutHeader = await FileHeaderManager.removeHeader(testFile); + + // 我们需要手动写入文件以验证 + await fs.writeFile(testFile, contentWithoutHeader); + console.log('✅ 移除操作完成,已写回文件。'); + + // 6. 验证移除结果 + console.log('\n[6/6] 验证移除结果...'); + const headerAfterRemove = await FileHeaderManager.readHeader(testFile); + if (!headerAfterRemove || Object.keys(headerAfterRemove).length === 0) { + console.log('✅ 验证成功:文件头已移除。'); + } else { + console.log('❌ 验证失败:文件头仍然存在:', headerAfterRemove); + throw new Error('Removal verification failed'); + } + + console.log('\n=== 测试全部通过 ==='); + + } catch (error) { + console.error('\n❌ 测试过程中发生错误:', error); + } finally { + // 7. 清理 + console.log('\n[7/7] 清理测试文件...'); + try { + await fs.unlink(testFile); + console.log('✅ 测试文件已删除。'); + } catch (e) { + console.error('⚠️ 删除测试文件失败 (可能文件不存在):', e.message); + } + } +} + +runTest(); diff --git a/utils/fileHeaderManager.js b/utils/fileHeaderManager.js index 534ce3e4..cd30b967 100644 --- a/utils/fileHeaderManager.js +++ b/utils/fileHeaderManager.js @@ -1,6 +1,7 @@ // fileHeaderManager.js import fs from 'fs/promises'; import path from 'path'; +import '../libs_drpy/_dist/json5.js'; class FileHeaderManager { static COMMENT_CONFIG = { @@ -21,9 +22,19 @@ class FileHeaderManager { headerRegex: /@header\(([\s\S]*?)\)/, createComment: (content) => `"""\n${content}\n"""`, topCommentsRegex: /^(\s*(#[^\n]*\n|'''[\s\S]*?'''|"""[\s\S]*?""")\s*)+/ + }, + '.php': { + start: '/*', + end: '*/', + // PHP 文件通常以 `/*\n${content}\n*/`, + topCommentsRegex: /(<\?php\s*)?(\s*(\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)\s*)+/ } }; + /** * Find the @header(...) block in the comment text * @param {string} text Comment text @@ -41,6 +52,7 @@ class FileHeaderManager { let stringChar = ''; let escape = false; let inLineComment = false; + let inBlockComment = false; for (; index < text.length; index++) { const char = text[index]; @@ -50,6 +62,14 @@ class FileHeaderManager { continue; } + if (inBlockComment) { + if (char === '*' && text[index + 1] === '/') { + inBlockComment = false; + index++; + } + continue; + } + if (inString) { if (escape) { escape = false; @@ -65,6 +85,9 @@ class FileHeaderManager { if (char === '/' && text[index + 1] === '/') { inLineComment = true; index++; + } else if (char === '/' && text[index + 1] === '*') { + inBlockComment = true; + index++; } else if (ext === '.py' && char === '#') { inLineComment = true; } else if (char === '"' || char === "'") { @@ -87,22 +110,25 @@ class FileHeaderManager { } /** - * 解析JavaScript对象字面量(支持无引号属性名) + * 解析对象字符串 * @param {string} str 对象字符串 * @returns {Object} 解析后的对象 */ static parseObjectLiteral(str) { - const normalized = str - .replace(/([{,]\s*)([a-zA-Z_$][\w$]*)(\s*:)/g, '$1"$2"$3') - .replace(/'([^']+)'/g, '"$1"'); - try { - return JSON.parse(normalized); + return JSON5.parse(str); } catch (e) { + // console.warn('JSON5 parse failed, falling back to eval:', e.message); try { + // 尝试处理一些常见的非标准JSON格式 + // 1. 给未加引号的键加上引号 (简单的正则替换,不够完美但能处理大部分情况) + // 注意:这里不再做简单的正则替换,因为 JSON5 已经能处理无引号键。 + // 如果 JSON5 失败,很可能是因为包含了函数或其他非数据类型,或者格式严重错误。 + + // 为了兼容旧的非标准写法,保留 eval 方式作为最后的手段 return (new Function(`return ${str}`))(); - } catch { - throw new Error(`Invalid header object: ${str}`); + } catch (evalError) { + throw new Error(`Invalid header object: ${str}. Error: ${evalError.message}`); } } } @@ -195,9 +221,7 @@ class FileHeaderManager { if (!config) throw new Error(`Unsupported file type: ${ext}`); - const headerStr = `@header(${JSON.stringify(headerObj, null, 2) - .replace(/"([a-zA-Z_$][\w$]*)":/g, '$1:') - .replace(/"/g, "'")})`; + const headerStr = `@header(${JSON5.stringify(headerObj, null, 2)})`; const match = content.match(config.regex); let newContent; @@ -209,10 +233,23 @@ class FileHeaderManager { // 确保匹配的注释块确实在文件开头(允许前面有空白字符) const beforeComment = content.substring(0, commentStartIndex); - if (beforeComment.trim() !== '') { - // 如果注释块前面有非空白内容,说明这不是文件头注释,创建新的头注释 - const newComment = config.createComment(headerStr) + '\n\n'; - newContent = newComment + content; + + // 针对 PHP 特殊处理:忽略开头的 { @@ -266,7 +339,7 @@ class FileHeaderManager { return trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*') && !trimmed.startsWith('*/') && !trimmed.startsWith('#') && !trimmed.startsWith('"""') && !trimmed.startsWith("'''") && - !trimmed.includes('@header('); + !trimmed.startsWith(' line.trim().length > 0) + .map(line => line.trim()) + .filter(line => line.length > 0) .join('\n'); - if (!cleanedInner.trim()) { - content = content.replace(fullComment, ''); + if (!cleanedInner) { + // 如果只剩下 header,整个注释块移除 + // 但如果是 PHP 文件, newComment); } } From 8c36ba53ea48afcaed785b80474bb2947fa18895 Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 11:16:35 +0800 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=A4=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test/test_cctv_header.js | 85 +++++++++++++++++++++++++++++++- utils/fileHeaderManager.js | 27 +++++++++- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/scripts/test/test_cctv_header.js b/scripts/test/test_cctv_header.js index 3ecf0a17..52b0ef5a 100644 --- a/scripts/test/test_cctv_header.js +++ b/scripts/test/test_cctv_header.js @@ -29,7 +29,12 @@ async function runTest() { // 2. 读取文件头 console.log('\n[2/6] 正在读取文件头...'); + const startTime = performance.now(); let header = await FileHeaderManager.readHeader(testFile); + const endTime = performance.now(); + const duration = endTime - startTime; + console.log(`⏱️ 读取耗时: ${duration.toFixed(4)} ms`); + if (header) { console.log('✅ 读取到的文件头:', JSON.stringify(header, null, 2)); } else { @@ -83,13 +88,89 @@ async function runTest() { throw new Error('Removal verification failed'); } + // 7. 性能测试 (循环 10000 次) + const iterations = 10000; + console.log(`\n[7/10] 性能基准测试 (读取 ${iterations} 次取平均)...`); + // 确保文件有头 + await fs.copyFile(sourceFile, testFile); + + const perfStart = performance.now(); + for(let i=0; i Date: Tue, 17 Mar 2026 11:24:48 +0800 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=A4=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test/test_cctv_header.js | 18 ++-------- utils/fileHeaderManager.js | 60 +++++++------------------------- 2 files changed, 15 insertions(+), 63 deletions(-) diff --git a/scripts/test/test_cctv_header.js b/scripts/test/test_cctv_header.js index 52b0ef5a..3851a865 100644 --- a/scripts/test/test_cctv_header.js +++ b/scripts/test/test_cctv_header.js @@ -105,22 +105,8 @@ async function runTest() { console.log(`平均每次读取耗时: ${avgTime.toFixed(4)} ms`); console.log('✅ 性能测试完成。'); - // 8. 优化后的 readHeader 性能测试 - console.log(`\n[8/10] 优化后的 readHeader (Partial Read) 性能测试 (读取 ${iterations} 次取平均)...`); - - const perfStartOptimized = performance.now(); - for(let i=0; i { - const trimmed = line.trim(); - return trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && - !trimmed.startsWith('*') && !trimmed.startsWith('*/') && - !trimmed.startsWith('#') && !trimmed.startsWith('"""') && !trimmed.startsWith("'''") && - !trimmed.startsWith(' { - const trimmed = line.trim(); - return trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && - !trimmed.startsWith('*') && !trimmed.startsWith('*/') && - !trimmed.startsWith('#') && !trimmed.startsWith('"""') && !trimmed.startsWith("'''") && - !trimmed.startsWith(' 5 && newCodeLines.length < originalCodeLines.length * 0.8) { - throw new Error('Content integrity check failed: significant code loss detected, operation aborted'); + // 简单的内容完整性检查:确保新内容大小与原始内容大小差异在合理范围内 + // 移除复杂的行级比对以提升性能 + const diffRatio = Math.abs(newContent.length - content.length) / content.length; + if (content.length > 100 && diffRatio > 0.5 && newContent.length < content.length) { + throw new Error('Content integrity check failed: significant size reduction detected, operation aborted'); } // 创建备份(如果启用) From 785065f4f5e163b8fb5cf4f6d1726084af31fed2 Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 11:49:42 +0800 Subject: [PATCH 06/17] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=A4=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test/test_cctv_header.js | 6 + scripts/test/test_cctv_header_large.js | 167 +++++++++++++++++++++++++ utils/fileHeaderManager.js | 133 ++++++++++++++++++-- 3 files changed, 299 insertions(+), 7 deletions(-) create mode 100644 scripts/test/test_cctv_header_large.js diff --git a/scripts/test/test_cctv_header.js b/scripts/test/test_cctv_header.js index 3851a865..4382785b 100644 --- a/scripts/test/test_cctv_header.js +++ b/scripts/test/test_cctv_header.js @@ -126,6 +126,12 @@ async function runTest() { const avgTimeWrite = totalTimeWrite / iterations; console.log(`总耗时 (Write ${iterations}次): ${totalTimeWrite.toFixed(2)} ms`); console.log(`平均每次写入耗时: ${avgTimeWrite.toFixed(4)} ms`); + + // 验证 Buffer 模式是否生效 (检查耗时是否显著低于 String 模式) + // 理论上 Buffer 模式会快很多 + if (avgTimeWrite < 1.0) { + console.log('✅ 写入性能极佳,推测已启用 Buffer 优化。'); + } // 10. 移除文件头性能测试 console.log(`\n[10/10] 移除文件头性能测试 (循环 ${iterations} 次)...`); diff --git a/scripts/test/test_cctv_header_large.js b/scripts/test/test_cctv_header_large.js new file mode 100644 index 00000000..879d50fd --- /dev/null +++ b/scripts/test/test_cctv_header_large.js @@ -0,0 +1,167 @@ + +import fs from 'fs/promises'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import FileHeaderManager from '../../utils/fileHeaderManager.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const sourceFile = path.resolve(__dirname, '../../spider/js/央视大全[官].js'); +const testFile = path.resolve(__dirname, 'test_cctv_header_large_temp.js'); + +// 模拟 Buffer 模式的 Header 移除 +async function removeHeaderBuffer(filePath) { + const handle = await fs.open(filePath, 'r'); + try { + const stats = await handle.stat(); + const buffer = Buffer.alloc(stats.size); + await handle.read(buffer, 0, stats.size, 0); + + // 查找注释块 + // /* = 0x2f 0x2a + // */ = 0x2a 0x2f + const start = buffer.indexOf('/*'); + if (start === -1) return; + + const end = buffer.indexOf('*/', start); + if (end === -1) return; + + // 提取注释内容进行检查 + const commentBuf = buffer.subarray(start, end + 2); + const commentStr = commentBuf.toString('utf8'); + + if (!commentStr.includes('@header(')) return; + + // 查找 @header + const headerStart = commentStr.indexOf('@header('); + // 这里简化处理:假设我们要移除整个 @header(...) + // 实际需要括号平衡逻辑,这里简单起见,假设只有一层括号或用 regex + // 为了公平对比,我们只测试 "读取 Buffer -> 查找位置 -> 拼接 Buffer -> 写入" 的过程 + + // 模拟:我们找到了 header 的 start 和 end + // 实际逻辑应该复用 FileHeaderManager.findHeaderBlock + // 但 findHeaderBlock 操作的是 string。 + + // 关键在于:是否可以只把 commentBlock 转 string,找到 offset, + // 然后在原始 Buffer 上进行 slice 和 concat? + + // 假设 headerStartOffset 和 headerEndOffset 是相对于 buffer 的 + const headerGlobalStart = start + headerStart; + // 简单模拟 header 长度为 100 字节 + const headerGlobalEnd = headerGlobalStart + 100; + + const newBuf = Buffer.concat([ + buffer.subarray(0, headerGlobalStart), + buffer.subarray(headerGlobalEnd) + ]); + + await fs.writeFile(filePath, newBuf); + + } finally { + await handle.close(); + } +} + +async function runTest() { + console.log('=== 开始大文件性能对比测试 ==='); + + // 1. 创建大文件 (500KB) + console.log('正在生成 500KB 测试文件...'); + let content = await fs.readFile(sourceFile, 'utf8'); + // 重复内容直到达到 500KB + while (Buffer.byteLength(content) < 500 * 1024) { + content += '\n// Padding content to increase file size...\n' + content; + } + // 确保头部有 @header + if (!content.includes('@header(')) { + console.warn('源文件没有 @header,测试可能无效'); + } + + await fs.writeFile(testFile, content); + const stats = await fs.stat(testFile); + console.log(`测试文件大小: ${(stats.size / 1024).toFixed(2)} KB`); + + const iterations = 100; // 大文件操作慢,减少次数 + + // 2. 测试现有 String 模式 (FileHeaderManager.removeHeader) + console.log(`\n[String Mode] removeHeader 测试 (${iterations} 次)...`); + + // 预热 + const header = await FileHeaderManager.readHeader(testFile); + + const startString = performance.now(); + for (let i = 0; i < iterations; i++) { + // removeHeader 目前只返回字符串,不写入 + // 为了模拟真实场景,我们需要包含 fs.readFile 和 fs.writeFile + // FileHeaderManager.removeHeader 内部如果不传 path 传 content,则不包含读。 + // 如果传 path,则包含读。 + // 但它不包含写。 + + // 模拟完整流程:读 -> 处理 -> 写 + const newContent = await FileHeaderManager.removeHeader(testFile); // 包含 readFile + await fs.writeFile(testFile, newContent); + + // 恢复文件头以便下一次循环 (为了测试 remove,必须先有) + // 这步不计入时间?不,这很麻烦。 + // 我们可以测试 "读取 -> 替换(即使没找到也算处理) -> 写入" 的开销 + // 或者只测试 removeHeader 的处理部分。 + + // 为了简单,我们测试 "Write Header" 吧,因为 writeHeader 包含 读 -> 改 -> 写 + await FileHeaderManager.writeHeader(testFile, header, { createBackup: false }); + } + const endString = performance.now(); + const timeString = endString - startString; + console.log(`String Mode 总耗时: ${timeString.toFixed(2)} ms`); + console.log(`String Mode 平均耗时: ${(timeString / iterations).toFixed(2)} ms`); + + + // 3. 测试模拟 Buffer 模式 + // 由于 FileHeaderManager 没有 Buffer 模式,我们只能模拟 "读 Buffer -> 改 Buffer -> 写 Buffer" + // 对比 "读 String -> 改 String -> 写 String" + + console.log(`\n[Buffer Mode (Simulated)] writeHeader 测试 (${iterations} 次)...`); + + const startBuffer = performance.now(); + for (let i = 0; i < iterations; i++) { + const handle = await fs.open(testFile, 'r'); + const bufStats = await handle.stat(); + const buffer = Buffer.alloc(bufStats.size); + await handle.read(buffer, 0, bufStats.size, 0); + await handle.close(); + + // 模拟修改:在 Buffer 中找到 header 位置并替换 + // 实际算法: + // 1. 找到 comment block (buffer.indexOf) -> 极快 + // 2. 将 comment block 转 string (很短) -> 极快 + // 3. 在 comment string 中找 header -> 极快 + // 4. 拼接 Buffer -> 极快 (不需要编解码大段代码) + + const startComment = buffer.indexOf('/*'); + const endComment = buffer.indexOf('*/', startComment); + + // 假设我们找到了 offset,进行拼接 + // 这里不做真实解析,只做 Buffer 操作模拟开销 + const newBuf = Buffer.concat([ + buffer.subarray(0, startComment), + Buffer.from('/* Updated Header */'), // 模拟新头 + buffer.subarray(endComment + 2) + ]); + + await fs.writeFile(testFile, newBuf); + + // 恢复 (其实上面已经写入了,不需要额外恢复,因为每次都覆盖) + } + const endBuffer = performance.now(); + const timeBuffer = endBuffer - startBuffer; + console.log(`Buffer Mode 总耗时: ${timeBuffer.toFixed(2)} ms`); + console.log(`Buffer Mode 平均耗时: ${(timeBuffer / iterations).toFixed(2)} ms`); + + const improvement = ((timeString - timeBuffer) / timeString * 100).toFixed(2); + console.log(`\n🚀 预计 Buffer 模式提升: ${improvement}%`); + + // 清理 + await fs.unlink(testFile); +} + +runTest(); diff --git a/utils/fileHeaderManager.js b/utils/fileHeaderManager.js index f9ca5b40..323092f1 100644 --- a/utils/fileHeaderManager.js +++ b/utils/fileHeaderManager.js @@ -208,13 +208,6 @@ class FileHeaderManager { throw new Error('Invalid header object'); } - let content; - try { - content = await fs.readFile(filePath, 'utf8'); - } catch (error) { - throw new Error(`Failed to read file: ${error.message}`); - } - const ext = path.extname(filePath); const config = this.COMMENT_CONFIG[ext]; @@ -222,6 +215,19 @@ class FileHeaderManager { const headerStr = `@header(${JSON5.stringify(headerObj, null, 2)})`; + // 尝试使用 Buffer 优化写入 (针对已存在 header 的情况) + const bufferSuccess = await this._replaceHeaderBuffer(filePath, headerStr, config, ext); + if (bufferSuccess) { + return; + } + + let content; + try { + content = await fs.readFile(filePath, 'utf8'); + } catch (error) { + throw new Error(`Failed to read file: ${error.message}`); + } + // 优化:先尝试只读取头部来匹配正则,避免全量匹配 // 但由于 writeHeader 需要重写整个文件,全量内容是必须的 // 所以这里的优化点主要在于减少不必要的全量正则匹配 @@ -371,6 +377,119 @@ class FileHeaderManager { } } + /** + * 使用 Buffer 高效地替换文件头,避免大文件 String 转换开销 + * @private + */ + static async _replaceHeaderBuffer(filePath, headerStr, config, ext) { + let handle; + try { + handle = await fs.open(filePath, 'r'); + const stats = await handle.stat(); + + // 如果文件太小,Buffer 优化的开销可能不划算,fallback 到 string 处理 + // 或者如果文件太大 (如 > 5MB),我们只读取前 64KB 寻找 header + const MAX_SCAN_SIZE = 64 * 1024; // 64KB + const scanSize = Math.min(stats.size, MAX_SCAN_SIZE); + + const buffer = Buffer.alloc(scanSize); + await handle.read(buffer, 0, scanSize, 0); + await handle.close(); + handle = null; + + // 将 buffer 转为 string 进行正则匹配 (只转前 64KB) + const contentHead = buffer.toString('utf8'); + + const match = contentHead.match(config.regex); + + if (match) { + const [fullComment] = match; + // 找到 headerBlock + const headerBlock = this.findHeaderBlock(fullComment, ext); + + if (headerBlock) { + // 计算 headerBlock 在文件中的 byte offset + // 注意:fullComment 是 regex 匹配出来的 string + // 我们需要找到 fullComment 在 buffer 中的 byte offset + + // Buffer.indexOf(string) 可以找到 byte offset + // 但 regex 匹配的 fullComment 可能包含 unicode,直接 indexOf string 是安全的 + // 前提是 encoding 一致 (utf8) + + const commentStartOffset = buffer.indexOf(fullComment); + if (commentStartOffset === -1) { + // Fallback if not found (encoding issues?) + return false; + } + + // headerBlock.start 是相对于 fullComment 字符串的 char index + // 我们需要 byte index。这有点麻烦,因为 fullComment 可能包含多字节字符。 + // 所以我们需要把 fullComment.substring(0, headerBlock.start) 转为 Buffer 算长度 + + const preHeaderStr = fullComment.substring(0, headerBlock.start); + const preHeaderLen = Buffer.byteLength(preHeaderStr); + + const postHeaderStr = fullComment.substring(headerBlock.end); + + // 构造新的 header buffer + const newHeaderBuf = Buffer.from(headerStr); + + // 计算替换点 + const replaceStart = commentStartOffset + preHeaderLen; + + // 原有的 header content byte length + const oldHeaderContentStr = headerBlock.content; // 这里包含了 header(...) 括号内的内容? + // findHeaderBlock 返回的是 {start, end, content} + // start/end 是相对于 fullComment 的 index + // content 是 header(...) 括号内的内容,还是 @header(...)? + // 看 findHeaderBlock 实现: + // content: text.substring(startIndex + startMarker.length, index) -> 只是括号内的内容 + // start: startIndex ( "@" 的位置 ) + // end: index + 1 ( ")" 后面的位置 ) + + // 所以 headerBlock.start 指向 "@header" 的 "@" + // headerBlock.end 指向 ")" 后面 + + // 原始的 header 部分 (string) + const oldHeaderFullStr = fullComment.substring(headerBlock.start, headerBlock.end); + const oldHeaderByteLen = Buffer.byteLength(oldHeaderFullStr); + + // 准备写入 + // 如果新旧 header 长度一致,可以直接 overwrite (极快) + // 如果不一致,需要重写文件剩余部分 + + if (newHeaderBuf.length === oldHeaderByteLen) { + // Overwrite inplace + const writeHandle = await fs.open(filePath, 'r+'); + await writeHandle.write(newHeaderBuf, 0, newHeaderBuf.length, replaceStart); + await writeHandle.close(); + return true; + } else { + // 重写文件 + // 读取整个文件为 Buffer 然后 concat + // 避免 string 转换 + + const fullFileBuf = await fs.readFile(filePath); + const finalBuf = Buffer.concat([ + fullFileBuf.subarray(0, replaceStart), + newHeaderBuf, + fullFileBuf.subarray(replaceStart + oldHeaderByteLen) + ]); + + await fs.writeFile(filePath, finalBuf); + return true; + } + } + } + + return false; // Fallback to string mode if no match or complex case + } catch (e) { + if (handle) await handle.close(); + return false; + } + } + + /** * 移除头信息区域 * @param {string} input 文件路径或文件内容 From 6cd3d62884e4ecfec7246f89785685c32b058bf7 Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 11:56:47 +0800 Subject: [PATCH 07/17] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=A4=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test/test_header_parser_perf.js | 162 ++++++++++++++++++++++++ utils/fileHeaderManager.js | 90 ++++++------- 2 files changed, 209 insertions(+), 43 deletions(-) create mode 100644 scripts/test/test_header_parser_perf.js diff --git a/scripts/test/test_header_parser_perf.js b/scripts/test/test_header_parser_perf.js new file mode 100644 index 00000000..983828cd --- /dev/null +++ b/scripts/test/test_header_parser_perf.js @@ -0,0 +1,162 @@ + +import FileHeaderManager from '../../utils/fileHeaderManager.js'; + +// 旧的算法实现 (为了对比) +function findHeaderBlockOld(text, ext) { + const startMarker = '@header('; + const startIndex = text.indexOf(startMarker); + if (startIndex === -1) return null; + + let index = startIndex + startMarker.length; + let balance = 1; + let inString = false; + let stringChar = ''; + let escape = false; + let inLineComment = false; + let inBlockComment = false; + + for (; index < text.length; index++) { + const char = text[index]; + + if (inLineComment) { + if (char === '\n') inLineComment = false; + continue; + } + + if (inBlockComment) { + if (char === '*' && text[index + 1] === '/') { + inBlockComment = false; + index++; + } + continue; + } + + if (inString) { + if (escape) { + escape = false; + } else if (char === '\\') { + escape = true; + } else if (char === stringChar) { + inString = false; + } + continue; + } + + // Start of comment + if (char === '/' && text[index + 1] === '/') { + inLineComment = true; + index++; + } else if (char === '/' && text[index + 1] === '*') { + inBlockComment = true; + index++; + } else if (ext === '.py' && char === '#') { + inLineComment = true; + } else if (char === '"' || char === "'") { + inString = true; + stringChar = char; + } else if (char === '(') { + balance++; + } else if (char === ')') { + balance--; + if (balance === 0) { + return { + start: startIndex, + end: index + 1, + content: text.substring(startIndex + startMarker.length, index) + }; + } + } + } + return null; +} + +// 构造测试数据 +const simpleHeader = ` +/* +@header({ + title: "Simple", + version: 1 +}) +*/ +`; + +const complexHeader = ` +/* +@header({ + title: "Complex Header", + description: "Contains strings with parens ) and comments // inside strings", + nested: { + obj: { a: 1, b: 2 }, + arr: [1, 2, 3, "string with )"] + }, + regex: "cctv://(.+)", + // This is a comment inside the block + /* Another block comment */ + code: "function() { return 'ok'; }" +}) +*/ +`; + +const largeHeader = ` +/* +@header({ + title: "Large Header", + data: "${'x'.repeat(5000)}", + items: [ + ${Array(100).fill('{ name: "item", value: "test string with ) inside" }').join(',\n')} + ] +}) +*/ +`; + +async function runBenchmark() { + console.log('=== findHeaderBlock 算法性能基准测试 ==='); + const iterations = 100000; // 增加循环次数以放大差异 + + const testCases = [ + { name: 'Simple Header', data: simpleHeader }, + { name: 'Complex Header', data: complexHeader }, + { name: 'Large Header (~10KB)', data: largeHeader } + ]; + + for (const testCase of testCases) { + console.log(`\n测试场景: ${testCase.name} (循环 ${iterations} 次)`); + + // 1. 测试旧算法 + const startOld = performance.now(); + for (let i = 0; i < iterations; i++) { + findHeaderBlockOld(testCase.data, '.js'); + } + const endOld = performance.now(); + const timeOld = endOld - startOld; + console.log(`[旧算法] 总耗时: ${timeOld.toFixed(4)} ms | 平均: ${(timeOld/iterations).toFixed(5)} ms`); + + // 2. 测试新算法 + const startNew = performance.now(); + for (let i = 0; i < iterations; i++) { + FileHeaderManager.findHeaderBlock(testCase.data, '.js'); + } + const endNew = performance.now(); + const timeNew = endNew - startNew; + console.log(`[新算法] 总耗时: ${timeNew.toFixed(4)} ms | 平均: ${(timeNew/iterations).toFixed(5)} ms`); + + // 3. 计算提升 + const improvement = ((timeOld - timeNew) / timeOld * 100).toFixed(2); + console.log(`🚀 性能提升: ${improvement}%`); + + // 验证正确性 + const resOld = findHeaderBlockOld(testCase.data, '.js'); + const resNew = FileHeaderManager.findHeaderBlock(testCase.data, '.js'); + if (resOld?.content !== resNew?.content) { + console.error('❌ 结果不一致!'); + console.log('Old Content Length:', resOld?.content?.length); + console.log('New Content Length:', resNew?.content?.length); + // console.log('Old:', resOld?.content); + // console.log('New:', resNew?.content); + } else { + console.log('✅ 结果一致'); + } + } +} + +runBenchmark(); diff --git a/utils/fileHeaderManager.js b/utils/fileHeaderManager.js index 323092f1..f327cc62 100644 --- a/utils/fileHeaderManager.js +++ b/utils/fileHeaderManager.js @@ -37,6 +37,7 @@ class FileHeaderManager { /** * Find the @header(...) block in the comment text + * Optimized state machine for parsing nested structures * @param {string} text Comment text * @param {string} ext File extension (.js or .py) * @returns {Object|null} { start, end, content } @@ -48,52 +49,31 @@ class FileHeaderManager { let index = startIndex + startMarker.length; let balance = 1; - let inString = false; - let stringChar = ''; - let escape = false; - let inLineComment = false; - let inBlockComment = false; - - for (; index < text.length; index++) { + const len = text.length; + + // Fast scan loop + while (index < len) { const char = text[index]; - if (inLineComment) { - if (char === '\n') inLineComment = false; - continue; - } - - if (inBlockComment) { - if (char === '*' && text[index + 1] === '/') { - inBlockComment = false; - index++; - } - continue; - } - - if (inString) { - if (escape) { - escape = false; - } else if (char === '\\') { - escape = true; - } else if (char === stringChar) { - inString = false; - } - continue; - } - - // Start of comment - if (char === '/' && text[index + 1] === '/') { - inLineComment = true; - index++; - } else if (char === '/' && text[index + 1] === '*') { - inBlockComment = true; + // 1. String literal detection (Most common content inside JSON) + if (char === '"' || char === "'") { + const quote = char; index++; - } else if (ext === '.py' && char === '#') { - inLineComment = true; - } else if (char === '"' || char === "'") { - inString = true; - stringChar = char; - } else if (char === '(') { + while (index < len) { + const c = text[index]; + if (c === '\\') { + index += 2; // Skip escaped char + } else if (c === quote) { + index++; // Include closing quote + break; // End of string + } else { + index++; + } + } + continue; // Continue outer loop + } + // 2. Parentheses balance + else if (char === '(') { balance++; } else if (char === ')') { balance--; @@ -105,6 +85,30 @@ class FileHeaderManager { }; } } + // 3. Comments skipping (Only if strictly needed inside header object, usually standard JSON doesn't have comments but JS objects might) + // Optimization: Assume standard JSON5/JS object format inside @header, check for comments only if / or # encountered + else if (char === '/') { + const next = text[index + 1]; + if (next === '/') { // Line comment + index += 2; + const newline = text.indexOf('\n', index); + index = newline === -1 ? len : newline; + continue; + } else if (next === '*') { // Block comment + index += 2; + const endComment = text.indexOf('*/', index); + if (endComment === -1) { index = len; } + else { index = endComment + 2; } + continue; + } + } + else if (ext === '.py' && char === '#') { // Python comment + const newline = text.indexOf('\n', index + 1); + index = newline === -1 ? len : newline; + continue; + } + + index++; } return null; } From 8a379fe28fbdac02526e11f31795ac8065d04771 Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 17:41:14 +0800 Subject: [PATCH 08/17] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=A4=B8?= =?UTF-8?q?=E5=85=8B=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drpy-node-bundle/libs/localDsCore.bundled.js | 140 ++- drpy-node-bundle/spider/js/_lib.tingyou.js | 922 ++++++++++++++++++ ...\345\220\254\345\217\213[\345\220\254].js" | 300 ++++++ package-bundle.js | 3 +- spider/js/_lib.tingyou.js | 922 ++++++++++++++++++ ...\345\220\254\345\217\213[\345\220\254].js" | 300 ++++++ utils/pan/quark.js | 4 +- 7 files changed, 2541 insertions(+), 50 deletions(-) create mode 100644 drpy-node-bundle/spider/js/_lib.tingyou.js create mode 100644 "drpy-node-bundle/spider/js/\345\220\254\345\217\213[\345\220\254].js" create mode 100644 spider/js/_lib.tingyou.js create mode 100644 "spider/js/\345\220\254\345\217\213[\345\220\254].js" diff --git a/drpy-node-bundle/libs/localDsCore.bundled.js b/drpy-node-bundle/libs/localDsCore.bundled.js index e67e3f1d..4215f478 100644 --- a/drpy-node-bundle/libs/localDsCore.bundled.js +++ b/drpy-node-bundle/libs/localDsCore.bundled.js @@ -115060,7 +115060,7 @@ var QuarkHandler = class { "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "referer": "https://pan.quark.cn/", - "Cookie": "_UP_A4A_11_=wb9ce1f8a7f74209b248cb5877a6a876; _UP_D_=pc; tfstk=gy2rZY6dQTBPmYbgblDFQOhX71M-pv7_ZJgIxkqnV40oVYZ3gkZN2Da7x2PEoyIJFYbJK2qTkbguRYTFLrko97gIP2kUvPSf5O6_2uHKEN_1C6GsZ4kK-9MhGxcbeD-R5O6_quHKKN__NjmeBVnnK0co-Ejq2qmoxy0oiEmZXv03-yj4mqnnK24nKirmkDDn-ycsi2063cllqNA0iO9nAbugqqJU2-ooaQEoupv33dGrSyg2Kp2q2kJ_DBpNOVVtPfguot9rLomUufNP7LzUxlFngJ8lVP40TymYMwRE7Wq76-GXxG0geo2tnj6Gbo2YPue4AHXncPg028DFIGHrE4DqDJ_vE2P0t8G-pUbEJ-r0EWSz_enc4z2LaeAEZmnq5iSVbveZi_88v3Rp9bFx0VsR2BdKZmnq5iS29BhoDmu10i5..; __uid=AARfyLe8Bke3UGL2FjhvNQn6; __sdid=AATHIp7rA8mvfKhhMvPnt0milUEzXi1ReJN0CjChc4LeUA6qYUdYA32jmaL/Lk2AA4s=; __pus=bd604e2725229c945e994c227de77f1cAASVpc7pXsTooap3JBI0XiJI9JU/JUngO9SJCOIRNOLXngLdOfozAiiwF9CnY/xOiEgE8NvKDDg1ZAwX7nVqe9H2ib5aMTAi+a2DfaPE6/6Won1vUOAfKCNELRmJOeYhVn3/eQUYx6B9EIw9HCYjQoKVh76daPI2lnp3QBTK/5zWGya9rVul08y86xpY3APre7I=; __kp=eb241d80-1ee6-11f1-85bf-6167dfcf1acd; __kuus=NenaSYNIw/O9SzABV2HtnjTDoH5aJthk7nOgDdt9pHeaJmMkCD/TY+jVFIrCn+WeFaQVR9h+E/YoStTBZAtB9va0ghzlZCgNuddki8Z8WOnYug==; __puus=2baf96cce907422be5e242bb1d280977AATi54Q/fKnsA5nM/iG/TZHL12hYZj/ELuUFbEwO/2jIXaSGwmvXppDKCITu73rdsZ1hRR0cY2QRpWdM0o+nFv65fCf5ZwPIaGHvUK9BOg3653jTngpTAj41u8kq5vTIDnPDPGWlYPYiUehDbyYBwxKYl12BFSjonkfKybr3Fpc2TYQcm566OQTrPzsupcOkOoa8CQllYvsoVyEBF2ZpiX/5" + "Cookie": this.cookie }, data }; @@ -115087,7 +115087,7 @@ var QuarkHandler = class { "Accept": "*/*,application/json;charset=utf-8", "Accept-Encoding": "gzip, deflate, br, zstd", "Content-Type": "application/json", - "Cookie": "b-user-id=7a421e0c-2ff4-1251-4069-cdfbfedcaf8d; _UP_A4A_11_=wb9cf15ce78d47ed921a9fab3faa85e8; xlly_s=1; __sdid=AAQCvlprpJtHMDSTmliW7CTXaxFJONVhZZyKc8NgJ6Y+XH4iO1v3Q8gYFeEi2hqeV4U=; _UP_D_=pc; __pus=97012d074b8d70d6eeaeb7dbe3fcb05dAAQB4QUahCaghYFA9y/NYud63QbrjWpcO/mp4OgGcA2UZ5O+VMUlfCpacaDzbRBV2O/DK9y9iAFBUL7iPmzdj2d9; __kp=ea759a90-1ed1-11f1-8f67-e7e56f2ee996; __kps=AARfyLe8Bke3UGL2FjhvNQn6; __ktd=axb8KS+96BcSmmEd+gddqg==; __uid=AARfyLe8Bke3UGL2FjhvNQn6; __puus=c6ca8123ab4024f139092c58678de061AATi54Q/fKnsA5nM/iG/TZHLzf8buD1i2D3OKKcgKFxmYvTq9ypLK3mbAQlXFlJz6zEGV7aROE4eXsRbMGviOH4EpwIMv8HZ3UUDPfRoIVsx+mV9xMqXujCxpQR96l9Alpe2B15pFBZl94/lBMSORn+M/cocX9mAe2wX82JvYhQYO46JRC1evvevyyyzaai9ItKcf72BRC6QzioBIic/XHI8; isg=BDg4TjMppS-k-Mk0_qdGtZTlCebKoZwrBz4YK3KgQXMmjdJ3GrWXu63lQY093VQD; tfstk=gTJiuT2wCC5_fZaEErWsOziN8jlKBO6X3EeAktQqTw7Ckctv0ty2knBOgVtv-Z7F2SJA0f_cTUs7_CCZSemDWnwA7NpAns8RR5Rx5qtFmULRuiSqcMkeuUYcGc_AuZYv0CnKeYK6ft62o4H-emTk0mLcbsyaLmScDPod_tj5i4Wqy4HL9k5sWt-TUIxGLH7CmiyNuEWULi7Ubl8V76zFqg6VuE8VTWSRcGPN7N-UTwsV3Z8V3DXFRiXVuEWqxHllpf763pJErkLuNeO_cCsGsa-N7nKvLPWT1nQ33-JHt1brQw243pjM09gPi8cAzQ_Owa8EpRXDYiYhwC0uIEx2VI5HnPo9zeRDuMOIoSbkghdWp6E4_HvcS_JNtoiJ4hs2usAIr8IGBBfkICib8h8RSQW6Du09x_AhNM5Um5W9w3pfadkgPwC5mefJs42MzglbT7y5t-sEDpPbG1SCxapyAdOShopf3Dm3am1NAG_-xDVbG1SCxannx7kf_Ms1y" + "Cookie": this.cookie }, data }; @@ -383630,6 +383630,7 @@ var FileHeaderManager = class { }; /** * Find the @header(...) block in the comment text + * Optimized state machine for parsing nested structures * @param {string} text Comment text * @param {string} ext File extension (.js or .py) * @returns {Object|null} { start, end, content } @@ -383639,40 +383640,21 @@ var FileHeaderManager = class { if (startIndex === -1) return null; let index = startIndex + 8; let balance = 1; - let inString = false; - let stringChar = ""; - let escape = false; - let inLineComment = false; - let inBlockComment = false; - for (; index < text.length; index++) { + const len = text.length; + while (index < len) { const char = text[index]; - if (inLineComment) { - if (char === "\n") inLineComment = false; - continue; - } - if (inBlockComment) { - if (char === "*" && text[index + 1] === "/") { - inBlockComment = false; - index++; + if (char === "\"" || char === "'") { + const quote = char; + index++; + while (index < len) { + const c = text[index]; + if (c === "\\") index += 2; + else if (c === quote) { + index++; + break; + } else index++; } continue; - } - if (inString) { - if (escape) escape = false; - else if (char === "\\") escape = true; - else if (char === stringChar) inString = false; - continue; - } - if (char === "/" && text[index + 1] === "/") { - inLineComment = true; - index++; - } else if (char === "/" && text[index + 1] === "*") { - inBlockComment = true; - index++; - } else if (ext === ".py" && char === "#") inLineComment = true; - else if (char === "\"" || char === "'") { - inString = true; - stringChar = char; } else if (char === "(") balance++; else if (char === ")") { balance--; @@ -383681,7 +383663,26 @@ var FileHeaderManager = class { end: index + 1, content: text.substring(startIndex + 8, index) }; + } else if (char === "/") { + const next = text[index + 1]; + if (next === "/") { + index += 2; + const newline = text.indexOf("\n", index); + index = newline === -1 ? len : newline; + continue; + } else if (next === "*") { + index += 2; + const endComment = text.indexOf("*/", index); + if (endComment === -1) index = len; + else index = endComment + 2; + continue; + } + } else if (ext === ".py" && char === "#") { + const newline = text.indexOf("\n", index + 1); + index = newline === -1 ? len : newline; + continue; } + index++; } return null; } @@ -383761,17 +383762,17 @@ var FileHeaderManager = class { const { createBackup = false } = options; if (!filePath || typeof filePath !== "string") throw new Error("Invalid file path"); if (!headerObj || typeof headerObj !== "object") throw new Error("Invalid header object"); + const ext = path.extname(filePath); + const config = this.COMMENT_CONFIG[ext]; + if (!config) throw new Error(`Unsupported file type: ${ext}`); + const headerStr = `@header(${JSON5.stringify(headerObj, null, 2)})`; + if (await this._replaceHeaderBuffer(filePath, headerStr, config, ext)) return; let content; try { content = await fs$1.readFile(filePath, "utf8"); } catch (error) { throw new Error(`Failed to read file: ${error.message}`); } - const originalContent = content; - const ext = path.extname(filePath); - const config = this.COMMENT_CONFIG[ext]; - if (!config) throw new Error(`Unsupported file type: ${ext}`); - const headerStr = `@header(${JSON5.stringify(headerObj, null, 2)})`; const match = content.match(config.regex); let newContent; if (match) { @@ -383809,15 +383810,8 @@ var FileHeaderManager = class { } if (!newContent.replace(config.regex, "").trim()) throw new Error("写入失败:内容不能只包含文件头而无原始内容"); if (!newContent || newContent.trim().length === 0) throw new Error("Generated content is empty, operation aborted"); - const originalCodeLines = originalContent.split("\n").filter((line) => { - const trimmed = line.trim(); - return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''") && !trimmed.startsWith(" { - const trimmed = line.trim(); - return trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") && !trimmed.startsWith("#") && !trimmed.startsWith("\"\"\"") && !trimmed.startsWith("'''") && !trimmed.startsWith(" 5 && newCodeLines.length < originalCodeLines.length * .8) throw new Error("Content integrity check failed: significant code loss detected, operation aborted"); + const diffRatio = Math.abs(newContent.length - content.length) / content.length; + if (content.length > 100 && diffRatio > .5 && newContent.length < content.length) throw new Error("Content integrity check failed: significant size reduction detected, operation aborted"); let backupPath = null; if (createBackup) try { backupPath = await this.createBackup(filePath); @@ -383842,6 +383836,58 @@ var FileHeaderManager = class { } } /** + * 使用 Buffer 高效地替换文件头,避免大文件 String 转换开销 + * @private + */ + static async _replaceHeaderBuffer(filePath, headerStr, config, ext) { + let handle; + try { + handle = await fs$1.open(filePath, "r"); + const stats = await handle.stat(); + const scanSize = Math.min(stats.size, 64 * 1024); + const buffer = Buffer.alloc(scanSize); + await handle.read(buffer, 0, scanSize, 0); + await handle.close(); + handle = null; + const match = buffer.toString("utf8").match(config.regex); + if (match) { + const [fullComment] = match; + const headerBlock = this.findHeaderBlock(fullComment, ext); + if (headerBlock) { + const commentStartOffset = buffer.indexOf(fullComment); + if (commentStartOffset === -1) return false; + const preHeaderStr = fullComment.substring(0, headerBlock.start); + const preHeaderLen = Buffer.byteLength(preHeaderStr); + const postHeaderStr = fullComment.substring(headerBlock.end); + const newHeaderBuf = Buffer.from(headerStr); + const replaceStart = commentStartOffset + preHeaderLen; + const oldHeaderContentStr = headerBlock.content; + const oldHeaderFullStr = fullComment.substring(headerBlock.start, headerBlock.end); + const oldHeaderByteLen = Buffer.byteLength(oldHeaderFullStr); + if (newHeaderBuf.length === oldHeaderByteLen) { + const writeHandle = await fs$1.open(filePath, "r+"); + await writeHandle.write(newHeaderBuf, 0, newHeaderBuf.length, replaceStart); + await writeHandle.close(); + return true; + } else { + const fullFileBuf = await fs$1.readFile(filePath); + const finalBuf = Buffer.concat([ + fullFileBuf.subarray(0, replaceStart), + newHeaderBuf, + fullFileBuf.subarray(replaceStart + oldHeaderByteLen) + ]); + await fs$1.writeFile(filePath, finalBuf); + return true; + } + } + } + return false; + } catch (e) { + if (handle) await handle.close(); + return false; + } + } + /** * 移除头信息区域 * @param {string} input 文件路径或文件内容 * @param {Object} [options] 配置选项 diff --git a/drpy-node-bundle/spider/js/_lib.tingyou.js b/drpy-node-bundle/spider/js/_lib.tingyou.js new file mode 100644 index 00000000..410345d6 --- /dev/null +++ b/drpy-node-bundle/spider/js/_lib.tingyou.js @@ -0,0 +1,922 @@ +function Co(e) { + for (let t = 0; t < e.length; t++) + e[t] = 0; + return e +} + +function _o(e, t=new Uint8Array(4), n=0) { + return t[n + 0] = e >>> 0, + t[n + 1] = e >>> 8, + t[n + 2] = e >>> 16, + t[n + 3] = e >>> 24, + t +} + +function Do(e, t, n) { + let a = 1634760805 + , o = 857760878 + , r = 2036477234 + , s = 1797285236 + , i = e[3] << 24 | e[2] << 16 | e[1] << 8 | e[0] + , l = e[7] << 24 | e[6] << 16 | e[5] << 8 | e[4] + , c = e[11] << 24 | e[10] << 16 | e[9] << 8 | e[8] + , u = e[15] << 24 | e[14] << 16 | e[13] << 8 | e[12] + , d = e[19] << 24 | e[18] << 16 | e[17] << 8 | e[16] + , f = e[23] << 24 | e[22] << 16 | e[21] << 8 | e[20] + , v = e[27] << 24 | e[26] << 16 | e[25] << 8 | e[24] + , p = e[31] << 24 | e[30] << 16 | e[29] << 8 | e[28] + , h = t[3] << 24 | t[2] << 16 | t[1] << 8 | t[0] + , m = t[7] << 24 | t[6] << 16 | t[5] << 8 | t[4] + , y = t[11] << 24 | t[10] << 16 | t[9] << 8 | t[8] + , g = t[15] << 24 | t[14] << 16 | t[13] << 8 | t[12]; + for (let k = 0; k < 20; k += 2) + a = a + i | 0, + h ^= a, + h = h >>> 16 | h << 16, + d = d + h | 0, + i ^= d, + i = i >>> 20 | i << 12, + o = o + l | 0, + m ^= o, + m = m >>> 16 | m << 16, + f = f + m | 0, + l ^= f, + l = l >>> 20 | l << 12, + r = r + c | 0, + y ^= r, + y = y >>> 16 | y << 16, + v = v + y | 0, + c ^= v, + c = c >>> 20 | c << 12, + s = s + u | 0, + g ^= s, + g = g >>> 16 | g << 16, + p = p + g | 0, + u ^= p, + u = u >>> 20 | u << 12, + r = r + c | 0, + y ^= r, + y = y >>> 24 | y << 8, + v = v + y | 0, + c ^= v, + c = c >>> 25 | c << 7, + s = s + u | 0, + g ^= s, + g = g >>> 24 | g << 8, + p = p + g | 0, + u ^= p, + u = u >>> 25 | u << 7, + o = o + l | 0, + m ^= o, + m = m >>> 24 | m << 8, + f = f + m | 0, + l ^= f, + l = l >>> 25 | l << 7, + a = a + i | 0, + h ^= a, + h = h >>> 24 | h << 8, + d = d + h | 0, + i ^= d, + i = i >>> 25 | i << 7, + a = a + l | 0, + g ^= a, + g = g >>> 16 | g << 16, + v = v + g | 0, + l ^= v, + l = l >>> 20 | l << 12, + o = o + c | 0, + h ^= o, + h = h >>> 16 | h << 16, + p = p + h | 0, + c ^= p, + c = c >>> 20 | c << 12, + r = r + u | 0, + m ^= r, + m = m >>> 16 | m << 16, + d = d + m | 0, + u ^= d, + u = u >>> 20 | u << 12, + s = s + i | 0, + y ^= s, + y = y >>> 16 | y << 16, + f = f + y | 0, + i ^= f, + i = i >>> 20 | i << 12, + r = r + u | 0, + m ^= r, + m = m >>> 24 | m << 8, + d = d + m | 0, + u ^= d, + u = u >>> 25 | u << 7, + s = s + i | 0, + y ^= s, + y = y >>> 24 | y << 8, + f = f + y | 0, + i ^= f, + i = i >>> 25 | i << 7, + o = o + c | 0, + h ^= o, + h = h >>> 24 | h << 8, + p = p + h | 0, + c ^= p, + c = c >>> 25 | c << 7, + a = a + l | 0, + g ^= a, + g = g >>> 24 | g << 8, + v = v + g | 0, + l ^= v, + l = l >>> 25 | l << 7; + return _o(a, n, 0), + _o(o, n, 4), + _o(r, n, 8), + _o(s, n, 12), + _o(h, n, 16), + _o(m, n, 20), + _o(y, n, 24), + _o(g, n, 28), + n +} + +class Po { + nonceLength = 24; + tagLength = 16; + _key; + constructor(e) { + if (32 !== e.length) + throw new Error("ChaCha20Poly1305 needs 32-byte key"); + this._key = new Uint8Array(e) + } + seal(e, t, n, a) { + if (24 !== e.length) + throw new Error("XChaCha20Poly1305: incorrect nonce length"); + const o = Do(this._key, e.subarray(0, 16), new Uint8Array(32)) + , r = new Uint8Array(12); + r.set(e.subarray(16), 4); + const s = new jo(o) + , i = s.seal(r, t, n, a); + return Co(o), + Co(r), + s.clean(), + i + } + open(e, t, n, a) { + if (24 !== e.length) + throw new Error("XChaCha20Poly1305: incorrect nonce length"); + if (t.length < this.tagLength) + return null; + const o = Do(this._key, e.subarray(0, 16), new Uint8Array(32)) + , r = new Uint8Array(12); + r.set(e.subarray(16), 4); + const s = new jo(o) + , i = s.open(r, t, n, a); + return Co(o), + Co(r), + s.clean(), + i + } + clean() { + return Co(this._key), + this + } +} + +function So(e, t, n, a, o=0) { + if (32 !== e.length) + throw new Error("ChaCha: key size must be 32 bytes"); + if (a.length < n.length) + throw new Error("ChaCha: destination is shorter than source"); + let r, s; + if (0 === o) { + if (8 !== t.length && 12 !== t.length) + throw new Error("ChaCha nonce must be 8 or 12 bytes"); + r = new Uint8Array(16), + s = r.length - t.length, + r.set(t, s) + } else { + if (16 !== t.length) + throw new Error("ChaCha nonce with counter must be 16 bytes"); + r = t, + s = o + } + const i = new Uint8Array(64); + for (let l = 0; l < n.length; l += 64) { + Lo(i, r, e); + for (let e = l; e < l + 64 && e < n.length; e++) + a[e] = n[e] ^ i[e - l]; + Ao(r, 0, s) + } + return Co(i), + 0 === o && Co(r), + a +} + +function Ao(e, t, n) { + let a = 1; + for (; n--; ) + a = a + (255 & e[t]) | 0, + e[t] = 255 & a, + a >>>= 8, + t++; + if (a > 0) + throw new Error("ChaCha: counter overflow") +} + +function Lo(e, t, n) { + let a = 1634760805 + , o = 857760878 + , r = 2036477234 + , s = 1797285236 + , i = n[3] << 24 | n[2] << 16 | n[1] << 8 | n[0] + , l = n[7] << 24 | n[6] << 16 | n[5] << 8 | n[4] + , c = n[11] << 24 | n[10] << 16 | n[9] << 8 | n[8] + , u = n[15] << 24 | n[14] << 16 | n[13] << 8 | n[12] + , d = n[19] << 24 | n[18] << 16 | n[17] << 8 | n[16] + , f = n[23] << 24 | n[22] << 16 | n[21] << 8 | n[20] + , v = n[27] << 24 | n[26] << 16 | n[25] << 8 | n[24] + , p = n[31] << 24 | n[30] << 16 | n[29] << 8 | n[28] + , h = t[3] << 24 | t[2] << 16 | t[1] << 8 | t[0] + , m = t[7] << 24 | t[6] << 16 | t[5] << 8 | t[4] + , y = t[11] << 24 | t[10] << 16 | t[9] << 8 | t[8] + , g = t[15] << 24 | t[14] << 16 | t[13] << 8 | t[12] + , k = a + , b = o + , w = r + , _ = s + , x = i + , C = l + , L = c + , S = u + , M = d + , A = f + , D = v + , E = p + , I = h + , j = m + , P = y + , T = g; + for (let R = 0; R < 20; R += 2) + k = k + x | 0, + I ^= k, + I = I >>> 16 | I << 16, + M = M + I | 0, + x ^= M, + x = x >>> 20 | x << 12, + b = b + C | 0, + j ^= b, + j = j >>> 16 | j << 16, + A = A + j | 0, + C ^= A, + C = C >>> 20 | C << 12, + w = w + L | 0, + P ^= w, + P = P >>> 16 | P << 16, + D = D + P | 0, + L ^= D, + L = L >>> 20 | L << 12, + _ = _ + S | 0, + T ^= _, + T = T >>> 16 | T << 16, + E = E + T | 0, + S ^= E, + S = S >>> 20 | S << 12, + w = w + L | 0, + P ^= w, + P = P >>> 24 | P << 8, + D = D + P | 0, + L ^= D, + L = L >>> 25 | L << 7, + _ = _ + S | 0, + T ^= _, + T = T >>> 24 | T << 8, + E = E + T | 0, + S ^= E, + S = S >>> 25 | S << 7, + b = b + C | 0, + j ^= b, + j = j >>> 24 | j << 8, + A = A + j | 0, + C ^= A, + C = C >>> 25 | C << 7, + k = k + x | 0, + I ^= k, + I = I >>> 24 | I << 8, + M = M + I | 0, + x ^= M, + x = x >>> 25 | x << 7, + k = k + C | 0, + T ^= k, + T = T >>> 16 | T << 16, + D = D + T | 0, + C ^= D, + C = C >>> 20 | C << 12, + b = b + L | 0, + I ^= b, + I = I >>> 16 | I << 16, + E = E + I | 0, + L ^= E, + L = L >>> 20 | L << 12, + w = w + S | 0, + j ^= w, + j = j >>> 16 | j << 16, + M = M + j | 0, + S ^= M, + S = S >>> 20 | S << 12, + _ = _ + x | 0, + P ^= _, + P = P >>> 16 | P << 16, + A = A + P | 0, + x ^= A, + x = x >>> 20 | x << 12, + w = w + S | 0, + j ^= w, + j = j >>> 24 | j << 8, + M = M + j | 0, + S ^= M, + S = S >>> 25 | S << 7, + _ = _ + x | 0, + P ^= _, + P = P >>> 24 | P << 8, + A = A + P | 0, + x ^= A, + x = x >>> 25 | x << 7, + b = b + L | 0, + I ^= b, + I = I >>> 24 | I << 8, + E = E + I | 0, + L ^= E, + L = L >>> 25 | L << 7, + k = k + C | 0, + T ^= k, + T = T >>> 24 | T << 8, + D = D + T | 0, + C ^= D, + C = C >>> 25 | C << 7; + _o(k + a | 0, e, 0), + _o(b + o | 0, e, 4), + _o(w + r | 0, e, 8), + _o(_ + s | 0, e, 12), + _o(x + i | 0, e, 16), + _o(C + l | 0, e, 20), + _o(L + c | 0, e, 24), + _o(S + u | 0, e, 28), + _o(M + d | 0, e, 32), + _o(A + f | 0, e, 36), + _o(D + v | 0, e, 40), + _o(E + p | 0, e, 44), + _o(I + h | 0, e, 48), + _o(j + m | 0, e, 52), + _o(P + y | 0, e, 56), + _o(T + g | 0, e, 60) +} + +class Eo { + digestLength = 16; + _buffer = new Uint8Array(16); + _r = new Uint16Array(10); + _h = new Uint16Array(10); + _pad = new Uint16Array(8); + _leftover = 0; + _fin = 0; + _finished = !1; + constructor(e) { + let t = e[0] | e[1] << 8; + this._r[0] = 8191 & t; + let n = e[2] | e[3] << 8; + this._r[1] = 8191 & (t >>> 13 | n << 3); + let a = e[4] | e[5] << 8; + this._r[2] = 7939 & (n >>> 10 | a << 6); + let o = e[6] | e[7] << 8; + this._r[3] = 8191 & (a >>> 7 | o << 9); + let r = e[8] | e[9] << 8; + this._r[4] = 255 & (o >>> 4 | r << 12), + this._r[5] = r >>> 1 & 8190; + let s = e[10] | e[11] << 8; + this._r[6] = 8191 & (r >>> 14 | s << 2); + let i = e[12] | e[13] << 8; + this._r[7] = 8065 & (s >>> 11 | i << 5); + let l = e[14] | e[15] << 8; + this._r[8] = 8191 & (i >>> 8 | l << 8), + this._r[9] = l >>> 5 & 127, + this._pad[0] = e[16] | e[17] << 8, + this._pad[1] = e[18] | e[19] << 8, + this._pad[2] = e[20] | e[21] << 8, + this._pad[3] = e[22] | e[23] << 8, + this._pad[4] = e[24] | e[25] << 8, + this._pad[5] = e[26] | e[27] << 8, + this._pad[6] = e[28] | e[29] << 8, + this._pad[7] = e[30] | e[31] << 8 + } + _blocks(e, t, n) { + let a = this._fin ? 0 : 2048 + , o = this._h[0] + , r = this._h[1] + , s = this._h[2] + , i = this._h[3] + , l = this._h[4] + , c = this._h[5] + , u = this._h[6] + , d = this._h[7] + , f = this._h[8] + , v = this._h[9] + , p = this._r[0] + , h = this._r[1] + , m = this._r[2] + , y = this._r[3] + , g = this._r[4] + , k = this._r[5] + , b = this._r[6] + , w = this._r[7] + , _ = this._r[8] + , x = this._r[9]; + for (; n >= 16; ) { + let C = e[t + 0] | e[t + 1] << 8; + o += 8191 & C; + let L = e[t + 2] | e[t + 3] << 8; + r += 8191 & (C >>> 13 | L << 3); + let S = e[t + 4] | e[t + 5] << 8; + s += 8191 & (L >>> 10 | S << 6); + let M = e[t + 6] | e[t + 7] << 8; + i += 8191 & (S >>> 7 | M << 9); + let A = e[t + 8] | e[t + 9] << 8; + l += 8191 & (M >>> 4 | A << 12), + c += A >>> 1 & 8191; + let D = e[t + 10] | e[t + 11] << 8; + u += 8191 & (A >>> 14 | D << 2); + let E = e[t + 12] | e[t + 13] << 8; + d += 8191 & (D >>> 11 | E << 5); + let I = e[t + 14] | e[t + 15] << 8; + f += 8191 & (E >>> 8 | I << 8), + v += I >>> 5 | a; + let j = 0 + , P = j; + P += o * p, + P += r * (5 * x), + P += s * (5 * _), + P += i * (5 * w), + P += l * (5 * b), + j = P >>> 13, + P &= 8191, + P += c * (5 * k), + P += u * (5 * g), + P += d * (5 * y), + P += f * (5 * m), + P += v * (5 * h), + j += P >>> 13, + P &= 8191; + let T = j; + T += o * h, + T += r * p, + T += s * (5 * x), + T += i * (5 * _), + T += l * (5 * w), + j = T >>> 13, + T &= 8191, + T += c * (5 * b), + T += u * (5 * k), + T += d * (5 * g), + T += f * (5 * y), + T += v * (5 * m), + j += T >>> 13, + T &= 8191; + let R = j; + R += o * m, + R += r * h, + R += s * p, + R += i * (5 * x), + R += l * (5 * _), + j = R >>> 13, + R &= 8191, + R += c * (5 * w), + R += u * (5 * b), + R += d * (5 * k), + R += f * (5 * g), + R += v * (5 * y), + j += R >>> 13, + R &= 8191; + let B = j; + B += o * y, + B += r * m, + B += s * h, + B += i * p, + B += l * (5 * x), + j = B >>> 13, + B &= 8191, + B += c * (5 * _), + B += u * (5 * w), + B += d * (5 * b), + B += f * (5 * k), + B += v * (5 * g), + j += B >>> 13, + B &= 8191; + let O = j; + O += o * g, + O += r * y, + O += s * m, + O += i * h, + O += l * p, + j = O >>> 13, + O &= 8191, + O += c * (5 * x), + O += u * (5 * _), + O += d * (5 * w), + O += f * (5 * b), + O += v * (5 * k), + j += O >>> 13, + O &= 8191; + let U = j; + U += o * k, + U += r * g, + U += s * y, + U += i * m, + U += l * h, + j = U >>> 13, + U &= 8191, + U += c * p, + U += u * (5 * x), + U += d * (5 * _), + U += f * (5 * w), + U += v * (5 * b), + j += U >>> 13, + U &= 8191; + let W = j; + W += o * b, + W += r * k, + W += s * g, + W += i * y, + W += l * m, + j = W >>> 13, + W &= 8191, + W += c * h, + W += u * p, + W += d * (5 * x), + W += f * (5 * _), + W += v * (5 * w), + j += W >>> 13, + W &= 8191; + let H = j; + H += o * w, + H += r * b, + H += s * k, + H += i * g, + H += l * y, + j = H >>> 13, + H &= 8191, + H += c * m, + H += u * h, + H += d * p, + H += f * (5 * x), + H += v * (5 * _), + j += H >>> 13, + H &= 8191; + let N = j; + N += o * _, + N += r * w, + N += s * b, + N += i * k, + N += l * g, + j = N >>> 13, + N &= 8191, + N += c * y, + N += u * m, + N += d * h, + N += f * p, + N += v * (5 * x), + j += N >>> 13, + N &= 8191; + let V = j; + V += o * x, + V += r * _, + V += s * w, + V += i * b, + V += l * k, + j = V >>> 13, + V &= 8191, + V += c * g, + V += u * y, + V += d * m, + V += f * h, + V += v * p, + j += V >>> 13, + V &= 8191, + j = (j << 2) + j | 0, + j = j + P | 0, + P = 8191 & j, + j >>>= 13, + T += j, + o = P, + r = T, + s = R, + i = B, + l = O, + c = U, + u = W, + d = H, + f = N, + v = V, + t += 16, + n -= 16 + } + this._h[0] = o, + this._h[1] = r, + this._h[2] = s, + this._h[3] = i, + this._h[4] = l, + this._h[5] = c, + this._h[6] = u, + this._h[7] = d, + this._h[8] = f, + this._h[9] = v + } + finish(e, t=0) { + const n = new Uint16Array(10); + let a, o, r, s; + if (this._leftover) { + for (s = this._leftover, + this._buffer[s++] = 1; s < 16; s++) + this._buffer[s] = 0; + this._fin = 1, + this._blocks(this._buffer, 0, 16) + } + for (a = this._h[1] >>> 13, + this._h[1] &= 8191, + s = 2; s < 10; s++) + this._h[s] += a, + a = this._h[s] >>> 13, + this._h[s] &= 8191; + for (this._h[0] += 5 * a, + a = this._h[0] >>> 13, + this._h[0] &= 8191, + this._h[1] += a, + a = this._h[1] >>> 13, + this._h[1] &= 8191, + this._h[2] += a, + n[0] = this._h[0] + 5, + a = n[0] >>> 13, + n[0] &= 8191, + s = 1; s < 10; s++) + n[s] = this._h[s] + a, + a = n[s] >>> 13, + n[s] &= 8191; + for (n[9] -= 8192, + o = (1 ^ a) - 1, + s = 0; s < 10; s++) + n[s] &= o; + for (o = ~o, + s = 0; s < 10; s++) + this._h[s] = this._h[s] & o | n[s]; + for (this._h[0] = 65535 & (this._h[0] | this._h[1] << 13), + this._h[1] = 65535 & (this._h[1] >>> 3 | this._h[2] << 10), + this._h[2] = 65535 & (this._h[2] >>> 6 | this._h[3] << 7), + this._h[3] = 65535 & (this._h[3] >>> 9 | this._h[4] << 4), + this._h[4] = 65535 & (this._h[4] >>> 12 | this._h[5] << 1 | this._h[6] << 14), + this._h[5] = 65535 & (this._h[6] >>> 2 | this._h[7] << 11), + this._h[6] = 65535 & (this._h[7] >>> 5 | this._h[8] << 8), + this._h[7] = 65535 & (this._h[8] >>> 8 | this._h[9] << 5), + r = this._h[0] + this._pad[0], + this._h[0] = 65535 & r, + s = 1; s < 8; s++) + r = (this._h[s] + this._pad[s] | 0) + (r >>> 16) | 0, + this._h[s] = 65535 & r; + return e[t + 0] = this._h[0] >>> 0, + e[t + 1] = this._h[0] >>> 8, + e[t + 2] = this._h[1] >>> 0, + e[t + 3] = this._h[1] >>> 8, + e[t + 4] = this._h[2] >>> 0, + e[t + 5] = this._h[2] >>> 8, + e[t + 6] = this._h[3] >>> 0, + e[t + 7] = this._h[3] >>> 8, + e[t + 8] = this._h[4] >>> 0, + e[t + 9] = this._h[4] >>> 8, + e[t + 10] = this._h[5] >>> 0, + e[t + 11] = this._h[5] >>> 8, + e[t + 12] = this._h[6] >>> 0, + e[t + 13] = this._h[6] >>> 8, + e[t + 14] = this._h[7] >>> 0, + e[t + 15] = this._h[7] >>> 8, + this._finished = !0, + this + } + update(e) { + let t, n = 0, a = e.length; + if (this._leftover) { + t = 16 - this._leftover, + t > a && (t = a); + for (let a = 0; a < t; a++) + this._buffer[this._leftover + a] = e[n + a]; + if (a -= t, + n += t, + this._leftover += t, + this._leftover < 16) + return this; + this._blocks(this._buffer, 0, 16), + this._leftover = 0 + } + if (a >= 16 && (t = a - a % 16, + this._blocks(e, n, t), + n += t, + a -= t), + a) { + for (let t = 0; t < a; t++) + this._buffer[this._leftover + t] = e[n + t]; + this._leftover += a + } + return this + } + digest() { + if (this._finished) + throw new Error("Poly1305 was finished"); + let e = new Uint8Array(16); + return this.finish(e), + e + } + clean() { + return Co(this._buffer), + Co(this._r), + Co(this._h), + Co(this._pad), + this._leftover = 0, + this._fin = 0, + this._finished = !0, + this + } +} + +function Mo(e, t, n, a=0) { + return Co(n), + So(e, t, n, n, a) +} + +function xo(e, t=new Uint8Array(8), n=0) { + return _o(e >>> 0, t, n), + _o(e / 4294967296 >>> 0, t, n + 4), + t +} + +const Io = new Uint8Array(16); + +class jo { + nonceLength = 12; + tagLength = 16; + _key; + constructor(e) { + if (32 !== e.length) + throw new Error("ChaCha20Poly1305 needs 32-byte key"); + this._key = new Uint8Array(e) + } + seal(e, t, n, a) { + if (e.length > 16) + throw new Error("ChaCha20Poly1305: incorrect nonce length"); + const o = new Uint8Array(16); + o.set(e, o.length - e.length); + const r = new Uint8Array(32); + Mo(this._key, o, r, 4); + const s = t.length + this.tagLength; + let i; + if (a) { + if (a.length !== s) + throw new Error("ChaCha20Poly1305: incorrect destination length"); + i = a + } else + i = new Uint8Array(s); + return So(this._key, o, t, i, 4), + this._authenticate(i.subarray(i.length - this.tagLength, i.length), r, i.subarray(0, i.length - this.tagLength), n), + Co(o), + i + } + open(e, t, n, a) { + if (e.length > 16) + throw new Error("ChaCha20Poly1305: incorrect nonce length"); + if (t.length < this.tagLength) + return null; + const o = new Uint8Array(16); + o.set(e, o.length - e.length); + const r = new Uint8Array(32); + Mo(this._key, o, r, 4); + const s = new Uint8Array(this.tagLength); + if (this._authenticate(s, r, t.subarray(0, t.length - this.tagLength), n), + i = s, + l = t.subarray(t.length - this.tagLength, t.length), + 0 === i.length || 0 === l.length || 0 === function(e, t) { + if (e.length !== t.length) + return 0; + let n = 0; + for (let a = 0; a < e.length; a++) + n |= e[a] ^ t[a]; + return 1 & n - 1 >>> 8 + }(i, l)) + return null; + var i, l; + const c = t.length - this.tagLength; + let u; + if (a) { + if (a.length !== c) + throw new Error("ChaCha20Poly1305: incorrect destination length"); + u = a + } else + u = new Uint8Array(c); + return So(this._key, o, t.subarray(0, t.length - this.tagLength), u, 4), + Co(o), + u + } + clean() { + return Co(this._key), + this + } + _authenticate(e, t, n, a) { + const o = new Eo(t); + a && (o.update(a), + a.length % 16 > 0 && o.update(Io.subarray(a.length % 16))), + o.update(n), + n.length % 16 > 0 && o.update(Io.subarray(n.length % 16)); + const r = new Uint8Array(8); + a && xo(a.length, r), + o.update(r), + xo(n.length, r), + o.update(r); + const s = o.digest(); + for (let i = 0; i < s.length; i++) + e[i] = s[i]; + o.clean(), + Co(s), + Co(r) + } +} + + +function Uo(e) { + const t = new Uint8Array(e.length / 2); + for (let n = 0; n < t.length; n++) + t[n] = parseInt(e.slice(2 * n, 2 * n + 2), 16); + return t +} + +function decryptResponse(e, t, n=1) { + if (32 !== t.length) + throw new Error("密钥长度必须为32字节"); + if (e.length < 41) + throw new Error("密文长度不足"); + const a = e[0] + , o = e.subarray(1, 25) + , r = e.subarray(25) + , s = 2 === a ? r.slice().reverse() : r + , i = new Po(t).open(o, s); + if (!i) + throw new Error("认证失败"); + return i +} + +async function encryptRequest(e, t, n=1) { + if (32 !== t.length) + throw new Error("密钥长度必须为32字节"); + const a = new Uint8Array(12); + globalThis.crypto.getRandomValues(a); + const o = { + name: "AES-GCM", + iv: a + } + , r = await crypto.subtle.importKey("raw", t, "AES-GCM", !1, ["encrypt"]) + , s = new Uint8Array(await crypto.subtle.encrypt(o, r, e)); + 2 === n && function(e) { + for (let t = 0, n = e.length - 1; t < n; t++, + n--) { + const a = e[t]; + e[t] = e[n], + e[n] = a + } + }(s); + const i = new Uint8Array(1 + a.length + s.length); + return i[0] = 2 === n ? 2 : 1, + i.set(a, 1), + i.set(s, 1 + a.length), + i +} + +function n() { + return { + key : 'ea9d9d4f9a983fe6f6382f29c7b46b8d6dc47abc6da36662e6ddff8c78902f65', + version : 1 + } +} + + + +async function decryptText(encodeText) { + const {key: a, version: o} = n() + , r = Uo(a || ""); + const s = Number(o || 0) + const t = Uo(encodeText) + , xx = decryptResponse(t, r, s); + return (new TextDecoder).decode(xx) +} + +async function encryptText(data) { + const {key: a, version: o} = n() + , r = Uo(a || ""); + const b = await encryptRequest((new TextEncoder).encode(data), r); + return Array.from(b).map(e => e.toString(16).padStart(2, "0")).join("") +} + +$.exports = { + decryptText, + encryptText +} \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/\345\220\254\345\217\213[\345\220\254].js" "b/drpy-node-bundle/spider/js/\345\220\254\345\217\213[\345\220\254].js" new file mode 100644 index 00000000..4af3af6e --- /dev/null +++ "b/drpy-node-bundle/spider/js/\345\220\254\345\217\213[\345\220\254].js" @@ -0,0 +1,300 @@ +/* +@header({ + searchable: 2, + filterable: 1, + quickSearch: 0, + title: '听友[听]', + '类型': '听书', + lang: 'ds' +}) +*/ +const {getHtml,req_,req_proxy} = $.require('./_lib.request.js') +const {decryptText,encryptText} = $.require('./_lib.tingyou.js') +var rule = { + 类型: '听书', + title: '听友[听]', + host: 'https://tingyou.fm', + url: '/api/category_page/types/fyclass/popular/pfypage', + detailUrl:'/api/chapters_list/fyid', + searchUrl: '/api/search', + searchable: 2, + quickSearch: 0, + timeout: 5000, + class_parse:async function(){ + let classes = [ + { + "type_id": 46, + "type_name": "玄幻奇幻" + }, + { + "type_id": 11, + "type_name": "武侠小说" + }, + { + "type_id": 19, + "type_name": "言情通俗" + }, + { + "type_id": 21, + "type_name": "相声小品" + }, + { + "type_id": 14, + "type_name": "恐怖惊悚" + }, + { + "type_id": 17, + "type_name": "官场商战" + }, + { + "type_id": 15, + "type_name": "历史军事" + }, + { + "type_id": 9, + "type_name": "百家讲坛" + }, + { + "type_id": 16, + "type_name": "刑侦反腐" + }, + { + "type_id": 10, + "type_name": "有声文学" + }, + { + "type_id": 18, + "type_name": "人物纪实" + }, + { + "type_id": 36, + "type_name": "广播剧" + }, + { + "type_id": 22, + "type_name": "英文读物" + }, + { + "type_id": 23, + "type_name": "轻音清心" + }, + { + "type_id": 31, + "type_name": "二人转" + }, + { + "type_id": 33, + "type_name": "健康养生" + }, + { + "type_id": 34, + "type_name": "综艺娱乐" + }, + { + "type_id": 40, + "type_name": "头条" + }, + { + "type_id": 38, + "type_name": "戏曲" + }, + { + "type_id": 41, + "type_name": "脱口秀" + }, + { + "type_id": 42, + "type_name": "商业财经" + }, + { + "type_id": 43, + "type_name": "亲子教育" + }, + { + "type_id": 44, + "type_name": "教育培训" + }, + { + "type_id": 45, + "type_name": "时尚生活" + }, + { + "type_id": 20, + "type_name": "童话寓言" + }, + { + "type_id": 47, + "type_name": "未分类" + }, + { + "type_id": 1, + "type_name": "单田芳" + }, + { + "type_id": 2, + "type_name": "刘兰芳" + }, + { + "type_id": 3, + "type_name": "田连元" + }, + { + "type_id": 4, + "type_name": "袁阔成" + }, + { + "type_id": 5, + "type_name": "连丽如" + }, + { + "type_id": 8, + "type_name": "孙一" + }, + { + "type_id": 30, + "type_name": "王子封臣" + }, + { + "type_id": 25, + "type_name": "马长辉" + }, + { + "type_id": 26, + "type_name": "昊儒书场" + }, + { + "type_id": 27, + "type_name": "王军" + }, + { + "type_id": 28, + "type_name": "王玥波" + }, + { + "type_id": 29, + "type_name": "石连君" + }, + { + "type_id": 12, + "type_name": "粤语评书" + }, + { + "type_id": 35, + "type_name": "关永超" + }, + { + "type_id": 6, + "type_name": "张少佐" + }, + { + "type_id": 7, + "type_name": "田战义" + }, + { + "type_id": 13, + "type_name": "其他评书" + } + ] + + return { + class:classes + } + }, + headers:{ + 'User-Agent': 'zybk/1.0.6', + 'Accept-Encoding': 'gzip', + 'authorization': 'Bearer gAAAAABpsEFsPBrHfY1bGsn15TbqqXUC4WSFwm8VU97NW6qmSAewn1rbYMzbIXLajyJnSZ94oZsS6hil8Qkb-IknSuggyn9XLDitEE930CD9OapDQOzq1xSJb7foWNh5YdeT_7p4ZSyYyhW4b2ZWmI-Itb8YBTjDmWIM3FiTQ9MYATmWKJQ6d6IY5Z0bupvW6hjWoHppAa5v0_k2KkIEHdzyw7AiKTPPqUYVmKvrvISlBkMHGdyK83AgiOi80-mKwmCIY1kXuj_vg_mY1HxvmsPpNcYLaoOYUA==', + // 'cookie':'session=dvJGYQ7gKDJvm-FXEzmCCHPd0IK7fzjLz1hfmi5RSxlhGi6EayEATHlH9_c%3D;' + }, + play_parse: true, + 预处理: async () => { + let data = '02956c849999374a66745e3ac9957f00ad9022036ec6b1a20c7f86327c8817'; + let config = { + method: 'POST', + url: 'https://azybk.tingyou8.vip/apk/auth/me', + headers: { + 'User-Agent': 'zybk/1.0.6', + 'Accept-Encoding': 'gzip', + 'authorization': 'Bearer gAAAAABpsAkJlSPlhBJAKM71X7PpktM1kpibFvpxnJbGXBAN0ydCgx4gviLacuou6QJKAZ4FtCpdCe8HZLKdveUTv-BTfeVFB7wGBv6yiylsWmAeKwc5YLunEbwXQv1VtMLNn4W7e4PZQlLSSOTZHbwYsC0Herb1GohZ4NU47-85y_eNuz8qq0mmxtY_2JMG9T00YcFAevfvCkfkngr0ttUZEgLVK2W_SKfHL-CSvfe6mJWQxNh88p3ViNSTK0sbL-2XPgBdDPD3urXgA4eZ7pQfPYKF3aj3ow==' + }, + data: data + }; + let html = await axios.request(config).catch(e=>e.response.headers['set-cookie']) + if(html[0]){ + rule.headers['cookie'] = html[0].replace('Path=/; Domain=tingyou8.vip; Max-Age=604800; HttpOnly','') + } + return [] + }, + lazy: async function () { + let {input} = this; + console.log(input) + let data = { + "album_id": Number(input.split('^')[0]), + "chapter_idx": Number(input.split('^')[1]) + } + let encodeData = await encryptText(JSON.stringify(data)) + let html = await req_('https://azybk.tingyou8.vip/apk/play/play_token','post', rule.headers,encodeData); + let link = JSON.parse(await decryptText(html.payload)).play_url + return {url: link, parse: 0}; + }, + double: true, + 一级: async function () { + let {input} = this; + const videos = [] + let html = await req_(input); + let data = html.payload + let list = JSON.parse(await decryptText(data)).data + list.forEach(item => { + videos.push({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.cover_url, + vod_remarks: item.status === 1 ? "连载":"完结", + }) + }) + return videos; + }, + 二级: async function () { + let {input,orId} = this; + let urls = []; + let html = await req_(input); + let list = JSON.parse(await decryptText(html.payload)).chapters + list.forEach(item => { + urls.push(item.title+"$"+orId+"^"+item.index) + }) + let js = await req_(input.replace('chapters_list','album_detail')) + let content = JSON.parse(await decryptText(js.payload)) + let VOD = { + vod_id:content.id, + vod_name:content.title, + vod_pic:content.cover_url, + vod_remarks:content.status === 1 ? "连载":"完结", + vod_content:content.synosis||'', + vod_play_from: '球球啦', + vod_play_url: urls.join('#') + }; + return VOD; + }, + 搜索: async function (wd, quick, pg) { + let {input} = this; + let videos = []; + let data = { + "keyword": wd, + "page": pg, + "sort_by": "updated_at", + "sort_order": "desc" + } + let encodeData = await encryptText(JSON.stringify(data)) + let html = await req_('https://azybk.tingyou8.vip/apk/search','post', rule.headers,encodeData); + let list = JSON.parse(await decryptText(html.payload)).results + list.forEach(item => { + videos.push({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.cover_url, + vod_remarks: item.status === 1 ? "连载":"完结", + }) + }) + return videos; + } +} \ No newline at end of file diff --git a/package-bundle.js b/package-bundle.js index ed7e1824..ddf9676d 100644 --- a/package-bundle.js +++ b/package-bundle.js @@ -17,7 +17,8 @@ const EXTRA_FILES = [ '30wMV[听].js', '爱推图[画].js', '央视大全[官].js', - '设置中心.js' + '设置中心.js', + '听友[听].js', ]; // 获取脚本所在目录 (e:\gitwork\drpy-node) diff --git a/spider/js/_lib.tingyou.js b/spider/js/_lib.tingyou.js new file mode 100644 index 00000000..410345d6 --- /dev/null +++ b/spider/js/_lib.tingyou.js @@ -0,0 +1,922 @@ +function Co(e) { + for (let t = 0; t < e.length; t++) + e[t] = 0; + return e +} + +function _o(e, t=new Uint8Array(4), n=0) { + return t[n + 0] = e >>> 0, + t[n + 1] = e >>> 8, + t[n + 2] = e >>> 16, + t[n + 3] = e >>> 24, + t +} + +function Do(e, t, n) { + let a = 1634760805 + , o = 857760878 + , r = 2036477234 + , s = 1797285236 + , i = e[3] << 24 | e[2] << 16 | e[1] << 8 | e[0] + , l = e[7] << 24 | e[6] << 16 | e[5] << 8 | e[4] + , c = e[11] << 24 | e[10] << 16 | e[9] << 8 | e[8] + , u = e[15] << 24 | e[14] << 16 | e[13] << 8 | e[12] + , d = e[19] << 24 | e[18] << 16 | e[17] << 8 | e[16] + , f = e[23] << 24 | e[22] << 16 | e[21] << 8 | e[20] + , v = e[27] << 24 | e[26] << 16 | e[25] << 8 | e[24] + , p = e[31] << 24 | e[30] << 16 | e[29] << 8 | e[28] + , h = t[3] << 24 | t[2] << 16 | t[1] << 8 | t[0] + , m = t[7] << 24 | t[6] << 16 | t[5] << 8 | t[4] + , y = t[11] << 24 | t[10] << 16 | t[9] << 8 | t[8] + , g = t[15] << 24 | t[14] << 16 | t[13] << 8 | t[12]; + for (let k = 0; k < 20; k += 2) + a = a + i | 0, + h ^= a, + h = h >>> 16 | h << 16, + d = d + h | 0, + i ^= d, + i = i >>> 20 | i << 12, + o = o + l | 0, + m ^= o, + m = m >>> 16 | m << 16, + f = f + m | 0, + l ^= f, + l = l >>> 20 | l << 12, + r = r + c | 0, + y ^= r, + y = y >>> 16 | y << 16, + v = v + y | 0, + c ^= v, + c = c >>> 20 | c << 12, + s = s + u | 0, + g ^= s, + g = g >>> 16 | g << 16, + p = p + g | 0, + u ^= p, + u = u >>> 20 | u << 12, + r = r + c | 0, + y ^= r, + y = y >>> 24 | y << 8, + v = v + y | 0, + c ^= v, + c = c >>> 25 | c << 7, + s = s + u | 0, + g ^= s, + g = g >>> 24 | g << 8, + p = p + g | 0, + u ^= p, + u = u >>> 25 | u << 7, + o = o + l | 0, + m ^= o, + m = m >>> 24 | m << 8, + f = f + m | 0, + l ^= f, + l = l >>> 25 | l << 7, + a = a + i | 0, + h ^= a, + h = h >>> 24 | h << 8, + d = d + h | 0, + i ^= d, + i = i >>> 25 | i << 7, + a = a + l | 0, + g ^= a, + g = g >>> 16 | g << 16, + v = v + g | 0, + l ^= v, + l = l >>> 20 | l << 12, + o = o + c | 0, + h ^= o, + h = h >>> 16 | h << 16, + p = p + h | 0, + c ^= p, + c = c >>> 20 | c << 12, + r = r + u | 0, + m ^= r, + m = m >>> 16 | m << 16, + d = d + m | 0, + u ^= d, + u = u >>> 20 | u << 12, + s = s + i | 0, + y ^= s, + y = y >>> 16 | y << 16, + f = f + y | 0, + i ^= f, + i = i >>> 20 | i << 12, + r = r + u | 0, + m ^= r, + m = m >>> 24 | m << 8, + d = d + m | 0, + u ^= d, + u = u >>> 25 | u << 7, + s = s + i | 0, + y ^= s, + y = y >>> 24 | y << 8, + f = f + y | 0, + i ^= f, + i = i >>> 25 | i << 7, + o = o + c | 0, + h ^= o, + h = h >>> 24 | h << 8, + p = p + h | 0, + c ^= p, + c = c >>> 25 | c << 7, + a = a + l | 0, + g ^= a, + g = g >>> 24 | g << 8, + v = v + g | 0, + l ^= v, + l = l >>> 25 | l << 7; + return _o(a, n, 0), + _o(o, n, 4), + _o(r, n, 8), + _o(s, n, 12), + _o(h, n, 16), + _o(m, n, 20), + _o(y, n, 24), + _o(g, n, 28), + n +} + +class Po { + nonceLength = 24; + tagLength = 16; + _key; + constructor(e) { + if (32 !== e.length) + throw new Error("ChaCha20Poly1305 needs 32-byte key"); + this._key = new Uint8Array(e) + } + seal(e, t, n, a) { + if (24 !== e.length) + throw new Error("XChaCha20Poly1305: incorrect nonce length"); + const o = Do(this._key, e.subarray(0, 16), new Uint8Array(32)) + , r = new Uint8Array(12); + r.set(e.subarray(16), 4); + const s = new jo(o) + , i = s.seal(r, t, n, a); + return Co(o), + Co(r), + s.clean(), + i + } + open(e, t, n, a) { + if (24 !== e.length) + throw new Error("XChaCha20Poly1305: incorrect nonce length"); + if (t.length < this.tagLength) + return null; + const o = Do(this._key, e.subarray(0, 16), new Uint8Array(32)) + , r = new Uint8Array(12); + r.set(e.subarray(16), 4); + const s = new jo(o) + , i = s.open(r, t, n, a); + return Co(o), + Co(r), + s.clean(), + i + } + clean() { + return Co(this._key), + this + } +} + +function So(e, t, n, a, o=0) { + if (32 !== e.length) + throw new Error("ChaCha: key size must be 32 bytes"); + if (a.length < n.length) + throw new Error("ChaCha: destination is shorter than source"); + let r, s; + if (0 === o) { + if (8 !== t.length && 12 !== t.length) + throw new Error("ChaCha nonce must be 8 or 12 bytes"); + r = new Uint8Array(16), + s = r.length - t.length, + r.set(t, s) + } else { + if (16 !== t.length) + throw new Error("ChaCha nonce with counter must be 16 bytes"); + r = t, + s = o + } + const i = new Uint8Array(64); + for (let l = 0; l < n.length; l += 64) { + Lo(i, r, e); + for (let e = l; e < l + 64 && e < n.length; e++) + a[e] = n[e] ^ i[e - l]; + Ao(r, 0, s) + } + return Co(i), + 0 === o && Co(r), + a +} + +function Ao(e, t, n) { + let a = 1; + for (; n--; ) + a = a + (255 & e[t]) | 0, + e[t] = 255 & a, + a >>>= 8, + t++; + if (a > 0) + throw new Error("ChaCha: counter overflow") +} + +function Lo(e, t, n) { + let a = 1634760805 + , o = 857760878 + , r = 2036477234 + , s = 1797285236 + , i = n[3] << 24 | n[2] << 16 | n[1] << 8 | n[0] + , l = n[7] << 24 | n[6] << 16 | n[5] << 8 | n[4] + , c = n[11] << 24 | n[10] << 16 | n[9] << 8 | n[8] + , u = n[15] << 24 | n[14] << 16 | n[13] << 8 | n[12] + , d = n[19] << 24 | n[18] << 16 | n[17] << 8 | n[16] + , f = n[23] << 24 | n[22] << 16 | n[21] << 8 | n[20] + , v = n[27] << 24 | n[26] << 16 | n[25] << 8 | n[24] + , p = n[31] << 24 | n[30] << 16 | n[29] << 8 | n[28] + , h = t[3] << 24 | t[2] << 16 | t[1] << 8 | t[0] + , m = t[7] << 24 | t[6] << 16 | t[5] << 8 | t[4] + , y = t[11] << 24 | t[10] << 16 | t[9] << 8 | t[8] + , g = t[15] << 24 | t[14] << 16 | t[13] << 8 | t[12] + , k = a + , b = o + , w = r + , _ = s + , x = i + , C = l + , L = c + , S = u + , M = d + , A = f + , D = v + , E = p + , I = h + , j = m + , P = y + , T = g; + for (let R = 0; R < 20; R += 2) + k = k + x | 0, + I ^= k, + I = I >>> 16 | I << 16, + M = M + I | 0, + x ^= M, + x = x >>> 20 | x << 12, + b = b + C | 0, + j ^= b, + j = j >>> 16 | j << 16, + A = A + j | 0, + C ^= A, + C = C >>> 20 | C << 12, + w = w + L | 0, + P ^= w, + P = P >>> 16 | P << 16, + D = D + P | 0, + L ^= D, + L = L >>> 20 | L << 12, + _ = _ + S | 0, + T ^= _, + T = T >>> 16 | T << 16, + E = E + T | 0, + S ^= E, + S = S >>> 20 | S << 12, + w = w + L | 0, + P ^= w, + P = P >>> 24 | P << 8, + D = D + P | 0, + L ^= D, + L = L >>> 25 | L << 7, + _ = _ + S | 0, + T ^= _, + T = T >>> 24 | T << 8, + E = E + T | 0, + S ^= E, + S = S >>> 25 | S << 7, + b = b + C | 0, + j ^= b, + j = j >>> 24 | j << 8, + A = A + j | 0, + C ^= A, + C = C >>> 25 | C << 7, + k = k + x | 0, + I ^= k, + I = I >>> 24 | I << 8, + M = M + I | 0, + x ^= M, + x = x >>> 25 | x << 7, + k = k + C | 0, + T ^= k, + T = T >>> 16 | T << 16, + D = D + T | 0, + C ^= D, + C = C >>> 20 | C << 12, + b = b + L | 0, + I ^= b, + I = I >>> 16 | I << 16, + E = E + I | 0, + L ^= E, + L = L >>> 20 | L << 12, + w = w + S | 0, + j ^= w, + j = j >>> 16 | j << 16, + M = M + j | 0, + S ^= M, + S = S >>> 20 | S << 12, + _ = _ + x | 0, + P ^= _, + P = P >>> 16 | P << 16, + A = A + P | 0, + x ^= A, + x = x >>> 20 | x << 12, + w = w + S | 0, + j ^= w, + j = j >>> 24 | j << 8, + M = M + j | 0, + S ^= M, + S = S >>> 25 | S << 7, + _ = _ + x | 0, + P ^= _, + P = P >>> 24 | P << 8, + A = A + P | 0, + x ^= A, + x = x >>> 25 | x << 7, + b = b + L | 0, + I ^= b, + I = I >>> 24 | I << 8, + E = E + I | 0, + L ^= E, + L = L >>> 25 | L << 7, + k = k + C | 0, + T ^= k, + T = T >>> 24 | T << 8, + D = D + T | 0, + C ^= D, + C = C >>> 25 | C << 7; + _o(k + a | 0, e, 0), + _o(b + o | 0, e, 4), + _o(w + r | 0, e, 8), + _o(_ + s | 0, e, 12), + _o(x + i | 0, e, 16), + _o(C + l | 0, e, 20), + _o(L + c | 0, e, 24), + _o(S + u | 0, e, 28), + _o(M + d | 0, e, 32), + _o(A + f | 0, e, 36), + _o(D + v | 0, e, 40), + _o(E + p | 0, e, 44), + _o(I + h | 0, e, 48), + _o(j + m | 0, e, 52), + _o(P + y | 0, e, 56), + _o(T + g | 0, e, 60) +} + +class Eo { + digestLength = 16; + _buffer = new Uint8Array(16); + _r = new Uint16Array(10); + _h = new Uint16Array(10); + _pad = new Uint16Array(8); + _leftover = 0; + _fin = 0; + _finished = !1; + constructor(e) { + let t = e[0] | e[1] << 8; + this._r[0] = 8191 & t; + let n = e[2] | e[3] << 8; + this._r[1] = 8191 & (t >>> 13 | n << 3); + let a = e[4] | e[5] << 8; + this._r[2] = 7939 & (n >>> 10 | a << 6); + let o = e[6] | e[7] << 8; + this._r[3] = 8191 & (a >>> 7 | o << 9); + let r = e[8] | e[9] << 8; + this._r[4] = 255 & (o >>> 4 | r << 12), + this._r[5] = r >>> 1 & 8190; + let s = e[10] | e[11] << 8; + this._r[6] = 8191 & (r >>> 14 | s << 2); + let i = e[12] | e[13] << 8; + this._r[7] = 8065 & (s >>> 11 | i << 5); + let l = e[14] | e[15] << 8; + this._r[8] = 8191 & (i >>> 8 | l << 8), + this._r[9] = l >>> 5 & 127, + this._pad[0] = e[16] | e[17] << 8, + this._pad[1] = e[18] | e[19] << 8, + this._pad[2] = e[20] | e[21] << 8, + this._pad[3] = e[22] | e[23] << 8, + this._pad[4] = e[24] | e[25] << 8, + this._pad[5] = e[26] | e[27] << 8, + this._pad[6] = e[28] | e[29] << 8, + this._pad[7] = e[30] | e[31] << 8 + } + _blocks(e, t, n) { + let a = this._fin ? 0 : 2048 + , o = this._h[0] + , r = this._h[1] + , s = this._h[2] + , i = this._h[3] + , l = this._h[4] + , c = this._h[5] + , u = this._h[6] + , d = this._h[7] + , f = this._h[8] + , v = this._h[9] + , p = this._r[0] + , h = this._r[1] + , m = this._r[2] + , y = this._r[3] + , g = this._r[4] + , k = this._r[5] + , b = this._r[6] + , w = this._r[7] + , _ = this._r[8] + , x = this._r[9]; + for (; n >= 16; ) { + let C = e[t + 0] | e[t + 1] << 8; + o += 8191 & C; + let L = e[t + 2] | e[t + 3] << 8; + r += 8191 & (C >>> 13 | L << 3); + let S = e[t + 4] | e[t + 5] << 8; + s += 8191 & (L >>> 10 | S << 6); + let M = e[t + 6] | e[t + 7] << 8; + i += 8191 & (S >>> 7 | M << 9); + let A = e[t + 8] | e[t + 9] << 8; + l += 8191 & (M >>> 4 | A << 12), + c += A >>> 1 & 8191; + let D = e[t + 10] | e[t + 11] << 8; + u += 8191 & (A >>> 14 | D << 2); + let E = e[t + 12] | e[t + 13] << 8; + d += 8191 & (D >>> 11 | E << 5); + let I = e[t + 14] | e[t + 15] << 8; + f += 8191 & (E >>> 8 | I << 8), + v += I >>> 5 | a; + let j = 0 + , P = j; + P += o * p, + P += r * (5 * x), + P += s * (5 * _), + P += i * (5 * w), + P += l * (5 * b), + j = P >>> 13, + P &= 8191, + P += c * (5 * k), + P += u * (5 * g), + P += d * (5 * y), + P += f * (5 * m), + P += v * (5 * h), + j += P >>> 13, + P &= 8191; + let T = j; + T += o * h, + T += r * p, + T += s * (5 * x), + T += i * (5 * _), + T += l * (5 * w), + j = T >>> 13, + T &= 8191, + T += c * (5 * b), + T += u * (5 * k), + T += d * (5 * g), + T += f * (5 * y), + T += v * (5 * m), + j += T >>> 13, + T &= 8191; + let R = j; + R += o * m, + R += r * h, + R += s * p, + R += i * (5 * x), + R += l * (5 * _), + j = R >>> 13, + R &= 8191, + R += c * (5 * w), + R += u * (5 * b), + R += d * (5 * k), + R += f * (5 * g), + R += v * (5 * y), + j += R >>> 13, + R &= 8191; + let B = j; + B += o * y, + B += r * m, + B += s * h, + B += i * p, + B += l * (5 * x), + j = B >>> 13, + B &= 8191, + B += c * (5 * _), + B += u * (5 * w), + B += d * (5 * b), + B += f * (5 * k), + B += v * (5 * g), + j += B >>> 13, + B &= 8191; + let O = j; + O += o * g, + O += r * y, + O += s * m, + O += i * h, + O += l * p, + j = O >>> 13, + O &= 8191, + O += c * (5 * x), + O += u * (5 * _), + O += d * (5 * w), + O += f * (5 * b), + O += v * (5 * k), + j += O >>> 13, + O &= 8191; + let U = j; + U += o * k, + U += r * g, + U += s * y, + U += i * m, + U += l * h, + j = U >>> 13, + U &= 8191, + U += c * p, + U += u * (5 * x), + U += d * (5 * _), + U += f * (5 * w), + U += v * (5 * b), + j += U >>> 13, + U &= 8191; + let W = j; + W += o * b, + W += r * k, + W += s * g, + W += i * y, + W += l * m, + j = W >>> 13, + W &= 8191, + W += c * h, + W += u * p, + W += d * (5 * x), + W += f * (5 * _), + W += v * (5 * w), + j += W >>> 13, + W &= 8191; + let H = j; + H += o * w, + H += r * b, + H += s * k, + H += i * g, + H += l * y, + j = H >>> 13, + H &= 8191, + H += c * m, + H += u * h, + H += d * p, + H += f * (5 * x), + H += v * (5 * _), + j += H >>> 13, + H &= 8191; + let N = j; + N += o * _, + N += r * w, + N += s * b, + N += i * k, + N += l * g, + j = N >>> 13, + N &= 8191, + N += c * y, + N += u * m, + N += d * h, + N += f * p, + N += v * (5 * x), + j += N >>> 13, + N &= 8191; + let V = j; + V += o * x, + V += r * _, + V += s * w, + V += i * b, + V += l * k, + j = V >>> 13, + V &= 8191, + V += c * g, + V += u * y, + V += d * m, + V += f * h, + V += v * p, + j += V >>> 13, + V &= 8191, + j = (j << 2) + j | 0, + j = j + P | 0, + P = 8191 & j, + j >>>= 13, + T += j, + o = P, + r = T, + s = R, + i = B, + l = O, + c = U, + u = W, + d = H, + f = N, + v = V, + t += 16, + n -= 16 + } + this._h[0] = o, + this._h[1] = r, + this._h[2] = s, + this._h[3] = i, + this._h[4] = l, + this._h[5] = c, + this._h[6] = u, + this._h[7] = d, + this._h[8] = f, + this._h[9] = v + } + finish(e, t=0) { + const n = new Uint16Array(10); + let a, o, r, s; + if (this._leftover) { + for (s = this._leftover, + this._buffer[s++] = 1; s < 16; s++) + this._buffer[s] = 0; + this._fin = 1, + this._blocks(this._buffer, 0, 16) + } + for (a = this._h[1] >>> 13, + this._h[1] &= 8191, + s = 2; s < 10; s++) + this._h[s] += a, + a = this._h[s] >>> 13, + this._h[s] &= 8191; + for (this._h[0] += 5 * a, + a = this._h[0] >>> 13, + this._h[0] &= 8191, + this._h[1] += a, + a = this._h[1] >>> 13, + this._h[1] &= 8191, + this._h[2] += a, + n[0] = this._h[0] + 5, + a = n[0] >>> 13, + n[0] &= 8191, + s = 1; s < 10; s++) + n[s] = this._h[s] + a, + a = n[s] >>> 13, + n[s] &= 8191; + for (n[9] -= 8192, + o = (1 ^ a) - 1, + s = 0; s < 10; s++) + n[s] &= o; + for (o = ~o, + s = 0; s < 10; s++) + this._h[s] = this._h[s] & o | n[s]; + for (this._h[0] = 65535 & (this._h[0] | this._h[1] << 13), + this._h[1] = 65535 & (this._h[1] >>> 3 | this._h[2] << 10), + this._h[2] = 65535 & (this._h[2] >>> 6 | this._h[3] << 7), + this._h[3] = 65535 & (this._h[3] >>> 9 | this._h[4] << 4), + this._h[4] = 65535 & (this._h[4] >>> 12 | this._h[5] << 1 | this._h[6] << 14), + this._h[5] = 65535 & (this._h[6] >>> 2 | this._h[7] << 11), + this._h[6] = 65535 & (this._h[7] >>> 5 | this._h[8] << 8), + this._h[7] = 65535 & (this._h[8] >>> 8 | this._h[9] << 5), + r = this._h[0] + this._pad[0], + this._h[0] = 65535 & r, + s = 1; s < 8; s++) + r = (this._h[s] + this._pad[s] | 0) + (r >>> 16) | 0, + this._h[s] = 65535 & r; + return e[t + 0] = this._h[0] >>> 0, + e[t + 1] = this._h[0] >>> 8, + e[t + 2] = this._h[1] >>> 0, + e[t + 3] = this._h[1] >>> 8, + e[t + 4] = this._h[2] >>> 0, + e[t + 5] = this._h[2] >>> 8, + e[t + 6] = this._h[3] >>> 0, + e[t + 7] = this._h[3] >>> 8, + e[t + 8] = this._h[4] >>> 0, + e[t + 9] = this._h[4] >>> 8, + e[t + 10] = this._h[5] >>> 0, + e[t + 11] = this._h[5] >>> 8, + e[t + 12] = this._h[6] >>> 0, + e[t + 13] = this._h[6] >>> 8, + e[t + 14] = this._h[7] >>> 0, + e[t + 15] = this._h[7] >>> 8, + this._finished = !0, + this + } + update(e) { + let t, n = 0, a = e.length; + if (this._leftover) { + t = 16 - this._leftover, + t > a && (t = a); + for (let a = 0; a < t; a++) + this._buffer[this._leftover + a] = e[n + a]; + if (a -= t, + n += t, + this._leftover += t, + this._leftover < 16) + return this; + this._blocks(this._buffer, 0, 16), + this._leftover = 0 + } + if (a >= 16 && (t = a - a % 16, + this._blocks(e, n, t), + n += t, + a -= t), + a) { + for (let t = 0; t < a; t++) + this._buffer[this._leftover + t] = e[n + t]; + this._leftover += a + } + return this + } + digest() { + if (this._finished) + throw new Error("Poly1305 was finished"); + let e = new Uint8Array(16); + return this.finish(e), + e + } + clean() { + return Co(this._buffer), + Co(this._r), + Co(this._h), + Co(this._pad), + this._leftover = 0, + this._fin = 0, + this._finished = !0, + this + } +} + +function Mo(e, t, n, a=0) { + return Co(n), + So(e, t, n, n, a) +} + +function xo(e, t=new Uint8Array(8), n=0) { + return _o(e >>> 0, t, n), + _o(e / 4294967296 >>> 0, t, n + 4), + t +} + +const Io = new Uint8Array(16); + +class jo { + nonceLength = 12; + tagLength = 16; + _key; + constructor(e) { + if (32 !== e.length) + throw new Error("ChaCha20Poly1305 needs 32-byte key"); + this._key = new Uint8Array(e) + } + seal(e, t, n, a) { + if (e.length > 16) + throw new Error("ChaCha20Poly1305: incorrect nonce length"); + const o = new Uint8Array(16); + o.set(e, o.length - e.length); + const r = new Uint8Array(32); + Mo(this._key, o, r, 4); + const s = t.length + this.tagLength; + let i; + if (a) { + if (a.length !== s) + throw new Error("ChaCha20Poly1305: incorrect destination length"); + i = a + } else + i = new Uint8Array(s); + return So(this._key, o, t, i, 4), + this._authenticate(i.subarray(i.length - this.tagLength, i.length), r, i.subarray(0, i.length - this.tagLength), n), + Co(o), + i + } + open(e, t, n, a) { + if (e.length > 16) + throw new Error("ChaCha20Poly1305: incorrect nonce length"); + if (t.length < this.tagLength) + return null; + const o = new Uint8Array(16); + o.set(e, o.length - e.length); + const r = new Uint8Array(32); + Mo(this._key, o, r, 4); + const s = new Uint8Array(this.tagLength); + if (this._authenticate(s, r, t.subarray(0, t.length - this.tagLength), n), + i = s, + l = t.subarray(t.length - this.tagLength, t.length), + 0 === i.length || 0 === l.length || 0 === function(e, t) { + if (e.length !== t.length) + return 0; + let n = 0; + for (let a = 0; a < e.length; a++) + n |= e[a] ^ t[a]; + return 1 & n - 1 >>> 8 + }(i, l)) + return null; + var i, l; + const c = t.length - this.tagLength; + let u; + if (a) { + if (a.length !== c) + throw new Error("ChaCha20Poly1305: incorrect destination length"); + u = a + } else + u = new Uint8Array(c); + return So(this._key, o, t.subarray(0, t.length - this.tagLength), u, 4), + Co(o), + u + } + clean() { + return Co(this._key), + this + } + _authenticate(e, t, n, a) { + const o = new Eo(t); + a && (o.update(a), + a.length % 16 > 0 && o.update(Io.subarray(a.length % 16))), + o.update(n), + n.length % 16 > 0 && o.update(Io.subarray(n.length % 16)); + const r = new Uint8Array(8); + a && xo(a.length, r), + o.update(r), + xo(n.length, r), + o.update(r); + const s = o.digest(); + for (let i = 0; i < s.length; i++) + e[i] = s[i]; + o.clean(), + Co(s), + Co(r) + } +} + + +function Uo(e) { + const t = new Uint8Array(e.length / 2); + for (let n = 0; n < t.length; n++) + t[n] = parseInt(e.slice(2 * n, 2 * n + 2), 16); + return t +} + +function decryptResponse(e, t, n=1) { + if (32 !== t.length) + throw new Error("密钥长度必须为32字节"); + if (e.length < 41) + throw new Error("密文长度不足"); + const a = e[0] + , o = e.subarray(1, 25) + , r = e.subarray(25) + , s = 2 === a ? r.slice().reverse() : r + , i = new Po(t).open(o, s); + if (!i) + throw new Error("认证失败"); + return i +} + +async function encryptRequest(e, t, n=1) { + if (32 !== t.length) + throw new Error("密钥长度必须为32字节"); + const a = new Uint8Array(12); + globalThis.crypto.getRandomValues(a); + const o = { + name: "AES-GCM", + iv: a + } + , r = await crypto.subtle.importKey("raw", t, "AES-GCM", !1, ["encrypt"]) + , s = new Uint8Array(await crypto.subtle.encrypt(o, r, e)); + 2 === n && function(e) { + for (let t = 0, n = e.length - 1; t < n; t++, + n--) { + const a = e[t]; + e[t] = e[n], + e[n] = a + } + }(s); + const i = new Uint8Array(1 + a.length + s.length); + return i[0] = 2 === n ? 2 : 1, + i.set(a, 1), + i.set(s, 1 + a.length), + i +} + +function n() { + return { + key : 'ea9d9d4f9a983fe6f6382f29c7b46b8d6dc47abc6da36662e6ddff8c78902f65', + version : 1 + } +} + + + +async function decryptText(encodeText) { + const {key: a, version: o} = n() + , r = Uo(a || ""); + const s = Number(o || 0) + const t = Uo(encodeText) + , xx = decryptResponse(t, r, s); + return (new TextDecoder).decode(xx) +} + +async function encryptText(data) { + const {key: a, version: o} = n() + , r = Uo(a || ""); + const b = await encryptRequest((new TextEncoder).encode(data), r); + return Array.from(b).map(e => e.toString(16).padStart(2, "0")).join("") +} + +$.exports = { + decryptText, + encryptText +} \ No newline at end of file diff --git "a/spider/js/\345\220\254\345\217\213[\345\220\254].js" "b/spider/js/\345\220\254\345\217\213[\345\220\254].js" new file mode 100644 index 00000000..4af3af6e --- /dev/null +++ "b/spider/js/\345\220\254\345\217\213[\345\220\254].js" @@ -0,0 +1,300 @@ +/* +@header({ + searchable: 2, + filterable: 1, + quickSearch: 0, + title: '听友[听]', + '类型': '听书', + lang: 'ds' +}) +*/ +const {getHtml,req_,req_proxy} = $.require('./_lib.request.js') +const {decryptText,encryptText} = $.require('./_lib.tingyou.js') +var rule = { + 类型: '听书', + title: '听友[听]', + host: 'https://tingyou.fm', + url: '/api/category_page/types/fyclass/popular/pfypage', + detailUrl:'/api/chapters_list/fyid', + searchUrl: '/api/search', + searchable: 2, + quickSearch: 0, + timeout: 5000, + class_parse:async function(){ + let classes = [ + { + "type_id": 46, + "type_name": "玄幻奇幻" + }, + { + "type_id": 11, + "type_name": "武侠小说" + }, + { + "type_id": 19, + "type_name": "言情通俗" + }, + { + "type_id": 21, + "type_name": "相声小品" + }, + { + "type_id": 14, + "type_name": "恐怖惊悚" + }, + { + "type_id": 17, + "type_name": "官场商战" + }, + { + "type_id": 15, + "type_name": "历史军事" + }, + { + "type_id": 9, + "type_name": "百家讲坛" + }, + { + "type_id": 16, + "type_name": "刑侦反腐" + }, + { + "type_id": 10, + "type_name": "有声文学" + }, + { + "type_id": 18, + "type_name": "人物纪实" + }, + { + "type_id": 36, + "type_name": "广播剧" + }, + { + "type_id": 22, + "type_name": "英文读物" + }, + { + "type_id": 23, + "type_name": "轻音清心" + }, + { + "type_id": 31, + "type_name": "二人转" + }, + { + "type_id": 33, + "type_name": "健康养生" + }, + { + "type_id": 34, + "type_name": "综艺娱乐" + }, + { + "type_id": 40, + "type_name": "头条" + }, + { + "type_id": 38, + "type_name": "戏曲" + }, + { + "type_id": 41, + "type_name": "脱口秀" + }, + { + "type_id": 42, + "type_name": "商业财经" + }, + { + "type_id": 43, + "type_name": "亲子教育" + }, + { + "type_id": 44, + "type_name": "教育培训" + }, + { + "type_id": 45, + "type_name": "时尚生活" + }, + { + "type_id": 20, + "type_name": "童话寓言" + }, + { + "type_id": 47, + "type_name": "未分类" + }, + { + "type_id": 1, + "type_name": "单田芳" + }, + { + "type_id": 2, + "type_name": "刘兰芳" + }, + { + "type_id": 3, + "type_name": "田连元" + }, + { + "type_id": 4, + "type_name": "袁阔成" + }, + { + "type_id": 5, + "type_name": "连丽如" + }, + { + "type_id": 8, + "type_name": "孙一" + }, + { + "type_id": 30, + "type_name": "王子封臣" + }, + { + "type_id": 25, + "type_name": "马长辉" + }, + { + "type_id": 26, + "type_name": "昊儒书场" + }, + { + "type_id": 27, + "type_name": "王军" + }, + { + "type_id": 28, + "type_name": "王玥波" + }, + { + "type_id": 29, + "type_name": "石连君" + }, + { + "type_id": 12, + "type_name": "粤语评书" + }, + { + "type_id": 35, + "type_name": "关永超" + }, + { + "type_id": 6, + "type_name": "张少佐" + }, + { + "type_id": 7, + "type_name": "田战义" + }, + { + "type_id": 13, + "type_name": "其他评书" + } + ] + + return { + class:classes + } + }, + headers:{ + 'User-Agent': 'zybk/1.0.6', + 'Accept-Encoding': 'gzip', + 'authorization': 'Bearer gAAAAABpsEFsPBrHfY1bGsn15TbqqXUC4WSFwm8VU97NW6qmSAewn1rbYMzbIXLajyJnSZ94oZsS6hil8Qkb-IknSuggyn9XLDitEE930CD9OapDQOzq1xSJb7foWNh5YdeT_7p4ZSyYyhW4b2ZWmI-Itb8YBTjDmWIM3FiTQ9MYATmWKJQ6d6IY5Z0bupvW6hjWoHppAa5v0_k2KkIEHdzyw7AiKTPPqUYVmKvrvISlBkMHGdyK83AgiOi80-mKwmCIY1kXuj_vg_mY1HxvmsPpNcYLaoOYUA==', + // 'cookie':'session=dvJGYQ7gKDJvm-FXEzmCCHPd0IK7fzjLz1hfmi5RSxlhGi6EayEATHlH9_c%3D;' + }, + play_parse: true, + 预处理: async () => { + let data = '02956c849999374a66745e3ac9957f00ad9022036ec6b1a20c7f86327c8817'; + let config = { + method: 'POST', + url: 'https://azybk.tingyou8.vip/apk/auth/me', + headers: { + 'User-Agent': 'zybk/1.0.6', + 'Accept-Encoding': 'gzip', + 'authorization': 'Bearer gAAAAABpsAkJlSPlhBJAKM71X7PpktM1kpibFvpxnJbGXBAN0ydCgx4gviLacuou6QJKAZ4FtCpdCe8HZLKdveUTv-BTfeVFB7wGBv6yiylsWmAeKwc5YLunEbwXQv1VtMLNn4W7e4PZQlLSSOTZHbwYsC0Herb1GohZ4NU47-85y_eNuz8qq0mmxtY_2JMG9T00YcFAevfvCkfkngr0ttUZEgLVK2W_SKfHL-CSvfe6mJWQxNh88p3ViNSTK0sbL-2XPgBdDPD3urXgA4eZ7pQfPYKF3aj3ow==' + }, + data: data + }; + let html = await axios.request(config).catch(e=>e.response.headers['set-cookie']) + if(html[0]){ + rule.headers['cookie'] = html[0].replace('Path=/; Domain=tingyou8.vip; Max-Age=604800; HttpOnly','') + } + return [] + }, + lazy: async function () { + let {input} = this; + console.log(input) + let data = { + "album_id": Number(input.split('^')[0]), + "chapter_idx": Number(input.split('^')[1]) + } + let encodeData = await encryptText(JSON.stringify(data)) + let html = await req_('https://azybk.tingyou8.vip/apk/play/play_token','post', rule.headers,encodeData); + let link = JSON.parse(await decryptText(html.payload)).play_url + return {url: link, parse: 0}; + }, + double: true, + 一级: async function () { + let {input} = this; + const videos = [] + let html = await req_(input); + let data = html.payload + let list = JSON.parse(await decryptText(data)).data + list.forEach(item => { + videos.push({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.cover_url, + vod_remarks: item.status === 1 ? "连载":"完结", + }) + }) + return videos; + }, + 二级: async function () { + let {input,orId} = this; + let urls = []; + let html = await req_(input); + let list = JSON.parse(await decryptText(html.payload)).chapters + list.forEach(item => { + urls.push(item.title+"$"+orId+"^"+item.index) + }) + let js = await req_(input.replace('chapters_list','album_detail')) + let content = JSON.parse(await decryptText(js.payload)) + let VOD = { + vod_id:content.id, + vod_name:content.title, + vod_pic:content.cover_url, + vod_remarks:content.status === 1 ? "连载":"完结", + vod_content:content.synosis||'', + vod_play_from: '球球啦', + vod_play_url: urls.join('#') + }; + return VOD; + }, + 搜索: async function (wd, quick, pg) { + let {input} = this; + let videos = []; + let data = { + "keyword": wd, + "page": pg, + "sort_by": "updated_at", + "sort_order": "desc" + } + let encodeData = await encryptText(JSON.stringify(data)) + let html = await req_('https://azybk.tingyou8.vip/apk/search','post', rule.headers,encodeData); + let list = JSON.parse(await decryptText(html.payload)).results + list.forEach(item => { + videos.push({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.cover_url, + vod_remarks: item.status === 1 ? "连载":"完结", + }) + }) + return videos; + } +} \ No newline at end of file diff --git a/utils/pan/quark.js b/utils/pan/quark.js index af4de5f6..ff0f5f14 100644 --- a/utils/pan/quark.js +++ b/utils/pan/quark.js @@ -1029,7 +1029,7 @@ class QuarkHandler { 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'referer': 'https://pan.quark.cn/', - 'Cookie': '_UP_A4A_11_=wb9ce1f8a7f74209b248cb5877a6a876; _UP_D_=pc; tfstk=gy2rZY6dQTBPmYbgblDFQOhX71M-pv7_ZJgIxkqnV40oVYZ3gkZN2Da7x2PEoyIJFYbJK2qTkbguRYTFLrko97gIP2kUvPSf5O6_2uHKEN_1C6GsZ4kK-9MhGxcbeD-R5O6_quHKKN__NjmeBVnnK0co-Ejq2qmoxy0oiEmZXv03-yj4mqnnK24nKirmkDDn-ycsi2063cllqNA0iO9nAbugqqJU2-ooaQEoupv33dGrSyg2Kp2q2kJ_DBpNOVVtPfguot9rLomUufNP7LzUxlFngJ8lVP40TymYMwRE7Wq76-GXxG0geo2tnj6Gbo2YPue4AHXncPg028DFIGHrE4DqDJ_vE2P0t8G-pUbEJ-r0EWSz_enc4z2LaeAEZmnq5iSVbveZi_88v3Rp9bFx0VsR2BdKZmnq5iS29BhoDmu10i5..; __uid=AARfyLe8Bke3UGL2FjhvNQn6; __sdid=AATHIp7rA8mvfKhhMvPnt0milUEzXi1ReJN0CjChc4LeUA6qYUdYA32jmaL/Lk2AA4s=; __pus=bd604e2725229c945e994c227de77f1cAASVpc7pXsTooap3JBI0XiJI9JU/JUngO9SJCOIRNOLXngLdOfozAiiwF9CnY/xOiEgE8NvKDDg1ZAwX7nVqe9H2ib5aMTAi+a2DfaPE6/6Won1vUOAfKCNELRmJOeYhVn3/eQUYx6B9EIw9HCYjQoKVh76daPI2lnp3QBTK/5zWGya9rVul08y86xpY3APre7I=; __kp=eb241d80-1ee6-11f1-85bf-6167dfcf1acd; __kuus=NenaSYNIw/O9SzABV2HtnjTDoH5aJthk7nOgDdt9pHeaJmMkCD/TY+jVFIrCn+WeFaQVR9h+E/YoStTBZAtB9va0ghzlZCgNuddki8Z8WOnYug==; __puus=2baf96cce907422be5e242bb1d280977AATi54Q/fKnsA5nM/iG/TZHL12hYZj/ELuUFbEwO/2jIXaSGwmvXppDKCITu73rdsZ1hRR0cY2QRpWdM0o+nFv65fCf5ZwPIaGHvUK9BOg3653jTngpTAj41u8kq5vTIDnPDPGWlYPYiUehDbyYBwxKYl12BFSjonkfKybr3Fpc2TYQcm566OQTrPzsupcOkOoa8CQllYvsoVyEBF2ZpiX/5' + 'Cookie': this.cookie }, data: data }; @@ -1060,7 +1060,7 @@ class QuarkHandler { 'Accept': '*/*,application/json;charset=utf-8', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Content-Type': 'application/json', - 'Cookie': 'b-user-id=7a421e0c-2ff4-1251-4069-cdfbfedcaf8d; _UP_A4A_11_=wb9cf15ce78d47ed921a9fab3faa85e8; xlly_s=1; __sdid=AAQCvlprpJtHMDSTmliW7CTXaxFJONVhZZyKc8NgJ6Y+XH4iO1v3Q8gYFeEi2hqeV4U=; _UP_D_=pc; __pus=97012d074b8d70d6eeaeb7dbe3fcb05dAAQB4QUahCaghYFA9y/NYud63QbrjWpcO/mp4OgGcA2UZ5O+VMUlfCpacaDzbRBV2O/DK9y9iAFBUL7iPmzdj2d9; __kp=ea759a90-1ed1-11f1-8f67-e7e56f2ee996; __kps=AARfyLe8Bke3UGL2FjhvNQn6; __ktd=axb8KS+96BcSmmEd+gddqg==; __uid=AARfyLe8Bke3UGL2FjhvNQn6; __puus=c6ca8123ab4024f139092c58678de061AATi54Q/fKnsA5nM/iG/TZHLzf8buD1i2D3OKKcgKFxmYvTq9ypLK3mbAQlXFlJz6zEGV7aROE4eXsRbMGviOH4EpwIMv8HZ3UUDPfRoIVsx+mV9xMqXujCxpQR96l9Alpe2B15pFBZl94/lBMSORn+M/cocX9mAe2wX82JvYhQYO46JRC1evvevyyyzaai9ItKcf72BRC6QzioBIic/XHI8; isg=BDg4TjMppS-k-Mk0_qdGtZTlCebKoZwrBz4YK3KgQXMmjdJ3GrWXu63lQY093VQD; tfstk=gTJiuT2wCC5_fZaEErWsOziN8jlKBO6X3EeAktQqTw7Ckctv0ty2knBOgVtv-Z7F2SJA0f_cTUs7_CCZSemDWnwA7NpAns8RR5Rx5qtFmULRuiSqcMkeuUYcGc_AuZYv0CnKeYK6ft62o4H-emTk0mLcbsyaLmScDPod_tj5i4Wqy4HL9k5sWt-TUIxGLH7CmiyNuEWULi7Ubl8V76zFqg6VuE8VTWSRcGPN7N-UTwsV3Z8V3DXFRiXVuEWqxHllpf763pJErkLuNeO_cCsGsa-N7nKvLPWT1nQ33-JHt1brQw243pjM09gPi8cAzQ_Owa8EpRXDYiYhwC0uIEx2VI5HnPo9zeRDuMOIoSbkghdWp6E4_HvcS_JNtoiJ4hs2usAIr8IGBBfkICib8h8RSQW6Du09x_AhNM5Um5W9w3pfadkgPwC5mefJs42MzglbT7y5t-sEDpPbG1SCxapyAdOShopf3Dm3am1NAG_-xDVbG1SCxannx7kf_Ms1y' + 'Cookie': this.cookie }, data: data }; From f5c06302b60bcf2f7ca437e33665312775616ce4 Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 18:14:42 +0800 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20=E5=8F=91=E5=B8=83=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 +- docs/updateRecord.md | 9 + drpy-node-bundle/spider/js/_lib.cntv2026.cjs | 256 +++++++++++-------- package.json | 2 +- public/index.html | 14 +- spider/js/_lib.cntv2026.cjs | 256 +++++++++++-------- 6 files changed, 304 insertions(+), 261 deletions(-) diff --git a/README.md b/README.md index e1b77c4b..036b99ae 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,10 @@ nodejs作为服务端的drpy实现。全面升级异步写法 ## 更新记录 +### 20260317 + +更新至V1.3.30 + ### 20260315 更新至V1.3.29 @@ -79,30 +83,6 @@ nodejs作为服务端的drpy实现。全面升级异步写法 更新至V1.3.28 -### 20260301 - -更新至V1.3.27 - -### 20260228 - -更新至V1.3.26 - -### 20260225 - -更新至V1.3.25 - -### 20260214 - -更新至V1.3.24 - -### 20260212 - -更新至V1.3.23 - -### 20260208 - -更新至V1.3.22 - [点此查看完整更新记录](docs/updateRecord.md) **注意事项** diff --git a/docs/updateRecord.md b/docs/updateRecord.md index 227d570e..b07fb712 100644 --- a/docs/updateRecord.md +++ b/docs/updateRecord.md @@ -1,5 +1,14 @@ # drpyS更新记录 +### 20260317 + +更新至V1.3.30 + +1. 优化文件头处理工具,支持完美JSON5对象文件头识别,新增PHP文件头支持,提高文件头读写速度 +2. 修复夸克工具无限转存逻辑异常 +3. 新增源、并优化bundle打包逻辑 +4. 尝试处理了一些内存泄露问题 + ### 20260315 更新至V1.3.29 diff --git a/drpy-node-bundle/spider/js/_lib.cntv2026.cjs b/drpy-node-bundle/spider/js/_lib.cntv2026.cjs index 054bacbb..d885746b 100644 --- a/drpy-node-bundle/spider/js/_lib.cntv2026.cjs +++ b/drpy-node-bundle/spider/js/_lib.cntv2026.cjs @@ -1,5 +1,4 @@ const axios = require('axios'); -// const iconv = require('iconv-lite'); // ==================== 常量定义 ==================== const TS_PACKET_SIZE = 188; const VIDEO_PID = 256; @@ -93,11 +92,16 @@ function findNALUnits(buffer) { // ==================== H.264解密核心 ==================== // 解密H.264数据,保持原始数据结构不变,直接在原buffer上修改 +// 优化内存使用:避免创建不必要的buffer副本和对象 async function decryptH264InPlace(h264Data) { let curDate = Date.now().toString(); const MemoryExtend = 2048; let vmpTag = ''; + // 预分配临时解密buffer(复用) + const maxDecryptBufSize = 1024 * 1024; // 1MB上限,足够处理单个NAL + const tempDecryptBuf = Buffer.allocUnsafe(maxDecryptBufSize); + function _common(o) { const memory = CNTVH5PlayerModule._jsmalloc(curDate.length + MemoryExtend); CNTVH5PlayerModule.HEAP8.fill(0, memory, memory + curDate.length + MemoryExtend); @@ -132,9 +136,10 @@ async function decryptH264InPlace(h264Data) { return _common("UpdatePlayer"); } - function decrypt(buf) { + // 解密到预分配的buffer,返回实际解密长度 + function decryptToBuf(srcBuf, destBuf) { const pageHost = "https://tv.cctv.com"; - const addr = CNTVH5PlayerModule._jsmalloc(buf.length + MemoryExtend); + const addr = CNTVH5PlayerModule._jsmalloc(srcBuf.length + MemoryExtend); const StaticCallModuleVod = { H264NalSet: function (e, t, i, n, r) { return e._CNTV_jsdecVOD7(t, i, n, r); @@ -172,25 +177,28 @@ async function decryptH264InPlace(h264Data) { return StaticCallModuleVod[a](e, t, i, n, r); } - CNTVH5PlayerModule.HEAP8.set(buf, addr); - CNTVH5PlayerModule.HEAP8.set(Array.from(pageHost, e => e.charCodeAt(0)), addr + buf.length); + CNTVH5PlayerModule.HEAP8.set(srcBuf, addr); + CNTVH5PlayerModule.HEAP8.set(Array.from(pageHost, e => e.charCodeAt(0)), addr + srcBuf.length); const addr2 = CNTVH5PlayerModule._jsmalloc(curDate.length); CNTVH5PlayerModule.HEAP8.set(Array.from(curDate, e => e.charCodeAt(0)), addr2); for (const i in vmpTag) if ("0123456".includes(vmpTag[i])) - StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, buf.length, pageHost.length, Object.keys(StaticCallModuleVod)[i]); + StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, srcBuf.length, pageHost.length, Object.keys(StaticCallModuleVod)[i]); + + const decRet = StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, srcBuf.length, pageHost.length, Object.keys(StaticCallModuleVod)[8]); - const decRet = StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, buf.length, pageHost.length, Object.keys(StaticCallModuleVod)[8]); - const retBuffer = Buffer.from(CNTVH5PlayerModule.HEAP8.subarray(addr, addr + decRet)); + // 复制到预分配的buffer (HEAP8是Uint8Array,使用set方法) + const copyLen = Math.min(decRet, destBuf.length); + destBuf.set(CNTVH5PlayerModule.HEAP8.subarray(addr, addr + copyLen)); CNTVH5PlayerModule._jsfree(addr); CNTVH5PlayerModule._jsfree(addr2); - return retBuffer; + return decRet; } - // 找到所有NAL单元及其在原始buffer中的位置 - const nalUnits = []; + // 找到所有NAL单元的位置信息(只存储必要的位置信息,不存储数据引用) + const nalUnits = []; // { dataPos, dataEnd, header, nalUnitType } let i = 0; while (i < h264Data.length - 3) { if (h264Data[i] === 0x00 && h264Data[i + 1] === 0x00) { @@ -223,14 +231,12 @@ async function decryptH264InPlace(h264Data) { const dataEnd = nextStart; const nalUnitType = header & 0x1F; + // 只存储位置信息,不存储subarray引用 nalUnits.push({ - startCodePos: i, - startCodeLen: startCodeLen, - headerPos: nalStart, dataPos: dataStart, dataEnd: dataEnd, header: header, - data: h264Data.subarray(dataStart, dataEnd), + dataLen: dataEnd - dataStart, nalUnitType: nalUnitType }); @@ -241,45 +247,51 @@ async function decryptH264InPlace(h264Data) { i++; } - // 解密并替换原始buffer中的数据 + // 解密并替换原始buffer中的数据(原地修改) let shouldDecrypt = false; curDate = Date.now().toString(); InitPlayer(); - // 创建可写的buffer副本 - const resultBuffer = Buffer.from(h264Data); - for (const nal of nalUnits) { UpdatePlayer(); - const bufToDecrypt = Buffer.concat([Buffer.from([nal.header]), nal.data]); + // 将数据复制到预分配的buffer进行解密 + const nalLen = nal.dataLen + 1; // +1 for header + if (nalLen > maxDecryptBufSize) continue; // 跳过过大的NAL + + // 复制header + data到临时buffer + tempDecryptBuf[0] = nal.header; + h264Data.copy(tempDecryptBuf, 1, nal.dataPos, nal.dataEnd); + if (nal.nalUnitType === 25) { - shouldDecrypt = nal.data[0] === 1; + shouldDecrypt = h264Data[nal.dataPos] === 1; if (shouldDecrypt) { - decrypt(bufToDecrypt); + decryptToBuf(tempDecryptBuf.subarray(0, nalLen), tempDecryptBuf); } } else if ((nal.nalUnitType === 1 || nal.nalUnitType === 5) && shouldDecrypt) { - const decrypted = decrypt(bufToDecrypt); - // 直接替换原始数据,保持起始码不变 - const decryptedData = decrypted.subarray(1); // 跳过header,因为decrypted包含了header + const decLen = decryptToBuf(tempDecryptBuf.subarray(0, nalLen), tempDecryptBuf); + // 解密结果从索引1开始(跳过header) + const decryptedDataLen = decLen - 1; const writePos = nal.dataPos; - // 如果解密后数据小于等于原始空间,直接替换 - if (decryptedData.length <= nal.dataEnd - nal.dataPos) { - decryptedData.copy(resultBuffer, writePos); - // 如果解密后数据更小,填充0 - if (decryptedData.length < nal.dataEnd - nal.dataPos) { - resultBuffer.fill(0, writePos + decryptedData.length, nal.dataEnd); + // 原地替换 + if (decryptedDataLen <= nal.dataLen) { + // 解密数据更小或相等 + if (decryptedDataLen > 0) { + tempDecryptBuf.copy(h264Data, writePos, 1, 1 + decryptedDataLen); + } + if (decryptedDataLen < nal.dataLen) { + h264Data.fill(0, writePos + decryptedDataLen, nal.dataEnd); } } else { - // 解密后数据更大,截断到原始大小 - decryptedData.copy(resultBuffer, writePos, 0, nal.dataEnd - nal.dataPos); + // 解密数据更大,截断 + tempDecryptBuf.copy(h264Data, writePos, 1, 1 + nal.dataLen); } } } UnInitPlayer(); - return resultBuffer; + return h264Data; } // ==================== TS解密处理函数 ==================== @@ -289,10 +301,10 @@ async function Parse_TS(buffer) { const originalTS = Buffer.from(buffer); - // 提取所有视频PID的payload,按PES包分组 - const videoPESPackets = []; - let currentPESPayload = []; - let currentPESHeader = null; + // 第一遍扫描:计算视频数据总量并收集PES包信息 + const videoPESRanges = []; // 只存储位置和长度信息,不存储数据引用 + let totalH264Size = 0; + let currentPESDataSize = 0; let inPES = false; for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { @@ -314,129 +326,149 @@ async function Parse_TS(buffer) { } if (payload && offset < 188) { - const data = packet.subarray(offset); + const dataSize = 188 - offset; if (payloadStart) { - // 保存上一个PES包 - if (inPES && currentPESPayload.length > 0 && currentPESHeader) { - const h264Data = Buffer.concat(currentPESPayload); - videoPESPackets.push({ - header: currentPESHeader, - data: h264Data - }); + // 保存上一个PES包的范围信息 + if (inPES && currentPESDataSize > 0) { + videoPESRanges.push({ size: currentPESDataSize }); + totalH264Size += currentPESDataSize; } - // 开始新的PES包 - 保存完整PES头 - // 找到PES头结束位置:检查PES_header_data_length - // PES结构: [00 00 01] [stream_id] [len] [flags] [flags] [header_len] ... - let pesHeaderEnd = 9; // 最小PES头长度 + // 开始新的PES包 - 只计算数据大小 + let pesHeaderEnd = 9; + const data = packet.subarray(offset); if (data.length >= 9) { - // 第8字节是PES_header_data_length (从0开始计数) pesHeaderEnd = 9 + data[8]; } pesHeaderEnd = Math.min(pesHeaderEnd, data.length); - currentPESHeader = data.subarray(0, pesHeaderEnd); - currentPESPayload = [data.subarray(pesHeaderEnd)]; + const pesPayloadSize = dataSize - pesHeaderEnd; + currentPESDataSize = pesPayloadSize; inPES = true; } else if (inPES) { - currentPESPayload.push(data); + currentPESDataSize += dataSize; } } } // 保存最后一个PES包 - if (inPES && currentPESPayload.length > 0 && currentPESHeader) { - const h264Data = Buffer.concat(currentPESPayload); - videoPESPackets.push({ - header: currentPESHeader, - data: h264Data - }); + if (inPES && currentPESDataSize > 0) { + videoPESRanges.push({ size: currentPESDataSize }); + totalH264Size += currentPESDataSize; } - // 合并所有H.264数据进行解密 - let totalH264 = Buffer.alloc(0); - for (const pes of videoPESPackets) { - totalH264 = Buffer.concat([totalH264, pes.data]); + // 如果没有视频数据,直接返回原始TS + if (totalH264Size === 0) { + return originalTS; } - // 解密所有H.264数据 - const decryptedH264 = await decryptH264InPlace(totalH264); - - // 将解密后的数据按原始PES包大小分配回去 - let h264Offset = 0; - const resultTS = Buffer.alloc(originalTS.length); + // 第二遍扫描:一次性提取所有H.264数据到预分配的buffer + const h264Data = Buffer.allocUnsafe(totalH264Size); + let h264WritePos = 0; + inPES = false; for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { if (i + TS_PACKET_SIZE > originalTS.length) break; - const origPacket = originalTS.subarray(i, i + TS_PACKET_SIZE); - - // 复制非视频包 - if (origPacket[0] !== 0x47) { - origPacket.copy(resultTS, i); - continue; - } + const packet = originalTS.subarray(i, i + TS_PACKET_SIZE); + if (packet[0] !== 0x47) continue; - const pid = ((origPacket[1] & 0x1F) << 8) | origPacket[2]; - if (pid !== VIDEO_PID) { - origPacket.copy(resultTS, i); - continue; - } + const pid = ((packet[1] & 0x1F) << 8) | packet[2]; + if (pid !== VIDEO_PID) continue; - const payloadStart = (origPacket[1] & 0x40) !== 0; - const adaptation = (origPacket[3] & 0x20) !== 0; - const payload = (origPacket[3] & 0x10) !== 0; + const payloadStart = (packet[1] & 0x40) !== 0; + const adaptation = (packet[3] & 0x20) !== 0; + const payload = (packet[3] & 0x10) !== 0; let offset = 4; if (adaptation && offset < 188) { - const adaptationLen = origPacket[offset]; + const adaptationLen = packet[offset]; offset += adaptationLen + 1; } - // 创建新包 - const newPacket = Buffer.alloc(TS_PACKET_SIZE); - origPacket.copy(newPacket, 0, 0, offset); - if (payload && offset < 188) { - const data = origPacket.subarray(offset); - if (payloadStart) { - // PES包开始 - 复制PES头 let pesHeaderEnd = 9; + const data = packet.subarray(offset); if (data.length >= 9) { pesHeaderEnd = 9 + data[8]; } pesHeaderEnd = Math.min(pesHeaderEnd, data.length); - origPacket.copy(newPacket, offset, offset, offset + pesHeaderEnd); - offset += pesHeaderEnd; + const pesPayload = packet.subarray(offset + pesHeaderEnd); + pesPayload.copy(h264Data, h264WritePos); + h264WritePos += pesPayload.length; + inPES = true; + } else if (inPES) { + const data = packet.subarray(offset); + data.copy(h264Data, h264WritePos); + h264WritePos += data.length; + } + } + } + + // 解密H.264数据(原地修改) + const decryptedH264 = await decryptH264InPlace(h264Data); + + // 第三遍扫描:将解密数据写回,原地修改originalTS + let h264ReadPos = 0; + + for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { + if (i + TS_PACKET_SIZE > originalTS.length) break; + + const pid = ((originalTS[i + 1] & 0x1F) << 8) | originalTS[i + 2]; + if (pid !== VIDEO_PID) continue; + + const payloadStart = (originalTS[i + 1] & 0x40) !== 0; + const adaptation = (originalTS[i + 3] & 0x20) !== 0; + const payload = (originalTS[i + 3] & 0x10) !== 0; + + let offset = 4; + if (adaptation && offset < 188) { + const adaptationLen = originalTS[i + offset]; + offset += adaptationLen + 1; + } + + if (payload && offset < 188) { + if (payloadStart) { + let pesHeaderEnd = 9; + if (offset + 9 <= 188) { + pesHeaderEnd = 9 + originalTS[i + offset + 8]; + } + pesHeaderEnd = Math.min(pesHeaderEnd, 188 - offset); + + // 跳过PES头,直接写入解密后的H.264数据 + const writeOffset = i + offset + pesHeaderEnd; + const remaining = 188 - offset - pesHeaderEnd; + const toCopy = Math.min(remaining, decryptedH264.length - h264ReadPos); - // 填充解密后的H.264数据 - const remaining = TS_PACKET_SIZE - offset; - const toCopy = Math.min(remaining, decryptedH264.length - h264Offset); if (toCopy > 0) { - decryptedH264.copy(newPacket, offset, h264Offset, h264Offset + toCopy); - h264Offset += toCopy; - offset += toCopy; + decryptedH264.copy(originalTS, writeOffset, h264ReadPos, h264ReadPos + toCopy); + h264ReadPos += toCopy; + } + + // 填充剩余空间 + if (toCopy < remaining) { + originalTS.fill(0xFF, writeOffset + toCopy, i + 188); } - newPacket.fill(0xFF, offset); } else { - // 继续填充数据 - const remaining = TS_PACKET_SIZE - offset; - const toCopy = Math.min(remaining, decryptedH264.length - h264Offset); + const writeOffset = i + offset; + const remaining = 188 - offset; + const toCopy = Math.min(remaining, decryptedH264.length - h264ReadPos); + if (toCopy > 0) { - decryptedH264.copy(newPacket, offset, h264Offset, h264Offset + toCopy); - h264Offset += toCopy; - offset += toCopy; + decryptedH264.copy(originalTS, writeOffset, h264ReadPos, h264ReadPos + toCopy); + h264ReadPos += toCopy; + } + + if (toCopy < remaining) { + originalTS.fill(0xFF, writeOffset + toCopy, i + 188); } - newPacket.fill(0xFF, offset); } } - - newPacket.copy(resultTS, i); } - return resultTS; + return originalTS; } // ==================== 主处理函数 ==================== diff --git a/package.json b/package.json index 3d1553be..71864398 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drpy-node", - "version": "1.3.29", + "version": "1.3.30", "main": "index.js", "type": "module", "scripts": { diff --git a/public/index.html b/public/index.html index e747138e..23c6b916 100644 --- a/public/index.html +++ b/public/index.html @@ -72,22 +72,12 @@

免费壳子推荐

  • 皮卡丘
  • 更新记录

    +

    20260317

    +

    更新至V1.3.30

    20260315

    更新至V1.3.29

    20260314

    更新至V1.3.28

    -

    20260301

    -

    更新至V1.3.27

    -

    20260228

    -

    更新至V1.3.26

    -

    20260225

    -

    更新至V1.3.25

    -

    20260214

    -

    更新至V1.3.24

    -

    20260212

    -

    更新至V1.3.23

    -

    20260208

    -

    更新至V1.3.22

    点此查看完整更新记录

    注意事项

    总是有人遇到各种奇葩问题,像什么没弹幕,访问/config/1服务马上崩溃等等,能自行解决最好,解决不了我建议你使用下方安装教程 diff --git a/spider/js/_lib.cntv2026.cjs b/spider/js/_lib.cntv2026.cjs index 054bacbb..d885746b 100644 --- a/spider/js/_lib.cntv2026.cjs +++ b/spider/js/_lib.cntv2026.cjs @@ -1,5 +1,4 @@ const axios = require('axios'); -// const iconv = require('iconv-lite'); // ==================== 常量定义 ==================== const TS_PACKET_SIZE = 188; const VIDEO_PID = 256; @@ -93,11 +92,16 @@ function findNALUnits(buffer) { // ==================== H.264解密核心 ==================== // 解密H.264数据,保持原始数据结构不变,直接在原buffer上修改 +// 优化内存使用:避免创建不必要的buffer副本和对象 async function decryptH264InPlace(h264Data) { let curDate = Date.now().toString(); const MemoryExtend = 2048; let vmpTag = ''; + // 预分配临时解密buffer(复用) + const maxDecryptBufSize = 1024 * 1024; // 1MB上限,足够处理单个NAL + const tempDecryptBuf = Buffer.allocUnsafe(maxDecryptBufSize); + function _common(o) { const memory = CNTVH5PlayerModule._jsmalloc(curDate.length + MemoryExtend); CNTVH5PlayerModule.HEAP8.fill(0, memory, memory + curDate.length + MemoryExtend); @@ -132,9 +136,10 @@ async function decryptH264InPlace(h264Data) { return _common("UpdatePlayer"); } - function decrypt(buf) { + // 解密到预分配的buffer,返回实际解密长度 + function decryptToBuf(srcBuf, destBuf) { const pageHost = "https://tv.cctv.com"; - const addr = CNTVH5PlayerModule._jsmalloc(buf.length + MemoryExtend); + const addr = CNTVH5PlayerModule._jsmalloc(srcBuf.length + MemoryExtend); const StaticCallModuleVod = { H264NalSet: function (e, t, i, n, r) { return e._CNTV_jsdecVOD7(t, i, n, r); @@ -172,25 +177,28 @@ async function decryptH264InPlace(h264Data) { return StaticCallModuleVod[a](e, t, i, n, r); } - CNTVH5PlayerModule.HEAP8.set(buf, addr); - CNTVH5PlayerModule.HEAP8.set(Array.from(pageHost, e => e.charCodeAt(0)), addr + buf.length); + CNTVH5PlayerModule.HEAP8.set(srcBuf, addr); + CNTVH5PlayerModule.HEAP8.set(Array.from(pageHost, e => e.charCodeAt(0)), addr + srcBuf.length); const addr2 = CNTVH5PlayerModule._jsmalloc(curDate.length); CNTVH5PlayerModule.HEAP8.set(Array.from(curDate, e => e.charCodeAt(0)), addr2); for (const i in vmpTag) if ("0123456".includes(vmpTag[i])) - StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, buf.length, pageHost.length, Object.keys(StaticCallModuleVod)[i]); + StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, srcBuf.length, pageHost.length, Object.keys(StaticCallModuleVod)[i]); + + const decRet = StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, srcBuf.length, pageHost.length, Object.keys(StaticCallModuleVod)[8]); - const decRet = StaticCallModuleVodAPI(CNTVH5PlayerModule, addr2, addr, buf.length, pageHost.length, Object.keys(StaticCallModuleVod)[8]); - const retBuffer = Buffer.from(CNTVH5PlayerModule.HEAP8.subarray(addr, addr + decRet)); + // 复制到预分配的buffer (HEAP8是Uint8Array,使用set方法) + const copyLen = Math.min(decRet, destBuf.length); + destBuf.set(CNTVH5PlayerModule.HEAP8.subarray(addr, addr + copyLen)); CNTVH5PlayerModule._jsfree(addr); CNTVH5PlayerModule._jsfree(addr2); - return retBuffer; + return decRet; } - // 找到所有NAL单元及其在原始buffer中的位置 - const nalUnits = []; + // 找到所有NAL单元的位置信息(只存储必要的位置信息,不存储数据引用) + const nalUnits = []; // { dataPos, dataEnd, header, nalUnitType } let i = 0; while (i < h264Data.length - 3) { if (h264Data[i] === 0x00 && h264Data[i + 1] === 0x00) { @@ -223,14 +231,12 @@ async function decryptH264InPlace(h264Data) { const dataEnd = nextStart; const nalUnitType = header & 0x1F; + // 只存储位置信息,不存储subarray引用 nalUnits.push({ - startCodePos: i, - startCodeLen: startCodeLen, - headerPos: nalStart, dataPos: dataStart, dataEnd: dataEnd, header: header, - data: h264Data.subarray(dataStart, dataEnd), + dataLen: dataEnd - dataStart, nalUnitType: nalUnitType }); @@ -241,45 +247,51 @@ async function decryptH264InPlace(h264Data) { i++; } - // 解密并替换原始buffer中的数据 + // 解密并替换原始buffer中的数据(原地修改) let shouldDecrypt = false; curDate = Date.now().toString(); InitPlayer(); - // 创建可写的buffer副本 - const resultBuffer = Buffer.from(h264Data); - for (const nal of nalUnits) { UpdatePlayer(); - const bufToDecrypt = Buffer.concat([Buffer.from([nal.header]), nal.data]); + // 将数据复制到预分配的buffer进行解密 + const nalLen = nal.dataLen + 1; // +1 for header + if (nalLen > maxDecryptBufSize) continue; // 跳过过大的NAL + + // 复制header + data到临时buffer + tempDecryptBuf[0] = nal.header; + h264Data.copy(tempDecryptBuf, 1, nal.dataPos, nal.dataEnd); + if (nal.nalUnitType === 25) { - shouldDecrypt = nal.data[0] === 1; + shouldDecrypt = h264Data[nal.dataPos] === 1; if (shouldDecrypt) { - decrypt(bufToDecrypt); + decryptToBuf(tempDecryptBuf.subarray(0, nalLen), tempDecryptBuf); } } else if ((nal.nalUnitType === 1 || nal.nalUnitType === 5) && shouldDecrypt) { - const decrypted = decrypt(bufToDecrypt); - // 直接替换原始数据,保持起始码不变 - const decryptedData = decrypted.subarray(1); // 跳过header,因为decrypted包含了header + const decLen = decryptToBuf(tempDecryptBuf.subarray(0, nalLen), tempDecryptBuf); + // 解密结果从索引1开始(跳过header) + const decryptedDataLen = decLen - 1; const writePos = nal.dataPos; - // 如果解密后数据小于等于原始空间,直接替换 - if (decryptedData.length <= nal.dataEnd - nal.dataPos) { - decryptedData.copy(resultBuffer, writePos); - // 如果解密后数据更小,填充0 - if (decryptedData.length < nal.dataEnd - nal.dataPos) { - resultBuffer.fill(0, writePos + decryptedData.length, nal.dataEnd); + // 原地替换 + if (decryptedDataLen <= nal.dataLen) { + // 解密数据更小或相等 + if (decryptedDataLen > 0) { + tempDecryptBuf.copy(h264Data, writePos, 1, 1 + decryptedDataLen); + } + if (decryptedDataLen < nal.dataLen) { + h264Data.fill(0, writePos + decryptedDataLen, nal.dataEnd); } } else { - // 解密后数据更大,截断到原始大小 - decryptedData.copy(resultBuffer, writePos, 0, nal.dataEnd - nal.dataPos); + // 解密数据更大,截断 + tempDecryptBuf.copy(h264Data, writePos, 1, 1 + nal.dataLen); } } } UnInitPlayer(); - return resultBuffer; + return h264Data; } // ==================== TS解密处理函数 ==================== @@ -289,10 +301,10 @@ async function Parse_TS(buffer) { const originalTS = Buffer.from(buffer); - // 提取所有视频PID的payload,按PES包分组 - const videoPESPackets = []; - let currentPESPayload = []; - let currentPESHeader = null; + // 第一遍扫描:计算视频数据总量并收集PES包信息 + const videoPESRanges = []; // 只存储位置和长度信息,不存储数据引用 + let totalH264Size = 0; + let currentPESDataSize = 0; let inPES = false; for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { @@ -314,129 +326,149 @@ async function Parse_TS(buffer) { } if (payload && offset < 188) { - const data = packet.subarray(offset); + const dataSize = 188 - offset; if (payloadStart) { - // 保存上一个PES包 - if (inPES && currentPESPayload.length > 0 && currentPESHeader) { - const h264Data = Buffer.concat(currentPESPayload); - videoPESPackets.push({ - header: currentPESHeader, - data: h264Data - }); + // 保存上一个PES包的范围信息 + if (inPES && currentPESDataSize > 0) { + videoPESRanges.push({ size: currentPESDataSize }); + totalH264Size += currentPESDataSize; } - // 开始新的PES包 - 保存完整PES头 - // 找到PES头结束位置:检查PES_header_data_length - // PES结构: [00 00 01] [stream_id] [len] [flags] [flags] [header_len] ... - let pesHeaderEnd = 9; // 最小PES头长度 + // 开始新的PES包 - 只计算数据大小 + let pesHeaderEnd = 9; + const data = packet.subarray(offset); if (data.length >= 9) { - // 第8字节是PES_header_data_length (从0开始计数) pesHeaderEnd = 9 + data[8]; } pesHeaderEnd = Math.min(pesHeaderEnd, data.length); - currentPESHeader = data.subarray(0, pesHeaderEnd); - currentPESPayload = [data.subarray(pesHeaderEnd)]; + const pesPayloadSize = dataSize - pesHeaderEnd; + currentPESDataSize = pesPayloadSize; inPES = true; } else if (inPES) { - currentPESPayload.push(data); + currentPESDataSize += dataSize; } } } // 保存最后一个PES包 - if (inPES && currentPESPayload.length > 0 && currentPESHeader) { - const h264Data = Buffer.concat(currentPESPayload); - videoPESPackets.push({ - header: currentPESHeader, - data: h264Data - }); + if (inPES && currentPESDataSize > 0) { + videoPESRanges.push({ size: currentPESDataSize }); + totalH264Size += currentPESDataSize; } - // 合并所有H.264数据进行解密 - let totalH264 = Buffer.alloc(0); - for (const pes of videoPESPackets) { - totalH264 = Buffer.concat([totalH264, pes.data]); + // 如果没有视频数据,直接返回原始TS + if (totalH264Size === 0) { + return originalTS; } - // 解密所有H.264数据 - const decryptedH264 = await decryptH264InPlace(totalH264); - - // 将解密后的数据按原始PES包大小分配回去 - let h264Offset = 0; - const resultTS = Buffer.alloc(originalTS.length); + // 第二遍扫描:一次性提取所有H.264数据到预分配的buffer + const h264Data = Buffer.allocUnsafe(totalH264Size); + let h264WritePos = 0; + inPES = false; for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { if (i + TS_PACKET_SIZE > originalTS.length) break; - const origPacket = originalTS.subarray(i, i + TS_PACKET_SIZE); - - // 复制非视频包 - if (origPacket[0] !== 0x47) { - origPacket.copy(resultTS, i); - continue; - } + const packet = originalTS.subarray(i, i + TS_PACKET_SIZE); + if (packet[0] !== 0x47) continue; - const pid = ((origPacket[1] & 0x1F) << 8) | origPacket[2]; - if (pid !== VIDEO_PID) { - origPacket.copy(resultTS, i); - continue; - } + const pid = ((packet[1] & 0x1F) << 8) | packet[2]; + if (pid !== VIDEO_PID) continue; - const payloadStart = (origPacket[1] & 0x40) !== 0; - const adaptation = (origPacket[3] & 0x20) !== 0; - const payload = (origPacket[3] & 0x10) !== 0; + const payloadStart = (packet[1] & 0x40) !== 0; + const adaptation = (packet[3] & 0x20) !== 0; + const payload = (packet[3] & 0x10) !== 0; let offset = 4; if (adaptation && offset < 188) { - const adaptationLen = origPacket[offset]; + const adaptationLen = packet[offset]; offset += adaptationLen + 1; } - // 创建新包 - const newPacket = Buffer.alloc(TS_PACKET_SIZE); - origPacket.copy(newPacket, 0, 0, offset); - if (payload && offset < 188) { - const data = origPacket.subarray(offset); - if (payloadStart) { - // PES包开始 - 复制PES头 let pesHeaderEnd = 9; + const data = packet.subarray(offset); if (data.length >= 9) { pesHeaderEnd = 9 + data[8]; } pesHeaderEnd = Math.min(pesHeaderEnd, data.length); - origPacket.copy(newPacket, offset, offset, offset + pesHeaderEnd); - offset += pesHeaderEnd; + const pesPayload = packet.subarray(offset + pesHeaderEnd); + pesPayload.copy(h264Data, h264WritePos); + h264WritePos += pesPayload.length; + inPES = true; + } else if (inPES) { + const data = packet.subarray(offset); + data.copy(h264Data, h264WritePos); + h264WritePos += data.length; + } + } + } + + // 解密H.264数据(原地修改) + const decryptedH264 = await decryptH264InPlace(h264Data); + + // 第三遍扫描:将解密数据写回,原地修改originalTS + let h264ReadPos = 0; + + for (let i = 0; i < originalTS.length; i += TS_PACKET_SIZE) { + if (i + TS_PACKET_SIZE > originalTS.length) break; + + const pid = ((originalTS[i + 1] & 0x1F) << 8) | originalTS[i + 2]; + if (pid !== VIDEO_PID) continue; + + const payloadStart = (originalTS[i + 1] & 0x40) !== 0; + const adaptation = (originalTS[i + 3] & 0x20) !== 0; + const payload = (originalTS[i + 3] & 0x10) !== 0; + + let offset = 4; + if (adaptation && offset < 188) { + const adaptationLen = originalTS[i + offset]; + offset += adaptationLen + 1; + } + + if (payload && offset < 188) { + if (payloadStart) { + let pesHeaderEnd = 9; + if (offset + 9 <= 188) { + pesHeaderEnd = 9 + originalTS[i + offset + 8]; + } + pesHeaderEnd = Math.min(pesHeaderEnd, 188 - offset); + + // 跳过PES头,直接写入解密后的H.264数据 + const writeOffset = i + offset + pesHeaderEnd; + const remaining = 188 - offset - pesHeaderEnd; + const toCopy = Math.min(remaining, decryptedH264.length - h264ReadPos); - // 填充解密后的H.264数据 - const remaining = TS_PACKET_SIZE - offset; - const toCopy = Math.min(remaining, decryptedH264.length - h264Offset); if (toCopy > 0) { - decryptedH264.copy(newPacket, offset, h264Offset, h264Offset + toCopy); - h264Offset += toCopy; - offset += toCopy; + decryptedH264.copy(originalTS, writeOffset, h264ReadPos, h264ReadPos + toCopy); + h264ReadPos += toCopy; + } + + // 填充剩余空间 + if (toCopy < remaining) { + originalTS.fill(0xFF, writeOffset + toCopy, i + 188); } - newPacket.fill(0xFF, offset); } else { - // 继续填充数据 - const remaining = TS_PACKET_SIZE - offset; - const toCopy = Math.min(remaining, decryptedH264.length - h264Offset); + const writeOffset = i + offset; + const remaining = 188 - offset; + const toCopy = Math.min(remaining, decryptedH264.length - h264ReadPos); + if (toCopy > 0) { - decryptedH264.copy(newPacket, offset, h264Offset, h264Offset + toCopy); - h264Offset += toCopy; - offset += toCopy; + decryptedH264.copy(originalTS, writeOffset, h264ReadPos, h264ReadPos + toCopy); + h264ReadPos += toCopy; + } + + if (toCopy < remaining) { + originalTS.fill(0xFF, writeOffset + toCopy, i + 188); } - newPacket.fill(0xFF, offset); } } - - newPacket.copy(resultTS, i); } - return resultTS; + return originalTS; } // ==================== 主处理函数 ==================== From bdb4fb95252bdaf74730a6e90fd8bd2d540e049b Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 22:18:59 +0800 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0mcp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drpy-node-bundle/localDsCoreTest.js | 5 +- ...\345\275\261\350\247\206[\345\256\230].js" | 119 ++++++++ drpy-node-mcp/index.js | 12 + drpy-node-mcp/tools/apiTools.js | 262 ++++++++++++++++++ package-bundle.js | 1 + 5 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 "drpy-node-bundle/spider/js/360\345\275\261\350\247\206[\345\256\230].js" create mode 100644 drpy-node-mcp/tools/apiTools.js diff --git a/drpy-node-bundle/localDsCoreTest.js b/drpy-node-bundle/localDsCoreTest.js index aba50247..fd77b92c 100644 --- a/drpy-node-bundle/localDsCoreTest.js +++ b/drpy-node-bundle/localDsCoreTest.js @@ -31,4 +31,7 @@ const f = await getEngine('央视大全[官]', { }, {requestHost: 'http://127.0.0.1:5757',proxyPath:'https://dh5wswx02.v.cntv.cn/asp/h5e/hls/2000/0303000a/3/default/b90af013c16e44de9e4f1f56dab91f63/1.ts'}) const [statuCode, contentType, buffer, headers] = f; -console.log('resutl: header:', headers, 'buffer length:', buffer.length); \ No newline at end of file +console.log('resutl: header:', headers, 'buffer length:', buffer.length); + +const g = await getEngine('360影视[官]', {do: 'ds', ac: 'list', t: '2'}) +console.log(g) \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/360\345\275\261\350\247\206[\345\256\230].js" "b/drpy-node-bundle/spider/js/360\345\275\261\350\247\206[\345\256\230].js" new file mode 100644 index 00000000..dfa7603a --- /dev/null +++ "b/drpy-node-bundle/spider/js/360\345\275\261\350\247\206[\345\256\230].js" @@ -0,0 +1,119 @@ +/* +@header({ + searchable: 2, + filterable: 1, + quickSearch: 0, + title: '360影视[官]', + '类型': '影视', + lang: 'ds' +}) +*/ + +var rule = { + title: '360影视[官]', + host: 'https://www.360kan.com', + homeUrl: 'https://api.web.360kan.com/v1/rank?cat=2&size=9', + detailUrl: 'https://api.web.360kan.com/v1/detail?cat=fyclass&id=fyid', + searchUrl: 'https://api.so.360kan.com/index?force_v=1&kw=**&from=&pageno=fypage&v_ap=1&tab=all', + url: 'https://api.web.360kan.com/v1/fyfilter&size=35&pageno=fypage&callback=', + filterable: 1, + filter_url: 'filter/list?catid=fyclass&rank={{fl.排序}}&cat={{fl.类型}}&year={{fl.年代}}&area={{fl.地区}}', + filter: "H4sIAAAAAAAAA+2YS08jRxCA/4vPHGbMvrK3/IJcor1Eq4gDUqIlbLQhkVYrJINt1jYPA8vLa2MgYJtlMdhAiD1e4z8z3TP+Fxm7Xu0oGs2BQBRx81fVXV3V3VNV7XcxO/b8u3exV5NvY89jXrOjyouxsdj0xE+TJv82MfXr5HDgdCBW6eN+8nggDiA2O4bSrZLK1lCKQDov09TJNOoQeF7u2O2WaB4A6fTcqk5soQ6BbdbWVLtDNgHYZrYm6yHwvNy51z2heQA8r/JebCKwL5kd18mSLwCsmz/1ttZIB2DE5210JL4B8Lyt937WoXkA7Kdzorqb5CcA6dwv+/5ZA3UIbLO+5GfKZBOAfckf+Ye8LwCsW1lQ+QvSAbDNZE7PfySbAOJL1VtdYF+GwDbT126Hzg9h9uVACxdOlRpqyZELxxzpwi2kg/Fk/KjWL8jCbquuit1+taBb5zgCYXSEyjd0+4Y3ZAgc9GUjGEFBA/Dh3KyIDoF0/b1PokNgm9sVXTolmwCy3qa53qY5z19sig6B9+HmT9EhsG65oZwq6QAiH077yu0cGodDHOVw4lb8EcqGPw35uMjHTXlc5HFTbovcNuWWyC1Dbn/F8uCnIX8m8mem/KnIn5ryJyJ/Ysofi/yxKZd4bTNeW+K1zXhtidc247UlLtuMy5K4LDMuS+KyzLgsicuSuHTxSm9/Qs3UzPdvJyfejJy6XllXTl5OnXn01HUp4ffWvGRd7+yjtTcT069+eD0jSw2HuK3c6JCpiZnJX0ZGqfy2qnT985Qx6ufXP07PDBx7ORaL31aJ8o8TUhYQopSM0BQXUvbCykI/2VWtecoSAGxz7lol82QTIFIKXyi6ziLpADj2elUtUdlDiJL6Va6pettcvobAWbBS9s93KQsC8HqN5aBI0noAHPvuut6pUOxDMLdNnV0rp05LDmFEHdYwJNO6QBkfQQpj1b2hm4gQqZm4uyIGcFdFLKwYhRWxsOIXVqj0VkPl9tXuATc8xA9l56Hs/OfKzvitlZ1UU+UPvVqCPgtm/qJKe67jyAhh9ne17J1yKQHgbzWRlakIkrw+q2VuAAHYr7Oe38iQUwBmIk3tGYk0APZ2Y09fcbIE4HmFj+4XfikASE65dNurnFOGwL7MLakSvYQQjHzTPyRfECSHXasGrwfAulZLZ6iMIvC+NNZUqkf7AsBZtfvBn6dyiGC+ys7odYVgFBG9WZAiMgD5AC6Mlx5AlBebf/W716EYEHhes+t36WwRWLde1zkqsQj/j1dZ2AvqtBZcD9IB3EuSGeaPR7eVPwJP/ANODQBRGpdBaQ5uduXSKNXA/EXWN2Q2As/u5FW6RVMBojSFYV+WWljvF6gMIETJcHrlWBpwBNbNLXqZJukAWHfR0SnKcAjsS7ujk7Q1CDxv/0AVqQNFMNpPL8sxAMi1cKTpReCr7VyoOmU/BLaZ6gXfPdkE4AzQWw1WoQwAEOWBoRMn4icC60L+y/KKOV2gTIUQ0rjLvM8V4zEAILqyzvENBojSzntHN1JNEOQhtGg82AD+Vk+zdPTCUsP+UN2mMYKZRnzz4mtRI5Du2xeigt9Gj+ttnoz85zcikjfOktf5MPrfoCm6k3wclkv/scl/aNPvvU2XeG0z3n+3fQ9A4rXMeC2J1zLjtSReS+IdeQYg3FeBnv0LfL9z7fwYAAA=", + filter_def: {}, + headers: { + 'User-Agent': 'MOBILE_UA' + }, + timeout: 5000, + class_name: '电视剧&电影&综艺&动漫', + class_url: '2&1&3&4', + limit: 5, + multi: 1, + searchable: 2, + play_parse: true, + lazy: async function () { + let {input} = this; + if (input.includes('?') && !input.includes('video?vid=')) { + input = input.split("?")[0]; + } + return input + }, + 推荐: 'json:data;title;cover;comment;cat+ent_id;description', + 一级: 'json:data.movies;title;cover;pubdate;id;description', + 二级: async function () { + let {input, fetch_params} = this; + let html = JSON.parse(await request(input, fetch_params)); + let data = html.data; + let title = data.title; + let img = data.cdncover; + let vod_type = data.moviecategory.join(","); + let area = data.area.join(","); + let director = data.director.join(","); + let actor = data.actor.join(","); + let content = data.description; + let base_vod = { + vod_id: input, + vod_name: title, + type_name: vod_type, + vod_actor: actor, + vod_director: director, + vod_content: content, + vod_remarks: area, + vod_pic: urljoin(input, img) + }; + let delta = 50; + let vod_play = {}; + let sites = data.playlink_sites; + for (const site of sites) { + let playList = ""; + let vodItems = []; + // print(data) + if (data.allupinfo) { + let total = parseInt(data.allupinfo[site]); + // print(total) + for (let j = 1; j < total; j += delta) { + let end = Math.min(total, j + delta - 1); + // print(end) + let url2 = buildUrl(input, {start: j, end: end, site: site}); + let vod_data = JSON.parse(await request(url2), fetch_params).data; + if (vod_data != null) { + if (vod_data.allepidetail) { + vod_data = vod_data.allepidetail[site]; + vod_data.forEach(function (item, index) { + vodItems.push((item.playlink_num || "") + "$" + urlDeal(item.url || "")) + }) + } else { + vod_data = vod_data.defaultepisode; + vod_data.forEach(function (item, index) { + vodItems.push((item.period || "") + (item.name || "") + "$" + urlDeal(item.url) || "") + }) + } + } + } + } else { + let item = data.playlinksdetail[site]; + vodItems.push((item.sort || "") + "$" + urlDeal(item.default_url || "")) + } + if (vodItems.length > 0) { + playList = vodItems.join("#") + } + if (playList.length < 1) { + continue; + } + vod_play[site] = playList + } + let tabs = Object.keys(vod_play); + let playUrls = []; + for (let id in tabs) { + // print("id:" + id); + playUrls.push(vod_play[tabs[id]]) + } + if (tabs.length > 0) { + let vod_play_from = tabs.join("$$$"); + let vod_play_url = playUrls.join("$$$"); + base_vod.vod_play_from = vod_play_from; + base_vod.vod_play_url = vod_play_url + } + return base_vod; + }, + 搜索: 'json:data.longData.rows;titleTxt||titlealias;cover;cat_name;cat_id+en_id;description', +} \ No newline at end of file diff --git a/drpy-node-mcp/index.js b/drpy-node-mcp/index.js index ac912bf5..f64341d1 100644 --- a/drpy-node-mcp/index.js +++ b/drpy-node-mcp/index.js @@ -12,6 +12,7 @@ import * as fsTools from "./tools/fsTools.js"; import * as spiderTools from "./tools/spiderTools.js"; import * as dbTools from "./tools/dbTools.js"; import * as systemTools from "./tools/systemTools.js"; +import * as apiTools from "./tools/apiTools.js"; const server = new Server( { @@ -43,6 +44,9 @@ const toolHandlers = { validate_spider: spiderTools.validate_spider, check_syntax: spiderTools.check_syntax, + // API Tools + get_drpy_api_list: apiTools.get_drpy_api_list, + // DB Tools sql_query: dbTools.sql_query, @@ -130,6 +134,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { properties: {} }, }, + { + name: "get_drpy_api_list", + description: "Get the full list of drpy-node API interfaces with parameters and return examples", + inputSchema: { + type: "object", + properties: {} + }, + }, { name: "fetch_spider_url", description: "Fetch a URL using drpy-node's request library to debug connectivity and anti-crawling measures (UA/headers).", diff --git a/drpy-node-mcp/tools/apiTools.js b/drpy-node-mcp/tools/apiTools.js new file mode 100644 index 00000000..297cdaf4 --- /dev/null +++ b/drpy-node-mcp/tools/apiTools.js @@ -0,0 +1,262 @@ + +/** + * API Tools for drpy-node + * Provides documentation and information about the available API endpoints. + */ + +export const get_drpy_api_list = async () => { + const apiList = [ + { + category: "Core Video Source API", + endpoints: [ + { + path: "/api/:module", + method: "GET/POST", + description: "Main interface for video sources. Supports listing categories, details, searching, and playing.", + params: { + "module": { "type": "path", "required": true, "description": "Engine name. Available: 'drpyS', 'hipy', 'php', 'xbpq', 'catvod'" }, + "ac": { "type": "query", "required": false, "description": "Action type. 't'=Categories/List, 'ids'=Detail, 'action'=Custom Action. If omitted with 'wd', performs search." }, + "t": { "type": "query", "required": false, "description": "Category ID (tid) when ac=t" }, + "pg": { "type": "query", "required": false, "description": "Page number (default: 1)" }, + "wd": { "type": "query", "required": false, "description": "Search keyword" }, + "ids": { "type": "query", "required": false, "description": "Comma-separated VOD IDs when ac=ids" }, + "play": { "type": "query", "required": false, "description": "URL to resolve for playback" }, + "flag": { "type": "query", "required": false, "description": "Playlist flag (for play action)" }, + "filter": { "type": "query", "required": false, "description": "Filter conditions (boolean/string)" }, + "extend": { "type": "query", "required": false, "description": "Extended info (JSON string)" } + } + }, + { + path: "/proxy/:module/:url", + method: "GET", + description: "Proxy wrapper for source requests (headers, etc.)", + params: { + "module": "Engine name", + "url": "Target URL (may need encoding)" + } + }, + { + path: "/parse/:jx", + method: "GET", + description: "Video parsing interface", + params: { + "jx": "Parser name or alias", + "url": "Video URL to parse" + } + } + ] + }, + { + category: "Configuration", + endpoints: [ + { + path: "/config", + method: "GET", + description: "Get the generated JSON configuration for apps (TVBox, etc.)", + params: {} + }, + { + path: "/config/:id", + method: "GET", + description: "Get specific configuration variant" + } + ] + }, + { + category: "System & Tools", + endpoints: [ + { + path: "/", + method: "GET", + description: "Home page / README" + }, + { + path: "/health", + method: "GET", + description: "Server health check" + }, + { + path: "/encoder", + method: "POST", + description: "Encode text/url", + params: { "text": "Content to encode", "mode": "Encoding mode" } + }, + { + path: "/decoder", + method: "POST", + description: "Decode text/url", + params: { "text": "Content to decode" } + }, + { + path: "/authcoder", + method: "GET", + description: "Generate random strings", + params: { "len": "Length", "number": "Count" } + }, + { + path: "/http", + method: "POST", + description: "HTTP Proxy Request", + params: { "url": "Target URL", "method": "GET/POST", "headers": "Headers object", "data": "Body data" } + }, + { + path: "/ai", + method: "GET", + description: "Simple AI Chat interface", + params: { "text": "User input" } + }, + { + path: "/req/*", + method: "ALL", + description: "Request forwarder (if enabled)" + }, + { + path: "/gh/release", + method: "GET", + description: "Get latest GitHub release for drpy-node" + }, + { + path: "/cat/index.html", + method: "GET", + description: "Cat interface page" + } + ] + }, + { + category: "Admin & Management", + endpoints: [ + { + path: "/admin/encoder", + method: "GET", + description: "Encoder tool UI" + }, + { + path: "/admin/cookie-set", + method: "POST", + description: "Set system cookies", + params: { "cookie_auth_code": "Auth code", "key": "Cookie key", "value": "Cookie value" } + }, + { + path: "/admin/download", + method: "GET", + description: "Download page for project archives" + } + ] + }, + { + category: "Task Management", + endpoints: [ + { + path: "/execute-now/:taskName?", + method: "GET", + description: "Execute a cron task immediately" + }, + { + path: "/tasks", + method: "GET", + description: "List all scheduled tasks" + }, + { + path: "/tasks/:taskName", + method: "GET", + description: "Get details of a specific task" + } + ] + }, + { + category: "File & Image Services", + endpoints: [ + { + path: "/image/upload", + method: "POST", + description: "Upload image (base64)", + params: { "imageId": "Unique ID", "base64Data": "Base64 content" } + }, + { + path: "/image/:imageId", + method: "GET", + description: "Get image content" + }, + { + path: "/image/list", + method: "GET", + description: "List stored images" + }, + { + path: "/clipboard/add", + method: "POST", + description: "Add content to clipboard", + params: { "text": "Content", "mode": "append/overwrite" } + }, + { + path: "/clipboard/read", + method: "GET", + description: "Read clipboard content" + }, + { + path: "/source-checker/reports/save", + method: "POST", + description: "Save source check report" + }, + { + path: "/source-checker/reports/latest", + method: "GET", + description: "Get latest source check report" + } + ] + }, + { + category: "Proxy Services", + endpoints: [ + { + path: "/unified-proxy/proxy", + method: "GET/HEAD", + description: "Unified Smart Proxy (auto detects m3u8/file)", + params: { "url": "Target URL", "type": "force type (optional)" } + }, + { + path: "/m3u8-proxy/playlist", + method: "GET", + description: "M3U8 Playlist Proxy" + }, + { + path: "/mediaProxy", + method: "ALL", + description: "General Media Proxy" + }, + { + path: "/file-proxy/proxy", + method: "GET/HEAD", + description: "Remote File Proxy" + }, + { + path: "/webdav/file", + method: "GET", + description: "WebDAV File Proxy" + }, + { + path: "/ftp/file", + method: "GET/HEAD", + description: "FTP File Proxy" + } + ] + }, + { + category: "Realtime", + endpoints: [ + { + path: "/ws", + method: "GET", + description: "WebSocket connection for logs and status" + } + ] + } + ]; + + return { + content: [{ + type: "text", + text: JSON.stringify(apiList, null, 2) + }] + }; +}; diff --git a/package-bundle.js b/package-bundle.js index ddf9676d..06fef072 100644 --- a/package-bundle.js +++ b/package-bundle.js @@ -19,6 +19,7 @@ const EXTRA_FILES = [ '央视大全[官].js', '设置中心.js', '听友[听].js', + '360影视[官].js', ]; // 获取脚本所在目录 (e:\gitwork\drpy-node) From 4bb7b7da73caaf07a2e778cfde9c2bf9941d7b7a Mon Sep 17 00:00:00 2001 From: Taois Date: Tue, 17 Mar 2026 23:05:57 +0800 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96Bundle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drpy-node-bundle/libs/localDsCore.bundled.js | 16 ++++++++--- drpy-node-bundle/localDsCoreTest.js | 8 ++++-- drpy-node-bundle/rolldown.config.js | 3 +- drpy-node-bundle/shim/jsonpath-shim.js | 29 ++++++++++++++++++++ drpy-node-bundle/shim/original-jsonpath.js | 1 + libs_drpy/jsonpath-adapter.js | 22 +++++++++++++++ 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 drpy-node-bundle/shim/jsonpath-shim.js create mode 100644 drpy-node-bundle/shim/original-jsonpath.js create mode 100644 libs_drpy/jsonpath-adapter.js diff --git a/drpy-node-bundle/libs/localDsCore.bundled.js b/drpy-node-bundle/libs/localDsCore.bundled.js index 4215f478..9130c056 100644 --- a/drpy-node-bundle/libs/localDsCore.bundled.js +++ b/drpy-node-bundle/libs/localDsCore.bundled.js @@ -379411,8 +379411,8 @@ var require_tunnel = /* @__PURE__ */ __commonJSMin(((exports, module) => { module.exports = require_tunnel$1(); })); //#endregion -//#region ../libs_drpy/jsonpathplus.min.js -var require_jsonpathplus_min = /* @__PURE__ */ __commonJSMin(((exports, module) => { +//#region shim/original-jsonpath.js +var require_original_jsonpath = /* @__PURE__ */ __commonJSMin(((exports, module) => { (function(e, t) { "object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).JSONPath = {}); })(exports, function(e) { @@ -380658,13 +380658,21 @@ var require_jsonpathplus_min = /* @__PURE__ */ __commonJSMin(((exports, module) }); })); //#endregion -//#region ../libs_drpy/htmlParser.js +//#region shim/jsonpath-shim.js var import_tunnel = /* @__PURE__ */ __toESM(require_tunnel(), 1); +var import_original_jsonpath = /* @__PURE__ */ __toESM(require_original_jsonpath(), 1); +const JP = import_original_jsonpath.JSONPath || import_original_jsonpath.default?.JSONPath || import_original_jsonpath.default; +if (typeof globalThis !== "undefined") { + if (!globalThis.JSONPath) globalThis.JSONPath = {}; + if (typeof JP === "function") globalThis.JSONPath.JSONPath = JP; + else if (JP && JP.JSONPath) globalThis.JSONPath = JP; +} +//#endregion +//#region ../libs_drpy/htmlParser.js /** * HTML解析器工具 * 基于cheerio提供HTML和JSON解析功能,支持类似海阔视界的解析语法 */ -var import_jsonpathplus_min = require_jsonpathplus_min(); /** * JSONPath查询工具 */ diff --git a/drpy-node-bundle/localDsCoreTest.js b/drpy-node-bundle/localDsCoreTest.js index fd77b92c..5e9de76c 100644 --- a/drpy-node-bundle/localDsCoreTest.js +++ b/drpy-node-bundle/localDsCoreTest.js @@ -1,5 +1,6 @@ // import * as localtDsCore from './localDsCore.js'; import * as localtDsCore from './libs/localDsCore.bundled.js'; + const start = performance.now(); console.log(getEngine) @@ -28,10 +29,13 @@ const f = await getEngine('央视大全[官]', { do: 'ds', proxy: 1, url: '', -}, {requestHost: 'http://127.0.0.1:5757',proxyPath:'https://dh5wswx02.v.cntv.cn/asp/h5e/hls/2000/0303000a/3/default/b90af013c16e44de9e4f1f56dab91f63/1.ts'}) +}, { + requestHost: 'http://127.0.0.1:5757', + proxyPath: 'https://dh5wswx02.v.cntv.cn/asp/h5e/hls/2000/0303000a/3/default/b90af013c16e44de9e4f1f56dab91f63/1.ts' +}) const [statuCode, contentType, buffer, headers] = f; console.log('resutl: header:', headers, 'buffer length:', buffer.length); -const g = await getEngine('360影视[官]', {do: 'ds', ac: 'list', t: '2'}) +const g = await getEngine('360影视[官]', {do: 'ds', ac: 'list', t: '2', ext: 'eyLnsbvlnosiOiLoqIDmg4UifQ=='}) console.log(g) \ No newline at end of file diff --git a/drpy-node-bundle/rolldown.config.js b/drpy-node-bundle/rolldown.config.js index a9521b92..9cad7e15 100644 --- a/drpy-node-bundle/rolldown.config.js +++ b/drpy-node-bundle/rolldown.config.js @@ -45,7 +45,8 @@ const require = (moduleName) => { conditionNames: ['node', 'import'], alias: { 'pako': path.resolve(__dirname, '../libs_drpy/pako.min.js'), - 'puppeteer': path.resolve(__dirname, 'puppeteer-mock.js') + 'puppeteer': path.resolve(__dirname, 'puppeteer-mock.js'), + [path.resolve(__dirname, '../libs_drpy/jsonpathplus.min.js')]: path.resolve(__dirname, 'shim/jsonpath-shim.js') } }, external: (id) => { diff --git a/drpy-node-bundle/shim/jsonpath-shim.js b/drpy-node-bundle/shim/jsonpath-shim.js new file mode 100644 index 00000000..cdbd89f9 --- /dev/null +++ b/drpy-node-bundle/shim/jsonpath-shim.js @@ -0,0 +1,29 @@ +import * as lib from './original-jsonpath.js'; + +// 获取对象 +// Rolldown/ESM interop 可能会把 export 放在 default 或 namespace +const JP = lib.JSONPath || lib.default?.JSONPath || lib.default; + +// 确保全局挂载 +if (typeof globalThis !== 'undefined') { + if (!globalThis.JSONPath) { + globalThis.JSONPath = {}; // 初始化容器 + } + // min.js 的逻辑是 globalThis.JSONPath.JSONPath = F + // 如果 JP 是 F (构造函数) + if (typeof JP === 'function') { + globalThis.JSONPath.JSONPath = JP; + } else if (JP && JP.JSONPath) { + // 如果 JP 已经是 { JSONPath: F } + globalThis.JSONPath = JP; + } + + // 如果 htmlParser.js 直接使用 JSONPath 变量而不是 globalThis.JSONPath + // 在 Bundle 环境下这是不可能的,除非它是全局变量。 + // 但是在 Node 环境下,全局变量可以直接访问。 + // 我们这里只能保证 globalThis.JSONPath 存在。 +} + +// 导出,虽然 htmlParser.js 没用到,但为了模块完整性 +export { JP as JSONPath }; +export default JP; diff --git a/drpy-node-bundle/shim/original-jsonpath.js b/drpy-node-bundle/shim/original-jsonpath.js new file mode 100644 index 00000000..bc5ffbbb --- /dev/null +++ b/drpy-node-bundle/shim/original-jsonpath.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).JSONPath={})}(this,function(e){"use strict";function n(e,t,r){return t=l(t),function(e,t){{if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined")}return function(e){if(void 0!==e)return e;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(e)}(e,i()?Reflect.construct(t,r||[],l(e).constructor):t.apply(e,r))}function o(e,t,r){if(i())return Reflect.construct.apply(null,arguments);var n=[null];n.push.apply(n,t);n=new(e.bind.apply(e,n));return r&&h(n,r.prototype),n}function i(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(i=function(){return!!e})()}function t(t,e){var r,n=Object.keys(t);return Object.getOwnPropertySymbols&&(r=Object.getOwnPropertySymbols(t),e&&(r=r.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),n.push.apply(n,r)),n}function r(n){for(var e=1;ee.length)&&(t=e.length);for(var r=0,n=new Array(t);ru.prec:r<=u.prec);)o=n.pop(),t=n.pop().value,c=n.pop(),e={type:l.BINARY_EXP,operator:t,left:c,right:o},n.push(e);(e=this.gobbleToken())||this.throwError("Expected expression after "+s),n.push(i,e)}for(e=n[a=n.length-1];1=t.length&&this.throwError("Unexpected token "+String.fromCharCode(e));break}if(i===l.COMMA_CODE){if(this.index++,++n!==t.length)if(e===l.CPAREN_CODE)this.throwError("Unexpected token ,");else if(e===l.CBRACK_CODE)for(var o=t.length;o":7,"<=":7,">=":7,"<<":8,">>":8,">>>":8,"+":9,"-":9,"*":10,"/":10,"%":10},right_associative:new Set,additional_identifier_chars:new Set(["$","_"]),literals:{true:!0,false:!1,null:null},this_str:"this"}),v.max_unop_len=v.getMaxKeyLen(v.unary_ops),v.max_binop_len=v.getMaxKeyLen(v.binary_ops);var E=function(e){return new v(e).parse()};Object.getOwnPropertyNames(v).forEach(function(e){void 0===E[e]&&"prototype"!==e&&(E[e]=v[e])}),E.Jsep=v;b={name:"ternary",init:function(o){o.hooks.add("after-expression",function(e){if(e.node&&this.code===o.QUMARK_CODE){this.index++;var t=e.node,r=this.gobbleExpression();if(r||this.throwError("Expected expression"),this.gobbleSpaces(),this.code===o.COLON_CODE){this.index++;var n=this.gobbleExpression();if(n||this.throwError("Expected expression"),e.node={type:"ConditionalExpression",test:t,consequent:r,alternate:n},t.operator&&o.binary_ops[t.operator]<=.9){for(var i=t;i.right.operator&&o.binary_ops[i.right.operator]<=.9;)i=i.right;e.node.test=i.right,i.right=e.node,e.node=t}}else this.throwError("Expected :")}})}};E.plugins.register(b);var b={name:"regex",init:function(s){s.hooks.add("gobble-token",function(e){if(47===this.code){for(var t=++this.index,r=!1;this.index>=",">>>=","&=","^=","|="]),updateOperators:[43,45],assignmentPrecedence:.9,init:function(t){var n=[t.IDENTIFIER,t.MEMBER_EXP];g.assignmentOperators.forEach(function(e){return t.addBinaryOp(e,g.assignmentPrecedence,!0)}),t.hooks.add("gobble-token",function(e){var t=this,r=this.code;g.updateOperators.some(function(e){return e===r&&e===t.expr.charCodeAt(t.index+1)})&&(this.index+=2,e.node={type:"UpdateExpression",operator:43===r?"++":"--",argument:this.gobbleTokenProperty(this.gobbleIdentifier()),prefix:!0},e.node.argument&&n.includes(e.node.argument.type)||this.throwError("Unexpected ".concat(e.node.operator)))}),t.hooks.add("after-token",function(e){var t,r=this;e.node&&(t=this.code,g.updateOperators.some(function(e){return e===t&&e===r.expr.charCodeAt(r.index+1)})&&(n.includes(e.node.type)||this.throwError("Unexpected ".concat(e.node.operator)),this.index+=2,e.node={type:"UpdateExpression",operator:43===t?"++":"--",argument:e.node,prefix:!1}))}),t.hooks.add("after-expression",function(e){e.node&&!function t(e){g.assignmentOperators.has(e.operator)?(e.type="AssignmentExpression",t(e.left),t(e.right)):e.operator||Object.values(e).forEach(function(e){e&&"object"===C(e)&&t(e)})}(e.node)})}},A=Object.prototype.hasOwnProperty;function w(e,t){return(e=e.slice()).push(t),e}function k(e,t){return(t=t.slice()).unshift(e),t}var x=function(){function r(e){var t;return s(this,r),(t=n(this,r,['JSONPath should not be called with "new" (it prevents return of (unwrapped) scalar values)'])).avoidNew=!0,t.value=e,t.name="NewError",t}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&h(e,t)}(r,p(Error)),c(r)}();function F(e,t,r,n,i){if(!(this instanceof F))try{return new F(e,t,r,n,i)}catch(e){if(!e.avoidNew)throw e;return e.value}"string"==typeof e&&(i=n,n=r,r=t,t=e,e=null);var o=e&&"object"===C(e);if(e=e||{},this.json=e.json||r,this.path=e.path||t,this.resultType=e.resultType||"value",this.flatten=e.flatten||!1,this.wrap=!A.call(e,"wrap")||e.wrap,this.sandbox=e.sandbox||{},this.eval=void 0===e.eval?"safe":e.eval,this.ignoreEvalErrors=void 0!==e.ignoreEvalErrors&&e.ignoreEvalErrors,this.parent=e.parent||null,this.parentProperty=e.parentProperty||null,this.callback=e.callback||n||null,this.otherTypeCallback=e.otherTypeCallback||i||function(){throw new TypeError("You must supply an otherTypeCallback callback option with the @other() operator.")},!1!==e.autostart){var a={path:o?e.path:t};o?"json"in e&&(a.json=e.json):a.json=r;a=this.evaluate(a);if(!a||"object"!==C(a))throw new x(a);return a}}F.prototype.evaluate=function(e,t,r,n){var i=this,o=this.parent,a=this.parentProperty,s=this.flatten,u=this.wrap;if(this.currResultType=this.resultType,this.currEval=this.eval,this.currSandbox=this.sandbox,r=r||this.callback,this.currOtherTypeCallback=n||this.otherTypeCallback,t=t||this.json,(e=e||this.path)&&"object"===C(e)&&!Array.isArray(e)){if(!e.path&&""!==e.path)throw new TypeError('You must supply a "path" property when providing an object argument to JSONPath.evaluate().');if(!A.call(e,"json"))throw new TypeError('You must supply a "json" property when providing an object argument to JSONPath.evaluate().');t=e.json,s=A.call(e,"flatten")?e.flatten:s,this.currResultType=A.call(e,"resultType")?e.resultType:this.currResultType,this.currSandbox=A.call(e,"sandbox")?e.sandbox:this.currSandbox,u=A.call(e,"wrap")?e.wrap:u,this.currEval=A.call(e,"eval")?e.eval:this.currEval,r=A.call(e,"callback")?e.callback:r,this.currOtherTypeCallback=A.call(e,"otherTypeCallback")?e.otherTypeCallback:this.currOtherTypeCallback,o=A.call(e,"parent")?e.parent:o,a=A.call(e,"parentProperty")?e.parentProperty:a,e=e.path}if(o=o||null,a=a||null,Array.isArray(e)&&(e=F.toPathString(e)),(e||""===e)&&t){e=F.toPathArray(e);"$"===e[0]&&1@-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)['\[](\??\((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*?\))(?!(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])\)\])['\]]/g.exec(f);d?this._walk(n,function(e){var t=[d[2]],r=d[1]?n[e][d[1]]:n[e];0=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:t}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,a=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){a=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(a)throw i}}}}(c.split(","));try{for(E.s();!(g=E.n()).done;){var g=g.value;p(this._trace(k(g,l),n,i,o,a,s,!0))}}catch(e){E.e(e)}finally{E.f()}}else!r&&n&&A.call(n,c)&&p(this._trace(l,n[c],w(i,c),n,c,s,e,!0))}if(this._hasParentSelector)for(var x=0;x":function(e,t){return e>t()},"<=":function(e,t){return e<=t()},">=":function(e,t){return e>=t()},"<<":function(e,t){return e<>":function(e,t){return e>>t()},">>>":function(e,t){return e>>>t()},"+":function(e,t){return e+t()},"-":function(e,t){return e-t()},"*":function(e,t){return e*t()},"/":function(e,t){return e/t()},"%":function(e,t){return e%t()}}[e.operator](D.evalAst(e.left,t),function(){return D.evalAst(e.right,t)})},evalCompound:function(e,t){for(var r=0;r Date: Tue, 17 Mar 2026 23:13:37 +0800 Subject: [PATCH 12/17] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96Bundle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\346\235\277\351\205\215\347\275\256.json" | 253 ++++++++++++++++++ ...41\346\235\277\351\205\215\347\275\256.js" | 20 ++ .../js/AppShark[\346\250\241\346\235\277].js" | 11 + .../js/Appget[\346\250\241\346\235\277].js" | 11 + .../js/Appmuou[\346\250\241\346\235\277].js" | 11 + package-bundle.js | 5 + 6 files changed, 311 insertions(+) create mode 100644 "drpy-node-bundle/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" create mode 100644 "drpy-node-bundle/spider/js/APP\346\250\241\346\235\277\351\205\215\347\275\256.js" create mode 100644 "drpy-node-bundle/spider/js/AppShark[\346\250\241\346\235\277].js" create mode 100644 "drpy-node-bundle/spider/js/Appget[\346\250\241\346\235\277].js" create mode 100644 "drpy-node-bundle/spider/js/Appmuou[\346\250\241\346\235\277].js" diff --git "a/drpy-node-bundle/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" "b/drpy-node-bundle/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" new file mode 100644 index 00000000..eb9a2801 --- /dev/null +++ "b/drpy-node-bundle/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" @@ -0,0 +1,253 @@ +{ + "Appget": { + "示例": { + "muban": "模板类型(AppGet,AppQiji)", + "host": "host和hosturl二填一", + "hosturl": "域名地址", + "key": "数据加解密秘钥", + "iv": "一般同上", + "verify": "是否启用搜索验证,字符串true或false", + "username": "APP账号用户名", + "password": "APP账号密码", + "会员时长": "会员函数(gzip压缩)", + "lazyheader": "视频播放请求头,JSON格式" + }, + "蓝鹰": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://lanyinghz.oss-cn-hangzhou.aliyuncs.com/lanyingxmy.txt", + "key": "ca94b06ca359d80e", + "iv": "ca94b06ca359d80e", + "verify": "true", + "username": "", + "password": "", + "会员时长": "", + "lazyheader": {} + }, + "仓鼠": { + "muban": "AppQiji", + "host": "https://hk440cms.cs4k.top", + "hosturl": "", + "key": "fL7sY4zN4kB3pG4p", + "iv": "fL7sY4zN4kB3pG4p", + "verify": "true" + }, + "云云": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://staraugust123456.oss-cn-hangzhou.aliyuncs.com/2.txt", + "key": "staraugust123456", + "iv": "staraugust123456", + "verify": "true" + }, + "奇奇": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://yun-1316442804.cos.ap-guangzhou.myqcloud.com/a.txt", + "key": "123456789abcdefg", + "iv": "123456789abcdefg", + "verify": "true" + }, + "鲸鱼": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://jingyu4k-1312635929.cos.ap-nanjing.myqcloud.com/1.json", + "key": "AAdgrdghjfgswerA", + "iv": "AAdgrdghjfgswerA", + "verify": "true" + }, + "咖啡": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://daen-1256234123.cos.ap-shanghai.myqcloud.com/MuQi/mqxh.txt", + "key": "37kj83zs1q16jk6t", + "iv": "37kj83zs1q16jk6t", + "verify": "true" + }, + "小猪": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://tiantangyoulu.oss-cn-beijing.aliyuncs.com/tengxunyun.txt", + "key": "seb5tq9mykp2w9ry", + "iv": "seb5tq9mykp2w9ry", + "verify": "true" + }, + "影视": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://aysappto.oss-cn-chengdu.aliyuncs.com/qj2.txt", + "key": "sada21321sdq231d", + "iv": "sada21321sdq231d", + "verify": "true" + }, + "优兔": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://uututv-1319209748.cos.ap-shanghai.myqcloud.com/uutuv4.txt", + "key": "UrWKPnmQWJA8AQzd", + "iv": "UrWKPnmQWJA8AQzd", + "verify": "true" + }, + "王子": { + "muban": "AppGet", + "host": "https://app.95112475.xyz", + "hosturl": "", + "key": "5a9w6x58dsq6z3a6", + "iv": "5a9w6x58dsq6z3a6", + "verify": "true" + }, + "紫金": { + "muban": "AppGet", + "host": "", + "hosturl": "https://snysw.xyz/mf4kzs327.txt", + "key": "1234567887654321", + "iv": "1234567887654321", + "verify": "true" + }, + "数字": { + "muban": "AppGet", + "host": "http://app1-0-0.87333.cc", + "hosturl": "", + "key": "VwsHxkCViDXEExWa", + "iv": "VwsHxkCViDXEExWa", + "verify": "true" + }, + "火锅": { + "muban": "AppGet", + "host": "https://ios.hgyx.vip", + "hosturl": "", + "key": "062dec75d039980e", + "iv": "062dec75d039980e", + "verify": "true" + }, + "五八": { + "muban": "AppGet", + "host": "https://dy.58ys.vip", + "hosturl": "", + "key": "JEWibY1AgWF0V1xx", + "iv": "JEWibY1AgWF0V1xx", + "verify": "true" + }, + "旗星": { + "muban": "AppGet", + "host": "http://ys.qist.top", + "hosturl": "", + "key": "2SWSPFxugBLPPOKo", + "iv": "2SWSPFxugBLPPOKo", + "verify": "true" + }, + "灵虎": { + "muban": "AppGet", + "host": "", + "hosturl": "https://bind.315999.xyz/89.txt", + "key": "#getapp@TMD@2025", + "iv": "#getapp@TMD@2025", + "verify": "true" + }, + "剧梦": { + "muban": "AppGet", + "host": "https://www.jumengwu.com", + "hosturl": "", + "key": "1f0a873caf2550a5", + "iv": "1f0a873caf2550a5", + "verify": "true" + }, + "火狐": { + "muban": "AppGet", + "host": "http://huohu.yihn.cc", + "hosturl": "", + "key": "huohushipingetap", + "iv": "huohushipingetap", + "verify": "true" + }, + "金牌": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://dtqj.ggtvb.cc/dtjp.txt", + "key": "eecbio48dsq131ee", + "iv": "eecbio48dsq131ee", + "verify": "true" + }, + "爱盈": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://aysappto.oss-cn-chengdu.aliyuncs.com/qj3.txt", + "key": "sda1231sasddad21", + "iv": "sda1231sasddad21", + "verify": "true" + }, + "顾我": { + "muban": "AppQiji", + "host": "http://117.50.204.35:520", + "hosturl": "", + "key": "ca94b06ca3c7d80e", + "iv": "ca94b06ca3c7d80e", + "verify": "true" + }, + "爆炸": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://daen-1256234123.cos.ap-shanghai.myqcloud.com/MuQi/mqxh.txt", + "key": "37kj83zs1q16jk6t", + "iv": "37kj83zs1q16jk6t", + "verify": "true" + }, + "丫丫动漫": { + "muban": "AppGet", + "host": "http://tv.yy-fun.cc", + "hosturl": "", + "key": "qkxnwkfjwpcnwycl", + "iv": "qkxnwkfjwpcnwycl", + "verify": "true" + }, + "番薯动漫": { + "muban": "AppGet", + "host": "https://new.app.bytegooty.com", + "hosturl": "", + "key": "N4yj7l7xKxHF4*gz", + "iv": "N4yj7l7xKxHF4*gz", + "verify": "true" + }, + "咕咕动漫": { + "muban": "AppGet", + "host": "https://www.gugu3.com", + "hosturl": "", + "key": "nKfZ8KX6JTNWRzTD", + "iv": "nKfZ8KX6JTNWRzTD", + "verify": "true" + }, + "元咲动漫": { + "muban": "AppGet", + "host": "http://cic.aicg.fun", + "hosturl": "", + "key": "2c4h36abd96se10u", + "iv": "2c4h36abd96se10u", + "verify": "true" + }, + "方舟动漫": { + "muban": "AppGet", + "host": "https://www.cyfz.vip", + "hosturl": "", + "key": "e72cdfd629e8895d", + "iv": "e72cdfd629e8895d", + "verify": "true" + } + }, + "Appmuou": { + "示例": { + "host": "", + "hosturl": "", + "version": "" + } + }, + "AppShark": { + "示例": { + "host": "", + "hosturl": "", + "key": "", + "key1": "", + "key2": "", + "version": "" + } + } +} \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/APP\346\250\241\346\235\277\351\205\215\347\275\256.js" "b/drpy-node-bundle/spider/js/APP\346\250\241\346\235\277\351\205\215\347\275\256.js" new file mode 100644 index 00000000..fbfc9ccd --- /dev/null +++ "b/drpy-node-bundle/spider/js/APP\346\250\241\346\235\277\351\205\215\347\275\256.js" @@ -0,0 +1,20 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: 'APP模板配置', + more: { + sourceTag: '设置,动作', + actions: [ + { + name: 'APP模板|配置相关', + action: '{\'actionId\':\'单选菜单\',\'type\':\'menu\',\'title\':\'Action菜单\',\'width\':500,\'column\':1,\'option\':[\'导入分享配置$menu1\',\'分享APP配置$menu2\'],\'selectedIndex\':0}' + } + ] + }, + lang: 'ds' +}) +*/ + +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 \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/AppShark[\346\250\241\346\235\277].js" "b/drpy-node-bundle/spider/js/AppShark[\346\250\241\346\235\277].js" new file mode 100644 index 00000000..0f3a3852 --- /dev/null +++ "b/drpy-node-bundle/spider/js/AppShark[\346\250\241\346\235\277].js" @@ -0,0 +1,11 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: 'AppShark模板', + lang: 'ds' +}) +*/ + +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 \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/Appget[\346\250\241\346\235\277].js" "b/drpy-node-bundle/spider/js/Appget[\346\250\241\346\235\277].js" new file mode 100644 index 00000000..3b4a753a --- /dev/null +++ "b/drpy-node-bundle/spider/js/Appget[\346\250\241\346\235\277].js" @@ -0,0 +1,11 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: 'Appget[模板]', + lang: 'ds' +}) +*/ + +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 \ No newline at end of file diff --git "a/drpy-node-bundle/spider/js/Appmuou[\346\250\241\346\235\277].js" "b/drpy-node-bundle/spider/js/Appmuou[\346\250\241\346\235\277].js" new file mode 100644 index 00000000..56df3dc1 --- /dev/null +++ "b/drpy-node-bundle/spider/js/Appmuou[\346\250\241\346\235\277].js" @@ -0,0 +1,11 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: 'Appmuou模板', + lang: 'ds' +}) +*/ + +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 \ No newline at end of file diff --git a/package-bundle.js b/package-bundle.js index 06fef072..3299a7cd 100644 --- a/package-bundle.js +++ b/package-bundle.js @@ -8,6 +8,7 @@ const INCLUDE_ITEMS = [ 'libs', 'spider', 'jx', + 'json', 'localt5.js', 'localDsCoreTest.js' ]; @@ -20,6 +21,10 @@ const EXTRA_FILES = [ '设置中心.js', '听友[听].js', '360影视[官].js', + 'Appget[模板].js', + 'Appmuou[模板].js', + 'AppShark[模板].js', + 'APP模板配置.js', ]; // 获取脚本所在目录 (e:\gitwork\drpy-node) From ea109a4d130c8fd75f42380f81db357716ef16e9 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 18 Mar 2026 02:00:05 +0800 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20=E6=96=B0=E7=89=88=E5=87=86?= =?UTF-8?q?=E5=A4=87=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- controllers/admin.js | 151 ++ controllers/index.js | 2 + drpy-node-admin/README.md | 69 + drpy-node-admin/index.html | 13 + drpy-node-admin/package-lock.json | 2727 ++++++++++++++++++++ drpy-node-admin/package.json | 23 + drpy-node-admin/postcss.config.js | 6 + drpy-node-admin/public/favicon.svg | 6 + drpy-node-admin/src/App.vue | 49 + drpy-node-admin/src/api/client.js | 32 + drpy-node-admin/src/api/config.js | 28 + drpy-node-admin/src/api/db.js | 12 + drpy-node-admin/src/api/file.js | 50 + drpy-node-admin/src/api/spider.js | 52 + drpy-node-admin/src/api/system.js | 45 + drpy-node-admin/src/components/Header.vue | 56 + drpy-node-admin/src/components/Sidebar.vue | 61 + drpy-node-admin/src/main.js | 12 + drpy-node-admin/src/router/index.js | 70 + drpy-node-admin/src/stores/config.js | 45 + drpy-node-admin/src/stores/system.js | 84 + drpy-node-admin/src/stores/theme.js | 53 + drpy-node-admin/src/style.css | 78 + drpy-node-admin/src/views/ApiDocs.vue | 170 ++ drpy-node-admin/src/views/Config.vue | 237 ++ drpy-node-admin/src/views/Dashboard.vue | 226 ++ drpy-node-admin/src/views/Database.vue | 142 + drpy-node-admin/src/views/Files.vue | 206 ++ drpy-node-admin/src/views/Logs.vue | 322 +++ drpy-node-admin/src/views/SourceEditor.vue | 176 ++ drpy-node-admin/src/views/Sources.vue | 221 ++ drpy-node-admin/tailwind.config.js | 31 + drpy-node-admin/vite.config.js | 41 + drpy-node-mcp/tools/fsTools.js | 45 +- drpy-node-mcp/tools/systemTools.js | 12 +- package.json | 7 +- public/images/drpyS.png | Bin 0 -> 112369 bytes public/index.html | 3 +- 39 files changed, 5556 insertions(+), 12 deletions(-) create mode 100644 controllers/admin.js create mode 100644 drpy-node-admin/README.md create mode 100644 drpy-node-admin/index.html create mode 100644 drpy-node-admin/package-lock.json create mode 100644 drpy-node-admin/package.json create mode 100644 drpy-node-admin/postcss.config.js create mode 100644 drpy-node-admin/public/favicon.svg create mode 100644 drpy-node-admin/src/App.vue create mode 100644 drpy-node-admin/src/api/client.js create mode 100644 drpy-node-admin/src/api/config.js create mode 100644 drpy-node-admin/src/api/db.js create mode 100644 drpy-node-admin/src/api/file.js create mode 100644 drpy-node-admin/src/api/spider.js create mode 100644 drpy-node-admin/src/api/system.js create mode 100644 drpy-node-admin/src/components/Header.vue create mode 100644 drpy-node-admin/src/components/Sidebar.vue create mode 100644 drpy-node-admin/src/main.js create mode 100644 drpy-node-admin/src/router/index.js create mode 100644 drpy-node-admin/src/stores/config.js create mode 100644 drpy-node-admin/src/stores/system.js create mode 100644 drpy-node-admin/src/stores/theme.js create mode 100644 drpy-node-admin/src/style.css create mode 100644 drpy-node-admin/src/views/ApiDocs.vue create mode 100644 drpy-node-admin/src/views/Config.vue create mode 100644 drpy-node-admin/src/views/Dashboard.vue create mode 100644 drpy-node-admin/src/views/Database.vue create mode 100644 drpy-node-admin/src/views/Files.vue create mode 100644 drpy-node-admin/src/views/Logs.vue create mode 100644 drpy-node-admin/src/views/SourceEditor.vue create mode 100644 drpy-node-admin/src/views/Sources.vue create mode 100644 drpy-node-admin/tailwind.config.js create mode 100644 drpy-node-admin/vite.config.js create mode 100644 public/images/drpyS.png diff --git a/README.md b/README.md index 036b99ae..2593d6a4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # drpyS(drpy-node) - [![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=plastic&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/hjdhnx/drpy-node) -[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/hjdhnx/drpy-node) - +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/hjdhnx/drpy-node) +![drpyS](./public/images/drpys.png) nodejs作为服务端的drpy实现。全面升级异步写法 ~~积极开发中,每日一更~~,当前进度 `100%` ~~找工作中,随缘更新~~ diff --git a/controllers/admin.js b/controllers/admin.js new file mode 100644 index 00000000..bd8ce924 --- /dev/null +++ b/controllers/admin.js @@ -0,0 +1,151 @@ +/** + * Admin Controller - 后台管理界面控制器 + * 提供 admin 面板所需的 API 接口和静态文件服务 + */ + +import path from 'path'; +import fs from 'fs'; +import fastifyStatic from '@fastify/static'; + +// 配置相关 +const CONFIG_PATH = path.join(process.cwd(), 'config/env.json'); + +// 获取配置 +async function getConfig(req, reply) { + try { + const { key } = req.query; + + if (!fs.existsSync(CONFIG_PATH)) { + return reply.send({}); + } + + const configContent = fs.readFileSync(CONFIG_PATH, 'utf-8'); + const config = JSON.parse(configContent); + + if (key) { + const keys = key.split('.'); + let value = config; + for (const k of keys) { + value = value?.[k]; + } + return reply.send(value !== undefined ? value : null); + } + + return reply.send(config); + } catch (e) { + reply.code(500).send({ error: e.message }); + } +} + +// 更新配置 +async function updateConfig(req, reply) { + try { + const { action, key, value } = req.body; + + if (action === 'set') { + const systemTools = await import('../drpy-node-mcp/tools/systemTools.js'); + const result = await systemTools.manage_config({ action, key, value: String(value) }); + if (result.isError) { + return reply.code(400).send({ error: result.content[0].text }); + } + return reply.send({ success: true, message: result.content[0].text }); + } + + reply.code(400).send({ error: 'Invalid action' }); + } catch (e) { + reply.code(500).send({ error: e.message }); + } +} + +// MCP 工具调用接口 +async function callMCP(req, reply) { + try { + const { name, arguments: args } = req.body; + + let handler; + switch (name) { + case 'read_logs': + const systemTools = await import('../drpy-node-mcp/tools/systemTools.js'); + handler = systemTools.read_logs; + break; + case 'restart_service': + const systemTools2 = await import('../drpy-node-mcp/tools/systemTools.js'); + handler = systemTools2.restart_service; + break; + case 'list_sources': + const spiderTools = await import('../drpy-node-mcp/tools/spiderTools.js'); + handler = spiderTools.list_sources; + break; + case 'get_routes_info': + const spiderTools2 = await import('../drpy-node-mcp/tools/spiderTools.js'); + handler = spiderTools2.get_routes_info; + break; + case 'get_drpy_api_list': + const apiTools = await import('../drpy-node-mcp/tools/apiTools.js'); + handler = apiTools.get_drpy_api_list; + break; + case 'validate_spider': + case 'check_syntax': + case 'get_spider_template': + case 'debug_spider_rule': + const spiderTools3 = await import('../drpy-node-mcp/tools/spiderTools.js'); + handler = spiderTools3[name]; + break; + case 'sql_query': + const dbTools = await import('../drpy-node-mcp/tools/dbTools.js'); + handler = dbTools.sql_query; + break; + case 'list_directory': + case 'read_file': + const fsTools = await import('../drpy-node-mcp/tools/fsTools.js'); + handler = fsTools[name]; + break; + default: + return reply.code(404).send({ error: 'Tool not found' }); + } + + if (!handler) { + return reply.code(404).send({ error: 'Tool not found' }); + } + + const result = await handler(args || {}); + + if (result.isError) { + return reply.code(400).send({ error: result.content[0].text }); + } + + const content = result.content[0].text; + try { + return reply.send(JSON.parse(content)); + } catch { + return reply.send(content); + } + } catch (e) { + reply.code(500).send({ error: e.message }); + } +} + +// 导出路由配置 - 使用标准控制器模式 +export default (fastify, options, done) => { + // Admin 面板静态文件目录 + const adminDistPath = path.join(process.cwd(), 'drpy-node-admin/dist'); + + if (fs.existsSync(adminDistPath)) { + fastify.log.info('Serving admin panel from ' + adminDistPath); + + // 注册静态文件服务(在 API 路由之前注册,避免冲突) + fastify.register(fastifyStatic, { + root: adminDistPath, + prefix: '/admin/', + decorateReply: false, + index: ['index.html'] + }); + } + + // API 路由(必须在静态文件服务之后注册,避免被静态文件拦截) + fastify.get('/admin/config', getConfig); + fastify.post('/admin/config', updateConfig); + fastify.post('/admin/mcp', callMCP); + + done(); +} diff --git a/controllers/index.js b/controllers/index.js index aa6f2853..91705c88 100644 --- a/controllers/index.js +++ b/controllers/index.js @@ -24,6 +24,7 @@ import m3u8ProxyController from './m3u8-proxy.js'; import unifiedProxyController from './unified-proxy.js'; import githubController from './github.js'; import websocketServerController from "./websocketServer.js"; +// import adminController from './admin.js'; export const registerRoutes = (fastify, options) => { fastify.register(formBody); @@ -52,6 +53,7 @@ export const registerRoutes = (fastify, options) => { fastify.register(m3u8ProxyController, options); fastify.register(unifiedProxyController, options); fastify.register(githubController, options); + // fastify.register(adminController, options); }; export const registerWsRoutes = (wsApp, options) => { diff --git a/drpy-node-admin/README.md b/drpy-node-admin/README.md new file mode 100644 index 00000000..5f785dd0 --- /dev/null +++ b/drpy-node-admin/README.md @@ -0,0 +1,69 @@ +# DRPY Node Admin + +drpy-node 后台管理界面 - 基于 Vue3 + Tailwind CSS 构建的现代化管理系统。 + +## 功能特性 + +- 🎨 现代化 UI 设计,支持亮色/暗色主题 +- 📱 完全响应式,适配 PC 和移动端 +- 🚀 基于 Vite 构建,快速开发体验 +- 🔧 环境变量可视化配置 +- 📦 源文件管理和验证 +- 📋 实时日志查看 +- 📚 API 文档查看 +- 📁 文件浏览和编辑 +- 🗄️ 数据库查询 + +## 开发 + +```bash +# 安装依赖 +npm install + +# 启动开发服务器 +npm run dev + +# 构建生产版本 +npm run build + +# 预览生产构建 +npm run preview +``` + +## 技术栈 + +- **Vue 3** - 渐进式 JavaScript 框架 +- **Vite** - 下一代前端构建工具 +- **Tailwind CSS** - 实用优先的 CSS 框架 +- **Vue Router** - 官方路由管理 +- **Pinia** - Vue 3 状态管理 +- **Axios** - HTTP 客户端 + +## 项目结构 + +``` +drpy-node-admin/ +├── src/ +│ ├── api/ # API 接口 +│ ├── components/ # 公共组件 +│ ├── router/ # 路由配置 +│ ├── stores/ # Pinia 状态管理 +│ ├── utils/ # 工具函数 +│ ├── views/ # 页面组件 +│ ├── App.vue # 根组件 +│ ├── main.js # 入口文件 +│ └── style.css # 全局样式 +├── public/ # 静态资源 +├── index.html # HTML 模板 +├── vite.config.js # Vite 配置 +├── tailwind.config.js # Tailwind 配置 +└── package.json # 项目配置 +``` + +## 与 drpy-node 集成 + +Admin 面板通过 drpy-node-mcp 与主项目通信,需要后端提供相应的 API 接口。 + +## License + +MIT diff --git a/drpy-node-admin/index.html b/drpy-node-admin/index.html new file mode 100644 index 00000000..f33579d3 --- /dev/null +++ b/drpy-node-admin/index.html @@ -0,0 +1,13 @@ + + + + + + DRPY Node Admin + + + +

    + + + diff --git a/drpy-node-admin/package-lock.json b/drpy-node-admin/package-lock.json new file mode 100644 index 00000000..97406b6d --- /dev/null +++ b/drpy-node-admin/package-lock.json @@ -0,0 +1,2727 @@ +{ + "name": "drpy-node-admin", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "drpy-node-admin", + "version": "1.0.0", + "dependencies": { + "axios": "^1.7.9", + "pinia": "^2.2.8", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "vite": "^6.0.11" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.30.tgz", + "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/shared": "3.5.30", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz", + "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", + "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/compiler-core": "3.5.30", + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz", + "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.30.tgz", + "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.30.tgz", + "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz", + "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/runtime-core": "3.5.30", + "@vue/shared": "3.5.30", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.30.tgz", + "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30" + }, + "peerDependencies": { + "vue": "3.5.30" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.30.tgz", + "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==", + "license": "MIT" + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001780", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vue": { + "version": "3.5.30", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.30.tgz", + "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-sfc": "3.5.30", + "@vue/runtime-dom": "3.5.30", + "@vue/server-renderer": "3.5.30", + "@vue/shared": "3.5.30" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + } + } +} diff --git a/drpy-node-admin/package.json b/drpy-node-admin/package.json new file mode 100644 index 00000000..1e539d35 --- /dev/null +++ b/drpy-node-admin/package.json @@ -0,0 +1,23 @@ +{ + "name": "drpy-node-admin", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.13", + "vue-router": "^4.5.0", + "pinia": "^2.2.8", + "axios": "^1.7.9" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "vite": "^6.0.11" + } +} diff --git a/drpy-node-admin/postcss.config.js b/drpy-node-admin/postcss.config.js new file mode 100644 index 00000000..2e7af2b7 --- /dev/null +++ b/drpy-node-admin/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/drpy-node-admin/public/favicon.svg b/drpy-node-admin/public/favicon.svg new file mode 100644 index 00000000..14f166b5 --- /dev/null +++ b/drpy-node-admin/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/drpy-node-admin/src/App.vue b/drpy-node-admin/src/App.vue new file mode 100644 index 00000000..d1c8e4d0 --- /dev/null +++ b/drpy-node-admin/src/App.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/drpy-node-admin/src/api/client.js b/drpy-node-admin/src/api/client.js new file mode 100644 index 00000000..2b8561e7 --- /dev/null +++ b/drpy-node-admin/src/api/client.js @@ -0,0 +1,32 @@ +import axios from 'axios' + +const apiClient = axios.create({ + baseURL: '/api-proxy', + timeout: 30000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// Request interceptor +apiClient.interceptors.request.use( + (config) => { + // Add auth if needed + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// Response interceptor +apiClient.interceptors.response.use( + (response) => response.data, + (error) => { + const message = error.response?.data?.message || error.message || '请求失败' + console.error('API Error:', message) + return Promise.reject(new Error(message)) + } +) + +export default apiClient diff --git a/drpy-node-admin/src/api/config.js b/drpy-node-admin/src/api/config.js new file mode 100644 index 00000000..b28f9ac3 --- /dev/null +++ b/drpy-node-admin/src/api/config.js @@ -0,0 +1,28 @@ +import apiClient from './client' + +export const configApi = { + // Get configuration via MCP + async getConfig() { + // This will be proxied through a backend endpoint + const response = await apiClient.get('/admin/config') + return response + }, + + // Update configuration via MCP + async updateConfig(key, value) { + const response = await apiClient.post('/admin/config', { + action: 'set', + key, + value: typeof value === 'string' ? value : JSON.stringify(value) + }) + return response + }, + + // Get single config value + async getConfigValue(key) { + const response = await apiClient.get('/admin/config', { + params: { key } + }) + return response + } +} diff --git a/drpy-node-admin/src/api/db.js b/drpy-node-admin/src/api/db.js new file mode 100644 index 00000000..44d03ef2 --- /dev/null +++ b/drpy-node-admin/src/api/db.js @@ -0,0 +1,12 @@ +import apiClient from './client' + +export const dbApi = { + // Execute SQL query via MCP + async query(sql) { + const response = await apiClient.post('/admin/mcp', { + name: 'sql_query', + arguments: { query: sql } + }) + return response + } +} diff --git a/drpy-node-admin/src/api/file.js b/drpy-node-admin/src/api/file.js new file mode 100644 index 00000000..0e0e1865 --- /dev/null +++ b/drpy-node-admin/src/api/file.js @@ -0,0 +1,50 @@ +import apiClient from './client' + +export const fileApi = { + // List directory via MCP + async listDirectory(path = '.') { + const response = await apiClient.post('/admin/mcp', { + name: 'list_directory', + arguments: { path } + }) + // MCP returns array format, convert to {files: [...]} + if (Array.isArray(response)) { + return { + files: response.map(f => ({ + name: f.name, + path: path === '.' ? f.name : `${path}/${f.name}`.replace(/^\.\//, ''), + isDirectory: f.isDirectory, + size: f.size + })) + } + } + return response + }, + + // Read file via MCP + async readFile(path) { + const response = await apiClient.post('/admin/mcp', { + name: 'read_file', + arguments: { path } + }) + return response + }, + + // Write file via MCP + async writeFile(path, content) { + const response = await apiClient.post('/admin/mcp', { + name: 'write_file', + arguments: { path, content } + }) + return response + }, + + // Delete file via MCP + async deleteFile(path) { + const response = await apiClient.post('/admin/mcp', { + name: 'delete_file', + arguments: { path } + }) + return response + } +} diff --git a/drpy-node-admin/src/api/spider.js b/drpy-node-admin/src/api/spider.js new file mode 100644 index 00000000..4ac6d42a --- /dev/null +++ b/drpy-node-admin/src/api/spider.js @@ -0,0 +1,52 @@ +import apiClient from './client' + +export const spiderApi = { + // List all sources via MCP + async listSources() { + const response = await apiClient.post('/admin/mcp', { + name: 'list_sources', + arguments: {} + }) + // Transform response format from {spider/js, spider/catvod} to {js, catvod} + return { + js: response['spider/js'] || [], + catvod: response['spider/catvod'] || [] + } + }, + + // Validate spider via MCP + async validateSpider(path) { + const response = await apiClient.post('/admin/mcp', { + name: 'validate_spider', + arguments: { path } + }) + return response + }, + + // Check syntax via MCP + async checkSyntax(path) { + const response = await apiClient.post('/admin/mcp', { + name: 'check_syntax', + arguments: { path } + }) + return response + }, + + // Get spider template via MCP + async getTemplate() { + const response = await apiClient.post('/admin/mcp', { + name: 'get_spider_template', + arguments: {} + }) + return response + }, + + // Debug spider rule via MCP + async debugRule(params) { + const response = await apiClient.post('/admin/mcp', { + name: 'debug_spider_rule', + arguments: params + }) + return response + } +} diff --git a/drpy-node-admin/src/api/system.js b/drpy-node-admin/src/api/system.js new file mode 100644 index 00000000..38f3941c --- /dev/null +++ b/drpy-node-admin/src/api/system.js @@ -0,0 +1,45 @@ +import apiClient from './client' + +export const systemApi = { + // Get health status + async getHealth() { + const response = await apiClient.get('/health') + return response + }, + + // Get logs via MCP + async getLogs(lines = 100) { + const response = await apiClient.post('/admin/mcp', { + name: 'read_logs', + arguments: { lines } + }) + return response + }, + + // Get routes info via MCP + async getRoutes() { + const response = await apiClient.post('/admin/mcp', { + name: 'get_routes_info', + arguments: {} + }) + return response + }, + + // Restart service via MCP + async restartService() { + const response = await apiClient.post('/admin/mcp', { + name: 'restart_service', + arguments: {} + }) + return response + }, + + // Get API list via MCP + async getApiList() { + const response = await apiClient.post('/admin/mcp', { + name: 'get_drpy_api_list', + arguments: {} + }) + return response + } +} diff --git a/drpy-node-admin/src/components/Header.vue b/drpy-node-admin/src/components/Header.vue new file mode 100644 index 00000000..257ae2d8 --- /dev/null +++ b/drpy-node-admin/src/components/Header.vue @@ -0,0 +1,56 @@ + + + diff --git a/drpy-node-admin/src/components/Sidebar.vue b/drpy-node-admin/src/components/Sidebar.vue new file mode 100644 index 00000000..ade741cb --- /dev/null +++ b/drpy-node-admin/src/components/Sidebar.vue @@ -0,0 +1,61 @@ + + + diff --git a/drpy-node-admin/src/main.js b/drpy-node-admin/src/main.js new file mode 100644 index 00000000..4a4f2bf4 --- /dev/null +++ b/drpy-node-admin/src/main.js @@ -0,0 +1,12 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import router from './router' +import App from './App.vue' +import './style.css' + +const app = createApp(App) + +app.use(createPinia()) +app.use(router) + +app.mount('#app') diff --git a/drpy-node-admin/src/router/index.js b/drpy-node-admin/src/router/index.js new file mode 100644 index 00000000..f2c23f80 --- /dev/null +++ b/drpy-node-admin/src/router/index.js @@ -0,0 +1,70 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useThemeStore } from '../stores/theme' + +const routes = [ + { + path: '/', + name: 'dashboard', + component: () => import('../views/Dashboard.vue'), + meta: { title: '仪表盘' } + }, + { + path: '/config', + name: 'config', + component: () => import('../views/Config.vue'), + meta: { title: '环境配置' } + }, + { + path: '/sources', + name: 'sources', + component: () => import('../views/Sources.vue'), + meta: { title: '源管理' } + }, + { + path: '/sources/editor', + name: 'source-editor', + component: () => import('../views/SourceEditor.vue'), + meta: { title: '源编辑器' } + }, + { + path: '/logs', + name: 'logs', + component: () => import('../views/Logs.vue'), + meta: { title: '日志查看' } + }, + { + path: '/api-docs', + name: 'api', + component: () => import('../views/ApiDocs.vue'), + meta: { title: 'API 文档' } + }, + { + path: '/files', + name: 'files', + component: () => import('../views/Files.vue'), + meta: { title: '文件管理' } + }, + { + path: '/database', + name: 'database', + component: () => import('../views/Database.vue'), + meta: { title: '数据库' } + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +// Navigation guard +router.beforeEach((to, from, next) => { + const themeStore = useThemeStore() + // Close sidebar on mobile when navigating + if (window.innerWidth < 1024) { + themeStore.closeSidebar() + } + next() +}) + +export default router diff --git a/drpy-node-admin/src/stores/config.js b/drpy-node-admin/src/stores/config.js new file mode 100644 index 00000000..2cf5c080 --- /dev/null +++ b/drpy-node-admin/src/stores/config.js @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { configApi } from '../api/config' + +export const useConfigStore = defineStore('config', () => { + const config = ref(null) + const loading = ref(false) + const error = ref(null) + + const fetchConfig = async () => { + loading.value = true + error.value = null + try { + config.value = await configApi.getConfig() + } catch (e) { + error.value = e.message + console.error('Failed to fetch config:', e) + } finally { + loading.value = false + } + } + + const updateConfig = async (key, value) => { + loading.value = true + error.value = null + try { + await configApi.updateConfig(key, value) + await fetchConfig() + } catch (e) { + error.value = e.message + console.error('Failed to update config:', e) + throw e + } finally { + loading.value = false + } + } + + return { + config, + loading, + error, + fetchConfig, + updateConfig + } +}) diff --git a/drpy-node-admin/src/stores/system.js b/drpy-node-admin/src/stores/system.js new file mode 100644 index 00000000..126f97c5 --- /dev/null +++ b/drpy-node-admin/src/stores/system.js @@ -0,0 +1,84 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { systemApi } from '../api/system' +import { spiderApi } from '../api/spider' + +export const useSystemStore = defineStore('system', () => { + const health = ref({ status: 'unknown' }) + const logs = ref([]) + const routes = ref([]) + const sources = ref({ js: [], catvod: [] }) + const loading = ref(false) + const error = ref(null) + + const checkHealth = async () => { + try { + health.value = await systemApi.getHealth() + } catch (e) { + health.value = { status: 'error', message: e.message } + } + } + + const fetchLogs = async (lines = 100) => { + loading.value = true + error.value = null + try { + logs.value = await systemApi.getLogs(lines) + } catch (e) { + error.value = e.message + console.error('Failed to fetch logs:', e) + } finally { + loading.value = false + } + } + + const fetchRoutes = async () => { + try { + routes.value = await systemApi.getRoutes() + } catch (e) { + error.value = e.message + console.error('Failed to fetch routes:', e) + } + } + + const fetchSources = async () => { + loading.value = true + error.value = null + try { + sources.value = await spiderApi.listSources() + } catch (e) { + error.value = e.message + console.error('Failed to fetch sources:', e) + } finally { + loading.value = false + } + } + + const restartService = async () => { + loading.value = true + error.value = null + try { + await systemApi.restartService() + } catch (e) { + error.value = e.message + console.error('Failed to restart service:', e) + throw e + } finally { + loading.value = false + } + } + + return { + health, + logs, + routes, + sources, + loading, + error, + checkHealth, + fetchLogs, + fetchRoutes, + fetchSources, + restartService + } +}) diff --git a/drpy-node-admin/src/stores/theme.js b/drpy-node-admin/src/stores/theme.js new file mode 100644 index 00000000..3a68fd2a --- /dev/null +++ b/drpy-node-admin/src/stores/theme.js @@ -0,0 +1,53 @@ +import { defineStore } from 'pinia' +import { ref, watch } from 'vue' + +export const useThemeStore = defineStore('theme', () => { + const isDark = ref(false) + const sidebarOpen = ref(false) + + // Initialize theme from localStorage or system preference + const initTheme = () => { + const saved = localStorage.getItem('theme') + if (saved) { + isDark.value = saved === 'dark' + } else { + isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches + } + updateTheme() + } + + const updateTheme = () => { + if (isDark.value) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + } + + const toggleTheme = () => { + isDark.value = !isDark.value + } + + const closeSidebar = () => { + sidebarOpen.value = false + } + + const toggleSidebar = () => { + sidebarOpen.value = !sidebarOpen.value + } + + // Watch for changes and save to localStorage + watch(isDark, () => { + localStorage.setItem('theme', isDark.value ? 'dark' : 'light') + updateTheme() + }) + + return { + isDark, + sidebarOpen, + initTheme, + toggleTheme, + closeSidebar, + toggleSidebar + } +}) diff --git a/drpy-node-admin/src/style.css b/drpy-node-admin/src/style.css new file mode 100644 index 00000000..c9f0d038 --- /dev/null +++ b/drpy-node-admin/src/style.css @@ -0,0 +1,78 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100; + font-feature-settings: "rlig" 1, "calt" 1; + } +} + +@layer components { + .btn { + @apply inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none; + } + + .btn-primary { + @apply btn bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500; + } + + .btn-secondary { + @apply btn bg-gray-200 text-gray-900 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600 focus:ring-gray-500; + } + + .btn-danger { + @apply btn bg-red-600 text-white hover:bg-red-700 focus:ring-red-500; + } + + .btn-success { + @apply btn bg-green-600 text-white hover:bg-green-700 focus:ring-green-500; + } + + .card { + @apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700; + } + + .input { + @apply w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-colors; + } + + .badge { + @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; + } + + .badge-success { + @apply badge bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200; + } + + .badge-warning { + @apply badge bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200; + } + + .badge-error { + @apply badge bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200; + } + + .badge-info { + @apply badge bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200; + } +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + @apply bg-gray-100 dark:bg-gray-800; +} + +::-webkit-scrollbar-thumb { + @apply bg-gray-300 dark:bg-gray-600 rounded-full; +} + +::-webkit-scrollbar-thumb:hover { + @apply bg-gray-400 dark:bg-gray-500; +} diff --git a/drpy-node-admin/src/views/ApiDocs.vue b/drpy-node-admin/src/views/ApiDocs.vue new file mode 100644 index 00000000..dbca10dd --- /dev/null +++ b/drpy-node-admin/src/views/ApiDocs.vue @@ -0,0 +1,170 @@ + + + diff --git a/drpy-node-admin/src/views/Config.vue b/drpy-node-admin/src/views/Config.vue new file mode 100644 index 00000000..0da65509 --- /dev/null +++ b/drpy-node-admin/src/views/Config.vue @@ -0,0 +1,237 @@ + + +