diff --git a/README.md b/README.md index f650d25..97a13b5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # drpyS(drpy-node) nodejs作为服务端的drpy实现。全面升级异步写法 -~~积极开发中,每日一更~~,当前进度 `48%` +~~积极开发中,每日一更~~,当前进度 `49%` 找工作中,随缘更新 * [本地配置接口-动态本地](/config?pwd=) @@ -19,6 +19,18 @@ nodejs作为服务端的drpy实现。全面升级异步写法 ## 更新记录 +### 20250310 + +更新至V1.1.23 + +### 20250227 + +更新至V1.1.22 + +### 20250226 + +更新至V1.1.21 + ### 20250225 更新至V1.1.20 diff --git a/docs/updateRecord.md b/docs/updateRecord.md index fd01646..1acf857 100644 --- a/docs/updateRecord.md +++ b/docs/updateRecord.md @@ -1,5 +1,25 @@ # drpyS更新记录 +### 20250310 + +更新至V1.1.23 + +1. 修复番茄小说 +2. 新增2个源 + +### 20250227 + +更新至V1.1.22 + +1. 优化123网盘的逻辑和推送示例 +2. 优化sqlite3库兼容装逼壳 + +### 20250226 + +更新至V1.1.21 + +1. 增加123网盘的逻辑和推送示例 + ### 20250225 更新至V1.1.20 diff --git a/js/gaze.js b/js/gaze.js new file mode 100644 index 0000000..c210c0e --- /dev/null +++ b/js/gaze.js @@ -0,0 +1,591 @@ +globalThis.window = globalThis.global = globalThis; + +class Go { + constructor() { + let s = new TextEncoder("utf-8"), + i = new TextDecoder("utf-8"), + r = new DataView(new ArrayBuffer(8)); + var o = []; + + this._callbackTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + let e = () => new DataView(this._inst.exports.m.buffer); + let t = (e) => { + r.setBigInt64(0, e, !0); + let t = r.getFloat64(0, !0); + if (0 === t) return; + if (!isNaN(t)) return t; + let n = 4294967295n & e; + return this._values[n]; + }; + let n = (n) => { + let s = e().getBigUint64(n, !0); + return t(s); + }; + let l = (e) => { + if ("number" == typeof e) + return isNaN(e) + ? 2146959360n << 32n + : 0 === e + ? (2146959360n << 32n) | 1n + : (r.setFloat64(0, e, !0), r.getBigInt64(0, !0)); + switch (e) { + case void 0: + return 0n; + case null: + return (2146959360n << 32n) | 2n; + case !0: + return (2146959360n << 32n) | 3n; + case !1: + return (2146959360n << 32n) | 4n; + } + let t = this._ids.get(e); + void 0 === t && + (void 0 === (t = this._idPool.pop()) && + (t = BigInt(this._values.length)), + (this._values[t] = e), + (this._goRefCounts[t] = 0), + this._ids.set(e, t)), + this._goRefCounts[t]++; + let n = 1n; + switch (typeof e) { + case "string": + n = 2n; + break; + case "symbol": + n = 3n; + break; + case "function": + n = 4n; + } + return t | ((2146959360n | n) << 32n); + }; + let a = (t, n) => { + let s = l(n); + e().setBigUint64(t, s, !0); + }; + let c = (e, t, n) => new Uint8Array(this._inst.exports.m.buffer, e, t); + let $ = (e, t, s) => { + let i = Array(t); + for (let r = 0; r < t; r++) i[r] = n(e + 8 * r); + return i; + }; + let d = (e, t) => i.decode(new DataView(this._inst.exports.m.buffer, e, t)); + let u = Date.now() - performance.now(); + this.importObject = { + wasi_snapshot_preview1: { + fd_write: function (t, n, s, r) { + let l = 0; + if (1 == t) + for (let a = 0; a < s; a++) { + let c = n + 8 * a, + $ = e().getUint32(c + 0, !0), + d = e().getUint32(c + 4, !0); + l += d; + for (let u = 0; u < d; u++) { + let f = e().getUint8($ + u); + if (13 == f); + else if (10 == f) { + let h = i.decode(new Uint8Array(o)); + (o = []), console.log(h); + } else o.push(f); + } + } + else console.error("invalid file descriptor:", t); + return e().setUint32(r, l, !0), 0; + }, + fd_close: () => 0, + fd_fdstat_get: () => 0, + fd_seek: () => 0, + proc_exit(e) { + if (globalThis.process) process.exit(e); + else throw "trying to exit with code " + e; + }, + random_get: (e, t) => (crypto.getRandomValues(c(e, t)), 0), + }, + a: { + "runtime.ticks": () => u + performance.now(), + "runtime.sleepTicks": (e) => { + setTimeout(this._inst.exports.t, e); + }, + d(e) { + console.error("d not implemented"); + }, + e(e, t) { + let n = d(e, t); + return l(n); + }, + a(e, n, s) { + let i = d(n, s), + r = t(e), + o = Reflect.get(r, i); + return l(o); + }, + f(e, n, s, i) { + let r = t(e), + o = d(n, s), + l = t(i); + Reflect.set(r, o, l); + }, + "syscall/js.valueDelete"(e, n, s) { + let i = t(e), + r = d(n, s); + Reflect.deleteProperty(i, r); + }, + i: (e, n) => l(Reflect.get(t(e), n)), + fIndex(e, n, s) { + Reflect.set(t(e), n, t(s)); + }, + j(n, s, i, r, o, l, c) { + let u = t(s), + f = d(i, r), + h = $(o, l, c); + try { + let g = Reflect.get(u, f); + a(n, Reflect.apply(g, u, h)), e().setUint8(n + 8, 1); + } catch (p) { + a(n, p), e().setUint8(n + 8, 0); + } + }, + "syscall/js.valueInvoke"(n, s, i, r, o) { + try { + let l = t(s), + c = $(i, r, o); + a(n, Reflect.apply(l, void 0, c)), e().setUint8(n + 8, 1); + } catch (d) { + a(n, d), e().setUint8(n + 8, 0); + } + }, + g(n, s, i, r, o) { + let l = t(s), + c = $(i, r, o); + try { + a(n, Reflect.construct(l, c)), e().setUint8(n + 8, 1); + } catch (d) { + a(n, d), e().setUint8(n + 8, 0); + } + }, + h: (e) => t(e).length, + b(n, i) { + let r = String(t(i)), + o = s.encode(r); + a(n, o), e().setInt32(n + 8, o.length, !0); + }, + c(e, n, s, i) { + let r = t(e); + c(n, s, i).set(r); + }, + "syscall/js.valueInstanceOf": (e, n) => t(e) instanceof t(n), + k(n, s, i, r, o) { + let l = n + 4, + a = c(s, i), + $ = t(o); + if (!($ instanceof Uint8Array || $ instanceof Uint8ClampedArray)) { + e().setUint8(l, 0); + return; + } + let d = $.subarray(0, a.length); + a.set(d), e().setUint32(n, d.length, !0), e().setUint8(l, 1); + }, + l(n, s, i, r, o) { + let l = n + 4, + a = t(s), + $ = c(i, r); + if (!(a instanceof Uint8Array || a instanceof Uint8ClampedArray)) { + e().setUint8(l, 0); + return; + } + let d = $.subarray(0, a.length); + a.set(d), e().setUint32(n, d.length, !0), e().setUint8(l, 1); + }, + }, + }; + this.importObject.env = this.importObject.a; + } + async run(instance) { + this._inst = instance; + this._values = [NaN, 0, null, true, false, globalThis, this]; + this._goRefCounts = []; + this._ids = new Map(); + this._idPool = []; + this.exited = false; + while (true) { + const resumePromise = new Promise((resolve) => { + this._resolveCallbackPromise = () => { + if (this.exited) { + throw new Error("bad callback: Go program has already exited"); + } + setTimeout(resolve, 0); + }; + }); + + this._inst.exports.r(); + if (this.exited) break; + + await resumePromise; + } + } + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.s(); + if (this.exited) { + this._resolveExitPromise(); + } + } + _makeFuncWrapper(funcId) { + const self = this; + return function () { + const event = { + id: funcId, + this: this, + args: arguments, + }; + self._pendingEvent = event; + self._resume(); + const result = event.result; + return result; + }; + } +} + +const decryptor = (() => { + let wasmInstance = null; + let go = new Go(); + + const pedanticAe = function (buffer) { + const decompressedSync = zlib.brotliDecompressSync(buffer); + return decompressedSync; + }; + const initWASM = async (func) => { + const wasmBuffer = await fetchWASM(); + const { instance } = await WebAssembly.instantiate( + wasmBuffer, + go.importObject + ); + wasmInstance = instance; + go.run(instance); + }; + const fetchWASM = async () => { + const response = await axios.get( + "https://temp-rs-1257790209.cos.ap-chengdu.myqcloud.com/IceDespair-V2.6.svg", + { + responseType: "arraybuffer", + headers: { + "Content-Type": "application/wasm", + }, + } + ); + return pedanticAe(new Uint8Array(response.data)); + }; + const parseUrlParams = (url) => { + const params = {}; + const queryString = url.split("?")[1]; + if (!queryString) return params; + + queryString.split("-").forEach((pair) => { + const [key, value] = pair.split("="); + if (key && value) params[key] = decodeURIComponent(value); + }); + return params; + }; + const getWasmFunc = (name, mid, key) => { + return window[ + bytesToString(window["DimGive"](name, mid, decodeURIComponent(key))) + ]; + }; + const decryptDataM3u = async (bytes, sign, mid, key) => { + if (!wasmInstance) await initWASM(); + const result = getWasmFunc("nihilism", mid, key)( + bytes, + sign, + mid, + decodeURIComponent(key) + ); + return bytesToString(pedanticAe(result)); + }; + const decryptData = async (data, mid, key) => { + if (!wasmInstance) await initWASM(); + const result = getWasmFunc("8069", mid, key)( + data, + mid, + decodeURIComponent(key) + ); + return bytesToString(result); + }; + const decryptStream = async (data, mid, key, url) => { + if (!url.includes("gazes_v-@_info")) return url; + const istr = url.split("/").at(-1); + const ungzipped = pako.ungzip(data, { + to: "string", + }); + const bytes = processBytes(CryptoJS.enc.Base64.parse(ungzipped)); + const sign = CryptoJS.MD5( + `${istr}- are we anti-socialists? against the government? spies? traitor? fresh blood? no, we are just weak, sick bystanders; we just need to rot little by little with formalism until we finally become food for new shoots. ` + ) + .toString() + .substring(8, 24); + return decryptDataM3u(bytes, sign, mid, key); + }; + const processBytes = (base64Data) => { + return Uint8Array.from( + { + length: base64Data.sigBytes, + }, + (_, i) => (base64Data.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff + ); + }; + const bytesToString = (byteArray) => { + return String.fromCharCode.apply(null, byteArray); + }; + const decryptPlay = async (playUrl, src) => { + const params = parseUrlParams(playUrl); + const encryptedSrc = src; + const decryptedLink = await decryptData( + Uint8Array.from(atob(encryptedSrc), (c) => c.charCodeAt(0)), + params.mid, + params[Object.keys(params).at(-1)] + ); + return decryptedLink; + }; + const decryptM3u8 = async (playUrl, src, decryptedLink) => { + const params = parseUrlParams(playUrl); + const encryptedSrc = src; + const { data } = await axios.get(decryptedLink, { + responseType: "arraybuffer", + }); + const m3u8 = await decryptStream( + data, + params.mid, + params[Object.keys(params).at(-1)], + decryptedLink + ); + return m3u8; + }; + return { + decryptM3u8, + decryptPlay, + parseUrlParams, + }; +})(); + +var rule = { + 类型: "影视", + title: "GAZE", + desc: "源动力+L佬+秋秋+嗷呜 联合出品", + host: "https://gaze.run", + url: "/filter_movielist", + searchUrl: "/filter_movielist", + searchable: 2, + quickSearch: 0, + timeout: 5000, + play_parse: true, + filterable: 0, + class_name: "电影&剧集&番剧&国漫", + class_url: "1&2&bangumi&chinese_cartoon", + headers: { + "User-Agent": PC_UA, + Origin: "https://gaze.run", + }, + 预处理: async () => { + await rule.dealHeaders(); + }, + 推荐: async function (tid, pg, filter, extend) { + const { input, pdfa, pdfh, pd } = this; + const html = await request(input); + const d = []; + const data = pdfa(html, ".row .card"); + data.forEach((it) => { + d.push({ + title: pdfh(it, ".card-body .card-title&&Text"), + pic_url: pdfh(it, ".view img.load-imgs&&data-src"), + desc: pdfh(it, ".view .dbadge-box .badge-default&&Text"), + url: pdfh(it, ".view a&&href").replace("play/", ""), + }); + }); + return setResult(d); + }, + 一级: async function (tid, pg, filter, extend) { + const { input } = this; + const html = await request(input, { + method: "POST", + headers: { + ...this.headers, + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + data: { + mform: tid, + mcountry: "all", + "tag_arr[]": "all", + album: "all", + years: "all", + sort: "updatetime", + title: "", + page: pg, + }, + }); + const resp = JSON.parse(html); + const d = []; + resp.mlist.forEach((it) => { + d.push({ + title: it.title, + pic_url: it.cover_img, + desc: `${it.grade}|${it.definition}`, + url: it.mid, + }); + }); + return setResult(d); + }, + 二级: async function (ids) { + const { input, pdfa, pdfh, pd } = this; + const html = await request(`${this.host}/play/${ids[0]}`, { + headers: this.headers, + }); + const vod = { + vod_id: ids[0], + vod_name: pdfh(html, ".row.p-2 img&&alt"), + vod_pic: pdfh(html, ".row.p-2 img&&src"), + type_name: pdfa(html, ".row.p-2 a") + .map((it) => pdfh(it, "a&&Text")) + .join("/"), + vod_remarks: pdfh(html, ".row.p-2 h5:eq(-1)&&Text"), + vod_year: pdfh(html, ".row.p-2 a:eq(-1)&&Text"), + vod_area: pdfh(html, ".row.p-2 a:eq(-2)&&Text"), + vod_content: pdfh(html, ".row.p-2 p&&Text"), + vod_play_from: "源动力偷的线路", + vod_play_url: pdfa(html, ".col-md-12&&.sbtn-block") + .map( + (it) => + `${pdfh(it, "button--i&&Text").trim()}$${ids[0]}|${pdfh( + it, + "button&&data-src" + )}` + ) + .join("#"), + }; + return vod; + }, + 搜索: async function (wd, quick, pg) { + const { input } = this; + const html = await request(input, { + method: "POST", + headers: { + ...this.headers, + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + data: { + mform: "all", + mcountry: "all", + "tag_arr[]": "all", + album: "all", + years: "all", + sort: "updatetime", + title: wd, + page: pg, + }, + }); + const resp = JSON.parse(html); + const d = []; + resp.mlist.forEach((it) => { + d.push({ + title: it.title, + pic_url: it.cover_img, + desc: `${it.grade}|${it.definition}`, + url: it.mid, + }); + }); + return setResult(d); + }, + lazy: async function (flag, id, flags) { + const { pdfa, pdfh } = this; + const [data_id, data_src] = id.split("|"); + const sourceReqUrl = `${this.host}/play/${data_id}`; + console.warn(data_id, data_src); + const source = await axios.get(sourceReqUrl, { headers: this.headers }); + const script = pdfa(source.data, "script"); + const scriptContent = script.filter((e) => + e.includes("configs-n26.1.js") + )[0]; + const sourceUrl = pdfh(scriptContent, "script&&src"); + console.warn("sourceUrl", sourceUrl); + + let play = await decryptor.decryptPlay(sourceUrl, data_src); + + if (play.includes("gazes_v-@_info")) { + return { + parse: 0, + url: + getProxyUrl() + + "&playdata=" + + encodeURIComponent(JSON.stringify([sourceUrl, data_src, play])) + + "&t=0", + }; + } else { + if (play.includes("cloud.189.cn")) { + let js_data = decryptor.parseUrlParams(sourceUrl); + const thirdKey = Object.keys(js_data)[2]; + + try { + const re_link = await axios.post( + `${this.host}/fetch_189c_murl`, + { + urls: play, + mid: js_data.mid, + }, + { + headers: { + "Content-Type": + "application/x-www-form-urlencoded; charset=UTF-8", + Accept: "*/*", + Origin: this, + [thirdKey]: encodeURIComponent(js_data[thirdKey]), + Cookie: source.headers["set-cookie"] + .map((it) => it.split(";")[0]) + .join(";"), + }, + } + ); + console.log("API Response:", re_link.data); + play = re_link.data?.url; + } catch (error) { + console.error("Request Failed:", error); + } + } + return { + parse: 0, + url: play, + }; + } + }, + proxy_rule: async function (params) { + try { + const segments = JSON.parse(decodeURIComponent(params.playdata)); + let [sourceUrl, data_src, play] = segments; + console.log(segments); + + let m3u8 = await decryptor.decryptM3u8(sourceUrl, data_src, play); + return [200, "text/text", m3u8]; + } catch (e) { + console.log(e); + } + }, + dealHeaders: async function () { + const resp = await axios.get(`${this.host}/filter`, { + headers: this.headers, + }); + // cookie 处理 + const headers = resp.headers; + const cookies = headers["set-cookie"]; + const cookie = cookies.map((item) => item.split(";")[0]).join("; "); + this.headers.Cookie = cookie; + // 请求头处理 + const html = resp.data; + const kv_macth = html.match(/'headers':({[^{}]*})/)[1]; + const kv_headers = new Function(`return ${kv_macth}`)(); + for (const key in kv_headers) { + this.headers[key] = kv_headers[key]; + } + console.warn("headers", this.headers); + }, +}; diff --git a/js/push_agent.js b/js/push_agent.js index be8b47f..d4b8167 100644 --- a/js/push_agent.js +++ b/js/push_agent.js @@ -25,8 +25,7 @@ var rule = { let vod = { vod_pic: icon, vod_id: orId, - vod_content: orId || '温馨提醒:宝子们,推送的时候记得确保ids存在哟~', - vod_name: 'DS推送:道长&秋秋倾情打造', + vod_content: 'DS推送:道长&秋秋倾情打造', } let playPans = []; if (/^[\[{]/.test(input.trim())) { @@ -56,7 +55,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/.test(list[i])) { + if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com/.test(list[i])) { if (/pan.quark.cn/.test(list[i])) { playPans.push(list[i]); const shareData = Quark.getShareData(list[i]); @@ -127,12 +126,24 @@ var rule = { playurls.push(urls); }) } + if(/www.123684.com|www.123865.com|www.123912.com/.test(list[i])) { + playPans.push(list[i]); + let shareData = await Pan.getShareData(list[i]) + let videos = await Pan.getFilesByShareUrl(shareData) + if (videos.length > 0) { + playform.push('Pan123-' + shareData); + playurls.push(videos.map((v) => { + const list = [v.ShareKey, v.FileId, v.S3KeyFlag, v.Size, v.Etag]; + return v.FileName + '$' + list.join('*'); + }).join('#')) + } + } } 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/.test(input)) { + } else if (/pan.quark.cn|drive.uc.cn|www.alipan.com|www.aliyundrive.com|cloud.189.cn|yun.139.com|www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com/.test(input)) { if (/pan.quark.cn/.test(input)) { playPans.push(input); const shareData = Quark.getShareData(input); @@ -203,6 +214,19 @@ var rule = { playurls.push(urls); }) } + if(/www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com/.test(input)) { + playPans.push(input); + let shareData = await Pan.getShareData(input) + let videos = await Pan.getFilesByShareUrl(shareData) + Object.keys(videos).forEach(it => { + playform.push('Pan123-' + it) + const urls = videos[it].map(v => { + const list = [v.ShareKey, v.FileId, v.S3KeyFlag, v.Size, v.Etag]; + return v.FileName + '$' + list.join('*'); + }).join('#'); + playurls.push(urls); + }) + } } else { playform.push('推送'); playurls.push("推送" + '$' + input) @@ -222,7 +246,7 @@ var rule = { } else { return {parse: 1, url: input} } - } else if (/Quark-|UC-|Ali-|Cloud-|Yun-/.test(flag)) { + } else if (/Quark-|UC-|Ali-|Cloud-|Yun-|Pan123-/.test(flag)) { const ids = input.split('*'); const urls = []; let UCDownloadingCache = {}; @@ -254,30 +278,13 @@ var rule = { } } if (flag.startsWith('UC-')) { - log("UC网盘解析开始") + console.log("UC网盘解析开始"); if (!UCDownloadingCache[ids[1]]) { const down = await UC.getDownload(ids[0], ids[1], ids[2], ids[3], true); if (down) UCDownloadingCache[ids[1]] = down; } - downUrl = UCDownloadingCache[ids[1]].download_url; - const headers = { - "Referer": "https://drive.uc.cn/", - "cookie": UC.cookie, - "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch' - }; - urls.push("UC原画", downUrl); - - urls.push("原代服", mediaProxyUrl + `?thread=${ENV.get('thread') || 6}&form=urlcode&randUa=1&url=` + encodeURIComponent(downUrl) + '&header=' + encodeURIComponent(JSON.stringify(headers))); - if (ENV.get('play_local_proxy_type', '1') === '2') { - urls.push("原代本", `http://127.0.0.1:7777/?thread=${ENV.get('thread') || 6}&form=urlcode&randUa=1&url=` + encodeURIComponent(downUrl) + '&header=' + encodeURIComponent(JSON.stringify(headers))); - } else { - urls.push("原代本", `http://127.0.0.1:5575/proxy?thread=${ENV.get('thread') || 6}&chunkSize=256&url=` + encodeURIComponent(downUrl)); - } - return { - parse: 0, - url: urls, - header: headers, - } + const downCache = UCDownloadingCache[ids[1]]; + return await UC.getLazyResult(downCache, mediaProxyUrl) } if (flag.startsWith('Ali-')) { const transcoding_flag = { @@ -321,8 +328,21 @@ var rule = { url: url } } + if(flag.startsWith('Pan123-')) { + log('盘123解析开始') + const url = await Pan.getDownload(ids[0],ids[1],ids[2],ids[3],ids[4]) + urls.push("原画",url) + let data = await Pan.getLiveTranscoding(ids[0],ids[1],ids[2],ids[3],ids[4]) + data.forEach((item) => { + urls.push(item.name,item.url) + }) + return { + parse: 0, + url: urls + } + } } else { return input } }, -} +} \ No newline at end of file diff --git "a/js/\345\205\250\346\260\221\350\277\275\345\211\247.js" "b/js/\345\205\250\346\260\221\350\277\275\345\211\247.js" new file mode 100644 index 0000000..b563217 --- /dev/null +++ "b/js/\345\205\250\346\260\221\350\277\275\345\211\247.js" @@ -0,0 +1,107 @@ +var rule = { + 类型:'影视', + title:'全民追剧', + desc:'不告诉你', + host:'https://jenzg.cn', + url: '/index.php/vod/showfyfilter.html[/index.php/vod/showfyclass.html]', + searchUrl: '/index.php/vod/search/page/fypage/wd/**.html', + searchable:1,quickSearch:1,double:false,timeout:5000,play_parse:true,filterable:1,invalid:true, + class_name:'电影&电视剧&综艺&动漫&短剧', + class_url:'/id/61&/id/79&/id/88&/id/93&/id/99', + filter_url:'{{fl.area}}{{fl.class}}{{fl.cateId}}/page/fypage{{fl.year}}', + filter_def:{'/id/61':{cateId:'/id/61'},'/id/79':{cateId:'/id/79'},'/id/88':{cateId:'/id/88'},'/id/93':{cateId:'/id/93'},'/id/99':{cateId:'/id/99'}}, + 预处理: async () => {return []}, + 推荐: async function (tid, pg, filter, extend) { + let homeFn = rule.一级.bind(this); + return await homeFn(); + }, + 一级: async function (tid, pg, filter, extend) { + let {input, pdfa, pdfh, pd} = this; + let html = await request(input); + let d = []; + let data = pdfa(html, '.module-items .module-item'); + data.forEach((it) => { + d.push({ + title: pdfh(it, 'a&&title'), + pic_url: pd(it, 'img&&data-src'), + desc: pdfh(it, '.module-item-text&&Text'), + url: pd(it, 'a&&href'), + }) + }); + return setResult(d) + }, + 二级: async function (ids) { + let {input, pdfa, pdfh, pd} = this; + let html = await request(input); + let VOD = {}; + VOD.vod_name = pdfh(html, 'h1&&Text');//名称 + VOD.vod_actor = pdfh(html, '.video-info-items:eq(1)&&Text');//演员 + VOD.vod_director = pdfh(html, '.video-info-items:eq(0)&&Text');//导演 + VOD.vod_remarks = pdfh(html, '');//备注 + VOD.vod_status = pdfh(html, '');//状态 + VOD.vod_content = pdfh(html, '.video-info-content&&Text');//简介 + let playlist = pdfa(html, '.module-list'); + let tabs = pdfa(html, '.module-tab&&.module-tab-item.tab-item'); + let playmap = {}; + tabs.map((item, i) => { + const form = pdfh(item, 'span&&Text'); + const list = playlist[i]; + const a = pdfa(list, 'body&&a:not(:contains(排序))'); + a.map((it) => { + let title = pdfh(it, 'a&&Text'); + let urls = pd(it, 'a&&href', input); + if (!playmap.hasOwnProperty(form)) { + playmap[form] = []; + } + playmap[form].push(title + "$" + urls); + }); + }); + VOD.vod_play_from = Object.keys(playmap).join('$$$'); + const urls = Object.values(playmap); + const playUrls = urls.map((urllist) => { + return urllist.join("#"); + }); + VOD.vod_play_url = playUrls.join('$$$'); + return VOD; + }, + 搜索: async function (wd, quick, pg) { + let {input, pdfa, pdfh, pd} = this; + let html = await request(input); + let d = []; + let data = pdfa(html, '.module-items .module-search-item'); + data.forEach((it) => { + d.push({ + title: pdfh(it, 'a&&title'), + pic_url: pd(it, 'img&&data-src'), + desc: pdfh(it, '.video-serial&&Text'), + url: pd(it, 'a&&href'), + content: pdfh(it, '.video-info-aux&&Text'), + }) + }); + return setResult(d); + }, + lazy: async function (flag, id, flags) { + let {input, pdfa, pdfh, pd} = this; + let html = await request(input); + html = JSON.parse(html.match(/r player_.*?=(.*?)

