drpyInject.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. import axios, {toFormData} from 'axios';
  2. import axiosX from './axios.min.js';
  3. import crypto from 'crypto';
  4. import http from "http";
  5. import https from 'https';
  6. import fs from 'node:fs';
  7. import qs from 'qs';
  8. import _ from './underscore-esm.min.js'
  9. // import _ from './underscore-esm.js'
  10. // import _ from 'underscore'
  11. import tunnel from "tunnel";
  12. import iconv from 'iconv-lite';
  13. import {jsonpath, jsoup} from './htmlParser.js';
  14. import hlsParser from './hls-parser.js'
  15. import {keysToLowerCase} from '../utils/utils.js'
  16. import {ENV} from '../utils/env.js';
  17. // import {batchFetch1, batchFetch2, batchFetch3} from './drpyBatchFetch.js';
  18. import {batchFetch3} from './hikerBatchFetch.js';
  19. globalThis.batchFetch = batchFetch3;
  20. globalThis.axios = axios;
  21. globalThis.axiosX = axiosX;
  22. globalThis.hlsParser = hlsParser;
  23. globalThis.qs = qs;
  24. const AgentOption = {keepAlive: true, maxSockets: 64, timeout: 30000}; // 最大连接数64,30秒定期清理空闲连接
  25. const httpAgent = new http.Agent(AgentOption);
  26. let httpsAgent = new https.Agent({rejectUnauthorized: false, ...AgentOption});
  27. // 配置 axios 使用代理
  28. const _axios = axios.create({
  29. httpAgent, // 用于 HTTP 请求的代理
  30. httpsAgent, // 用于 HTTPS 请求的代理
  31. });
  32. // 请求拦截器
  33. _axios.interceptors.request.use((config) => {
  34. // 生成 curl 命令
  35. const curlCommand = generateCurlCommand(config);
  36. if (ENV.get('show_curl', '0') === '1') {
  37. console.log(`Generated cURL command:\n${curlCommand}`);
  38. }
  39. return config;
  40. }, (error) => {
  41. return Promise.reject(error);
  42. });
  43. /**
  44. * 生成 curl 命令
  45. * @param {Object} config Axios 请求配置
  46. * @returns {string} curl 命令
  47. */
  48. function generateCurlCommand(config) {
  49. const {method, url, headers, data} = config;
  50. let curlCommand = `curl -X ${method.toUpperCase()} '${url}'`;
  51. // 添加 headers
  52. if (headers) {
  53. for (const [key, value] of Object.entries(headers)) {
  54. curlCommand += ` -H '${key}: ${value}'`;
  55. }
  56. }
  57. // 添加 body 数据
  58. if (data) {
  59. if (typeof data === 'object') {
  60. curlCommand += ` -d '${JSON.stringify(data)}'`;
  61. } else {
  62. curlCommand += ` -d '${data}'`;
  63. }
  64. }
  65. return curlCommand;
  66. }
  67. const confs = {};
  68. function initLocalStorage(storage) {
  69. if (!_.has(confs, storage)) {
  70. if (!fs.existsSync('local')) {
  71. fs.mkdirSync('local');
  72. }
  73. const storagePath = 'local/js_' + storage;
  74. if (!fs.existsSync(storagePath)) {
  75. fs.writeFileSync(storagePath, '{}');
  76. confs[storage] = {};
  77. } else {
  78. confs[storage] = JSON.parse(fs.readFileSync(storagePath).toString());
  79. }
  80. }
  81. }
  82. function localGet(storage, key) {
  83. initLocalStorage(storage);
  84. return _.get(confs[storage], key, '');
  85. }
  86. function localSet(storage, key, value) {
  87. initLocalStorage(storage);
  88. confs[storage][key] = value;
  89. fs.writeFileSync('local/js_' + storage, JSON.stringify(confs[storage]));
  90. }
  91. function localDelete(storage, key) {
  92. initLocalStorage(storage);
  93. delete confs[storage][key];
  94. fs.writeFileSync('local/js_' + storage, JSON.stringify(confs[storage]));
  95. }
  96. async function request(url, opt = {}) {
  97. // console.log('进入了req...');
  98. // 解构参数并设置默认值
  99. const {
  100. data: _data = null,
  101. body = '',
  102. postType = null,
  103. buffer: returnBuffer = 0,
  104. timeout = 5000,
  105. redirect = 1,
  106. encoding: userEncoding = '',
  107. headers: userHeaders = {},
  108. method = 'get',
  109. proxy = false,
  110. stream = null,
  111. } = opt;
  112. let data = body || _data;
  113. let encoding = userEncoding;
  114. // 设置默认 Content-Type
  115. const headers = keysToLowerCase({
  116. ...userHeaders,
  117. ...(postType === 'form' && {'Content-Type': 'application/x-www-form-urlencoded'}),
  118. ...(postType === 'form-data' && {'Content-Type': 'multipart/form-data'}),
  119. });
  120. // 添加accept属性防止获取网页源码编码不正确问题
  121. if (!Object.keys(headers).includes('accept')) {
  122. headers['accept'] = '*/*';
  123. }
  124. // 尝试从 Content-Type 中提取编码
  125. if (headers['content-type'] && /charset=(.*)/i.test(headers['content-type'])) {
  126. encoding = headers['content-type'].match(/charset=(.*)/i)[1];
  127. }
  128. // 根据 postType 处理数据
  129. if (postType === 'form' && data != null) {
  130. data = qs.stringify(data, {encode: false});
  131. } else if (postType === 'form-data') {
  132. data = toFormData(data);
  133. }
  134. // 配置代理或 HTTPS Agent
  135. // httpsAgent = new https.Agent({rejectUnauthorized: false});
  136. const agent = proxy ? tunnel.httpsOverHttp({proxy: {host: '127.0.0.1', port: 7890}}) : httpsAgent;
  137. // 设置响应类型为 arraybuffer,确保能正确处理编码
  138. const respType = returnBuffer ? 'arraybuffer' : 'arraybuffer';
  139. if (ENV.get('show_req', '0') === '1') {
  140. console.log(`req: ${url} headers: ${JSON.stringify(headers)} data: ${JSON.stringify(data)}`);
  141. }
  142. try {
  143. // 发送请求
  144. const resp = await _axios({
  145. url: typeof url === 'object' ? url.url : url,
  146. method,
  147. headers,
  148. data,
  149. timeout,
  150. responseType: respType,
  151. maxRedirects: redirect ? undefined : 0,
  152. httpsAgent: agent,
  153. });
  154. let responseData = resp.data;
  155. // 构建响应头
  156. const resHeader = Object.fromEntries(
  157. Object.entries(resp.headers).map(([key, value]) => [key, Array.isArray(value) ? (value.length === 1 ? value[0] : value) : value])
  158. );
  159. // 解码逻辑
  160. if (!returnBuffer) {
  161. const buffer = Buffer.from(responseData);
  162. if (encoding && encoding.toLowerCase() !== 'utf-8') {
  163. // console.log('Detected encoding:', encoding);
  164. responseData = iconv.decode(buffer, encoding);
  165. } else {
  166. responseData = buffer.toString('utf-8');
  167. }
  168. } else if (returnBuffer === 1) {
  169. return {code: resp.status, headers: resHeader, content: responseData};
  170. } else if (returnBuffer === 2) {
  171. return {code: resp.status, headers: resHeader, content: Buffer.from(responseData).toString('base64')};
  172. } else if (returnBuffer === 3 && stream) {
  173. if (stream.onResp) await stream.onResp({code: resp.status, headers: resHeader});
  174. if (stream.onData) {
  175. responseData.on('data', async (chunk) => {
  176. await stream.onData(chunk);
  177. });
  178. responseData.on('end', async () => {
  179. if (stream.onDone) await stream.onDone();
  180. });
  181. } else if (stream.onDone) {
  182. await stream.onDone();
  183. }
  184. return 'stream...';
  185. }
  186. return {code: resp.status, headers: resHeader, content: responseData};
  187. } catch (error) {
  188. const {response: resp} = error;
  189. console.error(`Request error: ${error.message}`);
  190. let responseData = '';
  191. // console.log('responseData:',responseData);
  192. try {
  193. const buffer = Buffer.from(resp.data);
  194. if (encoding && encoding.toLowerCase() !== 'utf-8') {
  195. // console.log('Detected encoding:', encoding);
  196. responseData = iconv.decode(buffer, encoding);
  197. } else {
  198. responseData = buffer.toString('utf-8');
  199. }
  200. } catch (e) {
  201. console.error(`get error response Text failed: ${e.message}`);
  202. }
  203. // console.log('responseData:',responseData);
  204. return {
  205. code: resp?.status || 500,
  206. headers: resp?.headers || {},
  207. content: responseData || '',
  208. };
  209. }
  210. }
  211. function base64EncodeBuf(buff, urlsafe = false) {
  212. return buff.toString(urlsafe ? 'base64url' : 'base64');
  213. }
  214. function base64Encode(text, urlsafe = false) {
  215. return base64EncodeBuf(Buffer.from(text, 'utf8'), urlsafe);
  216. }
  217. function base64DecodeBuf(text) {
  218. return Buffer.from(text, 'base64');
  219. }
  220. function base64Decode(text) {
  221. return base64DecodeBuf(text).toString('utf8');
  222. }
  223. function responseBase64(data) {
  224. const buffer = Buffer.from(data, 'binary');
  225. return buffer.toString('base64');
  226. }
  227. function md5(text) {
  228. return crypto.createHash('md5').update(Buffer.from(text, 'utf8')).digest('hex');
  229. }
  230. function aes(mode, encrypt, input, inBase64, key, iv, outBase64) {
  231. if (iv.length == 0) iv = null;
  232. try {
  233. if (mode.startsWith('AES/CBC')) {
  234. switch (key.length) {
  235. case 16:
  236. mode = 'aes-128-cbc';
  237. break;
  238. case 32:
  239. mode = 'aes-256-cbc';
  240. break;
  241. }
  242. } else if (mode.startsWith('AES/ECB')) {
  243. switch (key.length) {
  244. case 16:
  245. mode = 'aes-128-ecb';
  246. break;
  247. case 32:
  248. mode = 'aes-256-ecb';
  249. break;
  250. }
  251. }
  252. const inBuf = inBase64 ? base64DecodeBuf(input) : Buffer.from(input, 'utf8');
  253. let keyBuf = Buffer.from(key);
  254. if (keyBuf.length < 16) keyBuf = Buffer.concat([keyBuf], 16);
  255. let ivBuf = iv == null ? Buffer.alloc(0) : Buffer.from(iv);
  256. if (iv != null && ivBuf.length < 16) ivBuf = Buffer.concat([ivBuf], 16);
  257. const cipher = encrypt ? crypto.createCipheriv(mode, keyBuf, ivBuf) : crypto.createDecipheriv(mode, keyBuf, ivBuf);
  258. const outBuf = Buffer.concat([cipher.update(inBuf), cipher.final()]);
  259. return outBase64 ? base64EncodeBuf(outBuf) : outBuf.toString('utf8');
  260. } catch (error) {
  261. console.log(error);
  262. }
  263. return '';
  264. }
  265. function des(mode, encrypt, input, inBase64, key, iv, outBase64) {
  266. try {
  267. if (mode.startsWith('DESede/CBC')) {
  268. // https://stackoverflow.com/questions/29831300/convert-desede-ecb-nopadding-algorithm-written-in-java-into-nodejs-using-crypto
  269. switch (key.length) {
  270. case 16:
  271. mode = 'des-ede-cbc';
  272. break;
  273. case 24:
  274. mode = 'des-ede3-cbc';
  275. break;
  276. }
  277. }
  278. const inBuf = inBase64 ? base64DecodeBuf(input) : Buffer.from(input, 'utf8');
  279. let keyBuf = Buffer.from(key);
  280. if (keyBuf.length < 16) keyBuf = Buffer.concat([keyBuf], 16);
  281. let ivBuf = iv == null ? Buffer.alloc(0) : Buffer.from(iv);
  282. if (iv != null && ivBuf.length < 8) ivBuf = Buffer.concat([ivBuf], 8);
  283. const cipher = encrypt ? crypto.createCipheriv(mode, keyBuf, ivBuf) : crypto.createDecipheriv(mode, keyBuf, ivBuf);
  284. const outBuf = Buffer.concat([cipher.update(inBuf), cipher.final()]);
  285. return outBase64 ? base64EncodeBuf(outBuf) : outBuf.toString('utf8');
  286. } catch (error) {
  287. console.log(error);
  288. }
  289. return '';
  290. }
  291. // pkcs8 only
  292. function rsa(mode, pub, encrypt, input, inBase64, key, outBase64) {
  293. try {
  294. let pd = undefined;
  295. const keyObj = pub ? crypto.createPublicKey(key) : crypto.createPrivateKey(key);
  296. if (!keyObj.asymmetricKeyDetails || !keyObj.asymmetricKeyDetails.modulusLength) return '';
  297. const moduleLen = keyObj.asymmetricKeyDetails.modulusLength;
  298. let blockLen = moduleLen / 8;
  299. switch (mode) {
  300. case 'RSA/PKCS1':
  301. pd = crypto.constants.RSA_PKCS1_PADDING;
  302. blockLen = encrypt ? blockLen - 11 : blockLen;
  303. break;
  304. case 'RSA/None/NoPadding':
  305. pd = crypto.constants.RSA_NO_PADDING;
  306. break;
  307. case 'RSA/None/OAEPPadding':
  308. pd = crypto.constants.RSA_PKCS1_OAEP_PADDING;
  309. blockLen = encrypt ? blockLen - 41 : blockLen;
  310. break;
  311. default:
  312. throw Error('not support ' + mode);
  313. }
  314. let inBuf = inBase64 ? base64DecodeBuf(input) : Buffer.from(input, 'utf8');
  315. let bufIdx = 0;
  316. let outBuf = Buffer.alloc(0);
  317. while (bufIdx < inBuf.length) {
  318. const bufEndIdx = Math.min(bufIdx + blockLen, inBuf.length);
  319. let tmpInBuf = inBuf.subarray(bufIdx, bufEndIdx);
  320. if (pd == crypto.constants.RSA_NO_PADDING) {
  321. if (tmpInBuf.length < blockLen) {
  322. tmpInBuf = Buffer.concat([Buffer.alloc(128 - tmpInBuf.length), tmpInBuf]);
  323. }
  324. }
  325. let tmpBuf;
  326. if (pub) {
  327. tmpBuf = encrypt ? crypto.publicEncrypt({
  328. key: keyObj, padding: pd
  329. }, tmpInBuf) : crypto.publicDecrypt({key: keyObj, padding: pd}, tmpInBuf);
  330. } else {
  331. tmpBuf = encrypt ? crypto.privateEncrypt({
  332. key: keyObj, padding: pd
  333. }, tmpInBuf) : crypto.privateDecrypt({key: keyObj, padding: pd}, tmpInBuf);
  334. }
  335. bufIdx = bufEndIdx;
  336. outBuf = Buffer.concat([outBuf, tmpBuf]);
  337. }
  338. return outBase64 ? base64EncodeBuf(outBuf) : outBuf.toString('utf8');
  339. } catch (error) {
  340. console.log(error);
  341. }
  342. return '';
  343. }
  344. var charStr = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789';
  345. function randStr(len, withNum) {
  346. var _str = '';
  347. let containsNum = withNum === undefined ? true : withNum;
  348. for (var i = 0; i < len; i++) {
  349. let idx = _.random(0, containsNum ? charStr.length - 1 : charStr.length - 11);
  350. _str += charStr[idx];
  351. }
  352. return _str;
  353. }
  354. globalThis.local = {
  355. get: function (storage, key) {
  356. return localGet(storage, key);
  357. }, set: function (storage, key, val) {
  358. localSet(storage, key, val);
  359. }, delete: function (storage, key) {
  360. localDelete(storage, key);
  361. }
  362. };
  363. globalThis.md5X = md5;
  364. globalThis.rsaX = rsa;
  365. globalThis.aesX = aes;
  366. globalThis.desX = des;
  367. globalThis.req = request;
  368. globalThis.responseBase64 = responseBase64;
  369. /**
  370. * Constructor for the JSProxyStream class.
  371. *
  372. * @constructor
  373. */
  374. globalThis.JSProxyStream = function () {
  375. /**
  376. * Set proxy stream http code & headers
  377. *
  378. * @param {Number} code - http status code
  379. * @param {Map} headers - http response headers
  380. */
  381. this.head = async function (code, headers) {
  382. };
  383. /**
  384. * Writes the given buffer.
  385. *
  386. * @param {ArrayBuffer} buf - the buffer to write
  387. * @return {Number} 1 if the write was successful, 0 stream read is paused, -1 strean was closed
  388. */
  389. this.write = async function (buf) {
  390. return 1;
  391. };
  392. /**
  393. * Stream will be closed.
  394. */
  395. this.done = async function () {
  396. };
  397. /**
  398. * Stream will be closed cause by error happened.
  399. */
  400. this.error = async function (err) {
  401. };
  402. };
  403. /**
  404. * Creates a new JSFile object with the specified path.
  405. *
  406. * @param {string} path - The path to the file.
  407. * @return {JSFile} - The JSFile object.
  408. */
  409. globalThis.JSFile = function (path) {
  410. this._path = path;
  411. this.fd = null;
  412. /**
  413. * Returns the raw path of the object.
  414. *
  415. * @return {string} The raw path of the file. Runtime path is not same with _path.
  416. */
  417. this.path = async function () {
  418. return this._path;
  419. };
  420. /**
  421. * Opens a file with the specified mode.
  422. *
  423. * @param {string} mode - The mode in which to open the file. Can be 'r' for read, 'w' for write, or 'a' for append.
  424. * @return {boolean} Returns true if the file was successfully opened, false otherwise.
  425. */
  426. this.open = async function (mode) {
  427. const file = this;
  428. return await new Promise((resolve, reject) => {
  429. if (mode == 'w' || mode == 'a') {
  430. const directoryPath = dirname(file._path);
  431. if (!fs.existsSync(directoryPath)) {
  432. fs.mkdirSync(directoryPath, {recursive: true});
  433. }
  434. }
  435. fs.open(file._path, mode, null, (e, f) => {
  436. if (!e) file.fd = f;
  437. if (file.fd) resolve(true); else resolve(false);
  438. });
  439. });
  440. };
  441. /**
  442. * Reads data from a file asynchronously.
  443. *
  444. * @param {number} length - The number of bytes to read.
  445. * @param {number} position - The position in the file to start reading from.
  446. * @return {ArrayBuffer} The data read from the file.
  447. */
  448. this.read = async function (length, position) {
  449. const file = this;
  450. return await new Promise((resolve, reject) => {
  451. let arraybuffer = new ArrayBuffer(length);
  452. let arr = new Int8Array(arraybuffer);
  453. fs.read(file.fd, arr, 0, length, position, (err, bytesRead, buffer) => {
  454. if (length > bytesRead) {
  455. arraybuffer = buffer.slice(0, bytesRead).buffer;
  456. }
  457. resolve(arraybuffer);
  458. });
  459. });
  460. };
  461. /**
  462. * Writes data from an ArrayBuffer to a file at a given position.
  463. *
  464. * @param {ArrayBuffer} arraybuffer - The ArrayBuffer containing the data to write.
  465. * @param {number} position - The position within the file to start writing.
  466. * @return {boolean} Returns true if the write operation was successful.
  467. */
  468. this.write = async function (arraybuffer, position) {
  469. const file = this;
  470. return await new Promise((resolve, reject) => {
  471. fs.write(file.fd, new Int8Array(arraybuffer), 0, arraybuffer.byteLength, position, (err, written, buffer) => {
  472. if (!err) resolve(true); else resolve(false);
  473. });
  474. });
  475. };
  476. /**
  477. * Flush buffers to disk.
  478. */
  479. this.flush = async function () {
  480. return;
  481. };
  482. /**
  483. * Closes the file descriptor.
  484. *
  485. * @return {Promise<void>} A promise that resolves once the file descriptor is closed.
  486. */
  487. this.close = async function () {
  488. const file = this;
  489. return await new Promise((resolve, reject) => {
  490. fs.close(file.fd, (err) => {
  491. resolve();
  492. });
  493. });
  494. };
  495. /**
  496. * Moves the file to a new path.
  497. *
  498. * @param {string} newPath - The new path where the file will be moved.
  499. * @return {Promise<boolean>} A promise that resolves with `true` if the file was successfully moved, otherwise returns false.
  500. */
  501. this.move = async function (newPath) {
  502. const file = this;
  503. return await new Promise((resolve, reject) => {
  504. fs.rename(file._path, newPath, (err) => {
  505. if (!err) resolve(true); else resolve(false);
  506. });
  507. });
  508. };
  509. /**
  510. * Copies the file to a new path.
  511. *
  512. * @param {string} newPath - The path of the new location where the file will be copied.
  513. * @return {Promise<boolean>} A promise that resolves with `true` if the file is successfully copied, and `false` otherwise.
  514. */
  515. this.copy = async function (newPath) {
  516. const file = this;
  517. return await new Promise((resolve, reject) => {
  518. fs.copyFile(file._path, newPath, (err) => {
  519. if (!err) resolve(true); else resolve(false);
  520. });
  521. });
  522. };
  523. /**
  524. * Deletes the file associated with this object.
  525. *
  526. */
  527. this.delete = async function () {
  528. const file = this;
  529. return await new Promise((resolve, reject) => {
  530. fs.rm(file._path, (err) => {
  531. resolve();
  532. });
  533. });
  534. };
  535. /**
  536. * Checks if the file exists.
  537. *
  538. * @return {Promise<boolean>} A promise that resolves to a boolean value indicating whether the file exists or not.
  539. */
  540. this.exist = async function () {
  541. const file = this;
  542. return await new Promise((resolve, reject) => {
  543. fs.exists(file._path, (stat) => {
  544. resolve(stat);
  545. });
  546. });
  547. };
  548. /**
  549. * @returns the file length
  550. */
  551. this.size = async function () {
  552. const file = this;
  553. return await new Promise((resolve, reject) => {
  554. fs.stat(file._path, (err, stat) => {
  555. if (err) {
  556. resolve(0);
  557. } else {
  558. resolve(stat.size);
  559. }
  560. });
  561. });
  562. };
  563. };
  564. globalThis.js2Proxy = function (dynamic, siteType, site, url, headers) {
  565. let hd = Object.keys(headers).length == 0 ? '_' : encodeURIComponent(JSON.stringify(headers));
  566. return (dynamic ? 'js2p://_WEB_/' : 'http://127.0.0.1:13333/jp/') + randStr(6) + '/' + siteType + '/' + site + '/' + hd + '/' + encodeURIComponent(url);
  567. };
  568. globalThis.jsp = new jsoup();
  569. globalThis.pdfh = (html, parse, base_url = '') => {
  570. const jsp = new jsoup(base_url);
  571. return jsp.pdfh(html, parse, base_url);
  572. };
  573. globalThis.pd = (html, parse, base_url = '') => {
  574. const jsp = new jsoup(base_url);
  575. return jsp.pd(html, parse);
  576. };
  577. globalThis.pdfa = (html, parse) => {
  578. const jsp = new jsoup();
  579. return jsp.pdfa(html, parse);
  580. };
  581. globalThis.pdfl = (html, parse, list_text, list_url, url_key) => {
  582. const jsp = new jsoup();
  583. return jsp.pdfl(html, parse, list_text, list_url, url_key);
  584. };
  585. globalThis.pq = (html) => {
  586. const jsp = new jsoup();
  587. return jsp.pq(html);
  588. };
  589. globalThis.pjfh = (html, parse, addUrl = false) => {
  590. const jsp = new jsoup();
  591. return jsp.pjfh(html, parse, addUrl);
  592. };
  593. globalThis.pj = (html, parse) => {
  594. const jsp = new jsoup();
  595. return jsp.pj(html, parse);
  596. };
  597. globalThis.pjfa = (html, parse) => {
  598. const jsp = new jsoup();
  599. return jsp.pjfa(html, parse);
  600. };
  601. globalThis.log = console.log;
  602. globalThis.print = console.log;
  603. globalThis.jsonpath = jsonpath;
  604. globalThis.jsoup = jsoup;
  605. // 将 JSON 对象转换为 cookie 字符串
  606. function jsonToCookie(json) {
  607. return qs.stringify(json, {
  608. delimiter: ';',
  609. encoder: value => String(value).trim()
  610. });
  611. }
  612. // 将 cookie 字符串转换回 JSON 对象
  613. function cookieToJson(cookieString) {
  614. return qs.parse(cookieString, {
  615. delimiter: ';',
  616. decoder: value => value.trim()
  617. });
  618. }
  619. globalThis.jsonToCookie = jsonToCookie;
  620. globalThis.cookieToJson = cookieToJson;
  621. globalThis.keysToLowerCase = keysToLowerCase;
  622. export default {};