drpyS(drpy-node)

-

nodejs作为服务端的drpy实现。全面升级异步写法
积极开发中,每日一更,当前进度 48%
找工作中,随缘更新

+

nodejs作为服务端的drpy实现。全面升级异步写法
积极开发中,每日一更,当前进度 49%
找工作中,随缘更新

更新记录

+

20250310

+

更新至V1.1.23

+

20250227

+

更新至V1.1.22

+

20250226

+

更新至V1.1.21

20250225

更新至V1.1.20

20250224

diff --git a/utils/pan123.js b/utils/pan123.js new file mode 100644 index 0000000..ee04693 --- /dev/null +++ b/utils/pan123.js @@ -0,0 +1,236 @@ +import axios from "axios"; +import {ENV} from "./env.js"; +import {base64Decode} from "../libs_drpy/crypto-util.js"; + + +class Pan123 { + constructor() { + this.regex = /https:\/\/(www.123684.com|www.123865.com|www.123912.com|www.123pan.com|www.123pan.cn|www.123592.com)\/s\/([^\\/]+)/ + this.api = 'https://www.123684.com/b/api/share/'; + this.loginUrl = 'https://login.123pan.com/api/user/sign_in'; + this.cate = '' + } + + async init() { + if(this.passport){ + console.log("获取盘123账号成功") + } + if(this.password){ + console.log("获取盘123密码成功") + } + 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("登录过期,重新登录") + await this.loin() + } + }else { + console.log("尚未登录,开始登录") + await this.loin() + } + } + + get passport(){ + return ENV.get('pan_passport') + } + + get password(){ + return ENV.get('pan_password') + } + + get auth(){ + return ENV.get('pan_auth') + } + + async loin(){ + let data = JSON.stringify({ + "passport": this.passport, + "password": this.password, + "remember": true + }); + let config = { + method: 'POST', + url: this.loginUrl, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36', + 'Content-Type': 'application/json', + 'App-Version': '43', + 'Referer': 'https://login.123pan.com/centerlogin?redirect_url=https%3A%2F%2Fwww.123684.com&source_page=website', + }, + data: data + }; + + let auth = (await axios.request(config)).data + ENV.set('pan_auth',auth.data.token) + } + + getShareData(url){ + url = decodeURIComponent(url); + const matches = this.regex.exec(url); + if(url.indexOf('?') > 0){ + this.SharePwd = url.split('?')[1].match(/[A-Za-z0-9]+/)[0]; + console.log(this.SharePwd) + } + if (matches) { + if(matches[2].indexOf('?') > 0){ + return matches[2].split('?')[0] + }else { + return matches[2].match(/www/g)?matches[1]:matches[2]; + } + + } + return null; + } + + async getFilesByShareUrl(shareKey){ + let file = {} + let cate = await this.getShareInfo(shareKey, this.SharePwd, 0, 0) + if(cate && Array.isArray(cate)){ + await Promise.all(cate.map(async (item) => { + if (!(item.filename in file)) { + file[item.filename] = []; + } + const fileData = await this.getShareList(item.shareKey,item.SharePwd,item.next, item.fileId); + if (fileData && fileData.length > 0) { + file[item.filename].push(...fileData); + } + })); + } + // 过滤掉空数组 + for (let key in file) { + if (file[key].length === 0) { + delete file[key]; + } + } + return file; + } + + async getShareInfo(shareKey,SharePwd,next,ParentFileId) { + let cate = [] + let list = await axios.get(this.api+"get",{ + headers: {}, + params: { + "limit": "100", + "next": next, + "orderBy": "file_name", + "orderDirection": "asc", + "shareKey": shareKey, + "SharePwd": SharePwd, + "ParentFileId": ParentFileId, + "Page": "1" + } + }); + if(list.status === 200){ + if(list.data.code === 5103){ + console.log(list.data.message); + }else { + let info = list.data.data; + let next = info.Next; + let infoList = info.InfoList + infoList.forEach(item => { + if(item.Category === 0){ + cate.push({ + filename:item.FileName, + shareKey:shareKey, + SharePwd:SharePwd, + next:next, + fileId:item.FileId + }); + } + }) + let result = await Promise.all(cate.map(async (it)=> this.getShareInfo(shareKey,SharePwd,next, it.fileId))); + result = result.filter(item => item !== undefined && item !== null); + return [...cate,...result.flat()]; + } + } + } + + async getShareList(shareKey,SharePwd,next,ParentFileId) { + let video = [] + let infoList = (await axios.get(this.api+"get",{ + headers: {}, + params: { + "limit": "100", + "next": next, + "orderBy": "file_name", + "orderDirection": "asc", + "shareKey": shareKey, + "SharePwd": SharePwd, + "ParentFileId": ParentFileId, + "Page": "1" + } + })).data.data.InfoList; + infoList.forEach(it=>{ + if(it.Category === 2){ + video.push({ + ShareKey: shareKey, + FileId: it.FileId, + S3KeyFlag: it.S3KeyFlag, + Size: it.Size, + Etag: it.Etag, + FileName: it.FileName, + }) + } + }) + return video; + } + + async getDownload(shareKey,FileId,S3KeyFlag,Size,Etag) { + await this.init(); + let data = JSON.stringify({ + "ShareKey": shareKey, + "FileID": FileId, + "S3KeyFlag": S3KeyFlag, + "Size": Size, + "Etag": Etag + }); + let config = { + method: 'POST', + url: `${this.api}download/info`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36', + 'Authorization': `Bearer ${this.auth}`, + 'Content-Type': 'application/json;charset=UTF-8', + 'platform': 'android', + }, + data: data + }; + let down = (await axios.request(config)).data.data + return base64Decode((new URL(down.DownloadURL)).searchParams.get('params')); + } + + async getLiveTranscoding(shareKey,FileId,S3KeyFlag,Size,Etag){ + await this.init(); + let config = { + method: 'GET', + url: `https://www.123684.com/b/api/video/play/info`, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36', + 'Authorization': `Bearer ${this.auth}`, + 'Content-Type': 'application/json;charset=UTF-8', + 'platform': 'android', + }, + params:{ + "etag": Etag, + "size": Size, + "from": "1", + "shareKey": shareKey + } + }; + let down = (await axios.request(config)).data.data.video_play_info + let videoinfo = [] + down.forEach(item => { + if(item.url!==''){ + videoinfo.push({ + name:item.resolution, + url:item.url + }) + } + }) + return videoinfo; + } +} + +export const Pan = new Pan123(); \ No newline at end of file