diff --git a/.env.development b/.env.development index 6edf51d2..6aba1651 100644 --- a/.env.development +++ b/.env.development @@ -34,6 +34,7 @@ QQ_SMTP_AUTH_CODE = # 调试猫源-推荐开启 CAT_DEBUG=1 +PHP_PATH= PYTHON_PATH= VIRTUAL_ENV= daemonMode=0 diff --git a/.gitignore b/.gitignore index 28c6287f..7bbca429 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,4 @@ dist /apps/salary/ /jx/_30wmv.js .DS_Store +/spider/catvod/mtv60w[差].js diff --git a/.plugins.example.js b/.plugins.example.js index c61674e6..6b429d01 100644 --- a/.plugins.example.js +++ b/.plugins.example.js @@ -35,14 +35,14 @@ const plugins = [ path: 'plugins/pup-sniffer', // 插件路径 params: '-port 57573', // 启动参数:端口57573 desc: 'drplayer嗅探服务', // 插件描述:提供视频适配代理功能 - active: true // 是否激活:true表示启用此插件 + active: false // 是否激活:true表示启用此插件 }, { name: 'mediaProxy', // 插件名称 path: 'plugins/mediaProxy', // 插件路径 params: '-port 57574', // 启动参数:端口57574 desc: 'go媒体代理服务', // 插件描述:提供视频适配代理功能 - active: true // 是否激活:true表示启用此插件 + active: false // 是否激活:true表示启用此插件 }, ] diff --git a/Dockerfile b/Dockerfile index 0377fc19..63bb1162 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,13 +43,28 @@ RUN cp /app/.env.development /app/.env && \ # 但是,我们仍然需要安装Node.js运行时本身(除非drpys项目是一个纯静态资源服务,不需要Node.js运行时) RUN apk add --no-cache nodejs +# 安装php8.3及其扩展 +RUN apk add --no-cache \ + php83 \ + php83-cli \ + php83-curl \ + php83-mbstring \ + php83-xml \ + php83-pdo \ + php83-pdo_mysql \ + php83-pdo_sqlite \ + php83-openssl \ + php83-sqlite3 \ + php83-json +RUN ln -sf /usr/bin/php83 /usr/bin/php + # 安装python3依赖 RUN apk add --no-cache python3 \ py3-pip \ py3-setuptools \ py3-wheel -# 激活python3虚拟环境并安装依pip3赖 +# 激活python3虚拟环境并安装requirements依赖 RUN python3 -m venv /app/.venv && \ . /app/.venv/bin/activate && \ pip3 install -r /app/spider/py/base/requirements.txt diff --git a/README.md b/README.md index 3cbdcc29..0e0387e6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ nodejs作为服务端的drpy实现。全面升级异步写法 ### 常用超链接 * [本项目主页-免翻](https://github.com/hjdhnx/drpy-node) -* [最新DS本地包-适配皮卡丘](/gh/release) +* ~~[最新DS本地包-适配皮卡丘](/gh/release)~~ +* [DS本地包下载中心](/admin/download) * [接口文档](docs/apidoc.md) | [接口列表如定时任务](docs/apiList.md) | ~~[小猫影视-待对接T4](https://github.com/waifu-project/movie/pull/135)~~ * [代码质量评估工具说明](docs/codeCheck.md) | [DS项目代码评估报告](docs/codeCheckReport.md) @@ -19,9 +20,11 @@ nodejs作为服务端的drpy实现。全面升级异步写法 * [本地配置接口-动态外网/局域网](/config/1?healthy=1&pwd=$pwd) * [其他配置接口-订阅过滤](/docs/sub.md) * [python环境](/docs/pyenv.md) | [DS项目环境变量说明](/docs/envdoc.md) +* php环境(详见 spider/php/readme.md) 不在这里赘述 * [猫源调试教程](/docs/catDebug.md) * [接口压测教程](/docs/httpTest.md) * [AI编程工具 trae](https://www.trae.ai/account-setting#subscription) | 邮编ZIP输入: 518000 +* [推荐使用AI模型-GLM4.7](https://www.bigmodel.cn/glm-coding?ic=DRV3C8M5NX) | [GLM配置文档](https://docs.bigmodel.cn/cn/coding-plan/tool/trae) * [免费AI-360纳米](https://bot.n.cn/)|[免费AI-当贝AI](https://ai.dangbei.com/chat)|[国外聚合全模型](https://lmarena.ai/) * [本站防止爬虫协议](/robots.txt) * [油猴脚本-反切屏检测](/public/monkey/check_screen_leave.user.js) @@ -44,6 +47,7 @@ nodejs作为服务端的drpy实现。全面升级异步写法 * [DS时钟插件-白色时钟](/apps/clock/white_clock.html)|[日历时钟](/apps/clock/index.html) * [DS庆祝页面-完结撒花](/apps/happy/index.html) * [bookReader](/apps/book-reader) +* [系统备份与恢复](/apps/backup-restore/index.html) * [代码加解密工具](/admin/encoder) * [央视点播解析工具](/proxy/央视大全[官]/index.html) * [在线猫ds源主页](/cat/index.html) @@ -66,21 +70,17 @@ nodejs作为服务端的drpy实现。全面升级异步写法 ## 更新记录 -### 20260118 +### 20260208 -更新至V1.3.18 +更新至V1.3.22 -### 20260115 +### 20260131 -更新至V1.3.17 +更新至V1.3.21 -### 20260113 +### 20260127 -更新至V1.3.16 - -### 20260112 - -更新至V1.3.15 +更新至V1.3.20 [点此查看完整更新记录](docs/updateRecord.md) diff --git a/apps/backup-restore/index.html b/apps/backup-restore/index.html new file mode 100644 index 00000000..98a03231 --- /dev/null +++ b/apps/backup-restore/index.html @@ -0,0 +1,373 @@ + + + + + + 系统备份与恢复 + + + +
+

📂 系统备份与恢复

+ +
+
+

备份说明: 点击“立即备份”将系统配置、插件及脚本备份到同级 backup 目录。

+

恢复说明: 点击“恢复备份”将从 backup 目录恢复文件覆盖当前系统。

+
+ +
+
+ 涉及文件清单 + 只读 +
+
+ 加载中... +
+
+
上次备份:--
+
上次恢复:--
+
+
+
+ +
+ + +
+ +
+
系统准备就绪,等待操作...
+
+
+ + + + diff --git a/config/map.txt b/config/map.txt index 13760290..21d1298f 100644 --- a/config/map.txt +++ b/config/map.txt @@ -29,6 +29,8 @@ UC分享@@?type=url¶ms=../json/UC分享.json@@UC分享[盘] 网盘[模板]@@?type=url¶ms=../json/域名配置.json$多多@@多多ᵐ[盘] 网盘[模板]@@?type=url¶ms=../json/域名配置.json$欧歌@@欧歌ᵐ[盘] 网盘[模板]@@?type=url¶ms=../json/域名配置.json$至臻@@至臻ᵐ[盘] +AppFox@@https://qh.70qh.top@@麒麟[AFX] +AppFox@@http://host1.sopython.top/host.json@@粉象[AFX] AppFox@@http://app.hktvyb.cc@@TVB云播[AFX] AppFox@@{"host":"http://www.yezitv.top/dtym.json"}@@木瓜影视[AFX] AppFox@@{"host":"http://nico.oiio.fun"}@@花柳影视[AFX] diff --git a/config/parses.conf b/config/parses.conf index f150792d..95a61601 100644 --- a/config/parses.conf +++ b/config/parses.conf @@ -3,13 +3,14 @@ # 名称,链接,类型,ua,flag (ua不填默认 Mozilla/5.0) 可以手动填 Dart/2.14 (dart:io) # JSON解析排前面 -J1,https://zy.qiaoji8.com/gouzi.php?url=,1 -J2,https://jxjson.icu/neibu.php?url=,1 +J1,https://kalbim.xatut.top/kalbim2025/781718/play/video_player.php?url=,1 +J2,http://sspa8.top:8100/api/?key=1060089351&url=,1 # J3,http://pan.qiaoji8.com/tvbox/neibu.php?url=,1 # J4,http://yunhai.qijiyun.vip/home/api?type=ys&uid=177259&key=dijnouxKNOQSTUWXY5&url=,1 J芒果4k,http://mg.itufm.top/mg.php?url=,1 -HGvip,http://1.94.221.189:88/algorithm.php?url=,1 # J皮皮虾,http://45.207.215.101:5423/index.php?url=,1 +J腾讯关姐,{{hostName}}:5759/tencent.php/?url=,1 +# 295关姐,{{hostName}}:5759/295yun.php?url=,1 # WEB解析放后面 W花旗,https://www.huaqi.live/?url= @@ -18,15 +19,18 @@ W盘古,https://www.playm3u8.cn/jiexi.php?url= # W虾米,https://jx.xmflv.com/?url= # W无双,http://103.117.123.193:1980/players/?url= W1,https://jx.xymp4.cc/?url= -# W2,https://cdn.zyc888.top/?url= -# W3,https://yparse.ik9.cc/index.php?url= -# W4,https://jx.yparse.com/index.php?url= -# W5,https://jx.2s0.cn/player/?url= -# W6,https://jx.quankan.app/?url= +#W2,https://im1907.top/?jx= +W3,https://yparse.ik9.cc/index.php?url= +W4,https://jiexi.site/?url= +W5,https://jx.m3u8.tv/jiexi/?url= +W7,https://www.pangujiexi.com/jiexi/?url= +W8,https://www.pouyun.com/?url= +W9,https://jx.xmflv.com/?url= +Wa,https://jx.xmflv.cc/?url= +Wb,https://jx.yparse.com/index.php?url= +Wc,https://www.8090g.cn/?url= # W7,https://jx.aidouer.net/?url= # W8,https://www.8090g.cn/?url= # W9,https://jx.yangtu.top?url= # W10,https://jx.m3u8.tv/jiexi/?url= -W11,https://www.ckplayer.vip/jiexi/?url= -腾讯关姐,{{hostName}}:5759/tencent.php/?url=,1 -295关姐,{{hostName}}:5759/295yun.php?url=,1 +Wz,https://www.ckplayer.vip/jiexi/?url= diff --git a/controllers/api.js b/controllers/api.js index 1f24be45..6e878bd8 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -24,16 +24,18 @@ import {validatePwd} from "../utils/api_validate.js"; import {startJsonWatcher, getApiEngine} from "../utils/api_helper.js"; import * as drpyS from '../libs/drpyS.js'; import hipy from '../libs/hipy.js'; +import php from '../libs/php.js'; import xbpq from '../libs/xbpq.js'; import catvod from '../libs/catvod.js'; /** * 支持的引擎映射表 - * 包含drpyS、hipy、xbpq、catvod四种引擎 + * 包含drpyS、hipy、phipy、xbpq、catvod五种引擎 */ const ENGINES = { drpyS, hipy, + php, xbpq, catvod, }; diff --git a/controllers/config.js b/controllers/config.js index 1b8aae62..546aee37 100644 --- a/controllers/config.js +++ b/controllers/config.js @@ -23,6 +23,7 @@ import {validateBasicAuth, validatePwd} from "../utils/api_validate.js"; import {getSitesMap} from "../utils/sites-map.js"; import {getParsesDict} from "../utils/file.js"; import batchExecute from '../libs_drpy/batchExecute.js'; +import {isPhpAvailable} from '../utils/phpEnv.js'; const {jsEncoder} = drpyS; @@ -76,6 +77,7 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { const jsDir = options.jsDir; const dr2Dir = options.dr2Dir; const pyDir = options.pyDir; + const phpDir = options.phpDir; const catDir = options.catDir; const configDir = options.configDir; const jsonDir = options.jsonDir; @@ -188,6 +190,7 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { title: ruleObject.title, author: ruleObject.author, 类型: ruleObject.类型 || '影视', + mergeList: ruleObject.二级 === '*' || ruleObject.mergeList, searchable: ruleObject.searchable, filterable: ruleObject.filterable, quickSearch: ruleObject.quickSearch, @@ -195,6 +198,13 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { logo: ruleObject.logo, lang: 'ds', }); + if (ruleMeta.mergeList) { + if (ruleMeta.more && typeof ruleMeta.more === 'object') { + ruleMeta.more.mergeList = 1; + } else { + ruleMeta.more = {mergeList: 1}; + } + } // console.log('ds ruleMeta:', ruleMeta); await FileHeaderManager.writeHeader(filePath, ruleMeta); } else { @@ -291,6 +301,7 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { title: ruleObject.title, author: ruleObject.author, 类型: ruleObject.类型 || '影视', + mergeList: ruleObject.二级 === '*' || ruleObject.mergeList, searchable: ruleObject.searchable, filterable: ruleObject.filterable, quickSearch: ruleObject.quickSearch, @@ -298,6 +309,13 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { logo: ruleObject.logo, lang: 'dr2', }); + if (ruleMeta.mergeList) { + if (ruleMeta.more && typeof ruleMeta.more === 'object') { + ruleMeta.more.mergeList = 1; + } else { + ruleMeta.more = {mergeList: 1}; + } + } // console.log('dr2 ruleMeta:', ruleMeta); await FileHeaderManager.writeHeader(filePath, ruleMeta); } else { @@ -417,6 +435,11 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { filterable: 1, // 固定值 quickSearch: 1, // 固定值 }; + if (baseName.includes('[画]')) { + ruleObject.类型 = '漫画' + } else if (baseName.includes('[书]')) { + ruleObject.类型 = '小说' + } let ruleMeta = {...ruleObject}; const filePath = path.join(pyDir, file); const header = await FileHeaderManager.readHeader(filePath); @@ -490,6 +513,68 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { await batchExecute(py_tasks, listener); } + + // 根据用户是否启用php源去生成对应配置 + const enable_php = ENV.get('enable_php', '1'); + console.log('isPhpAvailable:', isPhpAvailable); + if ((enable_php === '1' && isPhpAvailable) || enable_php === '2') { + const php_files = readdirSync(phpDir); + const api_type = enable_php === '2' ? 3 : 4; + let php_valid_files = php_files.filter((file) => file.endsWith('.php') && !file.startsWith('_') && !['config.php', 'index.php', 'test_runner.php'].includes(file)); + log(`开始生成php的T${api_type}配置,phpDir:${phpDir},源数量: ${php_valid_files.length}`); + + const php_tasks = php_valid_files.map((file) => { + return { + func: async ({file, phpDir, requestHost, pwd, SitesMap}) => { + const baseName = path.basename(file, '.php'); + let api = enable_php === '2' ? `${requestHost}/php/${file}` : `${requestHost}/api/${baseName}?do=php`; + let ext = ''; + if (pwd) { + api += enable_php === '2' ? `?pwd=${pwd}` : `&pwd=${pwd}`; + } + let ruleObject = { + searchable: 1, + filterable: 1, + quickSearch: 1, + }; + if (baseName.includes('[画]')) { + ruleObject.类型 = '漫画' + } else if (baseName.includes('[书]')) { + ruleObject.类型 = '小说' + } + let ruleMeta = {...ruleObject}; + const filePath = path.join(phpDir, file); + + Object.assign(ruleMeta, { + title: baseName, + lang: 'php', + }); + ruleMeta.title = enableRuleName ? ruleMeta.title || baseName : baseName; + + let fileSites = []; + let key = `php_${ruleMeta.title}`; + let name = `${ruleMeta.title}(PHP)`; + fileSites.push({key, name, ext}); + + fileSites.forEach((fileSite) => { + const site = { + key: fileSite.key, + name: fileSite.name, + type: api_type, + api, + ...ruleMeta, + ext: fileSite.ext || "", + }; + sites.push(site); + }); + }, + param: {file, phpDir, requestHost, pwd, SitesMap}, + id: file, + }; + }); + await batchExecute(php_tasks, listener); + } + const enable_cat = ENV.get('enable_cat', '1'); // 根据用户是否启用cat源去生成对应配置 if (enable_cat === '1' || enable_cat === '2') { @@ -520,6 +605,11 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { filterable: 1, // 固定值 quickSearch: 1, // 固定值 }; + if (baseName.includes('[画]')) { + ruleObject.类型 = '漫画' + } else if (baseName.includes('[书]')) { + ruleObject.类型 = '小说' + } let ruleMeta = {...ruleObject}; const filePath = path.join(catDir, file); const header = await FileHeaderManager.readHeader(filePath); @@ -660,78 +750,82 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { * @returns {Promise} 包含parses数组的对象 */ async function generateParseJSON(jxDir, requestHost) { - const files = readdirSync(jxDir); - const jx_files = files.filter((file) => file.endsWith('.js') && !file.startsWith('_')) // 筛选出不是 "_" 开头的 .js 文件 - const jx_dict = getParsesDict(requestHost); + let enable_self_jx = ENV.get('enable_self_jx', '0') === '1'; let parses = []; - const tasks = jx_files.map((file) => { - return { - func: async ({file, jxDir, requestHost, drpyS}) => { - const baseName = path.basename(file, '.js'); // 去掉文件扩展名 - const api = `${requestHost}/parse/${baseName}?url=`; // 使用请求的 host 地址,避免硬编码端口 - - let jxObject = { - type: 1, // 固定值 - ext: { - flag: [ - "qiyi", - "imgo", - "爱奇艺", - "奇艺", - "qq", - "qq 预告及花絮", - "腾讯", - "youku", - "优酷", - "pptv", - "PPTV", - "letv", - "乐视", - "leshi", - "mgtv", - "芒果", - "sohu", - "xigua", - "fun", - "风行" - ] - }, - header: { - "User-Agent": "Mozilla/5.0" + let sorted_parses = []; + const jx_dict = getParsesDict(requestHost); + if (enable_self_jx) { + const files = readdirSync(jxDir); + const jx_files = files.filter((file) => file.endsWith('.js') && !file.startsWith('_')) // 筛选出不是 "_" 开头的 .js 文件 + const tasks = jx_files.map((file) => { + return { + func: async ({file, jxDir, requestHost, drpyS}) => { + const baseName = path.basename(file, '.js'); // 去掉文件扩展名 + const api = `${requestHost}/parse/${baseName}?url=`; // 使用请求的 host 地址,避免硬编码端口 + + let jxObject = { + type: 1, // 固定值 + ext: { + flag: [ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + header: { + "User-Agent": "Mozilla/5.0" + } + }; + try { + let _jxObject = await drpyS.getJx(path.join(jxDir, file)); + jxObject = {...jxObject, ..._jxObject}; + } catch (e) { + throw new Error(`Error parsing jx object for file: ${file}, ${e.message}`); } - }; - try { - let _jxObject = await drpyS.getJx(path.join(jxDir, file)); - jxObject = {...jxObject, ..._jxObject}; - } catch (e) { - throw new Error(`Error parsing jx object for file: ${file}, ${e.message}`); - } - parses.push({ - name: baseName, - url: jxObject.url || api, - type: jxObject.type, - ext: jxObject.ext, - header: jxObject.header - }); + parses.push({ + name: baseName, + url: jxObject.url || api, + type: jxObject.type, + ext: jxObject.ext, + header: jxObject.header + }); + }, + param: {file, jxDir, requestHost, drpyS}, + id: file, + }; + }); + + const listener = { + func: (param, id, error, result) => { + if (error) { + console.error(`Error processing file ${id}:`, error.message); + } else { + // console.log(`Successfully processed file ${id}:`, result); + } }, - param: {file, jxDir, requestHost, drpyS}, - id: file, + param: {}, // 外部参数可以在这里传入 }; - }); - - const listener = { - func: (param, id, error, result) => { - if (error) { - console.error(`Error processing file ${id}:`, error.message); - } else { - // console.log(`Successfully processed file ${id}:`, result); - } - }, - param: {}, // 外部参数可以在这里传入 - }; - await batchExecute(tasks, listener); - let sorted_parses = naturalSort(parses, 'name', ['JSON并发', 'JSON合集', '虾米', '奇奇']); + await batchExecute(tasks, listener); + sorted_parses = naturalSort(parses, 'name', ['JSON并发', 'JSON合集', '虾米', '奇奇']); + } let sorted_jx_dict = naturalSort(jx_dict, 'name', ['J', 'W']); parses = sorted_parses.concat(sorted_jx_dict); return {parses}; diff --git a/controllers/fastlogger.js b/controllers/fastlogger.js index 474f4475..352d851d 100644 --- a/controllers/fastlogger.js +++ b/controllers/fastlogger.js @@ -11,9 +11,9 @@ dotenv.config(); const LOG_WITH_FILE = Number(process.env.LOG_WITH_FILE) || 0; const LOG_LEVEL = process.env.LOG_LEVEL && ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].includes(process.env.LOG_LEVEL) ? process.env.LOG_LEVEL : 'info'; const COOKIE_AUTH_CODE = process.env.COOKIE_AUTH_CODE || 'drpys'; -console.log('LOG_WITH_FILE:', LOG_WITH_FILE); -console.log('LOG_LEVEL:', LOG_LEVEL); -console.log('COOKIE_AUTH_CODE:', COOKIE_AUTH_CODE); +// console.log('LOG_WITH_FILE:', LOG_WITH_FILE); +// console.log('LOG_LEVEL:', LOG_LEVEL); +// console.log('COOKIE_AUTH_CODE:', COOKIE_AUTH_CODE); let _logger = true; let logStream = null; diff --git a/controllers/index.js b/controllers/index.js index afe461b6..aa6f2853 100644 --- a/controllers/index.js +++ b/controllers/index.js @@ -1,120 +1,59 @@ -/** - * 控制器路由注册模块 - * 统一管理和注册所有控制器路由 - * 提供应用程序的所有API端点和功能模块 - */ import formBody from '@fastify/formbody'; import websocket from '@fastify/websocket'; -// WebSocket实时日志控制器-最早引入才能全局拦截console日志 import websocketController from './websocket.js'; -// 静态文件服务控制器 import staticController from './static.js'; -// 文档服务控制器 import docsController from './docs.js'; -// 配置管理控制器 import configController from './config.js'; -// API接口控制器 import apiController from './api.js'; -// 媒体代理控制器 import mediaProxyController from './mediaProxy.js'; -// 根路径控制器 import rootController from './root.js'; -// 编码器控制器 import encoderController from './encoder.js'; -// 解码器控制器 import decoderController from './decoder.js'; -// 认证编码控制器 -import authCoderController from './authcoder.js'; -// Web界面控制器 +import authcoderController from './authcoder.js'; import webController from './web.js'; -// HTTP请求控制器 import httpController from './http.js'; -// 剪贴板推送控制器 import clipboardPusherController from './clipboard-pusher.js'; -// 任务控制器(已注释) -// import taskController from './tasker.js'; -// 定时任务控制器 +// import taskerController from './tasker.js'; import cronTaskerController from './cron-tasker.js'; -// 源检查控制器 import sourceCheckerController from './source-checker.js'; -// 图片存储控制器 import imageStoreController from './image-store.js'; -// WebDAV 代理控制器 import webdavProxyController from './webdav-proxy.js'; -// FTP 代理控制器 import ftpProxyController from './ftp-proxy.js'; -// 文件代理控制器 import fileProxyController from './file-proxy.js'; import m3u8ProxyController from './m3u8-proxy.js'; import unifiedProxyController from './unified-proxy.js'; -// WebSocket实时弹幕日志控制器 -import websocketServerController from "./websocketServer.js"; import githubController from './github.js'; +import websocketServerController from "./websocketServer.js"; -/** - * 注册所有路由控制器 - * 将各个功能模块的路由注册到Fastify实例中 - * @param {Object} fastify - Fastify应用实例 - * @param {Object} options - 路由配置选项 - */ export const registerRoutes = (fastify, options) => { - // 注册插件以支持 application/x-www-form-urlencoded fastify.register(formBody); - // 注册WebSocket插件 fastify.register(websocket); - // 注册WebSocket路由 + fastify.register(websocketController, options); - // 注册静态文件服务路由 fastify.register(staticController, options); - // 注册文档服务路由 fastify.register(docsController, options); - // 注册配置管理路由 fastify.register(configController, options); - // 注册API接口路由 fastify.register(apiController, options); - // 注册媒体代理路由 fastify.register(mediaProxyController, options); - // 注册根路径路由 fastify.register(rootController, options); - // 注册编码器路由 fastify.register(encoderController, options); - // 注册解码器路由 fastify.register(decoderController, options); - // 注册认证编码路由 - fastify.register(authCoderController, options); - // 注册Web界面路由 + fastify.register(authcoderController, options); fastify.register(webController, options); - // 注册HTTP请求路由 fastify.register(httpController, options); - // 注册剪贴板推送路由 fastify.register(clipboardPusherController, options); - // 注册任务路由(已注释) - // fastify.register(taskController, options); - // 注册定时任务路由 + // fastify.register(taskerController, options); fastify.register(cronTaskerController, options); - // 注册源检查路由 fastify.register(sourceCheckerController, options); - // 注册图片存储路由 fastify.register(imageStoreController, options); - // 注册 WebDAV 代理路由 fastify.register(webdavProxyController, options); - // 注册 FTP 代理路由 fastify.register(ftpProxyController, options); - // 注册文件代理路由 fastify.register(fileProxyController, options); fastify.register(m3u8ProxyController, options); - // 注册统一代理路由 fastify.register(unifiedProxyController, options); - // 注册GitHub Release路由 fastify.register(githubController, options); }; -/** - * 注册弹幕路由控制器 - * 将弹幕功能模块的路由注册到Fastify实例中 - * @param {Object} wsApp - Ws实时弹幕预览应用实例 - * @param {Object} options - 路由配置选项 - */ export const registerWsRoutes = (wsApp, options) => { wsApp.register(websocketServerController, options); -} \ No newline at end of file +}; diff --git a/controllers/static.js b/controllers/static.js index 18ccb24e..66493679 100644 --- a/controllers/static.js +++ b/controllers/static.js @@ -79,6 +79,19 @@ export default (fastify, options, done) => { } }); + // 注册PHP脚本文件服务 - 用于存放PHP相关的脚本文件 + fastify.register(fastifyStatic, { + root: options.phpDir, // PHP脚本根目录 + prefix: '/php/', // URL访问前缀 + decorateReply: false, // 禁用 sendFile 装饰器 + setHeaders: (res, path) => { + // 为PHP文件设置正确的Content-Type,确保浏览器以纯文本形式显示 + if (path.endsWith('.php')) { + res.setHeader('Content-Type', 'text/plain; charset=utf-8') + } + } + }); + // 注册CAT相关文件服务 - 用于存放CAT视频源相关文件 fastify.register(fastifyStatic, { root: options.catDir, // CAT文件根目录 diff --git a/controllers/web.js b/controllers/web.js index ce58537a..516cffb0 100644 --- a/controllers/web.js +++ b/controllers/web.js @@ -1,9 +1,93 @@ -import {readFileSync, existsSync} from 'fs'; +import {readFileSync, existsSync, readdirSync, statSync, unlinkSync, mkdirSync, copyFileSync, lstatSync, writeFileSync} from 'fs'; +import {createReadStream} from 'fs'; +import {execSync} from 'child_process'; import path from 'path'; +import {fileURLToPath} from 'url'; +import {createHash} from 'crypto'; import {ENV} from '../utils/env.js'; import COOKIE from '../utils/cookieManager.js'; +import {validateBasicAuth} from '../utils/api_validate.js'; const COOKIE_AUTH_CODE = process.env.COOKIE_AUTH_CODE || 'drpys'; +const IS_VERCEL = process.env.VERCEL; +const DOWNLOAD_AUTH_SECRET = process.env.DOWNLOAD_AUTH_SECRET || 'drpys_download_secret'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const projectRootDir = path.dirname(__dirname); +const pkg = JSON.parse(readFileSync(path.join(projectRootDir, 'package.json'), 'utf-8')); + +const generateDownloadToken = (filename) => { + const timestamp = Date.now(); + const data = `${filename}-${timestamp}-${DOWNLOAD_AUTH_SECRET}`; + const token = createHash('md5').update(data).digest('hex'); + return `${token}-${timestamp}`; +}; + +const validateDownloadToken = (filename, token) => { + if (!token) return false; + const parts = token.split('-'); + if (parts.length < 2) return false; + const timestamp = parseInt(parts.pop()); + const hash = parts.join('-'); + const data = `${filename}-${timestamp}-${DOWNLOAD_AUTH_SECRET}`; + const expectedHash = createHash('md5').update(data).digest('hex'); + const now = Date.now(); + return hash === expectedHash && (now - timestamp) < 3600000; +}; + +const findLatestPackage = (projectDir, packageName) => { + try { + const parentDir = path.dirname(projectDir); + const files = readdirSync(parentDir); + + const isGreen = packageName.includes('-green'); + const ext = packageName.split('.').pop(); + const baseName = packageName.replace(/-green\.[^.]+$/, '').replace(/\.[^.]+$/, ''); + const pattern = new RegExp(`^${baseName.replace(/\./g, '\\.')}-\\d{8}${isGreen ? '-green' : ''}\\.${ext}`); + + console.log(`查找包: ${packageName}, 正则: ${pattern.source}, 父目录: ${parentDir}`); + console.log('目录中的文件:', files.filter(f => f.includes('drpy-node'))); + + const packageFiles = files + .filter(file => pattern.test(file)) + .map(file => { + const filePath = path.join(parentDir, file); + const stats = statSync(filePath); + return {file, filePath, mtime: stats.mtime, size: stats.size}; + }) + .sort((a, b) => b.mtime - a.mtime); + + console.log('匹配到的文件:', packageFiles.map(f => f.file)); + return packageFiles.length > 0 ? packageFiles[0] : null; + } catch (error) { + console.error('查找包失败:', error.message); + return null; + } +}; + +const buildPackage = (packageName) => { + try { + let command = 'node package.js'; + if (packageName.includes('-green')) { + command += ' -g'; + } + if (packageName.includes('.zip')) { + command += ' -z'; + } + + console.log(`执行打包命令: ${command}, 目录: ${projectRootDir}`); + const output = execSync(command, {cwd: projectRootDir, stdio: 'pipe'}); + console.log('打包输出:', output.toString()); + const result = findLatestPackage(projectRootDir, packageName); + console.log('打包后查找结果:', result ? result.file : '未找到'); + return result; + } catch (error) { + console.error('打包失败:', error.message); + console.error('错误详情:', error.stdout?.toString(), error.stderr?.toString()); + throw error; + } +}; export default (fastify, options, done) => { fastify.get('/admin/encoder', async (request, reply) => { @@ -75,5 +159,375 @@ export default (fastify, options, done) => { } }); + fastify.get('/admin/download', { + preHandler: validateBasicAuth + }, async (request, reply) => { + try { + if (IS_VERCEL) { + return reply.code(403).send({ + success: false, + message: 'Vercel 环境不支持文件下载功能', + }); + } + + const projectName = path.basename(projectRootDir); + const templatePath = path.join(projectRootDir, 'public', 'download.html'); + + if (!existsSync(templatePath)) { + return reply.code(500).send({ + success: false, + message: '下载页面模板不存在', + }); + } + + let html = readFileSync(templatePath, 'utf-8'); + + const files = [ + {name: `${projectName}.7z`, desc: '7z 压缩包(标准版)'}, + {name: `${projectName}.zip`, desc: 'ZIP 压缩包(标准版)'}, + {name: `${projectName}-green.7z`, desc: '7z 压缩包(绿色版,不含[密]文件)'}, + {name: `${projectName}-green.zip`, desc: 'ZIP 压缩包(绿色版,不含[密]文件)'} + ]; + + const formatFileSize = (bytes) => { + if (!bytes || bytes === 0) return '未打包'; + const mb = bytes / (1024 * 1024); + return mb.toFixed(2) + ' MB'; + }; + + const downloadItems = files.map(file => { + const latestPackage = findLatestPackage(projectRootDir, file.name); + const fileSize = latestPackage ? formatFileSize(latestPackage.size) : '未打包'; + const sizeClass = latestPackage ? '' : ' not-packed'; + const token = generateDownloadToken(file.name); + const downloadUrl = `/admin/download/${file.name}?auth=${token}`; + + let buildTime = '未打包'; + if (latestPackage && latestPackage.mtime) { + const date = new Date(latestPackage.mtime); + buildTime = date.toLocaleString('zh-CN', { hour12: false }); + } + + return '
' + + '
' + + '
' + file.name + '
' + + '
' + file.desc + '
' + + '
版本: ' + pkg.version + ' | 打包时间: ' + buildTime + '
' + + '
' + + '
' + fileSize + '
' + + '
' + + '下载' + + '' + + '
' + + '
'; + }).join(''); + + html = html.replace(/\{\{projectName\}\}/g, projectName); + html = html.replace(/\{\{downloadItems\}\}/g, downloadItems); + + reply.type('text/html').send(html); + } catch (error) { + console.error('获取下载页面失败:', error.message); + return reply.code(500).send({ + success: false, + message: '获取下载页面失败', + error: error.message, + }); + } + }); + + fastify.get('/admin/download/:filename', { + preHandler: async (request, reply) => { + const {auth} = request.query; + if (validateDownloadToken(request.params.filename, auth)) { + return; + } + const authHeader = request.headers.authorization; + if (!authHeader) { + reply.header('WWW-Authenticate', 'Basic'); + return reply.code(401).send('Authentication required'); + } + const base64Credentials = authHeader.split(' ')[1]; + const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8'); + const [username, password] = credentials.split(':'); + const validUsername = process.env.API_AUTH_NAME || ''; + const validPassword = process.env.API_AUTH_CODE || ''; + if (username === validUsername && password === validPassword) { + return; + } + reply.header('WWW-Authenticate', 'Basic'); + return reply.code(401).send('Invalid credentials'); + } + }, async (request, reply) => { + try { + if (IS_VERCEL) { + return reply.code(403).send({ + success: false, + message: 'Vercel 环境不支持文件下载功能', + }); + } + + const {filename} = request.params; + const projectName = path.basename(projectRootDir); + + const validFilenames = [ + `${projectName}.7z`, + `${projectName}.zip`, + `${projectName}-green.7z`, + `${projectName}-green.zip` + ]; + + if (!validFilenames.includes(filename)) { + return reply.code(400).send({ + success: false, + message: '无效的文件名', + }); + } + + let latestPackage = findLatestPackage(projectRootDir, filename); + + if (!latestPackage) { + console.log(`未找到 ${filename},开始打包...`); + latestPackage = buildPackage(filename); + if (!latestPackage) { + return reply.code(500).send({ + success: false, + message: '打包失败,无法创建压缩文件', + }); + } + } + + const fileStream = createReadStream(latestPackage.filePath); + const contentType = filename.endsWith('.zip') ? 'application/zip' : 'application/x-7z-compressed'; + reply.header('Content-Type', contentType); + reply.header('Content-Disposition', `attachment; filename="${encodeURIComponent(latestPackage.file)}"`); + return reply.send(fileStream); + } catch (error) { + console.error('下载文件失败:', error.message); + return reply.code(500).send({ + success: false, + message: '下载失败', + error: error.message, + }); + } + }); + + fastify.post('/admin/download/clear', { + preHandler: validateBasicAuth + }, async (request, reply) => { + try { + if (IS_VERCEL) { + return reply.code(403).send({ + success: false, + message: 'Vercel 环境不支持文件操作', + }); + } + + const parentDir = path.dirname(projectRootDir); + const projectName = path.basename(projectRootDir); + const files = readdirSync(parentDir); + const pattern = new RegExp(`^${projectName.replace(/\./g, '\\.')}-\\d{8}(-green)?\\.(7z|zip)$`); + + let deletedCount = 0; + const deletedFiles = []; + + for (const file of files) { + if (pattern.test(file)) { + const filePath = path.join(parentDir, file); + try { + unlinkSync(filePath); + deletedFiles.push(file); + deletedCount++; + } catch (error) { + console.error(`删除文件失败: ${file}`, error.message); + } + } + } + + return reply.send({ + success: true, + count: deletedCount, + deletedFiles, + message: `已清除 ${deletedCount} 个历史文件` + }); + } catch (error) { + console.error('清除历史文件失败:', error.message); + return reply.code(500).send({ + success: false, + message: '清除历史文件失败', + error: error.message, + }); + } + }); + + const BACKUP_PATHS = [ + '.env', + '.plugins.js', + 'config/env.json', + 'config/map.txt', + 'config/parses.conf', + 'config/player.json', + 'scripts/cron', + 'plugins' + ]; + + const BACKINFO_FILENAME = '.backinfo'; + + const getBackupRootDir = () => { + return path.join(path.dirname(projectRootDir), path.basename(projectRootDir) + '-backup'); + }; + + const getBackinfoPath = (backupDir) => { + return path.join(backupDir, BACKINFO_FILENAME); + }; + + const loadBackinfo = (backupDir) => { + const infoPath = getBackinfoPath(backupDir); + if (!existsSync(infoPath)) { + return null; + } + try { + const content = readFileSync(infoPath, 'utf-8'); + return JSON.parse(content); + } catch (e) { + return null; + } + }; + + const saveBackinfo = (backupDir, data) => { + const infoPath = getBackinfoPath(backupDir); + writeFileSync(infoPath, JSON.stringify(data, null, 2), 'utf-8'); + }; + + const getEffectiveBackupPaths = (backupDir) => { + const info = loadBackinfo(backupDir); + if (info && Array.isArray(info.paths) && info.paths.length > 0) { + return {paths: info.paths, info}; + } + return {paths: BACKUP_PATHS, info}; + }; + + const copyRecursiveSync = (src, dest) => { + const stats = lstatSync(src); + if (stats.isDirectory()) { + if (!existsSync(dest)) { + mkdirSync(dest, { recursive: true }); + } + readdirSync(src).forEach((childItemName) => { + copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName)); + }); + } else { + const destDir = path.dirname(dest); + if (!existsSync(destDir)) { + mkdirSync(destDir, { recursive: true }); + } + copyFileSync(src, dest); + } + }; + + fastify.get('/admin/backup/config', { + preHandler: validateBasicAuth + }, async (request, reply) => { + const backupDir = getBackupRootDir(); + let paths; + let lastBackupAt = null; + let lastRestoreAt = null; + if (!existsSync(backupDir)) { + paths = BACKUP_PATHS; + } else { + const result = getEffectiveBackupPaths(backupDir); + paths = result.paths; + if (result.info) { + lastBackupAt = result.info.lastBackupAt || null; + lastRestoreAt = result.info.lastRestoreAt || null; + } + } + return reply.send({success: true, paths, lastBackupAt, lastRestoreAt}); + }); + + fastify.post('/admin/backup', { + preHandler: validateBasicAuth + }, async (request, reply) => { + if (IS_VERCEL) { + return reply.code(403).send({ success: false, message: 'Vercel环境不支持备份' }); + } + try { + const backupDir = getBackupRootDir(); + if (!existsSync(backupDir)) { + mkdirSync(backupDir, { recursive: true }); + } + + const {paths, info} = getEffectiveBackupPaths(backupDir); + const details = []; + for (const item of paths) { + const srcPath = path.join(projectRootDir, item); + const destPath = path.join(backupDir, item); + + if (existsSync(srcPath)) { + copyRecursiveSync(srcPath, destPath); + details.push(`Backed up: ${item}`); + } else { + details.push(`Skipped (not found): ${item}`); + } + } + + const now = new Date().toISOString(); + const customPaths = info && Array.isArray(info.paths) && info.paths.length > 0 ? info.paths : []; + const backinfoData = { + paths: customPaths, + lastBackupAt: now, + lastRestoreAt: info && info.lastRestoreAt ? info.lastRestoreAt : null + }; + saveBackinfo(backupDir, backinfoData); + + return reply.send({ success: true, message: '备份完成', backupDir, details }); + } catch (error) { + fastify.log.error(`Backup failed: ${error.message}`); + return reply.code(500).send({ success: false, message: '备份失败: ' + error.message }); + } + }); + + fastify.post('/admin/restore', { + preHandler: validateBasicAuth + }, async (request, reply) => { + if (IS_VERCEL) { + return reply.code(403).send({ success: false, message: 'Vercel环境不支持恢复' }); + } + try { + const backupDir = getBackupRootDir(); + if (!existsSync(backupDir)) { + return reply.code(404).send({ success: false, message: '备份目录不存在' }); + } + + const {paths, info} = getEffectiveBackupPaths(backupDir); + const details = []; + for (const item of paths) { + const srcPath = path.join(backupDir, item); + const destPath = path.join(projectRootDir, item); + + if (existsSync(srcPath)) { + copyRecursiveSync(srcPath, destPath); + details.push(`Restored: ${item}`); + } else { + details.push(`Skipped (not found in backup): ${item}`); + } + } + + const now = new Date().toISOString(); + const customPaths = info && Array.isArray(info.paths) && info.paths.length > 0 ? info.paths : []; + const backinfoData = { + paths: customPaths, + lastBackupAt: info && info.lastBackupAt ? info.lastBackupAt : null, + lastRestoreAt: now + }; + saveBackinfo(backupDir, backinfoData); + + return reply.send({ success: true, message: '恢复完成', backupDir, details }); + } catch (error) { + fastify.log.error(`Restore failed: ${error.message}`); + return reply.code(500).send({ success: false, message: '恢复失败: ' + error.message }); + } + }); + done(); }; diff --git a/docs/envdoc.md b/docs/envdoc.md index 8d4ea81c..f50c7c8a 100644 --- a/docs/envdoc.md +++ b/docs/envdoc.md @@ -26,6 +26,7 @@ | VIRTUAL_ENV | 本地python虚拟环境路径 | 同上,差别在于虚拟环境会自动拼scripts路径下的python.exe,跟真实环境二选一 | | daemonMode | 守护进程版本 | 0: 旗舰版 1:轻量版 | | DS_REQ_LIB | ds/cat 默认req实现 | 0:fetch 1:axios (已知模式1为前面版本默认功能,但是后面发现某些场景无法获取源码,新写了模式0,不保证完全兼容) | +| PHP_PATH | 本地PHP可执行文件路径 | php (全局) 或 /usr/bin/php8.3 (指定路径) | | CLIPBOARD_MAX_SIZE | 单次文本传输最大体积 默认100KB | 102400 | | CLIPBOARD_SECURITY_CODE | 剪切板接口请求头安全码 | drpys | | CLIPBOARD_ALLOWED_CHARSET | 允许字符集,默认utf-8 | utf-8 | @@ -35,3 +36,65 @@ | MAX_TEXT_SIZE | 设置最大文本大小(剪切板插件) | 0.1 * 1024 * 1024 | | MAX_IMAGE_SIZE | 设置最大图片大小(图片插件) | 0.5 * 1024 * 1024 | +# 用户自定义配置 (config/env.json) + +该文件位于 `config/env.json`,存储用户自定义的运行时配置。 + +| 参数键 | 参数说明 | 备注 | +| :--- | :--- | :--- | +| enable_php | 是否开启 PHP 源支持 | 0:关闭 1:开启(本地执行T4,需环境) 2:开启(远程加载T3,免环境) | +| api_pwd | 全局接口访问密码 | 访问敏感接口或文件时需要 | +| thread | 爬虫并发数 | 建议设置在 4-8 之间 | +| quark_cookie | 夸克网盘 Cookie | 观看夸克网盘资源需要 | +| uc_cookie | UC 网盘 Cookie | 观看 UC 网盘资源需要 | +| ali_token | 阿里云盘 Token | 观看阿里云盘资源需要 | +| deepseek_apiKey | DeepSeek API Key | AI 搜索/对话功能需要 | +| kimi_apiKey | Kimi API Key | AI 搜索/对话功能需要 | +| bili_cookie | Bilibili Cookie | B站相关资源需要 | +| play_proxy_mode | 播放代理模式 | 0:直接播放 1:代理播放 | + +## 环境搭建指南 + +### 1. PHP 环境搭建 (推荐) + +本项目支持 PHP 爬虫源(`spider/php/*.php`),需要本地安装 PHP 环境。 + +#### Linux (Ubuntu/Debian) + +推荐使用 PPA 安装 PHP 8.3+: + +```bash +# 1. 添加 PPA 源 +sudo apt install software-properties-common -y +sudo add-apt-repository ppa:ondrej/php -y +sudo apt update + +# 2. 安装 PHP 8.3 及常用扩展 (drpy 爬虫需要 curl, mbstring, xml, mysql 等) +sudo apt install php8.3-cli php8.3-curl php8.3-mbstring php8.3-xml php8.3-mysql -y + +# 3. 验证安装 +php -v +``` + +#### Windows + +1. 下载 PHP 8.3+ NTS 版本 (推荐)。 +2. 解压到 `C:\php` 等目录。 +3. 将解压目录添加到系统 `Path` 环境变量中。 +4. 修改 `php.ini`,开启 `extension=curl`, `mbstring`, `openssl` 等扩展。 + +### 2. 7-Zip 工具安装 (可选) + +部分功能可能依赖 7z 进行解压操作。 + +#### Linux (Ubuntu/Debian) + +```bash +sudo apt update +sudo apt install p7zip-full -y +``` + +验证安装: +```bash +7z +``` diff --git a/docs/updateRecord.md b/docs/updateRecord.md index 4a487152..10ebc5db 100644 --- a/docs/updateRecord.md +++ b/docs/updateRecord.md @@ -1,5 +1,46 @@ # drpyS更新记录 +### 20260208 + +更新至V1.3.22 + +1. 增强海阔`跳过形式二级` 功能适配更多壳子,配置/首页分类接口会返回 `mergeList` 属性代表此源可以合并 +2. 修改了支持合并属性的历史源,壳子适配后可以把一级列表数据合并后在详情页顺序播放 +3. dr2 fast模式支持,需要爱佬修改版的so,加解密性能提升,补充缺失的文件 `public/dist/drpy-core-fast.min.js`,当 + `enable_dr2 = 2` 时走这个逻辑 + +写法说明: + +1. ds/dr2 源里写了 `rule.二级 = '*'` 或者 `rule.mergeList = 1`,自动生成配置会自动处理 +2. 所有源(不含php),文件头@header里有 `mergeList = 1` 或者 `more.mergeList = 1` + +### 20260131 + +更新至V1.3.21 + +1. 更新一点文档和文件名称 +2. 修复番茄动漫ds源在皮卡丘壳子上无法使用问题( + BUG羊的壳子tid为链接时处理逻辑一团乱,http链接被篡改成https就算了,链接含有{{page}}变量竟然被篡改成1了) +3. 更新文档、生成配置类型,使php、py源也更兼容皮卡丘的漫画小说 + +### 20260127 + +更新至V1.3.20 + +重磅升级来了!!! + +1. 支持了php适配器,支持自动加载php源,环境变量新增 `PHP_PATH=`,如果不指定默认则是'php',可以配置成自己的路径 +2. 尝试启动加速,插件异步加载 +3. 启动日志大幅精简,还你一个干净清爽的启动界面 +4. 设置中心修改,支持启用/关闭 PHP的源 + +### 20260125 + +更新至V1.3.19 + +1. 合并了E佬修复&新增的源 +2. 增加了PHP的T4源标准 + ### 20260118 更新至V1.3.18 @@ -118,7 +159,7 @@ 2. 修复 `cut` 逻辑错误导致的`番茄小说`二级无数据,增强字符串的`parseX`改用`JSON5` 3. 新增 `网盘` 模板源及map示例 4. 新收录了几个源 -5. 增加 `hikerSkipEr` 属性,允许海阔跳过T4源的形式二级 +5. 增加 `mergeList` 属性,允许海阔跳过T4源的形式二级 6. 修复百度盘特殊路径(如含#)的选集解析与播放 ### 20250919 diff --git a/index.js b/index.js index 2c27bfac..f31b67ae 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,9 @@ +import { performance } from 'perf_hooks'; +const startTime = performance.now(); + import * as fastlogger from './controllers/fastlogger.js' import path from 'path'; +import {checkPhpAvailable} from './utils/phpEnv.js'; import os from 'os'; import qs from 'qs'; import {fileURLToPath} from 'url'; @@ -30,16 +34,23 @@ const jsonDir = path.join(__dirname, 'json'); const jsDir = path.join(__dirname, 'spider/js'); const dr2Dir = path.join(__dirname, 'spider/js_dr2'); const pyDir = path.join(__dirname, 'spider/py'); +const phpDir = path.join(__dirname, 'spider/php'); const catDir = path.join(__dirname, 'spider/catvod'); const catLibDir = path.join(__dirname, 'spider/catLib'); const xbpqDir = path.join(__dirname, 'spider/xbpq'); const configDir = path.join(__dirname, 'config'); -const pluginProcs = startAllPlugins(__dirname); -// console.log('pluginProcs:', pluginProcs); +// 异步启动插件,不阻塞主线程 +let pluginProcs = {}; +setTimeout(() => { + pluginProcs = startAllPlugins(__dirname); +}, 0); // 添加钩子事件 fastify.addHook('onReady', async () => { + await checkPhpAvailable(); + const endTime = performance.now(); + console.log(`🚀 Server started in ${(endTime - startTime).toFixed(2)}ms`); try { await daemon.startDaemon(); fastify.log.info('Python守护进程已启动'); @@ -168,6 +179,7 @@ const registerOptions = { jsDir, dr2Dir, pyDir, + phpDir, catDir, catLibDir, xbpqDir, diff --git "a/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" "b/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" index c78b8a10..eb9a2801 100644 --- "a/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" +++ "b/json/App\346\250\241\346\235\277\351\205\215\347\275\256.json" @@ -26,18 +26,10 @@ }, "仓鼠": { "muban": "AppQiji", - "host": "", - "hosturl": "https://ceshi307386.oss-cn-beijing.aliyuncs.com/ceshi421.txt", - "key": "da61247f5b662597", - "iv": "da61247f5b662597", - "verify": "true" - }, - "紫金": { - "muban": "AppGet", - "host": "", - "hosturl": "https://snysw.xyz/mf4kzs327.txt", - "key": "1234567887654321", - "iv": "1234567887654321", + "host": "https://hk440cms.cs4k.top", + "hosturl": "", + "key": "fL7sY4zN4kB3pG4p", + "iv": "fL7sY4zN4kB3pG4p", "verify": "true" }, "云云": { @@ -88,6 +80,30 @@ "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", @@ -162,8 +178,8 @@ }, "顾我": { "muban": "AppQiji", - "host": "", - "hosturl": "https://guwozj-1319364746.cos.ap-guangzhou.myqcloud.com/guwo.txt", + "host": "http://117.50.204.35:520", + "hosturl": "", "key": "ca94b06ca3c7d80e", "iv": "ca94b06ca3c7d80e", "verify": "true" @@ -184,6 +200,14 @@ "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", diff --git a/libs/catvod.js b/libs/catvod.js index efb465cf..320d850d 100644 --- a/libs/catvod.js +++ b/libs/catvod.js @@ -14,7 +14,7 @@ const _config_path = path.join(__dirname, '../config'); const _lib_path = path.join(__dirname, '../spider/catvod'); const enable_cat_debug = Number(process.env.CAT_DEBUG) !== 2; -console.log('enable_cat_debug:', enable_cat_debug); +// console.log('enable_cat_debug:', enable_cat_debug); const json2Object = function (json) { if (!json) { @@ -184,10 +184,16 @@ const category = async function (filePath, env, tid, pg = 1, filter = 1, extend const detail = async function (filePath, env, ids) { const moduleObject = await init(filePath, env); const vod_id = Array.isArray(ids) ? ids[0] : ids; - return json2Object(await moduleObject.detail(vod_id)); + let detailResult = '{}'; + // console.log('type of detailContent:', typeof moduleObject.detailContent); + if (moduleObject.detailContent) { // tvbox形式猫源二级参数传ids列表 + detailResult = await moduleObject.detailContent(ids); + } else { // ds/cat传非id + detailResult = await moduleObject.detail(vod_id); + } + return json2Object(detailResult); } - const search = async function (filePath, env, wd, quick = 0, pg = 1) { const moduleObject = await init(filePath, env); return json2Object(await moduleObject.search(wd, quick, pg)); diff --git a/libs/drpyS.js b/libs/drpyS.js index 6d57ba76..f178db21 100644 --- a/libs/drpyS.js +++ b/libs/drpyS.js @@ -416,9 +416,10 @@ export async function init(filePath, env = {}, refresh) { // ruleScript.runInContext(context); // const result = await ruleScript.runInContext(context); const executeWithTimeout = (script, context, timeout) => { + let timer; return Promise.race([ new Promise((_, reject) => - setTimeout(() => reject(new Error('Code execution timed out')), timeout) + timer = setTimeout(() => reject(new Error('Code execution timed out')), timeout) ), new Promise((resolve, reject) => { try { @@ -434,7 +435,9 @@ export async function init(filePath, env = {}, refresh) { reject(error); } }) - ]); + ]).finally(() => { + if (timer) clearTimeout(timer); + }); }; const result = await executeWithTimeout(ruleScript, context, 30000); // log('result:', result); @@ -708,7 +711,7 @@ async function invokeMethod(filePath, env, method, args = [], injectVars = {}) { result = await searchParseAfter(moduleObject, result, args[2]); log(`[invokeMethod js:] 搜索 ${injectVars.input} 执行完毕,结果为:`, JSON.stringify(result.list.slice(0, 2))); } else if (method === 'class_parse') { - result = await homeParseAfter(result, moduleObject.类型, moduleObject.hikerListCol, moduleObject.hikerClassListCol, moduleObject.hikerSkipEr, injectVars); + result = await homeParseAfter(result, moduleObject.类型, moduleObject.hikerListCol, moduleObject.hikerClassListCol, moduleObject.mergeList, injectVars); } return result; } @@ -798,9 +801,9 @@ async function initParse(rule, env, vm, context) { if (!rule.hasOwnProperty('sniffer')) { // 默认关闭辅助嗅探 rule.sniffer = false; } - // 二级为*自动添加hikerSkipEr属性允许跳过形式二级 - if (!rule.hasOwnProperty('hikerSkipEr') && rule.二级 === '*') { - rule.hikerSkipEr = 1; + // 二级为*自动添加mergeList属性允许跳过形式二级 + if (!rule.hasOwnProperty('mergeList') && rule.二级 === '*') { + rule.mergeList = 1; } rule.sniffer = rule.hasOwnProperty('sniffer') ? rule.sniffer : ''; rule.sniffer = !!(rule.sniffer && rule.sniffer !== '0' && rule.sniffer !== 'false'); diff --git a/libs/drpysParser.js b/libs/drpysParser.js index 36bd3b60..388bce9a 100644 --- a/libs/drpysParser.js +++ b/libs/drpysParser.js @@ -424,7 +424,7 @@ export async function homeParse(rule) { return context; } -export async function homeParseAfter(d, _type, hikerListCol, hikerClassListCol, hikerSkipEr, injectVars) { +export async function homeParseAfter(d, _type, hikerListCol, hikerClassListCol, mergeList, injectVars) { if (!d) { d = {}; } @@ -436,8 +436,9 @@ export async function homeParseAfter(d, _type, hikerListCol, hikerClassListCol, d.hikerClassListCol = hikerClassListCol; } // 跳过形式二级 - if (hikerSkipEr) { - d.hikerSkipEr = hikerSkipEr; + if (mergeList) { + d.hikerSkipEr = mergeList; + d.mergeList = mergeList; } const { classes, @@ -1489,7 +1490,7 @@ export async function invokeWithInjectVars(rule, method, injectVars, args) { } break; case 'class_parse': - result = await homeParseAfter(result, rule.类型, rule.hikerListCol, rule.hikerClassListCol, rule.hikerSkipEr, injectVars); + result = await homeParseAfter(result, rule.类型, rule.hikerListCol, rule.hikerClassListCol, rule.mergeList, injectVars); break; case '一级': result = await cateParseAfter(rule, result, args[1]); diff --git a/libs/php.js b/libs/php.js new file mode 100644 index 00000000..4e582268 --- /dev/null +++ b/libs/php.js @@ -0,0 +1,220 @@ +import path from "path"; +import {readFile} from "fs/promises"; +import {fileURLToPath} from 'url'; +import {execFile} from 'child_process'; +import {promisify} from 'util'; +import {getSitesMap} from "../utils/sites-map.js"; +import {computeHash, deepCopy, getNowTime, urljoin} from "../utils/utils.js"; +import {prepareBinary} from "../utils/binHelper.js"; +import {md5} from "../libs_drpy/crypto-util.js"; +import {fastify} from "../controllers/fastlogger.js"; +// import dotenv from 'dotenv'; +// +// dotenv.config({ path: path.join(process.cwd(), '.env.development') }); + +const execFileAsync = promisify(execFile); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const _config_path = path.join(__dirname, '../config'); +const _bridge_path = path.join(__dirname, '../spider/php/_bridge.php'); + +// Cache for module objects +const moduleCache = new Map(); + +// Mapping from JS method names to PHP Spider method names +const methodMapping = { + 'init': 'init', + 'home': 'homeContent', + 'homeVod': 'homeVideoContent', + 'category': 'categoryContent', + 'detail': 'detailContent', + 'search': 'searchContent', + 'play': 'playerContent', + 'proxy': 'proxy', // Not standard in BaseSpider, but might exist + 'action': 'action' // Not standard +}; + +// Helper to stringify args for CLI +function stringify(arg) { + if (arg === undefined) return 'null'; + return JSON.stringify(arg); +} + +// Helper to parse JSON output +function json2Object(json) { + if (!json) return {}; + if (typeof json === 'object') return json; + try { + return JSON.parse(json); + } catch (e) { + return json; + } +} + +// Execute PHP bridge +const callPhpMethod = async (filePath, methodName, env, ...args) => { + let phpPath = process.env.PHP_PATH || 'php'; + + const validPath = prepareBinary(phpPath); + if (!validPath) { + throw new Error(`PHP executable not found or invalid: ${phpPath}`); + } + phpPath = validPath; + + const phpMethodName = methodMapping[methodName] || methodName; + + const cliArgs = [ + _bridge_path, + filePath, + phpMethodName, + JSON.stringify(env), + ...args.map(stringify) + ]; + + try { + // fastify.log.info(`Calling PHP: ${phpPath} ${cliArgs.join(' ')}`); + const {stdout, stderr} = await execFileAsync(phpPath, cliArgs, { + encoding: 'utf8', + maxBuffer: 10 * 1024 * 1024, // 10MB buffer + env: { + ...process.env, + PYTHONIOENCODING: 'utf-8', // Just in case + // Add any PHP specific env vars if needed + } + }); + + if (stderr) { + // Log stderr but don't fail immediately unless stdout is empty or error + // fastify.log.warn(`PHP Stderr: ${stderr}`); + console.error(`PHP Stderr: ${stderr}`); + } + + const result = json2Object(stdout.trim()); + + if (result && result.error) { + throw new Error(`PHP Error: ${result.error}\nTrace: ${result.traceback}`); + } + + return result; + + } catch (error) { + console.error(`Error calling PHP method ${methodName}:`, error); + throw error; + } +}; + +const loadEsmWithHash = async function (filePath, fileHash, env) { + const spiderProxy = {}; + const spiderMethods = Object.keys(methodMapping); + + spiderMethods.forEach(method => { + spiderProxy[method] = async (...args) => { + return callPhpMethod(filePath, method, env, ...args); + }; + }); + + return spiderProxy; +}; + +const init = async function (filePath, env = {}, refresh) { + try { + const fileContent = await readFile(filePath, 'utf-8'); + const fileHash = computeHash(fileContent); + const moduleName = path.basename(filePath, '.php'); // .php extension + let moduleExt = env.ext || ''; + + // Logic for SitesMap and moduleExt (similar to hipy.js) + let SitesMap = getSitesMap(_config_path); + if (moduleExt && SitesMap[moduleName]) { + // ... logic for compressed ext ... + // Simplified for now, assuming plain string or handled by caller + } + + let hashMd5 = md5(filePath + '#php#' + moduleExt); + + if (moduleCache.has(hashMd5) && !refresh) { + const cached = moduleCache.get(hashMd5); + if (cached.hash === fileHash) { + return cached.moduleObject; + } + } + + fastify.log.info(`Loading PHP module: ${filePath}`); + let t1 = getNowTime(); + + const module = await loadEsmWithHash(filePath, fileHash, env); + const rule = module; + + // Initialize the spider + const initValue = await rule.init(moduleExt) || {}; + + let t2 = getNowTime(); + const moduleObject = deepCopy(rule); + moduleObject.cost = t2 - t1; + + moduleCache.set(hashMd5, {moduleObject, hash: fileHash}); + return {...moduleObject, ...initValue}; + + } catch (error) { + fastify.log.error(`Error in php.init :${filePath}`, error); + throw new Error(`Failed to initialize PHP module:${error.message}`); + } +}; + +const getRule = async function (filePath, env) { + const moduleObject = await init(filePath, env); + return JSON.stringify(moduleObject); +}; + +const home = async function (filePath, env, filter = 1) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.home(filter)); +}; + +const homeVod = async function (filePath, env) { + const moduleObject = await init(filePath, env); + const homeVodResult = json2Object(await moduleObject.homeVod()); + return homeVodResult && homeVodResult.list ? homeVodResult.list : homeVodResult; +}; + +const category = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.category(tid, pg, filter, extend)); +}; + +const detail = async function (filePath, env, ids) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.detail(ids)); +}; + +const search = async function (filePath, env, wd, quick = 0, pg = 1) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.search(wd, quick, pg)); +}; + +const play = async function (filePath, env, flag, id, flags) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.play(flag, id, flags)); +}; + +const proxy = async function (filePath, env, params) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.proxy(params)); +}; + +const action = async function (filePath, env, action, value) { + const moduleObject = await init(filePath, env); + return json2Object(await moduleObject.action(action, value)); +}; + +export default { + getRule, + init, + home, + homeVod, + category, + detail, + search, + play, + proxy, + action +}; diff --git a/libs_drpy/drpyInject.js b/libs_drpy/drpyInject.js index 75d25c8c..27598677 100644 --- a/libs_drpy/drpyInject.js +++ b/libs_drpy/drpyInject.js @@ -163,10 +163,10 @@ async function request(url, opt = {}) { let effectivePostType = postType; if (!effectivePostType) { // 查找不区分大小写的 Content-Type 头 - const contentTypeKey = Object.keys(headers).find(key => + const contentTypeKey = Object.keys(headers).find(key => key.toLowerCase() === 'content-type' ); - + if (contentTypeKey && headers[contentTypeKey]) { const contentType = headers[contentTypeKey].toLowerCase(); if (contentType.includes('application/x-www-form-urlencoded')) { @@ -176,7 +176,7 @@ async function request(url, opt = {}) { } } } - + // 根据有效的 postType 处理数据 if (effectivePostType === 'form' && data != null && typeof data === 'object') { data = qs.stringify(data, {encode: false}); @@ -724,4 +724,58 @@ globalThis.jsonToCookie = jsonToCookie; globalThis.cookieToJson = cookieToJson; globalThis.keysToLowerCase = keysToLowerCase; +class BaseSpider { + constructor() { + this.home = this.homeContent; + this.category = this.categoryContent; + // this.detail = this.detailContent; + this.search = this.searchContent; + this.play = this.playerContent; + this.homeVod = this.homeVideoContent; + this.proxy = this.localProxy; + // this.fetch = request; + } + + async fetch(url, options) { + const resp = await req(url, options); + return { + ...resp, + get data() { // data尝试返回object + try { + return this.content.parseX; + } catch (e) { + return {}; + } + } + }; + } + + async homeContent() { + } + + async categoryContent() { + } + + async detailContent() { + } + + async searchContent() { + } + + async playerContent() { + } + + async homeVideoContent() { + } + + async localProxy() { + + } + + async action() { + + } +} + +globalThis.BaseSpider = BaseSpider; export default {}; diff --git a/libs_drpy/fetchAxios.js b/libs_drpy/fetchAxios.js index 789fe5c2..4e90ffa8 100644 --- a/libs_drpy/fetchAxios.js +++ b/libs_drpy/fetchAxios.js @@ -4,6 +4,50 @@ */ import FormData from 'form-data'; import https from "https"; +import diagnosticsChannel from 'diagnostics_channel'; + +let undiciStripUASubscribed = false; + +function ensureUndiciStripUASubscription() { + if (undiciStripUASubscribed) { + return; + } + undiciStripUASubscribed = true; + + diagnosticsChannel.channel('undici:request:create').subscribe(({request}) => { + if (!request || !Array.isArray(request.headers)) { + return; + } + const headers = request.headers; + + let shouldStrip = false; + for (let i = 0; i < headers.length; i += 2) { + const k = headers[i]; + if (typeof k === 'string' && k.toLowerCase() === 'x-remove-user-agent') { + shouldStrip = true; + break; + } + } + if (!shouldStrip) { + return; + } + + for (let i = 0; i < headers.length; i += 2) { + const k = headers[i]; + if (typeof k === 'string' && k.toLowerCase() === 'x-remove-user-agent') { + headers.splice(i, 2); + i -= 2; + } + } + for (let i = 0; i < headers.length; i += 2) { + const k = headers[i]; + if (typeof k === 'string' && k.toLowerCase() === 'user-agent') { + headers.splice(i, 2); + i -= 2; + } + } + }); +} /** * FetchAxios类 - HTTP客户端实现 @@ -71,6 +115,18 @@ class FetchAxios { finalConfig = await interceptor(finalConfig) || finalConfig; } + if (finalConfig.headers) { + const headerKeys = Object.keys(finalConfig.headers); + for (const key of headerKeys) { + if (key.toLowerCase() === 'user-agent' && finalConfig.headers[key] === 'RemoveUserAgent') { + delete finalConfig.headers[key]; + finalConfig.headers['x-remove-user-agent'] = '1'; + ensureUndiciStripUASubscription(); + break; + } + } + } + // 拼接查询参数 if (finalConfig.params) { const query = new URLSearchParams(finalConfig.params).toString(); @@ -300,4 +356,4 @@ export function createHttpsInstance() { responseType: 'arraybuffer', httpsAgent: httpsAgent }); -} \ No newline at end of file +} diff --git a/libs_drpy/req-extend.js b/libs_drpy/req-extend.js index 86a75912..cf080579 100644 --- a/libs_drpy/req-extend.js +++ b/libs_drpy/req-extend.js @@ -40,6 +40,9 @@ async function request(url, obj = {}, ocr_flag = false) { obj.headers["Content-Type"] = 'text/html; charset=' + rule.encoding; } } + if (rule.timeout && typeof obj.timeout === 'undefined') { + obj.timeout = rule.timeout; + } if (typeof (obj.body) != 'undefined' && obj.body && typeof (obj.body) === 'string') { // 传body加 "Content-Type":"application/x-www-form-urlencoded;" 即可post form if (!obj.headers.hasOwnProperty('Content-Type') && !obj.headers.hasOwnProperty('content-type')) { // 手动指定了就不管 @@ -75,7 +78,7 @@ async function request(url, obj = {}, ocr_flag = false) { // } log(`[request] headers: ${JSON.stringify(obj.headers)}`); - log('[request] url:' + url + ` |method:${obj.method || 'GET'} |body:${obj.body || ''}`); + log('[request] url:' + url + ` |method:${obj.method || 'GET'}|timeout:${obj.timeout} |body:${obj.body || ''}`); let res = await req(url, obj); let html = res.content || ''; if (obj.withHeaders) { diff --git a/package.js b/package.js index 1e52492e..c718bc78 100644 --- a/package.js +++ b/package.js @@ -4,10 +4,10 @@ import {join, basename, dirname, resolve, relative} from 'path'; import url from 'url'; // 要排除的目录列表 -const EXCLUDE_DIRS = ['.git', '.idea', 'soft', 'examples', 'apps/cat', 'plugins/pvideo', 'plugins/req-proxy', 'plugins/pup-sniffer', 'plugins/mediaProxy', 'pyTools', 'drop_code', 'jstest', 'local', 'logs', '对话1.txt', 'vod_cache', 'data/mv']; +const EXCLUDE_DIRS = ['.git', '.idea', 'soft', 'examples', 'apps/cat', 'plugins/pvideo', 'plugins/req-proxy', 'plugins/pup-sniffer', 'plugins/mediaProxy', 'pyTools', 'drop_code','local', 'logs', '对话1.txt', 'vod_cache', 'data/mv']; // 要排除的文件列表 -const EXCLUDE_FILES = ['config/env.json', '.env', '.claude', 'clipboard.txt', 'clipboard.txt.bak', '.plugins.js', 'yarn.lock', 't4_daemon.pid', 'spider/js/UC分享.js', 'spider/js/百忙无果[官].js', 'json/UC分享.json', 'jx/_30wmv.js', 'jx/奇奇.js', 'jx/芒果关姐.js', 'data/settings/link_data.json', 'index.json', 'custom.json']; +const EXCLUDE_FILES = ['config/env.json', '.env', '.claude', 'clipboard.txt', 'clipboard.txt.bak', '.plugins.js', 'yarn.lock', 't4_daemon.pid', 'spider/js/UC分享.js', 'spider/js/百忙无果[官].js', 'spider/catvod/mtv60w[差].js', 'json/UC分享.json', 'jx/_30wmv.js', 'jx/奇奇.js', 'jx/芒果关姐.js', 'data/settings/link_data.json', 'index.json', 'custom.json']; // 获取脚本所在目录 const getScriptDir = () => dirname(resolve(url.fileURLToPath(import.meta.url))); @@ -37,7 +37,7 @@ const filterGreenFiles = (scriptDir) => { }; // 压缩目录 -const compressDirectory = (scriptDir, green) => { +const compressDirectory = (scriptDir, green, useZip) => { const currentDir = basename(scriptDir); const currentTime = new Date().toLocaleDateString('zh-CN', { year: 'numeric', @@ -45,12 +45,13 @@ const compressDirectory = (scriptDir, green) => { day: '2-digit' }).replace(/\//g, ''); const archiveSuffix = green ? '-green' : ''; - const archiveName = `${currentDir}-${currentTime}${archiveSuffix}.7z`; + const archiveExt = useZip ? '.zip' : '.7z'; + const archiveName = `${currentDir}-${currentTime}${archiveSuffix}${archiveExt}`; const parentDir = resolve(scriptDir, '..'); const archivePath = join(parentDir, archiveName); - // 构建 7z 命令 + // 构建压缩命令参数 const excludeParams = []; // 排除目录 @@ -77,7 +78,7 @@ const compressDirectory = (scriptDir, green) => { } // 构建命令,打包目录内容而不包含目录本身 - const command = `7z a "${archivePath}" "${join(scriptDir, '*')}" -r ${excludeParams.join(' ')}`; + const command = `7z a -t${useZip ? 'zip' : '7z'} "${archivePath}" "${join(scriptDir, '*')}" -r ${excludeParams.join(' ')}`; console.log(`构建的 7z 命令: ${command}`); try { @@ -95,8 +96,9 @@ const main = () => { // 简单解析命令行参数 const args = process.argv.slice(2); const green = args.includes('-g') || args.includes('--green'); + const useZip = args.includes('-z') || args.includes('--zip'); - compressDirectory(scriptDir, green); + compressDirectory(scriptDir, green, useZip); }; main(); diff --git a/package.json b/package.json index e350953d..db4efb0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drpy-node", - "version": "1.3.18", + "version": "1.3.22", "main": "index.js", "type": "module", "scripts": { @@ -14,8 +14,12 @@ "node22-win": "chcp 65001 && node --trace-deprecation --experimental-sqlite index.js", "package": "python package.py", "package-green": "python package.py -g", + "package-zip": "python package.py -z", + "package-green-zip": "python package.py -g -z", "packageJS": "node package.js", "packageJS-green": "node package.js -g", + "packageJS-zip": "node package.js -z", + "packageJS-green-zip": "node package.js -g -z", "gzip-1": "node controllers/encoder.js json/十六万歌曲.json", "ungzip-1": "node controllers/decoder.js json/十六万歌曲.json.gz", "moontv": "node scripts/mjs/moontv.mjs 采集2025.json -p" diff --git a/package.py b/package.py index 827deca9..02854a94 100644 --- a/package.py +++ b/package.py @@ -7,7 +7,6 @@ EXCLUDE_DIRS = ['.git', '.idea', 'soft', 'examples', 'apps/cat', 'plugins/pvideo', 'plugins/req-proxy', 'plugins/pup-sniffer', 'plugins/mediaProxy', 'pyTools', 'drop_code', - 'jstest', 'local', 'logs', '对话1.txt', 'vod_cache', 'data/mv'] @@ -16,6 +15,7 @@ EXCLUDE_FILES = ['config/env.json', '.env', '.claude', 'clipboard.txt', 'clipboard.txt.bak', '.plugins.js', 'yarn.lock', 't4_daemon.pid', 'spider/js/UC分享.js', 'spider/js/百忙无果[官].js', + 'spider/catvod/mtv60w[差].js', 'json/UC分享.json', 'jx/_30wmv.js', 'jx/奇奇.js', 'jx/芒果关姐.js', 'data/settings/link_data.json', 'index.json', 'custom.json'] @@ -44,13 +44,14 @@ def filter_green_files(script_dir): return green_files -def generate_archive_name(script_dir, green=False): +def generate_archive_name(script_dir, green=False, use_zip=False): """ 生成压缩包文件名 Args: script_dir (str): 脚本所在目录 green (bool): 是否为green模式 + use_zip (bool): 是否使用ZIP格式 Returns: str: 压缩包的完整路径 @@ -63,7 +64,8 @@ def generate_archive_name(script_dir, green=False): # 根据是否传入 green 参数生成压缩包文件名 archive_suffix = "-green" if green else "" - archive_name = f"{current_dir}-{current_time}{archive_suffix}.7z" + archive_ext = ".zip" if use_zip else ".7z" + archive_name = f"{current_dir}-{current_time}{archive_suffix}{archive_ext}" # 压缩包输出路径 (脚本所在目录的外面) parent_dir = os.path.abspath(os.path.join(script_dir, "..")) @@ -107,7 +109,7 @@ def build_exclude_params(script_dir, green=False): return exclude_params -def execute_compression(archive_path, script_dir, exclude_params): +def execute_compression(archive_path, script_dir, exclude_params, use_zip=False): """ 执行7z压缩命令 @@ -115,9 +117,11 @@ def execute_compression(archive_path, script_dir, exclude_params): archive_path (str): 压缩包输出路径 script_dir (str): 脚本所在目录 exclude_params (list): 排除参数列表 + use_zip (bool): 是否使用ZIP格式 """ # 构建命令,打包目录内容而不包含目录本身 - command = f"7z a \"{archive_path}\" \"{script_dir}\\*\" " + " ".join(exclude_params) + archive_type = "zip" if use_zip else "7z" + command = f"7z a -t{archive_type} \"{archive_path}\" \"{script_dir}\\*\" " + " ".join(exclude_params) # 打印构建的命令进行调试 print(f"构建的 7z 命令: {command}") @@ -130,22 +134,23 @@ def execute_compression(archive_path, script_dir, exclude_params): print(f"压缩失败: {e}") -def compress_directory(script_dir, green=False): +def compress_directory(script_dir, green=False, use_zip=False): """ 压缩目录为7z包 Args: script_dir (str): 要压缩的目录路径 green (bool): 是否启用green模式,筛选带[密]的文件 + use_zip (bool): 是否使用ZIP格式 """ # 生成压缩包文件名和路径 - archive_path = generate_archive_name(script_dir, green) + archive_path = generate_archive_name(script_dir, green, use_zip) # 构建排除参数 exclude_params = build_exclude_params(script_dir, green) # 执行压缩 - execute_compression(archive_path, script_dir, exclude_params) + execute_compression(archive_path, script_dir, exclude_params, use_zip) if __name__ == "__main__": @@ -155,7 +160,8 @@ def compress_directory(script_dir, green=False): # 解析命令行参数 parser = argparse.ArgumentParser(description="压缩当前目录为 7z 包,支持可选参数。") parser.add_argument('-g', '--green', action='store_true', help="启用 green 模式,筛选 js 目录下所有带 [密] 的文件。") + parser.add_argument('-z', '--zip', action='store_true', help="使用 ZIP 格式打包,默认使用 7z 格式。") args = parser.parse_args() # 调用压缩函数 - compress_directory(script_dir, green=args.green) + compress_directory(script_dir, green=args.green, use_zip=args.zip) diff --git a/public/dist/drpy-core-fast.min.js b/public/dist/drpy-core-fast.min.js new file mode 100644 index 00000000..5d17f0ea --- /dev/null +++ b/public/dist/drpy-core-fast.min.js @@ -0,0 +1 @@ +var e={27:e=>{e.exports='(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.JSON5=factory())})(this,function(){"use strict";function createCommonjsModule(fn,module){return module={exports:{}},fn(module,module.exports),module.exports}var _global=createCommonjsModule(function(module){var global=module.exports=typeof window!="undefined"&&window.Math==Math?window:typeof self!="undefined"&&self.Math==Math?self:Function("return this")();if(typeof __g=="number"){__g=global}});var _core=createCommonjsModule(function(module){var core=module.exports={version:"2.6.5"};if(typeof __e=="number"){__e=core}});var _core_1=_core.version;var _isObject=function(it){return typeof it==="object"?it!==null:typeof it==="function"};var _anObject=function(it){if(!_isObject(it)){throw TypeError(it+" is not an object!")}return it};var _fails=function(exec){try{return!!exec()}catch(e){return true}};var _descriptors=!_fails(function(){return Object.defineProperty({},"a",{get:function(){return 7}}).a!=7});var document=_global.document;var is=_isObject(document)&&_isObject(document.createElement);var _domCreate=function(it){return is?document.createElement(it):{}};var _ie8DomDefine=!_descriptors&&!_fails(function(){return Object.defineProperty(_domCreate("div"),"a",{get:function(){return 7}}).a!=7});var _toPrimitive=function(it,S){if(!_isObject(it)){return it}var fn,val;if(S&&typeof(fn=it.toString)=="function"&&!_isObject(val=fn.call(it))){return val}if(typeof(fn=it.valueOf)=="function"&&!_isObject(val=fn.call(it))){return val}if(!S&&typeof(fn=it.toString)=="function"&&!_isObject(val=fn.call(it))){return val}throw TypeError("Can\'t convert object to primitive value")};var dP=Object.defineProperty;var f=_descriptors?Object.defineProperty:function defineProperty(O,P,Attributes){_anObject(O);P=_toPrimitive(P,true);_anObject(Attributes);if(_ie8DomDefine){try{return dP(O,P,Attributes)}catch(e){}}if("get"in Attributes||"set"in Attributes){throw TypeError("Accessors not supported!")}if("value"in Attributes){O[P]=Attributes.value}return O};var _objectDp={f:f};var _propertyDesc=function(bitmap,value){return{enumerable:!(bitmap&1),configurable:!(bitmap&2),writable:!(bitmap&4),value:value}};var _hide=_descriptors?function(object,key,value){return _objectDp.f(object,key,_propertyDesc(1,value))}:function(object,key,value){object[key]=value;return object};var hasOwnProperty={}.hasOwnProperty;var _has=function(it,key){return hasOwnProperty.call(it,key)};var id=0;var px=Math.random();var _uid=function(key){return"Symbol(".concat(key===undefined?"":key,")_",(++id+px).toString(36))};var _library=false;var _shared=createCommonjsModule(function(module){var SHARED="__core-js_shared__";var store=_global[SHARED]||(_global[SHARED]={});(module.exports=function(key,value){return store[key]||(store[key]=value!==undefined?value:{})})("versions",[]).push({version:_core.version,mode:_library?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})});var _functionToString=_shared("native-function-to-string",Function.toString);var _redefine=createCommonjsModule(function(module){var SRC=_uid("src");var TO_STRING="toString";var TPL=(""+_functionToString).split(TO_STRING);_core.inspectSource=function(it){return _functionToString.call(it)};(module.exports=function(O,key,val,safe){var isFunction=typeof val=="function";if(isFunction){_has(val,"name")||_hide(val,"name",key)}if(O[key]===val){return}if(isFunction){_has(val,SRC)||_hide(val,SRC,O[key]?""+O[key]:TPL.join(String(key)))}if(O===_global){O[key]=val}else if(!safe){delete O[key];_hide(O,key,val)}else if(O[key]){O[key]=val}else{_hide(O,key,val)}})(Function.prototype,TO_STRING,function toString(){return typeof this=="function"&&this[SRC]||_functionToString.call(this)})});var _aFunction=function(it){if(typeof it!="function"){throw TypeError(it+" is not a function!")}return it};var _ctx=function(fn,that,length){_aFunction(fn);if(that===undefined){return fn}switch(length){case 1:return function(a){return fn.call(that,a)};case 2:return function(a,b){return fn.call(that,a,b)};case 3:return function(a,b,c){return fn.call(that,a,b,c)}}return function(){return fn.apply(that,arguments)}};var PROTOTYPE="prototype";var $export=function(type,name,source){var IS_FORCED=type&$export.F;var IS_GLOBAL=type&$export.G;var IS_STATIC=type&$export.S;var IS_PROTO=type&$export.P;var IS_BIND=type&$export.B;var target=IS_GLOBAL?_global:IS_STATIC?_global[name]||(_global[name]={}):(_global[name]||{})[PROTOTYPE];var exports=IS_GLOBAL?_core:_core[name]||(_core[name]={});var expProto=exports[PROTOTYPE]||(exports[PROTOTYPE]={});var key,own,out,exp;if(IS_GLOBAL){source=name}for(key in source){own=!IS_FORCED&&target&&target[key]!==undefined;out=(own?target:source)[key];exp=IS_BIND&&own?_ctx(out,_global):IS_PROTO&&typeof out=="function"?_ctx(Function.call,out):out;if(target){_redefine(target,key,out,type&$export.U)}if(exports[key]!=out){_hide(exports,key,exp)}if(IS_PROTO&&expProto[key]!=out){expProto[key]=out}}};_global.core=_core;$export.F=1;$export.G=2;$export.S=4;$export.P=8;$export.B=16;$export.W=32;$export.U=64;$export.R=128;var _export=$export;var ceil=Math.ceil;var floor=Math.floor;var _toInteger=function(it){return isNaN(it=+it)?0:(it>0?floor:ceil)(it)};var _defined=function(it){if(it==undefined){throw TypeError("Can\'t call method on "+it)}return it};var _stringAt=function(TO_STRING){return function(that,pos){var s=String(_defined(that));var i=_toInteger(pos);var l=s.length;var a,b;if(i<0||i>=l){return TO_STRING?"":undefined}a=s.charCodeAt(i);return a<55296||a>56319||i+1===l||(b=s.charCodeAt(i+1))<56320||b>57343?TO_STRING?s.charAt(i):a:TO_STRING?s.slice(i,i+2):(a-55296<<10)+(b-56320)+65536}};var $at=_stringAt(false);_export(_export.P,"String",{codePointAt:function codePointAt(pos){return $at(this,pos)}});var codePointAt=_core.String.codePointAt;var max=Math.max;var min=Math.min;var _toAbsoluteIndex=function(index,length){index=_toInteger(index);return index<0?max(index+length,0):min(index,length)};var fromCharCode=String.fromCharCode;var $fromCodePoint=String.fromCodePoint;_export(_export.S+_export.F*(!!$fromCodePoint&&$fromCodePoint.length!=1),"String",{fromCodePoint:function fromCodePoint(x){var arguments$1=arguments;var res=[];var aLen=arguments.length;var i=0;var code;while(aLen>i){code=+arguments$1[i++];if(_toAbsoluteIndex(code,1114111)!==code){throw RangeError(code+" is not a valid code point")}res.push(code<65536?fromCharCode(code):fromCharCode(((code-=65536)>>10)+55296,code%1024+56320))}return res.join("")}});var fromCodePoint=_core.String.fromCodePoint;var Space_Separator=/[\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/;var ID_Start=/[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u08A0-\\u08B4\\u08B6-\\u08BD\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C60\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C88\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312E\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FEA\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7AE\\uA7B0-\\uA7B7\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB65\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]|\\uD800[\\uDC00-\\uDC0B\\uDC0D-\\uDC26\\uDC28-\\uDC3A\\uDC3C\\uDC3D\\uDC3F-\\uDC4D\\uDC50-\\uDC5D\\uDC80-\\uDCFA\\uDD40-\\uDD74\\uDE80-\\uDE9C\\uDEA0-\\uDED0\\uDF00-\\uDF1F\\uDF2D-\\uDF4A\\uDF50-\\uDF75\\uDF80-\\uDF9D\\uDFA0-\\uDFC3\\uDFC8-\\uDFCF\\uDFD1-\\uDFD5]|\\uD801[\\uDC00-\\uDC9D\\uDCB0-\\uDCD3\\uDCD8-\\uDCFB\\uDD00-\\uDD27\\uDD30-\\uDD63\\uDE00-\\uDF36\\uDF40-\\uDF55\\uDF60-\\uDF67]|\\uD802[\\uDC00-\\uDC05\\uDC08\\uDC0A-\\uDC35\\uDC37\\uDC38\\uDC3C\\uDC3F-\\uDC55\\uDC60-\\uDC76\\uDC80-\\uDC9E\\uDCE0-\\uDCF2\\uDCF4\\uDCF5\\uDD00-\\uDD15\\uDD20-\\uDD39\\uDD80-\\uDDB7\\uDDBE\\uDDBF\\uDE00\\uDE10-\\uDE13\\uDE15-\\uDE17\\uDE19-\\uDE33\\uDE60-\\uDE7C\\uDE80-\\uDE9C\\uDEC0-\\uDEC7\\uDEC9-\\uDEE4\\uDF00-\\uDF35\\uDF40-\\uDF55\\uDF60-\\uDF72\\uDF80-\\uDF91]|\\uD803[\\uDC00-\\uDC48\\uDC80-\\uDCB2\\uDCC0-\\uDCF2]|\\uD804[\\uDC03-\\uDC37\\uDC83-\\uDCAF\\uDCD0-\\uDCE8\\uDD03-\\uDD26\\uDD50-\\uDD72\\uDD76\\uDD83-\\uDDB2\\uDDC1-\\uDDC4\\uDDDA\\uDDDC\\uDE00-\\uDE11\\uDE13-\\uDE2B\\uDE80-\\uDE86\\uDE88\\uDE8A-\\uDE8D\\uDE8F-\\uDE9D\\uDE9F-\\uDEA8\\uDEB0-\\uDEDE\\uDF05-\\uDF0C\\uDF0F\\uDF10\\uDF13-\\uDF28\\uDF2A-\\uDF30\\uDF32\\uDF33\\uDF35-\\uDF39\\uDF3D\\uDF50\\uDF5D-\\uDF61]|\\uD805[\\uDC00-\\uDC34\\uDC47-\\uDC4A\\uDC80-\\uDCAF\\uDCC4\\uDCC5\\uDCC7\\uDD80-\\uDDAE\\uDDD8-\\uDDDB\\uDE00-\\uDE2F\\uDE44\\uDE80-\\uDEAA\\uDF00-\\uDF19]|\\uD806[\\uDCA0-\\uDCDF\\uDCFF\\uDE00\\uDE0B-\\uDE32\\uDE3A\\uDE50\\uDE5C-\\uDE83\\uDE86-\\uDE89\\uDEC0-\\uDEF8]|\\uD807[\\uDC00-\\uDC08\\uDC0A-\\uDC2E\\uDC40\\uDC72-\\uDC8F\\uDD00-\\uDD06\\uDD08\\uDD09\\uDD0B-\\uDD30\\uDD46]|\\uD808[\\uDC00-\\uDF99]|\\uD809[\\uDC00-\\uDC6E\\uDC80-\\uDD43]|[\\uD80C\\uD81C-\\uD820\\uD840-\\uD868\\uD86A-\\uD86C\\uD86F-\\uD872\\uD874-\\uD879][\\uDC00-\\uDFFF]|\\uD80D[\\uDC00-\\uDC2E]|\\uD811[\\uDC00-\\uDE46]|\\uD81A[\\uDC00-\\uDE38\\uDE40-\\uDE5E\\uDED0-\\uDEED\\uDF00-\\uDF2F\\uDF40-\\uDF43\\uDF63-\\uDF77\\uDF7D-\\uDF8F]|\\uD81B[\\uDF00-\\uDF44\\uDF50\\uDF93-\\uDF9F\\uDFE0\\uDFE1]|\\uD821[\\uDC00-\\uDFEC]|\\uD822[\\uDC00-\\uDEF2]|\\uD82C[\\uDC00-\\uDD1E\\uDD70-\\uDEFB]|\\uD82F[\\uDC00-\\uDC6A\\uDC70-\\uDC7C\\uDC80-\\uDC88\\uDC90-\\uDC99]|\\uD835[\\uDC00-\\uDC54\\uDC56-\\uDC9C\\uDC9E\\uDC9F\\uDCA2\\uDCA5\\uDCA6\\uDCA9-\\uDCAC\\uDCAE-\\uDCB9\\uDCBB\\uDCBD-\\uDCC3\\uDCC5-\\uDD05\\uDD07-\\uDD0A\\uDD0D-\\uDD14\\uDD16-\\uDD1C\\uDD1E-\\uDD39\\uDD3B-\\uDD3E\\uDD40-\\uDD44\\uDD46\\uDD4A-\\uDD50\\uDD52-\\uDEA5\\uDEA8-\\uDEC0\\uDEC2-\\uDEDA\\uDEDC-\\uDEFA\\uDEFC-\\uDF14\\uDF16-\\uDF34\\uDF36-\\uDF4E\\uDF50-\\uDF6E\\uDF70-\\uDF88\\uDF8A-\\uDFA8\\uDFAA-\\uDFC2\\uDFC4-\\uDFCB]|\\uD83A[\\uDC00-\\uDCC4\\uDD00-\\uDD43]|\\uD83B[\\uDE00-\\uDE03\\uDE05-\\uDE1F\\uDE21\\uDE22\\uDE24\\uDE27\\uDE29-\\uDE32\\uDE34-\\uDE37\\uDE39\\uDE3B\\uDE42\\uDE47\\uDE49\\uDE4B\\uDE4D-\\uDE4F\\uDE51\\uDE52\\uDE54\\uDE57\\uDE59\\uDE5B\\uDE5D\\uDE5F\\uDE61\\uDE62\\uDE64\\uDE67-\\uDE6A\\uDE6C-\\uDE72\\uDE74-\\uDE77\\uDE79-\\uDE7C\\uDE7E\\uDE80-\\uDE89\\uDE8B-\\uDE9B\\uDEA1-\\uDEA3\\uDEA5-\\uDEA9\\uDEAB-\\uDEBB]|\\uD869[\\uDC00-\\uDED6\\uDF00-\\uDFFF]|\\uD86D[\\uDC00-\\uDF34\\uDF40-\\uDFFF]|\\uD86E[\\uDC00-\\uDC1D\\uDC20-\\uDFFF]|\\uD873[\\uDC00-\\uDEA1\\uDEB0-\\uDFFF]|\\uD87A[\\uDC00-\\uDFE0]|\\uD87E[\\uDC00-\\uDE1D]/;var ID_Continue=/[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u0860-\\u086A\\u08A0-\\u08B4\\u08B6-\\u08BD\\u08D4-\\u08E1\\u08E3-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u09FC\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0AF9-\\u0AFF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58-\\u0C5A\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C80-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D00-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D54-\\u0D57\\u0D5F-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1C80-\\u1C88\\u1CD0-\\u1CD2\\u1CD4-\\u1CF9\\u1D00-\\u1DF9\\u1DFB-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312E\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FEA\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7AE\\uA7B0-\\uA7B7\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C5\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA8FD\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB65\\uAB70-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2F\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]|\\uD800[\\uDC00-\\uDC0B\\uDC0D-\\uDC26\\uDC28-\\uDC3A\\uDC3C\\uDC3D\\uDC3F-\\uDC4D\\uDC50-\\uDC5D\\uDC80-\\uDCFA\\uDD40-\\uDD74\\uDDFD\\uDE80-\\uDE9C\\uDEA0-\\uDED0\\uDEE0\\uDF00-\\uDF1F\\uDF2D-\\uDF4A\\uDF50-\\uDF7A\\uDF80-\\uDF9D\\uDFA0-\\uDFC3\\uDFC8-\\uDFCF\\uDFD1-\\uDFD5]|\\uD801[\\uDC00-\\uDC9D\\uDCA0-\\uDCA9\\uDCB0-\\uDCD3\\uDCD8-\\uDCFB\\uDD00-\\uDD27\\uDD30-\\uDD63\\uDE00-\\uDF36\\uDF40-\\uDF55\\uDF60-\\uDF67]|\\uD802[\\uDC00-\\uDC05\\uDC08\\uDC0A-\\uDC35\\uDC37\\uDC38\\uDC3C\\uDC3F-\\uDC55\\uDC60-\\uDC76\\uDC80-\\uDC9E\\uDCE0-\\uDCF2\\uDCF4\\uDCF5\\uDD00-\\uDD15\\uDD20-\\uDD39\\uDD80-\\uDDB7\\uDDBE\\uDDBF\\uDE00-\\uDE03\\uDE05\\uDE06\\uDE0C-\\uDE13\\uDE15-\\uDE17\\uDE19-\\uDE33\\uDE38-\\uDE3A\\uDE3F\\uDE60-\\uDE7C\\uDE80-\\uDE9C\\uDEC0-\\uDEC7\\uDEC9-\\uDEE6\\uDF00-\\uDF35\\uDF40-\\uDF55\\uDF60-\\uDF72\\uDF80-\\uDF91]|\\uD803[\\uDC00-\\uDC48\\uDC80-\\uDCB2\\uDCC0-\\uDCF2]|\\uD804[\\uDC00-\\uDC46\\uDC66-\\uDC6F\\uDC7F-\\uDCBA\\uDCD0-\\uDCE8\\uDCF0-\\uDCF9\\uDD00-\\uDD34\\uDD36-\\uDD3F\\uDD50-\\uDD73\\uDD76\\uDD80-\\uDDC4\\uDDCA-\\uDDCC\\uDDD0-\\uDDDA\\uDDDC\\uDE00-\\uDE11\\uDE13-\\uDE37\\uDE3E\\uDE80-\\uDE86\\uDE88\\uDE8A-\\uDE8D\\uDE8F-\\uDE9D\\uDE9F-\\uDEA8\\uDEB0-\\uDEEA\\uDEF0-\\uDEF9\\uDF00-\\uDF03\\uDF05-\\uDF0C\\uDF0F\\uDF10\\uDF13-\\uDF28\\uDF2A-\\uDF30\\uDF32\\uDF33\\uDF35-\\uDF39\\uDF3C-\\uDF44\\uDF47\\uDF48\\uDF4B-\\uDF4D\\uDF50\\uDF57\\uDF5D-\\uDF63\\uDF66-\\uDF6C\\uDF70-\\uDF74]|\\uD805[\\uDC00-\\uDC4A\\uDC50-\\uDC59\\uDC80-\\uDCC5\\uDCC7\\uDCD0-\\uDCD9\\uDD80-\\uDDB5\\uDDB8-\\uDDC0\\uDDD8-\\uDDDD\\uDE00-\\uDE40\\uDE44\\uDE50-\\uDE59\\uDE80-\\uDEB7\\uDEC0-\\uDEC9\\uDF00-\\uDF19\\uDF1D-\\uDF2B\\uDF30-\\uDF39]|\\uD806[\\uDCA0-\\uDCE9\\uDCFF\\uDE00-\\uDE3E\\uDE47\\uDE50-\\uDE83\\uDE86-\\uDE99\\uDEC0-\\uDEF8]|\\uD807[\\uDC00-\\uDC08\\uDC0A-\\uDC36\\uDC38-\\uDC40\\uDC50-\\uDC59\\uDC72-\\uDC8F\\uDC92-\\uDCA7\\uDCA9-\\uDCB6\\uDD00-\\uDD06\\uDD08\\uDD09\\uDD0B-\\uDD36\\uDD3A\\uDD3C\\uDD3D\\uDD3F-\\uDD47\\uDD50-\\uDD59]|\\uD808[\\uDC00-\\uDF99]|\\uD809[\\uDC00-\\uDC6E\\uDC80-\\uDD43]|[\\uD80C\\uD81C-\\uD820\\uD840-\\uD868\\uD86A-\\uD86C\\uD86F-\\uD872\\uD874-\\uD879][\\uDC00-\\uDFFF]|\\uD80D[\\uDC00-\\uDC2E]|\\uD811[\\uDC00-\\uDE46]|\\uD81A[\\uDC00-\\uDE38\\uDE40-\\uDE5E\\uDE60-\\uDE69\\uDED0-\\uDEED\\uDEF0-\\uDEF4\\uDF00-\\uDF36\\uDF40-\\uDF43\\uDF50-\\uDF59\\uDF63-\\uDF77\\uDF7D-\\uDF8F]|\\uD81B[\\uDF00-\\uDF44\\uDF50-\\uDF7E\\uDF8F-\\uDF9F\\uDFE0\\uDFE1]|\\uD821[\\uDC00-\\uDFEC]|\\uD822[\\uDC00-\\uDEF2]|\\uD82C[\\uDC00-\\uDD1E\\uDD70-\\uDEFB]|\\uD82F[\\uDC00-\\uDC6A\\uDC70-\\uDC7C\\uDC80-\\uDC88\\uDC90-\\uDC99\\uDC9D\\uDC9E]|\\uD834[\\uDD65-\\uDD69\\uDD6D-\\uDD72\\uDD7B-\\uDD82\\uDD85-\\uDD8B\\uDDAA-\\uDDAD\\uDE42-\\uDE44]|\\uD835[\\uDC00-\\uDC54\\uDC56-\\uDC9C\\uDC9E\\uDC9F\\uDCA2\\uDCA5\\uDCA6\\uDCA9-\\uDCAC\\uDCAE-\\uDCB9\\uDCBB\\uDCBD-\\uDCC3\\uDCC5-\\uDD05\\uDD07-\\uDD0A\\uDD0D-\\uDD14\\uDD16-\\uDD1C\\uDD1E-\\uDD39\\uDD3B-\\uDD3E\\uDD40-\\uDD44\\uDD46\\uDD4A-\\uDD50\\uDD52-\\uDEA5\\uDEA8-\\uDEC0\\uDEC2-\\uDEDA\\uDEDC-\\uDEFA\\uDEFC-\\uDF14\\uDF16-\\uDF34\\uDF36-\\uDF4E\\uDF50-\\uDF6E\\uDF70-\\uDF88\\uDF8A-\\uDFA8\\uDFAA-\\uDFC2\\uDFC4-\\uDFCB\\uDFCE-\\uDFFF]|\\uD836[\\uDE00-\\uDE36\\uDE3B-\\uDE6C\\uDE75\\uDE84\\uDE9B-\\uDE9F\\uDEA1-\\uDEAF]|\\uD838[\\uDC00-\\uDC06\\uDC08-\\uDC18\\uDC1B-\\uDC21\\uDC23\\uDC24\\uDC26-\\uDC2A]|\\uD83A[\\uDC00-\\uDCC4\\uDCD0-\\uDCD6\\uDD00-\\uDD4A\\uDD50-\\uDD59]|\\uD83B[\\uDE00-\\uDE03\\uDE05-\\uDE1F\\uDE21\\uDE22\\uDE24\\uDE27\\uDE29-\\uDE32\\uDE34-\\uDE37\\uDE39\\uDE3B\\uDE42\\uDE47\\uDE49\\uDE4B\\uDE4D-\\uDE4F\\uDE51\\uDE52\\uDE54\\uDE57\\uDE59\\uDE5B\\uDE5D\\uDE5F\\uDE61\\uDE62\\uDE64\\uDE67-\\uDE6A\\uDE6C-\\uDE72\\uDE74-\\uDE77\\uDE79-\\uDE7C\\uDE7E\\uDE80-\\uDE89\\uDE8B-\\uDE9B\\uDEA1-\\uDEA3\\uDEA5-\\uDEA9\\uDEAB-\\uDEBB]|\\uD869[\\uDC00-\\uDED6\\uDF00-\\uDFFF]|\\uD86D[\\uDC00-\\uDF34\\uDF40-\\uDFFF]|\\uD86E[\\uDC00-\\uDC1D\\uDC20-\\uDFFF]|\\uD873[\\uDC00-\\uDEA1\\uDEB0-\\uDFFF]|\\uD87A[\\uDC00-\\uDFE0]|\\uD87E[\\uDC00-\\uDE1D]|\\uDB40[\\uDD00-\\uDDEF]/;var unicode={Space_Separator:Space_Separator,ID_Start:ID_Start,ID_Continue:ID_Continue};var util={isSpaceSeparator:function isSpaceSeparator(c){return typeof c==="string"&&unicode.Space_Separator.test(c)},isIdStartChar:function isIdStartChar(c){return typeof c==="string"&&(c>="a"&&c<="z"||c>="A"&&c<="Z"||c==="$"||c==="_"||unicode.ID_Start.test(c))},isIdContinueChar:function isIdContinueChar(c){return typeof c==="string"&&(c>="a"&&c<="z"||c>="A"&&c<="Z"||c>="0"&&c<="9"||c==="$"||c==="_"||c==="‌"||c==="‍"||unicode.ID_Continue.test(c))},isDigit:function isDigit(c){return typeof c==="string"&&/[0-9]/.test(c)},isHexDigit:function isHexDigit(c){return typeof c==="string"&&/[0-9A-Fa-f]/.test(c)}};var source;var parseState;var stack;var pos;var line;var column;var token;var key;var root;var parse=function parse(text,reviver){source=String(text);parseState="start";stack=[];pos=0;line=1;column=0;token=undefined;key=undefined;root=undefined;do{token=lex();parseStates[parseState]()}while(token.type!=="eof");if(typeof reviver==="function"){return internalize({"":root},"",reviver)}return root};function internalize(holder,name,reviver){var value=holder[name];if(value!=null&&typeof value==="object"){if(Array.isArray(value)){for(var i=0;i0){var c=peek();if(!util.isHexDigit(c)){throw invalidChar(read())}buffer+=read()}return String.fromCodePoint(parseInt(buffer,16))}var parseStates={start:function start(){if(token.type==="eof"){throw invalidEOF()}push()},beforePropertyName:function beforePropertyName(){switch(token.type){case"identifier":case"string":key=token.value;parseState="afterPropertyName";return;case"punctuator":pop();return;case"eof":throw invalidEOF()}},afterPropertyName:function afterPropertyName(){if(token.type==="eof"){throw invalidEOF()}parseState="beforePropertyValue"},beforePropertyValue:function beforePropertyValue(){if(token.type==="eof"){throw invalidEOF()}push()},beforeArrayValue:function beforeArrayValue(){if(token.type==="eof"){throw invalidEOF()}if(token.type==="punctuator"&&token.value==="]"){pop();return}push()},afterPropertyValue:function afterPropertyValue(){if(token.type==="eof"){throw invalidEOF()}switch(token.value){case",":parseState="beforePropertyName";return;case"}":pop()}},afterArrayValue:function afterArrayValue(){if(token.type==="eof"){throw invalidEOF()}switch(token.value){case",":parseState="beforeArrayValue";return;case"]":pop()}},end:function end(){}};function push(){var value;switch(token.type){case"punctuator":switch(token.value){case"{":value={};break;case"[":value=[];break}break;case"null":case"boolean":case"numeric":case"string":value=token.value;break}if(root===undefined){root=value}else{var parent=stack[stack.length-1];if(Array.isArray(parent)){parent.push(value)}else{Object.defineProperty(parent,key,{value:value,writable:true,enumerable:true,configurable:true})}}if(value!==null&&typeof value==="object"){stack.push(value);if(Array.isArray(value)){parseState="beforeArrayValue"}else{parseState="beforePropertyName"}}else{var current=stack[stack.length-1];if(current==null){parseState="end"}else if(Array.isArray(current)){parseState="afterArrayValue"}else{parseState="afterPropertyValue"}}}function pop(){stack.pop();var current=stack[stack.length-1];if(current==null){parseState="end"}else if(Array.isArray(current)){parseState="afterArrayValue"}else{parseState="afterPropertyValue"}}function invalidChar(c){if(c===undefined){return syntaxError("JSON5: invalid end of input at "+line+":"+column)}return syntaxError("JSON5: invalid character \'"+formatChar(c)+"\' at "+line+":"+column)}function invalidEOF(){return syntaxError("JSON5: invalid end of input at "+line+":"+column)}function invalidIdentifier(){column-=5;return syntaxError("JSON5: invalid identifier character at "+line+":"+column)}function separatorChar(c){console.warn("JSON5: \'"+formatChar(c)+"\' in strings is not valid ECMAScript; consider escaping")}function formatChar(c){var replacements={"\'":"\\\\\'",\'"\':\'\\\\"\',"\\\\":"\\\\\\\\","\\b":"\\\\b","\\f":"\\\\f","\\n":"\\\\n","\\r":"\\\\r","\\t":"\\\\t","\\v":"\\\\v","\\0":"\\\\0","\\u2028":"\\\\u2028","\\u2029":"\\\\u2029"};if(replacements[c]){return replacements[c]}if(c<" "){var hexString=c.charCodeAt(0).toString(16);return"\\\\x"+("00"+hexString).substring(hexString.length)}return c}function syntaxError(message){var err=new SyntaxError(message);err.lineNumber=line;err.columnNumber=column;return err}var stringify=function stringify(value,replacer,space){var stack=[];var indent="";var propertyList;var replacerFunc;var gap="";var quote;if(replacer!=null&&typeof replacer==="object"&&!Array.isArray(replacer)){space=replacer.space;quote=replacer.quote;replacer=replacer.replacer}if(typeof replacer==="function"){replacerFunc=replacer}else if(Array.isArray(replacer)){propertyList=[];for(var i=0,list=replacer;i0){space=Math.min(10,Math.floor(space));gap=" ".substr(0,space)}}else if(typeof space==="string"){gap=space.substr(0,10)}return serializeProperty("",{"":value});function serializeProperty(key,holder){var value=holder[key];if(value!=null){if(typeof value.toJSON5==="function"){value=value.toJSON5(key)}else if(typeof value.toJSON==="function"){value=value.toJSON(key)}}if(replacerFunc){value=replacerFunc.call(holder,key,value)}if(value instanceof Number){value=Number(value)}else if(value instanceof String){value=String(value)}else if(value instanceof Boolean){value=value.valueOf()}switch(value){case null:return"null";case true:return"true";case false:return"false"}if(typeof value==="string"){return quoteString(value,false)}if(typeof value==="number"){return String(value)}if(typeof value==="object"){return Array.isArray(value)?serializeArray(value):serializeObject(value)}return undefined}function quoteString(value){var quotes={"\'":.1,\'"\':.2};var replacements={"\'":"\\\\\'",\'"\':\'\\\\"\',"\\\\":"\\\\\\\\","\\b":"\\\\b","\\f":"\\\\f","\\n":"\\\\n","\\r":"\\\\r","\\t":"\\\\t","\\v":"\\\\v","\\0":"\\\\0","\\u2028":"\\\\u2028","\\u2029":"\\\\u2029"};var product="";for(var i=0;i=0){throw TypeError("Converting circular structure to JSON5")}stack.push(value);var stepback=indent;indent=indent+gap;var keys=propertyList||Object.keys(value);var partial=[];for(var i=0,list=keys;i=0){throw TypeError("Converting circular structure to JSON5")}stack.push(value);var stepback=indent;indent=indent+gap;var partial=[];for(var i=0;i{e.exports='(function(root,factory){if(typeof exports==="object"){module.exports=exports=factory()}else if(typeof define==="function"&&define.amd){define([],factory)}else{globalThis.CryptoJS=factory()}})(this,function(){var CryptoJS=CryptoJS||function(Math,undefined){var crypto;if(typeof window!=="undefined"&&window.crypto){crypto=window.crypto}if(typeof self!=="undefined"&&self.crypto){crypto=self.crypto}if(typeof globalThis!=="undefined"&&globalThis.crypto){crypto=globalThis.crypto}if(!crypto&&typeof window!=="undefined"&&window.msCrypto){crypto=window.msCrypto}if(!crypto&&typeof global!=="undefined"&&global.crypto){crypto=global.crypto}if(!crypto&&typeof require==="function"){try{crypto=require("crypto")}catch(err){}}var cryptoSecureRandomInt=function(){if(crypto){if(typeof crypto.getRandomValues==="function"){try{return crypto.getRandomValues(new Uint32Array(1))[0]}catch(err){}}if(typeof crypto.randomBytes==="function"){try{return crypto.randomBytes(4).readInt32LE()}catch(err){}}}throw new Error("Native crypto module could not be used to get secure random number.")};var create=Object.create||function(){function F(){}return function(obj){var subtype;F.prototype=obj;subtype=new F;F.prototype=null;return subtype}}();var C={};var C_lib=C.lib={};var Base=C_lib.Base=function(){return{extend:function(overrides){var subtype=create(this);if(overrides){subtype.mixIn(overrides)}if(!subtype.hasOwnProperty("init")||this.init===subtype.init){subtype.init=function(){subtype.$super.init.apply(this,arguments)}}subtype.init.prototype=subtype;subtype.$super=this;return subtype},create:function(){var instance=this.extend();instance.init.apply(instance,arguments);return instance},init:function(){},mixIn:function(properties){for(var propertyName in properties){if(properties.hasOwnProperty(propertyName)){this[propertyName]=properties[propertyName]}}if(properties.hasOwnProperty("toString")){this.toString=properties.toString}},clone:function(){return this.init.prototype.extend(this)}}}();var WordArray=C_lib.WordArray=Base.extend({init:function(words,sigBytes){words=this.words=words||[];if(sigBytes!=undefined){this.sigBytes=sigBytes}else{this.sigBytes=words.length*4}},toString:function(encoder){return(encoder||Hex).stringify(this)},concat:function(wordArray){var thisWords=this.words;var thatWords=wordArray.words;var thisSigBytes=this.sigBytes;var thatSigBytes=wordArray.sigBytes;this.clamp();if(thisSigBytes%4){for(var i=0;i>>2]>>>24-i%4*8&255;thisWords[thisSigBytes+i>>>2]|=thatByte<<24-(thisSigBytes+i)%4*8}}else{for(var j=0;j>>2]=thatWords[j>>>2]}}this.sigBytes+=thatSigBytes;return this},clamp:function(){var words=this.words;var sigBytes=this.sigBytes;words[sigBytes>>>2]&=4294967295<<32-sigBytes%4*8;words.length=Math.ceil(sigBytes/4)},clone:function(){var clone=Base.clone.call(this);clone.words=this.words.slice(0);return clone},random:function(nBytes){var words=[];for(var i=0;i>>2]>>>24-i%4*8&255;hexChars.push((bite>>>4).toString(16));hexChars.push((bite&15).toString(16))}return hexChars.join("")},parse:function(hexStr){var hexStrLength=hexStr.length;var words=[];for(var i=0;i>>3]|=parseInt(hexStr.substr(i,2),16)<<24-i%8*4}return new WordArray.init(words,hexStrLength/2)}};var Latin1=C_enc.Latin1={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var latin1Chars=[];for(var i=0;i>>2]>>>24-i%4*8&255;latin1Chars.push(String.fromCharCode(bite))}return latin1Chars.join("")},parse:function(latin1Str){var latin1StrLength=latin1Str.length;var words=[];for(var i=0;i>>2]|=(latin1Str.charCodeAt(i)&255)<<24-i%4*8}return new WordArray.init(words,latin1StrLength)}};var Utf8=C_enc.Utf8={stringify:function(wordArray){try{return decodeURIComponent(escape(Latin1.stringify(wordArray)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(utf8Str){return Latin1.parse(unescape(encodeURIComponent(utf8Str)))}};var BufferedBlockAlgorithm=C_lib.BufferedBlockAlgorithm=Base.extend({reset:function(){this._data=new WordArray.init;this._nDataBytes=0},_append:function(data){if(typeof data=="string"){data=Utf8.parse(data)}this._data.concat(data);this._nDataBytes+=data.sigBytes},_process:function(doFlush){var processedWords;var data=this._data;var dataWords=data.words;var dataSigBytes=data.sigBytes;var blockSize=this.blockSize;var blockSizeBytes=blockSize*4;var nBlocksReady=dataSigBytes/blockSizeBytes;if(doFlush){nBlocksReady=Math.ceil(nBlocksReady)}else{nBlocksReady=Math.max((nBlocksReady|0)-this._minBufferSize,0)}var nWordsReady=nBlocksReady*blockSize;var nBytesReady=Math.min(nWordsReady*4,dataSigBytes);if(nWordsReady){for(var offset=0;offset>>2]|=typedArray[i]<<24-i%4*8}superInit.call(this,words,typedArrayByteLength)}else{superInit.apply(this,arguments)}};subInit.prototype=WordArray})();(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var C_enc=C.enc;var Utf16BE=C_enc.Utf16=C_enc.Utf16BE={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var utf16Chars=[];for(var i=0;i>>2]>>>16-i%4*8&65535;utf16Chars.push(String.fromCharCode(codePoint))}return utf16Chars.join("")},parse:function(utf16Str){var utf16StrLength=utf16Str.length;var words=[];for(var i=0;i>>1]|=utf16Str.charCodeAt(i)<<16-i%2*16}return WordArray.create(words,utf16StrLength*2)}};C_enc.Utf16LE={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var utf16Chars=[];for(var i=0;i>>2]>>>16-i%4*8&65535);utf16Chars.push(String.fromCharCode(codePoint))}return utf16Chars.join("")},parse:function(utf16Str){var utf16StrLength=utf16Str.length;var words=[];for(var i=0;i>>1]|=swapEndian(utf16Str.charCodeAt(i)<<16-i%2*16)}return WordArray.create(words,utf16StrLength*2)}};function swapEndian(word){return word<<8&4278255360|word>>>8&16711935}})();(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var C_enc=C.enc;var Base64=C_enc.Base64={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var map=this._map;wordArray.clamp();var base64Chars=[];for(var i=0;i>>2]>>>24-i%4*8&255;var byte2=words[i+1>>>2]>>>24-(i+1)%4*8&255;var byte3=words[i+2>>>2]>>>24-(i+2)%4*8&255;var triplet=byte1<<16|byte2<<8|byte3;for(var j=0;j<4&&i+j*.75>>6*(3-j)&63))}}var paddingChar=map.charAt(64);if(paddingChar){while(base64Chars.length%4){base64Chars.push(paddingChar)}}return base64Chars.join("")},parse:function(base64Str){var base64StrLength=base64Str.length;var map=this._map;var reverseMap=this._reverseMap;if(!reverseMap){reverseMap=this._reverseMap=[];for(var j=0;j>>6-i%4*2;var bitsCombined=bits1|bits2;words[nBytes>>>2]|=bitsCombined<<24-nBytes%4*8;nBytes++}}return WordArray.create(words,nBytes)}})();(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var C_enc=C.enc;var Base64url=C_enc.Base64url={stringify:function(wordArray,urlSafe=true){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var map=urlSafe?this._safe_map:this._map;wordArray.clamp();var base64Chars=[];for(var i=0;i>>2]>>>24-i%4*8&255;var byte2=words[i+1>>>2]>>>24-(i+1)%4*8&255;var byte3=words[i+2>>>2]>>>24-(i+2)%4*8&255;var triplet=byte1<<16|byte2<<8|byte3;for(var j=0;j<4&&i+j*.75>>6*(3-j)&63))}}var paddingChar=map.charAt(64);if(paddingChar){while(base64Chars.length%4){base64Chars.push(paddingChar)}}return base64Chars.join("")},parse:function(base64Str,urlSafe=true){var base64StrLength=base64Str.length;var map=urlSafe?this._safe_map:this._map;var reverseMap=this._reverseMap;if(!reverseMap){reverseMap=this._reverseMap=[];for(var j=0;j>>6-i%4*2;var bitsCombined=bits1|bits2;words[nBytes>>>2]|=bitsCombined<<24-nBytes%4*8;nBytes++}}return WordArray.create(words,nBytes)}})();(function(Math){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var Hasher=C_lib.Hasher;var C_algo=C.algo;var T=[];(function(){for(var i=0;i<64;i++){T[i]=Math.abs(Math.sin(i+1))*4294967296|0}})();var MD5=C_algo.MD5=Hasher.extend({_doReset:function(){this._hash=new WordArray.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(M,offset){for(var i=0;i<16;i++){var offset_i=offset+i;var M_offset_i=M[offset_i];M[offset_i]=(M_offset_i<<8|M_offset_i>>>24)&16711935|(M_offset_i<<24|M_offset_i>>>8)&4278255360}var H=this._hash.words;var M_offset_0=M[offset+0];var M_offset_1=M[offset+1];var M_offset_2=M[offset+2];var M_offset_3=M[offset+3];var M_offset_4=M[offset+4];var M_offset_5=M[offset+5];var M_offset_6=M[offset+6];var M_offset_7=M[offset+7];var M_offset_8=M[offset+8];var M_offset_9=M[offset+9];var M_offset_10=M[offset+10];var M_offset_11=M[offset+11];var M_offset_12=M[offset+12];var M_offset_13=M[offset+13];var M_offset_14=M[offset+14];var M_offset_15=M[offset+15];var a=H[0];var b=H[1];var c=H[2];var d=H[3];a=FF(a,b,c,d,M_offset_0,7,T[0]);d=FF(d,a,b,c,M_offset_1,12,T[1]);c=FF(c,d,a,b,M_offset_2,17,T[2]);b=FF(b,c,d,a,M_offset_3,22,T[3]);a=FF(a,b,c,d,M_offset_4,7,T[4]);d=FF(d,a,b,c,M_offset_5,12,T[5]);c=FF(c,d,a,b,M_offset_6,17,T[6]);b=FF(b,c,d,a,M_offset_7,22,T[7]);a=FF(a,b,c,d,M_offset_8,7,T[8]);d=FF(d,a,b,c,M_offset_9,12,T[9]);c=FF(c,d,a,b,M_offset_10,17,T[10]);b=FF(b,c,d,a,M_offset_11,22,T[11]);a=FF(a,b,c,d,M_offset_12,7,T[12]);d=FF(d,a,b,c,M_offset_13,12,T[13]);c=FF(c,d,a,b,M_offset_14,17,T[14]);b=FF(b,c,d,a,M_offset_15,22,T[15]);a=GG(a,b,c,d,M_offset_1,5,T[16]);d=GG(d,a,b,c,M_offset_6,9,T[17]);c=GG(c,d,a,b,M_offset_11,14,T[18]);b=GG(b,c,d,a,M_offset_0,20,T[19]);a=GG(a,b,c,d,M_offset_5,5,T[20]);d=GG(d,a,b,c,M_offset_10,9,T[21]);c=GG(c,d,a,b,M_offset_15,14,T[22]);b=GG(b,c,d,a,M_offset_4,20,T[23]);a=GG(a,b,c,d,M_offset_9,5,T[24]);d=GG(d,a,b,c,M_offset_14,9,T[25]);c=GG(c,d,a,b,M_offset_3,14,T[26]);b=GG(b,c,d,a,M_offset_8,20,T[27]);a=GG(a,b,c,d,M_offset_13,5,T[28]);d=GG(d,a,b,c,M_offset_2,9,T[29]);c=GG(c,d,a,b,M_offset_7,14,T[30]);b=GG(b,c,d,a,M_offset_12,20,T[31]);a=HH(a,b,c,d,M_offset_5,4,T[32]);d=HH(d,a,b,c,M_offset_8,11,T[33]);c=HH(c,d,a,b,M_offset_11,16,T[34]);b=HH(b,c,d,a,M_offset_14,23,T[35]);a=HH(a,b,c,d,M_offset_1,4,T[36]);d=HH(d,a,b,c,M_offset_4,11,T[37]);c=HH(c,d,a,b,M_offset_7,16,T[38]);b=HH(b,c,d,a,M_offset_10,23,T[39]);a=HH(a,b,c,d,M_offset_13,4,T[40]);d=HH(d,a,b,c,M_offset_0,11,T[41]);c=HH(c,d,a,b,M_offset_3,16,T[42]);b=HH(b,c,d,a,M_offset_6,23,T[43]);a=HH(a,b,c,d,M_offset_9,4,T[44]);d=HH(d,a,b,c,M_offset_12,11,T[45]);c=HH(c,d,a,b,M_offset_15,16,T[46]);b=HH(b,c,d,a,M_offset_2,23,T[47]);a=II(a,b,c,d,M_offset_0,6,T[48]);d=II(d,a,b,c,M_offset_7,10,T[49]);c=II(c,d,a,b,M_offset_14,15,T[50]);b=II(b,c,d,a,M_offset_5,21,T[51]);a=II(a,b,c,d,M_offset_12,6,T[52]);d=II(d,a,b,c,M_offset_3,10,T[53]);c=II(c,d,a,b,M_offset_10,15,T[54]);b=II(b,c,d,a,M_offset_1,21,T[55]);a=II(a,b,c,d,M_offset_8,6,T[56]);d=II(d,a,b,c,M_offset_15,10,T[57]);c=II(c,d,a,b,M_offset_6,15,T[58]);b=II(b,c,d,a,M_offset_13,21,T[59]);a=II(a,b,c,d,M_offset_4,6,T[60]);d=II(d,a,b,c,M_offset_11,10,T[61]);c=II(c,d,a,b,M_offset_2,15,T[62]);b=II(b,c,d,a,M_offset_9,21,T[63]);H[0]=H[0]+a|0;H[1]=H[1]+b|0;H[2]=H[2]+c|0;H[3]=H[3]+d|0},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;dataWords[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;var nBitsTotalH=Math.floor(nBitsTotal/4294967296);var nBitsTotalL=nBitsTotal;dataWords[(nBitsLeft+64>>>9<<4)+15]=(nBitsTotalH<<8|nBitsTotalH>>>24)&16711935|(nBitsTotalH<<24|nBitsTotalH>>>8)&4278255360;dataWords[(nBitsLeft+64>>>9<<4)+14]=(nBitsTotalL<<8|nBitsTotalL>>>24)&16711935|(nBitsTotalL<<24|nBitsTotalL>>>8)&4278255360;data.sigBytes=(dataWords.length+1)*4;this._process();var hash=this._hash;var H=hash.words;for(var i=0;i<4;i++){var H_i=H[i];H[i]=(H_i<<8|H_i>>>24)&16711935|(H_i<<24|H_i>>>8)&4278255360}return hash},clone:function(){var clone=Hasher.clone.call(this);clone._hash=this._hash.clone();return clone}});function FF(a,b,c,d,x,s,t){var n=a+(b&c|~b&d)+x+t;return(n<>>32-s)+b}function GG(a,b,c,d,x,s,t){var n=a+(b&d|c&~d)+x+t;return(n<>>32-s)+b}function HH(a,b,c,d,x,s,t){var n=a+(b^c^d)+x+t;return(n<>>32-s)+b}function II(a,b,c,d,x,s,t){var n=a+(c^(b|~d))+x+t;return(n<>>32-s)+b}C.MD5=Hasher._createHelper(MD5);C.HmacMD5=Hasher._createHmacHelper(MD5)})(Math);(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var Hasher=C_lib.Hasher;var C_algo=C.algo;var W=[];var SHA1=C_algo.SHA1=Hasher.extend({_doReset:function(){this._hash=new WordArray.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(M,offset){var H=this._hash.words;var a=H[0];var b=H[1];var c=H[2];var d=H[3];var e=H[4];for(var i=0;i<80;i++){if(i<16){W[i]=M[offset+i]|0}else{var n=W[i-3]^W[i-8]^W[i-14]^W[i-16];W[i]=n<<1|n>>>31}var t=(a<<5|a>>>27)+e+W[i];if(i<20){t+=(b&c|~b&d)+1518500249}else if(i<40){t+=(b^c^d)+1859775393}else if(i<60){t+=(b&c|b&d|c&d)-1894007588}else{t+=(b^c^d)-899497514}e=d;d=c;c=b<<30|b>>>2;b=a;a=t}H[0]=H[0]+a|0;H[1]=H[1]+b|0;H[2]=H[2]+c|0;H[3]=H[3]+d|0;H[4]=H[4]+e|0},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;dataWords[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;dataWords[(nBitsLeft+64>>>9<<4)+14]=Math.floor(nBitsTotal/4294967296);dataWords[(nBitsLeft+64>>>9<<4)+15]=nBitsTotal;data.sigBytes=dataWords.length*4;this._process();return this._hash},clone:function(){var clone=Hasher.clone.call(this);clone._hash=this._hash.clone();return clone}});C.SHA1=Hasher._createHelper(SHA1);C.HmacSHA1=Hasher._createHmacHelper(SHA1)})();(function(Math){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var Hasher=C_lib.Hasher;var C_algo=C.algo;var H=[];var K=[];(function(){function isPrime(n){var sqrtN=Math.sqrt(n);for(var factor=2;factor<=sqrtN;factor++){if(!(n%factor)){return false}}return true}function getFractionalBits(n){return(n-(n|0))*4294967296|0}var n=2;var nPrime=0;while(nPrime<64){if(isPrime(n)){if(nPrime<8){H[nPrime]=getFractionalBits(Math.pow(n,1/2))}K[nPrime]=getFractionalBits(Math.pow(n,1/3));nPrime++}n++}})();var W=[];var SHA256=C_algo.SHA256=Hasher.extend({_doReset:function(){this._hash=new WordArray.init(H.slice(0))},_doProcessBlock:function(M,offset){var H=this._hash.words;var a=H[0];var b=H[1];var c=H[2];var d=H[3];var e=H[4];var f=H[5];var g=H[6];var h=H[7];for(var i=0;i<64;i++){if(i<16){W[i]=M[offset+i]|0}else{var gamma0x=W[i-15];var gamma0=(gamma0x<<25|gamma0x>>>7)^(gamma0x<<14|gamma0x>>>18)^gamma0x>>>3;var gamma1x=W[i-2];var gamma1=(gamma1x<<15|gamma1x>>>17)^(gamma1x<<13|gamma1x>>>19)^gamma1x>>>10;W[i]=gamma0+W[i-7]+gamma1+W[i-16]}var ch=e&f^~e&g;var maj=a&b^a&c^b&c;var sigma0=(a<<30|a>>>2)^(a<<19|a>>>13)^(a<<10|a>>>22);var sigma1=(e<<26|e>>>6)^(e<<21|e>>>11)^(e<<7|e>>>25);var t1=h+sigma1+ch+K[i]+W[i];var t2=sigma0+maj;h=g;g=f;f=e;e=d+t1|0;d=c;c=b;b=a;a=t1+t2|0}H[0]=H[0]+a|0;H[1]=H[1]+b|0;H[2]=H[2]+c|0;H[3]=H[3]+d|0;H[4]=H[4]+e|0;H[5]=H[5]+f|0;H[6]=H[6]+g|0;H[7]=H[7]+h|0},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;dataWords[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;dataWords[(nBitsLeft+64>>>9<<4)+14]=Math.floor(nBitsTotal/4294967296);dataWords[(nBitsLeft+64>>>9<<4)+15]=nBitsTotal;data.sigBytes=dataWords.length*4;this._process();return this._hash},clone:function(){var clone=Hasher.clone.call(this);clone._hash=this._hash.clone();return clone}});C.SHA256=Hasher._createHelper(SHA256);C.HmacSHA256=Hasher._createHmacHelper(SHA256)})(Math);(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var C_algo=C.algo;var SHA256=C_algo.SHA256;var SHA224=C_algo.SHA224=SHA256.extend({_doReset:function(){this._hash=new WordArray.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var hash=SHA256._doFinalize.call(this);hash.sigBytes-=4;return hash}});C.SHA224=SHA256._createHelper(SHA224);C.HmacSHA224=SHA256._createHmacHelper(SHA224)})();(function(){var C=CryptoJS;var C_lib=C.lib;var Hasher=C_lib.Hasher;var C_x64=C.x64;var X64Word=C_x64.Word;var X64WordArray=C_x64.WordArray;var C_algo=C.algo;function X64Word_create(){return X64Word.create.apply(X64Word,arguments)}var K=[X64Word_create(1116352408,3609767458),X64Word_create(1899447441,602891725),X64Word_create(3049323471,3964484399),X64Word_create(3921009573,2173295548),X64Word_create(961987163,4081628472),X64Word_create(1508970993,3053834265),X64Word_create(2453635748,2937671579),X64Word_create(2870763221,3664609560),X64Word_create(3624381080,2734883394),X64Word_create(310598401,1164996542),X64Word_create(607225278,1323610764),X64Word_create(1426881987,3590304994),X64Word_create(1925078388,4068182383),X64Word_create(2162078206,991336113),X64Word_create(2614888103,633803317),X64Word_create(3248222580,3479774868),X64Word_create(3835390401,2666613458),X64Word_create(4022224774,944711139),X64Word_create(264347078,2341262773),X64Word_create(604807628,2007800933),X64Word_create(770255983,1495990901),X64Word_create(1249150122,1856431235),X64Word_create(1555081692,3175218132),X64Word_create(1996064986,2198950837),X64Word_create(2554220882,3999719339),X64Word_create(2821834349,766784016),X64Word_create(2952996808,2566594879),X64Word_create(3210313671,3203337956),X64Word_create(3336571891,1034457026),X64Word_create(3584528711,2466948901),X64Word_create(113926993,3758326383),X64Word_create(338241895,168717936),X64Word_create(666307205,1188179964),X64Word_create(773529912,1546045734),X64Word_create(1294757372,1522805485),X64Word_create(1396182291,2643833823),X64Word_create(1695183700,2343527390),X64Word_create(1986661051,1014477480),X64Word_create(2177026350,1206759142),X64Word_create(2456956037,344077627),X64Word_create(2730485921,1290863460),X64Word_create(2820302411,3158454273),X64Word_create(3259730800,3505952657),X64Word_create(3345764771,106217008),X64Word_create(3516065817,3606008344),X64Word_create(3600352804,1432725776),X64Word_create(4094571909,1467031594),X64Word_create(275423344,851169720),X64Word_create(430227734,3100823752),X64Word_create(506948616,1363258195),X64Word_create(659060556,3750685593),X64Word_create(883997877,3785050280),X64Word_create(958139571,3318307427),X64Word_create(1322822218,3812723403),X64Word_create(1537002063,2003034995),X64Word_create(1747873779,3602036899),X64Word_create(1955562222,1575990012),X64Word_create(2024104815,1125592928),X64Word_create(2227730452,2716904306),X64Word_create(2361852424,442776044),X64Word_create(2428436474,593698344),X64Word_create(2756734187,3733110249),X64Word_create(3204031479,2999351573),X64Word_create(3329325298,3815920427),X64Word_create(3391569614,3928383900),X64Word_create(3515267271,566280711),X64Word_create(3940187606,3454069534),X64Word_create(4118630271,4000239992),X64Word_create(116418474,1914138554),X64Word_create(174292421,2731055270),X64Word_create(289380356,3203993006),X64Word_create(460393269,320620315),X64Word_create(685471733,587496836),X64Word_create(852142971,1086792851),X64Word_create(1017036298,365543100),X64Word_create(1126000580,2618297676),X64Word_create(1288033470,3409855158),X64Word_create(1501505948,4234509866),X64Word_create(1607167915,987167468),X64Word_create(1816402316,1246189591)];var W=[];(function(){for(var i=0;i<80;i++){W[i]=X64Word_create()}})();var SHA512=C_algo.SHA512=Hasher.extend({_doReset:function(){this._hash=new X64WordArray.init([new X64Word.init(1779033703,4089235720),new X64Word.init(3144134277,2227873595),new X64Word.init(1013904242,4271175723),new X64Word.init(2773480762,1595750129),new X64Word.init(1359893119,2917565137),new X64Word.init(2600822924,725511199),new X64Word.init(528734635,4215389547),new X64Word.init(1541459225,327033209)])},_doProcessBlock:function(M,offset){var H=this._hash.words;var H0=H[0];var H1=H[1];var H2=H[2];var H3=H[3];var H4=H[4];var H5=H[5];var H6=H[6];var H7=H[7];var H0h=H0.high;var H0l=H0.low;var H1h=H1.high;var H1l=H1.low;var H2h=H2.high;var H2l=H2.low;var H3h=H3.high;var H3l=H3.low;var H4h=H4.high;var H4l=H4.low;var H5h=H5.high;var H5l=H5.low;var H6h=H6.high;var H6l=H6.low;var H7h=H7.high;var H7l=H7.low;var ah=H0h;var al=H0l;var bh=H1h;var bl=H1l;var ch=H2h;var cl=H2l;var dh=H3h;var dl=H3l;var eh=H4h;var el=H4l;var fh=H5h;var fl=H5l;var gh=H6h;var gl=H6l;var hh=H7h;var hl=H7l;for(var i=0;i<80;i++){var Wil;var Wih;var Wi=W[i];if(i<16){Wih=Wi.high=M[offset+i*2]|0;Wil=Wi.low=M[offset+i*2+1]|0}else{var gamma0x=W[i-15];var gamma0xh=gamma0x.high;var gamma0xl=gamma0x.low;var gamma0h=(gamma0xh>>>1|gamma0xl<<31)^(gamma0xh>>>8|gamma0xl<<24)^gamma0xh>>>7;var gamma0l=(gamma0xl>>>1|gamma0xh<<31)^(gamma0xl>>>8|gamma0xh<<24)^(gamma0xl>>>7|gamma0xh<<25);var gamma1x=W[i-2];var gamma1xh=gamma1x.high;var gamma1xl=gamma1x.low;var gamma1h=(gamma1xh>>>19|gamma1xl<<13)^(gamma1xh<<3|gamma1xl>>>29)^gamma1xh>>>6;var gamma1l=(gamma1xl>>>19|gamma1xh<<13)^(gamma1xl<<3|gamma1xh>>>29)^(gamma1xl>>>6|gamma1xh<<26);var Wi7=W[i-7];var Wi7h=Wi7.high;var Wi7l=Wi7.low;var Wi16=W[i-16];var Wi16h=Wi16.high;var Wi16l=Wi16.low;Wil=gamma0l+Wi7l;Wih=gamma0h+Wi7h+(Wil>>>0>>0?1:0);Wil=Wil+gamma1l;Wih=Wih+gamma1h+(Wil>>>0>>0?1:0);Wil=Wil+Wi16l;Wih=Wih+Wi16h+(Wil>>>0>>0?1:0);Wi.high=Wih;Wi.low=Wil}var chh=eh&fh^~eh&gh;var chl=el&fl^~el≷var majh=ah&bh^ah&ch^bh&ch;var majl=al&bl^al&cl^bl&cl;var sigma0h=(ah>>>28|al<<4)^(ah<<30|al>>>2)^(ah<<25|al>>>7);var sigma0l=(al>>>28|ah<<4)^(al<<30|ah>>>2)^(al<<25|ah>>>7);var sigma1h=(eh>>>14|el<<18)^(eh>>>18|el<<14)^(eh<<23|el>>>9);var sigma1l=(el>>>14|eh<<18)^(el>>>18|eh<<14)^(el<<23|eh>>>9);var Ki=K[i];var Kih=Ki.high;var Kil=Ki.low;var t1l=hl+sigma1l;var t1h=hh+sigma1h+(t1l>>>0>>0?1:0);var t1l=t1l+chl;var t1h=t1h+chh+(t1l>>>0>>0?1:0);var t1l=t1l+Kil;var t1h=t1h+Kih+(t1l>>>0>>0?1:0);var t1l=t1l+Wil;var t1h=t1h+Wih+(t1l>>>0>>0?1:0);var t2l=sigma0l+majl;var t2h=sigma0h+majh+(t2l>>>0>>0?1:0);hh=gh;hl=gl;gh=fh;gl=fl;fh=eh;fl=el;el=dl+t1l|0;eh=dh+t1h+(el>>>0
>>0?1:0)|0;dh=ch;dl=cl;ch=bh;cl=bl;bh=ah;bl=al;al=t1l+t2l|0;ah=t1h+t2h+(al>>>0>>0?1:0)|0}H0l=H0.low=H0l+al;H0.high=H0h+ah+(H0l>>>0>>0?1:0);H1l=H1.low=H1l+bl;H1.high=H1h+bh+(H1l>>>0>>0?1:0);H2l=H2.low=H2l+cl;H2.high=H2h+ch+(H2l>>>0>>0?1:0);H3l=H3.low=H3l+dl;H3.high=H3h+dh+(H3l>>>0
>>0?1:0);H4l=H4.low=H4l+el;H4.high=H4h+eh+(H4l>>>0>>0?1:0);H5l=H5.low=H5l+fl;H5.high=H5h+fh+(H5l>>>0>>0?1:0);H6l=H6.low=H6l+gl;H6.high=H6h+gh+(H6l>>>0>>0?1:0);H7l=H7.low=H7l+hl;H7.high=H7h+hh+(H7l>>>0>>0?1:0)},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;dataWords[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;dataWords[(nBitsLeft+128>>>10<<5)+30]=Math.floor(nBitsTotal/4294967296);dataWords[(nBitsLeft+128>>>10<<5)+31]=nBitsTotal;data.sigBytes=dataWords.length*4;this._process();var hash=this._hash.toX32();return hash},clone:function(){var clone=Hasher.clone.call(this);clone._hash=this._hash.clone();return clone},blockSize:1024/32});C.SHA512=Hasher._createHelper(SHA512);C.HmacSHA512=Hasher._createHmacHelper(SHA512)})();(function(){var C=CryptoJS;var C_x64=C.x64;var X64Word=C_x64.Word;var X64WordArray=C_x64.WordArray;var C_algo=C.algo;var SHA512=C_algo.SHA512;var SHA384=C_algo.SHA384=SHA512.extend({_doReset:function(){this._hash=new X64WordArray.init([new X64Word.init(3418070365,3238371032),new X64Word.init(1654270250,914150663),new X64Word.init(2438529370,812702999),new X64Word.init(355462360,4144912697),new X64Word.init(1731405415,4290775857),new X64Word.init(2394180231,1750603025),new X64Word.init(3675008525,1694076839),new X64Word.init(1203062813,3204075428)])},_doFinalize:function(){var hash=SHA512._doFinalize.call(this);hash.sigBytes-=16;return hash}});C.SHA384=SHA512._createHelper(SHA384);C.HmacSHA384=SHA512._createHmacHelper(SHA384)})();(function(Math){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var Hasher=C_lib.Hasher;var C_x64=C.x64;var X64Word=C_x64.Word;var C_algo=C.algo;var RHO_OFFSETS=[];var PI_INDEXES=[];var ROUND_CONSTANTS=[];(function(){var x=1,y=0;for(var t=0;t<24;t++){RHO_OFFSETS[x+5*y]=(t+1)*(t+2)/2%64;var newX=y%5;var newY=(2*x+3*y)%5;x=newX;y=newY}for(var x=0;x<5;x++){for(var y=0;y<5;y++){PI_INDEXES[x+5*y]=y+(2*x+3*y)%5*5}}var LFSR=1;for(var i=0;i<24;i++){var roundConstantMsw=0;var roundConstantLsw=0;for(var j=0;j<7;j++){if(LFSR&1){var bitPosition=(1<>>24)&16711935|(M2i<<24|M2i>>>8)&4278255360;M2i1=(M2i1<<8|M2i1>>>24)&16711935|(M2i1<<24|M2i1>>>8)&4278255360;var lane=state[i];lane.high^=M2i1;lane.low^=M2i}for(var round=0;round<24;round++){for(var x=0;x<5;x++){var tMsw=0,tLsw=0;for(var y=0;y<5;y++){var lane=state[x+5*y];tMsw^=lane.high;tLsw^=lane.low}var Tx=T[x];Tx.high=tMsw;Tx.low=tLsw}for(var x=0;x<5;x++){var Tx4=T[(x+4)%5];var Tx1=T[(x+1)%5];var Tx1Msw=Tx1.high;var Tx1Lsw=Tx1.low;var tMsw=Tx4.high^(Tx1Msw<<1|Tx1Lsw>>>31);var tLsw=Tx4.low^(Tx1Lsw<<1|Tx1Msw>>>31);for(var y=0;y<5;y++){var lane=state[x+5*y];lane.high^=tMsw;lane.low^=tLsw}}for(var laneIndex=1;laneIndex<25;laneIndex++){var tMsw;var tLsw;var lane=state[laneIndex];var laneMsw=lane.high;var laneLsw=lane.low;var rhoOffset=RHO_OFFSETS[laneIndex];if(rhoOffset<32){tMsw=laneMsw<>>32-rhoOffset;tLsw=laneLsw<>>32-rhoOffset}else{tMsw=laneLsw<>>64-rhoOffset;tLsw=laneMsw<>>64-rhoOffset}var TPiLane=T[PI_INDEXES[laneIndex]];TPiLane.high=tMsw;TPiLane.low=tLsw}var T0=T[0];var state0=state[0];T0.high=state0.high;T0.low=state0.low;for(var x=0;x<5;x++){for(var y=0;y<5;y++){var laneIndex=x+5*y;var lane=state[laneIndex];var TLane=T[laneIndex];var Tx1Lane=T[(x+1)%5+5*y];var Tx2Lane=T[(x+2)%5+5*y];lane.high=TLane.high^~Tx1Lane.high&Tx2Lane.high;lane.low=TLane.low^~Tx1Lane.low&Tx2Lane.low}}var lane=state[0];var roundConstant=ROUND_CONSTANTS[round];lane.high^=roundConstant.high;lane.low^=roundConstant.low}},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;var blockSizeBits=this.blockSize*32;dataWords[nBitsLeft>>>5]|=1<<24-nBitsLeft%32;dataWords[(Math.ceil((nBitsLeft+1)/blockSizeBits)*blockSizeBits>>>5)-1]|=128;data.sigBytes=dataWords.length*4;this._process();var state=this._state;var outputLengthBytes=this.cfg.outputLength/8;var outputLengthLanes=outputLengthBytes/8;var hashWords=[];for(var i=0;i>>24)&16711935|(laneMsw<<24|laneMsw>>>8)&4278255360;laneLsw=(laneLsw<<8|laneLsw>>>24)&16711935|(laneLsw<<24|laneLsw>>>8)&4278255360;hashWords.push(laneLsw);hashWords.push(laneMsw)}return new WordArray.init(hashWords,outputLengthBytes)},clone:function(){var clone=Hasher.clone.call(this);var state=clone._state=this._state.slice(0);for(var i=0;i<25;i++){state[i]=state[i].clone()}return clone}});C.SHA3=Hasher._createHelper(SHA3);C.HmacSHA3=Hasher._createHmacHelper(SHA3)})(Math);(function(Math){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var Hasher=C_lib.Hasher;var C_algo=C.algo;var _zl=WordArray.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]);var _zr=WordArray.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]);var _sl=WordArray.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]);var _sr=WordArray.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]);var _hl=WordArray.create([0,1518500249,1859775393,2400959708,2840853838]);var _hr=WordArray.create([1352829926,1548603684,1836072691,2053994217,0]);var RIPEMD160=C_algo.RIPEMD160=Hasher.extend({_doReset:function(){this._hash=WordArray.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(M,offset){for(var i=0;i<16;i++){var offset_i=offset+i;var M_offset_i=M[offset_i];M[offset_i]=(M_offset_i<<8|M_offset_i>>>24)&16711935|(M_offset_i<<24|M_offset_i>>>8)&4278255360}var H=this._hash.words;var hl=_hl.words;var hr=_hr.words;var zl=_zl.words;var zr=_zr.words;var sl=_sl.words;var sr=_sr.words;var al,bl,cl,dl,el;var ar,br,cr,dr,er;ar=al=H[0];br=bl=H[1];cr=cl=H[2];dr=dl=H[3];er=el=H[4];var t;for(var i=0;i<80;i+=1){t=al+M[offset+zl[i]]|0;if(i<16){t+=f1(bl,cl,dl)+hl[0]}else if(i<32){t+=f2(bl,cl,dl)+hl[1]}else if(i<48){t+=f3(bl,cl,dl)+hl[2]}else if(i<64){t+=f4(bl,cl,dl)+hl[3]}else{t+=f5(bl,cl,dl)+hl[4]}t=t|0;t=rotl(t,sl[i]);t=t+el|0;al=el;el=dl;dl=rotl(cl,10);cl=bl;bl=t;t=ar+M[offset+zr[i]]|0;if(i<16){t+=f5(br,cr,dr)+hr[0]}else if(i<32){t+=f4(br,cr,dr)+hr[1]}else if(i<48){t+=f3(br,cr,dr)+hr[2]}else if(i<64){t+=f2(br,cr,dr)+hr[3]}else{t+=f1(br,cr,dr)+hr[4]}t=t|0;t=rotl(t,sr[i]);t=t+er|0;ar=er;er=dr;dr=rotl(cr,10);cr=br;br=t}t=H[1]+cl+dr|0;H[1]=H[2]+dl+er|0;H[2]=H[3]+el+ar|0;H[3]=H[4]+al+br|0;H[4]=H[0]+bl+cr|0;H[0]=t},_doFinalize:function(){var data=this._data;var dataWords=data.words;var nBitsTotal=this._nDataBytes*8;var nBitsLeft=data.sigBytes*8;dataWords[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;dataWords[(nBitsLeft+64>>>9<<4)+14]=(nBitsTotal<<8|nBitsTotal>>>24)&16711935|(nBitsTotal<<24|nBitsTotal>>>8)&4278255360;data.sigBytes=(dataWords.length+1)*4;this._process();var hash=this._hash;var H=hash.words;for(var i=0;i<5;i++){var H_i=H[i];H[i]=(H_i<<8|H_i>>>24)&16711935|(H_i<<24|H_i>>>8)&4278255360}return hash},clone:function(){var clone=Hasher.clone.call(this);clone._hash=this._hash.clone();return clone}});function f1(x,y,z){return x^y^z}function f2(x,y,z){return x&y|~x&z}function f3(x,y,z){return(x|~y)^z}function f4(x,y,z){return x&z|y&~z}function f5(x,y,z){return x^(y|~z)}function rotl(x,n){return x<>>32-n}C.RIPEMD160=Hasher._createHelper(RIPEMD160);C.HmacRIPEMD160=Hasher._createHmacHelper(RIPEMD160)})(Math);(function(){var C=CryptoJS;var C_lib=C.lib;var Base=C_lib.Base;var C_enc=C.enc;var Utf8=C_enc.Utf8;var C_algo=C.algo;var HMAC=C_algo.HMAC=Base.extend({init:function(hasher,key){hasher=this._hasher=new hasher.init;if(typeof key=="string"){key=Utf8.parse(key)}var hasherBlockSize=hasher.blockSize;var hasherBlockSizeBytes=hasherBlockSize*4;if(key.sigBytes>hasherBlockSizeBytes){key=hasher.finalize(key)}key.clamp();var oKey=this._oKey=key.clone();var iKey=this._iKey=key.clone();var oKeyWords=oKey.words;var iKeyWords=iKey.words;for(var i=0;i>>2]&255;data.sigBytes-=nPaddingBytes}};var BlockCipher=C_lib.BlockCipher=Cipher.extend({cfg:Cipher.cfg.extend({mode:CBC,padding:Pkcs7}),reset:function(){var modeCreator;Cipher.reset.call(this);var cfg=this.cfg;var iv=cfg.iv;var mode=cfg.mode;if(this._xformMode==this._ENC_XFORM_MODE){modeCreator=mode.createEncryptor}else{modeCreator=mode.createDecryptor;this._minBufferSize=1}if(this._mode&&this._mode.__creator==modeCreator){this._mode.init(this,iv&&iv.words)}else{this._mode=modeCreator.call(mode,this,iv&&iv.words);this._mode.__creator=modeCreator}},_doProcessBlock:function(words,offset){this._mode.processBlock(words,offset)},_doFinalize:function(){var finalProcessedBlocks;var padding=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){padding.pad(this._data,this.blockSize);finalProcessedBlocks=this._process(!!"flush")}else{finalProcessedBlocks=this._process(!!"flush");padding.unpad(finalProcessedBlocks)}return finalProcessedBlocks},blockSize:128/32});var CipherParams=C_lib.CipherParams=Base.extend({init:function(cipherParams){this.mixIn(cipherParams)},toString:function(formatter){return(formatter||this.formatter).stringify(this)}});var C_format=C.format={};var OpenSSLFormatter=C_format.OpenSSL={stringify:function(cipherParams){var wordArray;var ciphertext=cipherParams.ciphertext;var salt=cipherParams.salt;if(salt){wordArray=WordArray.create([1398893684,1701076831]).concat(salt).concat(ciphertext)}else{wordArray=ciphertext}return wordArray.toString(Base64)},parse:function(openSSLStr){var salt;var ciphertext=Base64.parse(openSSLStr);var ciphertextWords=ciphertext.words;if(ciphertextWords[0]==1398893684&&ciphertextWords[1]==1701076831){salt=WordArray.create(ciphertextWords.slice(2,4));ciphertextWords.splice(0,4);ciphertext.sigBytes-=16}return CipherParams.create({ciphertext:ciphertext,salt:salt})}};var SerializableCipher=C_lib.SerializableCipher=Base.extend({cfg:Base.extend({format:OpenSSLFormatter}),encrypt:function(cipher,message,key,cfg){cfg=this.cfg.extend(cfg);var encryptor=cipher.createEncryptor(key,cfg);var ciphertext=encryptor.finalize(message);var cipherCfg=encryptor.cfg;return CipherParams.create({ciphertext:ciphertext,key:key,iv:cipherCfg.iv,algorithm:cipher,mode:cipherCfg.mode,padding:cipherCfg.padding,blockSize:cipher.blockSize,formatter:cfg.format})},decrypt:function(cipher,ciphertext,key,cfg){cfg=this.cfg.extend(cfg);ciphertext=this._parse(ciphertext,cfg.format);var plaintext=cipher.createDecryptor(key,cfg).finalize(ciphertext.ciphertext);return plaintext},_parse:function(ciphertext,format){if(typeof ciphertext=="string"){return format.parse(ciphertext,this)}else{return ciphertext}}});var C_kdf=C.kdf={};var OpenSSLKdf=C_kdf.OpenSSL={execute:function(password,keySize,ivSize,salt){if(!salt){salt=WordArray.random(64/8)}var key=EvpKDF.create({keySize:keySize+ivSize}).compute(password,salt);var iv=WordArray.create(key.words.slice(keySize),ivSize*4);key.sigBytes=keySize*4;return CipherParams.create({key:key,iv:iv,salt:salt})}};var PasswordBasedCipher=C_lib.PasswordBasedCipher=SerializableCipher.extend({cfg:SerializableCipher.cfg.extend({kdf:OpenSSLKdf}),encrypt:function(cipher,message,password,cfg){cfg=this.cfg.extend(cfg);var derivedParams=cfg.kdf.execute(password,cipher.keySize,cipher.ivSize);cfg.iv=derivedParams.iv;var ciphertext=SerializableCipher.encrypt.call(this,cipher,message,derivedParams.key,cfg);ciphertext.mixIn(derivedParams);return ciphertext},decrypt:function(cipher,ciphertext,password,cfg){cfg=this.cfg.extend(cfg);ciphertext=this._parse(ciphertext,cfg.format);var derivedParams=cfg.kdf.execute(password,cipher.keySize,cipher.ivSize,ciphertext.salt);cfg.iv=derivedParams.iv;var plaintext=SerializableCipher.decrypt.call(this,cipher,ciphertext,derivedParams.key,cfg);return plaintext}})}();CryptoJS.mode.CFB=function(){var CFB=CryptoJS.lib.BlockCipherMode.extend();CFB.Encryptor=CFB.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;generateKeystreamAndEncrypt.call(this,words,offset,blockSize,cipher);this._prevBlock=words.slice(offset,offset+blockSize)}});CFB.Decryptor=CFB.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var thisBlock=words.slice(offset,offset+blockSize);generateKeystreamAndEncrypt.call(this,words,offset,blockSize,cipher);this._prevBlock=thisBlock}});function generateKeystreamAndEncrypt(words,offset,blockSize,cipher){var keystream;var iv=this._iv;if(iv){keystream=iv.slice(0);this._iv=undefined}else{keystream=this._prevBlock}cipher.encryptBlock(keystream,0);for(var i=0;i>24&255)===255){var b1=word>>16&255;var b2=word>>8&255;var b3=word&255;if(b1===255){b1=0;if(b2===255){b2=0;if(b3===255){b3=0}else{++b3}}else{++b2}}else{++b1}word=0;word+=b1<<16;word+=b2<<8;word+=b3}else{word+=1<<24}return word}function incCounter(counter){if((counter[0]=incWord(counter[0]))===0){counter[1]=incWord(counter[1])}return counter}var Encryptor=CTRGladman.Encryptor=CTRGladman.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var iv=this._iv;var counter=this._counter;if(iv){counter=this._counter=iv.slice(0);this._iv=undefined}incCounter(counter);var keystream=counter.slice(0);cipher.encryptBlock(keystream,0);for(var i=0;i>>2]|=nPaddingBytes<<24-lastBytePos%4*8;data.sigBytes+=nPaddingBytes},unpad:function(data){var nPaddingBytes=data.words[data.sigBytes-1>>>2]&255;data.sigBytes-=nPaddingBytes}};CryptoJS.pad.Iso10126={pad:function(data,blockSize){var blockSizeBytes=blockSize*4;var nPaddingBytes=blockSizeBytes-data.sigBytes%blockSizeBytes;data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes-1)).concat(CryptoJS.lib.WordArray.create([nPaddingBytes<<24],1))},unpad:function(data){var nPaddingBytes=data.words[data.sigBytes-1>>>2]&255;data.sigBytes-=nPaddingBytes}};CryptoJS.pad.Iso97971={pad:function(data,blockSize){data.concat(CryptoJS.lib.WordArray.create([2147483648],1));CryptoJS.pad.ZeroPadding.pad(data,blockSize)},unpad:function(data){CryptoJS.pad.ZeroPadding.unpad(data);data.sigBytes--}};CryptoJS.pad.ZeroPadding={pad:function(data,blockSize){var blockSizeBytes=blockSize*4;data.clamp();data.sigBytes+=blockSizeBytes-(data.sigBytes%blockSizeBytes||blockSizeBytes)},unpad:function(data){var dataWords=data.words;var i=data.sigBytes-1;for(var i=data.sigBytes-1;i>=0;i--){if(dataWords[i>>>2]>>>24-i%4*8&255){data.sigBytes=i+1;break}}}};CryptoJS.pad.NoPadding={pad:function(){},unpad:function(){}};(function(undefined){var C=CryptoJS;var C_lib=C.lib;var CipherParams=C_lib.CipherParams;var C_enc=C.enc;var Hex=C_enc.Hex;var C_format=C.format;var HexFormatter=C_format.Hex={stringify:function(cipherParams){return cipherParams.ciphertext.toString(Hex)},parse:function(input){var ciphertext=Hex.parse(input);return CipherParams.create({ciphertext:ciphertext})}}})();(function(){var C=CryptoJS;var C_lib=C.lib;var BlockCipher=C_lib.BlockCipher;var C_algo=C.algo;var SBOX=[];var INV_SBOX=[];var SUB_MIX_0=[];var SUB_MIX_1=[];var SUB_MIX_2=[];var SUB_MIX_3=[];var INV_SUB_MIX_0=[];var INV_SUB_MIX_1=[];var INV_SUB_MIX_2=[];var INV_SUB_MIX_3=[];(function(){var d=[];for(var i=0;i<256;i++){if(i<128){d[i]=i<<1}else{d[i]=i<<1^283}}var x=0;var xi=0;for(var i=0;i<256;i++){var sx=xi^xi<<1^xi<<2^xi<<3^xi<<4;sx=sx>>>8^sx&255^99;SBOX[x]=sx;INV_SBOX[sx]=x;var x2=d[x];var x4=d[x2];var x8=d[x4];var t=d[sx]*257^sx*16843008;SUB_MIX_0[x]=t<<24|t>>>8;SUB_MIX_1[x]=t<<16|t>>>16;SUB_MIX_2[x]=t<<8|t>>>24;SUB_MIX_3[x]=t;var t=x8*16843009^x4*65537^x2*257^x*16843008;INV_SUB_MIX_0[sx]=t<<24|t>>>8;INV_SUB_MIX_1[sx]=t<<16|t>>>16;INV_SUB_MIX_2[sx]=t<<8|t>>>24;INV_SUB_MIX_3[sx]=t;if(!x){x=xi=1}else{x=x2^d[d[d[x8^x2]]];xi^=d[d[xi]]}}})();var RCON=[0,1,2,4,8,16,32,64,128,27,54];var AES=C_algo.AES=BlockCipher.extend({_doReset:function(){var t;if(this._nRounds&&this._keyPriorReset===this._key){return}var key=this._keyPriorReset=this._key;var keyWords=key.words;var keySize=key.sigBytes/4;var nRounds=this._nRounds=keySize+6;var ksRows=(nRounds+1)*4;var keySchedule=this._keySchedule=[];for(var ksRow=0;ksRow>>24;t=SBOX[t>>>24]<<24|SBOX[t>>>16&255]<<16|SBOX[t>>>8&255]<<8|SBOX[t&255];t^=RCON[ksRow/keySize|0]<<24}else if(keySize>6&&ksRow%keySize==4){t=SBOX[t>>>24]<<24|SBOX[t>>>16&255]<<16|SBOX[t>>>8&255]<<8|SBOX[t&255]}keySchedule[ksRow]=keySchedule[ksRow-keySize]^t}}var invKeySchedule=this._invKeySchedule=[];for(var invKsRow=0;invKsRow>>24]]^INV_SUB_MIX_1[SBOX[t>>>16&255]]^INV_SUB_MIX_2[SBOX[t>>>8&255]]^INV_SUB_MIX_3[SBOX[t&255]]}}},encryptBlock:function(M,offset){this._doCryptBlock(M,offset,this._keySchedule,SUB_MIX_0,SUB_MIX_1,SUB_MIX_2,SUB_MIX_3,SBOX)},decryptBlock:function(M,offset){var t=M[offset+1];M[offset+1]=M[offset+3];M[offset+3]=t;this._doCryptBlock(M,offset,this._invKeySchedule,INV_SUB_MIX_0,INV_SUB_MIX_1,INV_SUB_MIX_2,INV_SUB_MIX_3,INV_SBOX);var t=M[offset+1];M[offset+1]=M[offset+3];M[offset+3]=t},_doCryptBlock:function(M,offset,keySchedule,SUB_MIX_0,SUB_MIX_1,SUB_MIX_2,SUB_MIX_3,SBOX){var nRounds=this._nRounds;var s0=M[offset]^keySchedule[0];var s1=M[offset+1]^keySchedule[1];var s2=M[offset+2]^keySchedule[2];var s3=M[offset+3]^keySchedule[3];var ksRow=4;for(var round=1;round>>24]^SUB_MIX_1[s1>>>16&255]^SUB_MIX_2[s2>>>8&255]^SUB_MIX_3[s3&255]^keySchedule[ksRow++];var t1=SUB_MIX_0[s1>>>24]^SUB_MIX_1[s2>>>16&255]^SUB_MIX_2[s3>>>8&255]^SUB_MIX_3[s0&255]^keySchedule[ksRow++];var t2=SUB_MIX_0[s2>>>24]^SUB_MIX_1[s3>>>16&255]^SUB_MIX_2[s0>>>8&255]^SUB_MIX_3[s1&255]^keySchedule[ksRow++];var t3=SUB_MIX_0[s3>>>24]^SUB_MIX_1[s0>>>16&255]^SUB_MIX_2[s1>>>8&255]^SUB_MIX_3[s2&255]^keySchedule[ksRow++];s0=t0;s1=t1;s2=t2;s3=t3}var t0=(SBOX[s0>>>24]<<24|SBOX[s1>>>16&255]<<16|SBOX[s2>>>8&255]<<8|SBOX[s3&255])^keySchedule[ksRow++];var t1=(SBOX[s1>>>24]<<24|SBOX[s2>>>16&255]<<16|SBOX[s3>>>8&255]<<8|SBOX[s0&255])^keySchedule[ksRow++];var t2=(SBOX[s2>>>24]<<24|SBOX[s3>>>16&255]<<16|SBOX[s0>>>8&255]<<8|SBOX[s1&255])^keySchedule[ksRow++];var t3=(SBOX[s3>>>24]<<24|SBOX[s0>>>16&255]<<16|SBOX[s1>>>8&255]<<8|SBOX[s2&255])^keySchedule[ksRow++];M[offset]=t0;M[offset+1]=t1;M[offset+2]=t2;M[offset+3]=t3},keySize:256/32});C.AES=BlockCipher._createHelper(AES)})();(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var BlockCipher=C_lib.BlockCipher;var C_algo=C.algo;var PC1=[57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4];var PC2=[14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32];var BIT_SHIFTS=[1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28];var SBOX_P=[{0:8421888,268435456:32768,536870912:8421378,805306368:2,1073741824:512,1342177280:8421890,1610612736:8389122,1879048192:8388608,2147483648:514,2415919104:8389120,2684354560:33280,2952790016:8421376,3221225472:32770,3489660928:8388610,3758096384:0,4026531840:33282,134217728:0,402653184:8421890,671088640:33282,939524096:32768,1207959552:8421888,1476395008:512,1744830464:8421378,2013265920:2,2281701376:8389120,2550136832:33280,2818572288:8421376,3087007744:8389122,3355443200:8388610,3623878656:32770,3892314112:514,4160749568:8388608,1:32768,268435457:2,536870913:8421888,805306369:8388608,1073741825:8421378,1342177281:33280,1610612737:512,1879048193:8389122,2147483649:8421890,2415919105:8421376,2684354561:8388610,2952790017:33282,3221225473:514,3489660929:8389120,3758096385:32770,4026531841:0,134217729:8421890,402653185:8421376,671088641:8388608,939524097:512,1207959553:32768,1476395009:8388610,1744830465:2,2013265921:33282,2281701377:32770,2550136833:8389122,2818572289:514,3087007745:8421888,3355443201:8389120,3623878657:0,3892314113:33280,4160749569:8421378},{0:1074282512,16777216:16384,33554432:524288,50331648:1074266128,67108864:1073741840,83886080:1074282496,100663296:1073758208,117440512:16,134217728:540672,150994944:1073758224,167772160:1073741824,184549376:540688,201326592:524304,218103808:0,234881024:16400,251658240:1074266112,8388608:1073758208,25165824:540688,41943040:16,58720256:1073758224,75497472:1074282512,92274688:1073741824,109051904:524288,125829120:1074266128,142606336:524304,159383552:0,176160768:16384,192937984:1074266112,209715200:1073741840,226492416:540672,243269632:1074282496,260046848:16400,268435456:0,285212672:1074266128,301989888:1073758224,318767104:1074282496,335544320:1074266112,352321536:16,369098752:540688,385875968:16384,402653184:16400,419430400:524288,436207616:524304,452984832:1073741840,469762048:540672,486539264:1073758208,503316480:1073741824,520093696:1074282512,276824064:540688,293601280:524288,310378496:1074266112,327155712:16384,343932928:1073758208,360710144:1074282512,377487360:16,394264576:1073741824,411041792:1074282496,427819008:1073741840,444596224:1073758224,461373440:524304,478150656:0,494927872:16400,511705088:1074266128,528482304:540672},{0:260,1048576:0,2097152:67109120,3145728:65796,4194304:65540,5242880:67108868,6291456:67174660,7340032:67174400,8388608:67108864,9437184:67174656,10485760:65792,11534336:67174404,12582912:67109124,13631488:65536,14680064:4,15728640:256,524288:67174656,1572864:67174404,2621440:0,3670016:67109120,4718592:67108868,5767168:65536,6815744:65540,7864320:260,8912896:4,9961472:256,11010048:67174400,12058624:65796,13107200:65792,14155776:67109124,15204352:67174660,16252928:67108864,16777216:67174656,17825792:65540,18874368:65536,19922944:67109120,20971520:256,22020096:67174660,23068672:67108868,24117248:0,25165824:67109124,26214400:67108864,27262976:4,28311552:65792,29360128:67174400,30408704:260,31457280:65796,32505856:67174404,17301504:67108864,18350080:260,19398656:67174656,20447232:0,21495808:65540,22544384:67109120,23592960:256,24641536:67174404,25690112:65536,26738688:67174660,27787264:65796,28835840:67108868,29884416:67109124,30932992:67174400,31981568:4,33030144:65792},{0:2151682048,65536:2147487808,131072:4198464,196608:2151677952,262144:0,327680:4198400,393216:2147483712,458752:4194368,524288:2147483648,589824:4194304,655360:64,720896:2147487744,786432:2151678016,851968:4160,917504:4096,983040:2151682112,32768:2147487808,98304:64,163840:2151678016,229376:2147487744,294912:4198400,360448:2151682112,425984:0,491520:2151677952,557056:4096,622592:2151682048,688128:4194304,753664:4160,819200:2147483648,884736:4194368,950272:4198464,1015808:2147483712,1048576:4194368,1114112:4198400,1179648:2147483712,1245184:0,1310720:4160,1376256:2151678016,1441792:2151682048,1507328:2147487808,1572864:2151682112,1638400:2147483648,1703936:2151677952,1769472:4198464,1835008:2147487744,1900544:4194304,1966080:64,2031616:4096,1081344:2151677952,1146880:2151682112,1212416:0,1277952:4198400,1343488:4194368,1409024:2147483648,1474560:2147487808,1540096:64,1605632:2147483712,1671168:4096,1736704:2147487744,1802240:2151678016,1867776:4160,1933312:2151682048,1998848:4194304,2064384:4198464},{0:128,4096:17039360,8192:262144,12288:536870912,16384:537133184,20480:16777344,24576:553648256,28672:262272,32768:16777216,36864:537133056,40960:536871040,45056:553910400,49152:553910272,53248:0,57344:17039488,61440:553648128,2048:17039488,6144:553648256,10240:128,14336:17039360,18432:262144,22528:537133184,26624:553910272,30720:536870912,34816:537133056,38912:0,43008:553910400,47104:16777344,51200:536871040,55296:553648128,59392:16777216,63488:262272,65536:262144,69632:128,73728:536870912,77824:553648256,81920:16777344,86016:553910272,90112:537133184,94208:16777216,98304:553910400,102400:553648128,106496:17039360,110592:537133056,114688:262272,118784:536871040,122880:0,126976:17039488,67584:553648256,71680:16777216,75776:17039360,79872:537133184,83968:536870912,88064:17039488,92160:128,96256:553910272,100352:262272,104448:553910400,108544:0,112640:553648128,116736:16777344,120832:262144,124928:537133056,129024:536871040},{0:268435464,256:8192,512:270532608,768:270540808,1024:268443648,1280:2097152,1536:2097160,1792:268435456,2048:0,2304:268443656,2560:2105344,2816:8,3072:270532616,3328:2105352,3584:8200,3840:270540800,128:270532608,384:270540808,640:8,896:2097152,1152:2105352,1408:268435464,1664:268443648,1920:8200,2176:2097160,2432:8192,2688:268443656,2944:270532616,3200:0,3456:270540800,3712:2105344,3968:268435456,4096:268443648,4352:270532616,4608:270540808,4864:8200,5120:2097152,5376:268435456,5632:268435464,5888:2105344,6144:2105352,6400:0,6656:8,6912:270532608,7168:8192,7424:268443656,7680:270540800,7936:2097160,4224:8,4480:2105344,4736:2097152,4992:268435464,5248:268443648,5504:8200,5760:270540808,6016:270532608,6272:270540800,6528:270532616,6784:8192,7040:2105352,7296:2097160,7552:0,7808:268435456,8064:268443656},{0:1048576,16:33555457,32:1024,48:1049601,64:34604033,80:0,96:1,112:34603009,128:33555456,144:1048577,160:33554433,176:34604032,192:34603008,208:1025,224:1049600,240:33554432,8:34603009,24:0,40:33555457,56:34604032,72:1048576,88:33554433,104:33554432,120:1025,136:1049601,152:33555456,168:34603008,184:1048577,200:1024,216:34604033,232:1,248:1049600,256:33554432,272:1048576,288:33555457,304:34603009,320:1048577,336:33555456,352:34604032,368:1049601,384:1025,400:34604033,416:1049600,432:1,448:0,464:34603008,480:33554433,496:1024,264:1049600,280:33555457,296:34603009,312:1,328:33554432,344:1048576,360:1025,376:34604032,392:33554433,408:34603008,424:0,440:34604033,456:1049601,472:1024,488:33555456,504:1048577},{0:134219808,1:131072,2:134217728,3:32,4:131104,5:134350880,6:134350848,7:2048,8:134348800,9:134219776,10:133120,11:134348832,12:2080,13:0,14:134217760,15:133152,2147483648:2048,2147483649:134350880,2147483650:134219808,2147483651:134217728,2147483652:134348800,2147483653:133120,2147483654:133152,2147483655:32,2147483656:134217760,2147483657:2080,2147483658:131104,2147483659:134350848,2147483660:0,2147483661:134348832,2147483662:134219776,2147483663:131072,16:133152,17:134350848,18:32,19:2048,20:134219776,21:134217760,22:134348832,23:131072,24:0,25:131104,26:134348800,27:134219808,28:134350880,29:133120,30:2080,31:134217728,2147483664:131072,2147483665:2048,2147483666:134348832,2147483667:133152,2147483668:32,2147483669:134348800,2147483670:134217728,2147483671:134219808,2147483672:134350880,2147483673:134217760,2147483674:134219776,2147483675:0,2147483676:133120,2147483677:2080,2147483678:131104,2147483679:134350848}];var SBOX_MASK=[4160749569,528482304,33030144,2064384,129024,8064,504,2147483679];var DES=C_algo.DES=BlockCipher.extend({_doReset:function(){var key=this._key;var keyWords=key.words;var keyBits=[];for(var i=0;i<56;i++){var keyBitPos=PC1[i]-1;keyBits[i]=keyWords[keyBitPos>>>5]>>>31-keyBitPos%32&1}var subKeys=this._subKeys=[];for(var nSubKey=0;nSubKey<16;nSubKey++){var subKey=subKeys[nSubKey]=[];var bitShift=BIT_SHIFTS[nSubKey];for(var i=0;i<24;i++){subKey[i/6|0]|=keyBits[(PC2[i]-1+bitShift)%28]<<31-i%6;subKey[4+(i/6|0)]|=keyBits[28+(PC2[i+24]-1+bitShift)%28]<<31-i%6}subKey[0]=subKey[0]<<1|subKey[0]>>>31;for(var i=1;i<7;i++){subKey[i]=subKey[i]>>>(i-1)*4+3}subKey[7]=subKey[7]<<5|subKey[7]>>>27}var invSubKeys=this._invSubKeys=[];for(var i=0;i<16;i++){invSubKeys[i]=subKeys[15-i]}},encryptBlock:function(M,offset){this._doCryptBlock(M,offset,this._subKeys)},decryptBlock:function(M,offset){this._doCryptBlock(M,offset,this._invSubKeys)},_doCryptBlock:function(M,offset,subKeys){this._lBlock=M[offset];this._rBlock=M[offset+1];exchangeLR.call(this,4,252645135);exchangeLR.call(this,16,65535);exchangeRL.call(this,2,858993459);exchangeRL.call(this,8,16711935);exchangeLR.call(this,1,1431655765);for(var round=0;round<16;round++){var subKey=subKeys[round];var lBlock=this._lBlock;var rBlock=this._rBlock;var f=0;for(var i=0;i<8;i++){f|=SBOX_P[i][((rBlock^subKey[i])&SBOX_MASK[i])>>>0]}this._lBlock=rBlock;this._rBlock=lBlock^f}var t=this._lBlock;this._lBlock=this._rBlock;this._rBlock=t;exchangeLR.call(this,1,1431655765);exchangeRL.call(this,8,16711935);exchangeRL.call(this,2,858993459);exchangeLR.call(this,16,65535);exchangeLR.call(this,4,252645135);M[offset]=this._lBlock;M[offset+1]=this._rBlock},keySize:64/32,ivSize:64/32,blockSize:64/32});function exchangeLR(offset,mask){var t=(this._lBlock>>>offset^this._rBlock)&mask;this._rBlock^=t;this._lBlock^=t<>>offset^this._lBlock)&mask;this._lBlock^=t;this._rBlock^=t<192.")}var key1=keyWords.slice(0,2);var key2=keyWords.length<4?keyWords.slice(0,2):keyWords.slice(2,4);var key3=keyWords.length<6?keyWords.slice(0,2):keyWords.slice(4,6);this._des1=DES.createEncryptor(WordArray.create(key1));this._des2=DES.createEncryptor(WordArray.create(key2));this._des3=DES.createEncryptor(WordArray.create(key3))},encryptBlock:function(M,offset){this._des1.encryptBlock(M,offset);this._des2.decryptBlock(M,offset);this._des3.encryptBlock(M,offset)},decryptBlock:function(M,offset){this._des3.decryptBlock(M,offset);this._des2.encryptBlock(M,offset);this._des1.decryptBlock(M,offset)},keySize:192/32,ivSize:64/32,blockSize:64/32});C.TripleDES=BlockCipher._createHelper(TripleDES)})();(function(){var C=CryptoJS;var C_lib=C.lib;var StreamCipher=C_lib.StreamCipher;var C_algo=C.algo;var RC4=C_algo.RC4=StreamCipher.extend({_doReset:function(){var key=this._key;var keyWords=key.words;var keySigBytes=key.sigBytes;var S=this._S=[];for(var i=0;i<256;i++){S[i]=i}for(var i=0,j=0;i<256;i++){var keyByteIndex=i%keySigBytes;var keyByte=keyWords[keyByteIndex>>>2]>>>24-keyByteIndex%4*8&255;j=(j+S[i]+keyByte)%256;var t=S[i];S[i]=S[j];S[j]=t}this._i=this._j=0},_doProcessBlock:function(M,offset){M[offset]^=generateKeystreamWord.call(this)},keySize:256/32,ivSize:0});function generateKeystreamWord(){var S=this._S;var i=this._i;var j=this._j;var keystreamWord=0;for(var n=0;n<4;n++){i=(i+1)%256;j=(j+S[i])%256;var t=S[i];S[i]=S[j];S[j]=t;keystreamWord|=S[(S[i]+S[j])%256]<<24-n*8}this._i=i;this._j=j;return keystreamWord}C.RC4=StreamCipher._createHelper(RC4);var RC4Drop=C_algo.RC4Drop=RC4.extend({cfg:RC4.cfg.extend({drop:192}),_doReset:function(){RC4._doReset.call(this);for(var i=this.cfg.drop;i>0;i--){generateKeystreamWord.call(this)}}});C.RC4Drop=StreamCipher._createHelper(RC4Drop)})();(function(){var C=CryptoJS;var C_lib=C.lib;var StreamCipher=C_lib.StreamCipher;var C_algo=C.algo;var S=[];var C_=[];var G=[];var Rabbit=C_algo.Rabbit=StreamCipher.extend({_doReset:function(){var K=this._key.words;var iv=this.cfg.iv;for(var i=0;i<4;i++){K[i]=(K[i]<<8|K[i]>>>24)&16711935|(K[i]<<24|K[i]>>>8)&4278255360}var X=this._X=[K[0],K[3]<<16|K[2]>>>16,K[1],K[0]<<16|K[3]>>>16,K[2],K[1]<<16|K[0]>>>16,K[3],K[2]<<16|K[1]>>>16];var C=this._C=[K[2]<<16|K[2]>>>16,K[0]&4294901760|K[1]&65535,K[3]<<16|K[3]>>>16,K[1]&4294901760|K[2]&65535,K[0]<<16|K[0]>>>16,K[2]&4294901760|K[3]&65535,K[1]<<16|K[1]>>>16,K[3]&4294901760|K[0]&65535];this._b=0;for(var i=0;i<4;i++){nextState.call(this)}for(var i=0;i<8;i++){C[i]^=X[i+4&7]}if(iv){var IV=iv.words;var IV_0=IV[0];var IV_1=IV[1];var i0=(IV_0<<8|IV_0>>>24)&16711935|(IV_0<<24|IV_0>>>8)&4278255360;var i2=(IV_1<<8|IV_1>>>24)&16711935|(IV_1<<24|IV_1>>>8)&4278255360;var i1=i0>>>16|i2&4294901760;var i3=i2<<16|i0&65535;C[0]^=i0;C[1]^=i1;C[2]^=i2;C[3]^=i3;C[4]^=i0;C[5]^=i1;C[6]^=i2;C[7]^=i3;for(var i=0;i<4;i++){nextState.call(this)}}},_doProcessBlock:function(M,offset){var X=this._X;nextState.call(this);S[0]=X[0]^X[5]>>>16^X[3]<<16;S[1]=X[2]^X[7]>>>16^X[5]<<16;S[2]=X[4]^X[1]>>>16^X[7]<<16;S[3]=X[6]^X[3]>>>16^X[1]<<16;for(var i=0;i<4;i++){S[i]=(S[i]<<8|S[i]>>>24)&16711935|(S[i]<<24|S[i]>>>8)&4278255360;M[offset+i]^=S[i]}},blockSize:128/32,ivSize:64/32});function nextState(){var X=this._X;var C=this._C;for(var i=0;i<8;i++){C_[i]=C[i]}C[0]=C[0]+1295307597+this._b|0;C[1]=C[1]+3545052371+(C[0]>>>0>>0?1:0)|0;C[2]=C[2]+886263092+(C[1]>>>0>>0?1:0)|0;C[3]=C[3]+1295307597+(C[2]>>>0>>0?1:0)|0;C[4]=C[4]+3545052371+(C[3]>>>0>>0?1:0)|0;C[5]=C[5]+886263092+(C[4]>>>0>>0?1:0)|0;C[6]=C[6]+1295307597+(C[5]>>>0>>0?1:0)|0;C[7]=C[7]+3545052371+(C[6]>>>0>>0?1:0)|0;this._b=C[7]>>>0>>0?1:0;for(var i=0;i<8;i++){var gx=X[i]+C[i];var ga=gx&65535;var gb=gx>>>16;var gh=((ga*ga>>>17)+ga*gb>>>15)+gb*gb;var gl=((gx&4294901760)*gx|0)+((gx&65535)*gx|0);G[i]=gh^gl}X[0]=G[0]+(G[7]<<16|G[7]>>>16)+(G[6]<<16|G[6]>>>16)|0;X[1]=G[1]+(G[0]<<8|G[0]>>>24)+G[7]|0;X[2]=G[2]+(G[1]<<16|G[1]>>>16)+(G[0]<<16|G[0]>>>16)|0;X[3]=G[3]+(G[2]<<8|G[2]>>>24)+G[1]|0;X[4]=G[4]+(G[3]<<16|G[3]>>>16)+(G[2]<<16|G[2]>>>16)|0;X[5]=G[5]+(G[4]<<8|G[4]>>>24)+G[3]|0;X[6]=G[6]+(G[5]<<16|G[5]>>>16)+(G[4]<<16|G[4]>>>16)|0;X[7]=G[7]+(G[6]<<8|G[6]>>>24)+G[5]|0}C.Rabbit=StreamCipher._createHelper(Rabbit)})();(function(){var C=CryptoJS;var C_lib=C.lib;var StreamCipher=C_lib.StreamCipher;var C_algo=C.algo;var S=[];var C_=[];var G=[];var RabbitLegacy=C_algo.RabbitLegacy=StreamCipher.extend({_doReset:function(){var K=this._key.words;var iv=this.cfg.iv;var X=this._X=[K[0],K[3]<<16|K[2]>>>16,K[1],K[0]<<16|K[3]>>>16,K[2],K[1]<<16|K[0]>>>16,K[3],K[2]<<16|K[1]>>>16];var C=this._C=[K[2]<<16|K[2]>>>16,K[0]&4294901760|K[1]&65535,K[3]<<16|K[3]>>>16,K[1]&4294901760|K[2]&65535,K[0]<<16|K[0]>>>16,K[2]&4294901760|K[3]&65535,K[1]<<16|K[1]>>>16,K[3]&4294901760|K[0]&65535];this._b=0;for(var i=0;i<4;i++){nextState.call(this)}for(var i=0;i<8;i++){C[i]^=X[i+4&7]}if(iv){var IV=iv.words;var IV_0=IV[0];var IV_1=IV[1];var i0=(IV_0<<8|IV_0>>>24)&16711935|(IV_0<<24|IV_0>>>8)&4278255360;var i2=(IV_1<<8|IV_1>>>24)&16711935|(IV_1<<24|IV_1>>>8)&4278255360;var i1=i0>>>16|i2&4294901760;var i3=i2<<16|i0&65535;C[0]^=i0;C[1]^=i1;C[2]^=i2;C[3]^=i3;C[4]^=i0;C[5]^=i1;C[6]^=i2;C[7]^=i3;for(var i=0;i<4;i++){nextState.call(this)}}},_doProcessBlock:function(M,offset){var X=this._X;nextState.call(this);S[0]=X[0]^X[5]>>>16^X[3]<<16;S[1]=X[2]^X[7]>>>16^X[5]<<16;S[2]=X[4]^X[1]>>>16^X[7]<<16;S[3]=X[6]^X[3]>>>16^X[1]<<16;for(var i=0;i<4;i++){S[i]=(S[i]<<8|S[i]>>>24)&16711935|(S[i]<<24|S[i]>>>8)&4278255360;M[offset+i]^=S[i]}},blockSize:128/32,ivSize:64/32});function nextState(){var X=this._X;var C=this._C;for(var i=0;i<8;i++){C_[i]=C[i]}C[0]=C[0]+1295307597+this._b|0;C[1]=C[1]+3545052371+(C[0]>>>0>>0?1:0)|0;C[2]=C[2]+886263092+(C[1]>>>0>>0?1:0)|0;C[3]=C[3]+1295307597+(C[2]>>>0>>0?1:0)|0;C[4]=C[4]+3545052371+(C[3]>>>0>>0?1:0)|0;C[5]=C[5]+886263092+(C[4]>>>0>>0?1:0)|0;C[6]=C[6]+1295307597+(C[5]>>>0>>0?1:0)|0;C[7]=C[7]+3545052371+(C[6]>>>0>>0?1:0)|0;this._b=C[7]>>>0>>0?1:0;for(var i=0;i<8;i++){var gx=X[i]+C[i];var ga=gx&65535;var gb=gx>>>16;var gh=((ga*ga>>>17)+ga*gb>>>15)+gb*gb;var gl=((gx&4294901760)*gx|0)+((gx&65535)*gx|0);G[i]=gh^gl}X[0]=G[0]+(G[7]<<16|G[7]>>>16)+(G[6]<<16|G[6]>>>16)|0;X[1]=G[1]+(G[0]<<8|G[0]>>>24)+G[7]|0;X[2]=G[2]+(G[1]<<16|G[1]>>>16)+(G[0]<<16|G[0]>>>16)|0;X[3]=G[3]+(G[2]<<8|G[2]>>>24)+G[1]|0;X[4]=G[4]+(G[3]<<16|G[3]>>>16)+(G[2]<<16|G[2]>>>16)|0;X[5]=G[5]+(G[4]<<8|G[4]>>>24)+G[3]|0;X[6]=G[6]+(G[5]<<16|G[5]>>>16)+(G[4]<<16|G[4]>>>16)|0;X[7]=G[7]+(G[6]<<8|G[6]>>>24)+G[5]|0}C.RabbitLegacy=StreamCipher._createHelper(RabbitLegacy)})();return CryptoJS});'},156:(e,a,t)=>{t(642)(t(761))},321:e=>{e.exports='!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{function e(a){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(a)}!function(e){var a=Object.create(null);a.ftp=21,a.file=0,a.gopher=70,a.http=80,a.https=443,a.ws=80,a.wss=443;var t=Object.create(null);function r(e){return void 0!==a[e]}function c(){b.call(this),this._isInvalid=!0}function i(e){return""==e&&c.call(this),e.toLowerCase()}function n(e){var a=e.charCodeAt(0);return a>32&&a<127&&-1==[34,35,60,62,63,96].indexOf(a)?e:encodeURIComponent(e)}function f(e){var a=e.charCodeAt(0);return a>32&&a<127&&-1==[34,35,60,62,96].indexOf(a)?e:encodeURIComponent(e)}t["%2e"]=".",t[".%2e"]="..",t["%2e."]="..",t["%2e%2e"]="..";var d=void 0,u=/[a-zA-Z]/,o=/[a-zA-Z0-9\+\-\.]/;function s(e,s,b){function l(e){C.push(e)}var h=s||"scheme start",p=0,v="",D=!1,y=!1,C=[];e:for(;(e[p-1]!=d||0==p)&&!this._isInvalid;){var _=e[p];switch(h){case"scheme start":if(!_||!u.test(_)){if(s){l("Invalid scheme.");break e}v="",h="no scheme";continue}v+=_.toLowerCase(),h="scheme";break;case"scheme":if(_&&o.test(_))v+=_.toLowerCase();else{if(":"!=_){if(s){if(d==_)break e;l("Code point not allowed in scheme: "+_);break e}v="",p=0,h="no scheme";continue}if(this._scheme=v,v="",s)break e;r(this._scheme)&&(this._isRelative=!0),h="file"==this._scheme?"relative":this._isRelative&&b&&b._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==_?(this._query="?",h="query"):"#"==_?(this._fragment="#",h="fragment"):d!=_&&"\t"!=_&&"\n"!=_&&"\r"!=_&&(this._schemeData+=n(_));break;case"no scheme":if(b&&r(b._scheme)){h="relative";continue}l("Missing scheme."),c.call(this);break;case"relative or authority":if("/"!=_||"/"!=e[p+1]){l("Expected /, got: "+_),h="relative";continue}h="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=b._scheme),d==_){this._host=b._host,this._port=b._port,this._path=b._path.slice(),this._query=b._query,this._username=b._username,this._password=b._password;break e}if("/"==_||"\\"==_)"\\"==_&&l("\\ is an invalid code point."),h="relative slash";else if("?"==_)this._host=b._host,this._port=b._port,this._path=b._path.slice(),this._query="?",this._username=b._username,this._password=b._password,h="query";else{if("#"!=_){var g=e[p+1],m=e[p+2];("file"!=this._scheme||!u.test(_)||":"!=g&&"|"!=g||d!=m&&"/"!=m&&"\\"!=m&&"?"!=m&&"#"!=m)&&(this._host=b._host,this._port=b._port,this._username=b._username,this._password=b._password,this._path=b._path.slice(),this._path.pop()),h="relative path";continue}this._host=b._host,this._port=b._port,this._path=b._path.slice(),this._query=b._query,this._fragment="#",this._username=b._username,this._password=b._password,h="fragment"}break;case"relative slash":if("/"!=_&&"\\"!=_){"file"!=this._scheme&&(this._host=b._host,this._port=b._port,this._username=b._username,this._password=b._password),h="relative path";continue}"\\"==_&&l("\\ is an invalid code point."),h="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=_){l("Expected '/', got: "+_),h="authority ignore slashes";continue}h="authority second slash";break;case"authority second slash":if(h="authority ignore slashes","/"!=_){l("Expected '/', got: "+_);continue}break;case"authority ignore slashes":if("/"!=_&&"\\"!=_){h="authority";continue}l("Expected authority, got: "+_);break;case"authority":if("@"==_){D&&(l("@ already seen."),v+="%40"),D=!0;for(var A=0;A{t(642)(t(321))},481:e=>{e.exports='(function(root,factory){if(typeof exports==="object"){module.exports=exports=factory()}else if(typeof define==="function"&&define.amd){define([],factory)}else{globalThis.gbkTool=factory()}})(this,function(){var data=function(zipData){var re=zipData.replace(/#(\\d+)\\$/g,function(a,b){return Array(+b+3).join("#")}).replace(/#/g,"####").replace(/(\\w\\w):([\\w#]+)(?:,|$)/g,function(a,hd,dt){return dt.replace(/../g,function(a){if(a!="##"){return hd+a}else{return a}})});return re}("4e:020405060f12171f20212326292e2f313335373c40414244464a5155575a5b6263646567686a6b6c6d6e6f727475767778797a7b7c7d7f808182838485878a#909697999c9d9ea3aaafb0b1b4b6b7b8b9bcbdbec8cccfd0d2dadbdce0e2e6e7e9edeeeff1f4f8f9fafcfe,4f:00020304050607080b0c12131415161c1d212328292c2d2e31333537393b3e3f40414244454748494a4b4c525456616266686a6b6d6e7172757778797a7d8081828586878a8c8e909293959698999a9c9e9fa1a2a4abadb0b1b2b3b4b6b7b8b9babbbcbdbec0c1c2c6c7c8c9cbcccdd2d3d4d5d6d9dbe0e2e4e5e7ebecf0f2f4f5f6f7f9fbfcfdff,50:000102030405060708090a#0b0e1011131516171b1d1e20222324272b2f303132333435363738393b3d3f404142444546494a4b4d5051525354565758595b5d5e5f6061626364666768696a6b6d6e6f70717273747578797a7c7d818283848687898a8b8c8e8f909192939495969798999a9b9c9d9e9fa0a1a2a4a6aaabadaeafb0b1b3b4b5b6b7b8b9bcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdced0d1d2d3d4d5d7d8d9dbdcdddedfe0e1e2e3e4e5e8e9eaebeff0f1f2f4f6f7f8f9fafcfdfeff,51:00010203040508#090a0c0d0e0f1011131415161718191a1b1c1d1e1f2022232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e42474a4c4e4f5052535758595b5d5e5f606163646667696a6f727a7e7f838486878a8b8e8f90919394989a9d9e9fa1a3a6a7a8a9aaadaeb4b8b9babebfc1c2c3c5c8cacdced0d2d3d4d5d6d7d8d9dadcdedfe2e3e5e6e7e8e9eaeceef1f2f4f7fe,52:0405090b0c0f101314151c1e1f2122232526272a2c2f313234353c3e4445464748494b4e4f5253555758#595a5b5d5f6062636466686b6c6d6e7071737475767778797a7b7c7e808384858687898a8b8c8d8e8f91929495969798999a9ca4a5a6a7aeafb0b4b5b6b7b8b9babbbcbdc0c1c2c4c5c6c8cacccdcecfd1d3d4d5d7d9dadbdcdddee0e1e2e3e5e6e7e8e9eaebecedeeeff1f2f3f4f5f6f7f8fbfcfd,53:0102030407090a0b0c0e11121314181b1c1e1f2224252728292b2c2d2f3031323334353637383c3d404244464b4c4d505458595b5d65686a6c6d7276797b7c7d7e80818387888a8e8f#90919293949697999b9c9ea0a1a4a7aaabacadafb0b1b2b3b4b5b7b8b9babcbdbec0c3c4c5c6c7cecfd0d2d3d5dadcdddee1e2e7f4fafeff,54:000205070b1418191a1c2224252a303336373a3d3f4142444547494c4d4e4f515a5d5e5f6061636567696a6b6c6d6e6f7074797a7e7f8183858788898a8d919397989c9e9fa0a1a2a5aeb0b2b5b6b7b9babcbec3c5cacbd6d8dbe0e1e2e3e4ebeceff0f1f4f5f6f7f8f9fbfe,55:0002030405080a0b0c0d0e121315161718191a1c1d1e1f212526#28292b2d3234353638393a3b3d40424547484b4c4d4e4f515253545758595a5b5d5e5f60626368696b6f7071727374797a7d7f85868c8d8e9092939596979a9b9ea0a1a2a3a4a5a6a8a9aaabacadaeafb0b2b4b6b8babcbfc0c1c2c3c6c7c8cacbcecfd0d5d7d8d9dadbdee0e2e7e9edeef0f1f4f6f8f9fafbfcff,56:0203040506070a0b0d1011121314151617191a1c1d202122252628292a2b2e2f30333537383a3c3d3e404142434445464748494a4b4f5051525355565a5b5d5e5f6061#636566676d6e6f70727374757778797a7d7e7f80818283848788898a8b8c8d9091929495969798999a9b9c9d9e9fa0a1a2a4a5a6a7a8a9aaabacadaeb0b1b2b3b4b5b6b8b9babbbdbebfc0c1c2c3c4c5c6c7c8c9cbcccdcecfd0d1d2d3d5d6d8d9dce3e5e6e7e8e9eaeceeeff2f3f6f7f8fbfc,57:00010205070b0c0d0e0f101112131415161718191a1b1d1e202122242526272b313234353637383c3d3f414344454648494b52535455565859626365676c6e707172747578797a7d7e7f80#818788898a8d8e8f90919495969798999a9c9d9e9fa5a8aaacafb0b1b3b5b6b7b9babbbcbdbebfc0c1c4c5c6c7c8c9cacccdd0d1d3d6d7dbdcdee1e2e3e5e6e7e8e9eaebeceef0f1f2f3f5f6f7fbfcfeff,58:0103040508090a0c0e0f101213141617181a1b1c1d1f222325262728292b2c2d2e2f31323334363738393a3b3c3d3e3f4041424345464748494a4b4e4f505253555657595a5b5c5d5f6061626364666768696a6d6e6f707172737475767778797a7b7c7d7f82848687888a8b8c#8d8e8f909194959697989b9c9da0a1a2a3a4a5a6a7aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbdbebfc0c2c3c4c6c7c8c9cacbcccdcecfd0d2d3d4d6d7d8d9dadbdcdddedfe0e1e2e3e5e6e7e8e9eaedeff1f2f4f5f7f8fafbfcfdfeff,59:000103050608090a0b0c0e1011121317181b1d1e2021222326282c30323335363b3d3e3f404345464a4c4d505253595b5c5d5e5f616364666768696a6b6c6d6e6f70717275777a7b7c7e7f8085898b8c8e8f90919495989a9b9c9d9fa0a1a2a6#a7acadb0b1b3b4b5b6b7b8babcbdbfc0c1c2c3c4c5c7c8c9cccdcecfd5d6d9dbdedfe0e1e2e4e6e7e9eaebedeeeff0f1f2f3f4f5f6f7f8fafcfdfe,5a:00020a0b0d0e0f101214151617191a1b1d1e2122242627282a2b2c2d2e2f3033353738393a3b3d3e3f414243444547484b4c4d4e4f5051525354565758595b5c5d5e5f60616364656668696b6c6d6e6f7071727378797b7c7d7e808182838485868788898a8b8c8d8e8f9091939495969798999c9d9e9fa0a1a2a3a4a5a6a7a8a9abac#adaeafb0b1b4b6b7b9babbbcbdbfc0c3c4c5c6c7c8cacbcdcecfd0d1d3d5d7d9dadbdddedfe2e4e5e7e8eaecedeeeff0f2f3f4f5f6f7f8f9fafbfcfdfeff,5b:0001020304050607080a0b0c0d0e0f10111213141518191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303133353638393a3b3c3d3e3f4142434445464748494a4b4c4d4e4f52565e606167686b6d6e6f7274767778797b7c7e7f82868a8d8e90919294969fa7a8a9acadaeafb1b2b7babbbcc0c1c3c8c9cacbcdcecf#d1d4d5d6d7d8d9dadbdce0e2e3e6e7e9eaebecedeff1f2f3f4f5f6f7fdfe,5c:0002030507080b0c0d0e10121317191b1e1f2021232628292a2b2d2e2f303233353637434446474c4d5253545657585a5b5c5d5f62646768696a6b6c6d70727374757677787b7c7d7e808384858687898a8b8e8f9293959d9e9fa0a1a4a5a6a7a8aaaeafb0b2b4b6b9babbbcbec0c2c3c5c6c7c8c9cacccdcecfd0d1d3d4d5d6d7d8dadbdcdddedfe0e2e3e7e9ebeceeeff1f2f3f4f5f6f7f8f9fafcfdfeff,5d:00#01040508090a0b0c0d0f10111213151718191a1c1d1f2021222325282a2b2c2f3031323335363738393a3b3c3f4041424344454648494d4e4f5051525354555657595a5c5e5f6061626364656667686a6d6e7071727375767778797a7b7c7d7e7f8081838485868788898a8b8c8d8e8f9091929394959697989a9b9c9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b8b9babbbcbdbebfc0c1c2c3c4c6c7c8c9cacbcccecfd0d1d2d3d4d5d6d7d8d9dadcdfe0e3e4eaeced#f0f5f6f8f9fafbfcff,5e:000407090a0b0d0e1213171e1f20212223242528292a2b2c2f303233343536393a3e3f404143464748494a4b4d4e4f50515253565758595a5c5d5f60636465666768696a6b6c6d6e6f70717577797e8182838588898c8d8e92989b9da1a2a3a4a8a9aaabacaeafb0b1b2b4babbbcbdbfc0c1c2c3c4c5c6c7c8cbcccdcecfd0d4d5d7d8d9dadcdddedfe0e1e2e3e4e5e6e7e9ebecedeeeff0f1f2f3f5f8f9fbfcfd,5f:050607090c0d0e10121416191a1c1d1e21222324#282b2c2e30323334353637383b3d3e3f4142434445464748494a4b4c4d4e4f5154595a5b5c5e5f60636567686b6e6f72747576787a7d7e7f83868d8e8f919394969a9b9d9e9fa0a2a3a4a5a6a7a9abacafb0b1b2b3b4b6b8b9babbbebfc0c1c2c7c8cacbced3d4d5dadbdcdedfe2e3e5e6e8e9eceff0f2f3f4f6f7f9fafc,60:0708090b0c10111317181a1e1f2223242c2d2e3031323334363738393a3d3e404445464748494a4c4e4f5153545657585b5c5e5f606165666e71727475777e80#8182858687888a8b8e8f909193959798999c9ea1a2a4a5a7a9aaaeb0b3b5b6b7b9babdbebfc0c1c2c3c4c7c8c9cccdcecfd0d2d3d4d6d7d9dbdee1e2e3e4e5eaf1f2f5f7f8fbfcfdfeff,61:02030405070a0b0c1011121314161718191b1c1d1e21222528292a2c2d2e2f303132333435363738393a3b3c3d3e4041424344454647494b4d4f50525354565758595a5b5c5e5f606163646566696a6b6c6d6e6f717273747678797a7b7c7d7e7f808182838485868788898a8c8d8f9091929395#969798999a9b9c9e9fa0a1a2a3a4a5a6aaabadaeafb0b1b2b3b4b5b6b8b9babbbcbdbfc0c1c3c4c5c6c7c9cccdcecfd0d3d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e7e8e9eaebecedeeeff0f1f2f3f4f6f7f8f9fafbfcfdfe,62:00010203040507091314191c1d1e2023262728292b2d2f303132353638393a3b3c424445464a4f50555657595a5c5d5e5f6061626465687172747577787a7b7d818283858687888b8c8d8e8f9094999c9d9ea3a6a7a9aaadaeafb0b2b3b4b6b7b8babec0c1#c3cbcfd1d5dddee0e1e4eaebf0f2f5f8f9fafb,63:00030405060a0b0c0d0f10121314151718191c2627292c2d2e30313334353637383b3c3e3f40414447484a51525354565758595a5b5c5d60646566686a6b6c6f707273747578797c7d7e7f81838485868b8d9193949597999a9b9c9d9e9fa1a4a6abafb1b2b5b6b9bbbdbfc0c1c2c3c5c7c8cacbccd1d3d4d5d7d8d9dadbdcdddfe2e4e5e6e7e8ebeceeeff0f1f3f5f7f9fafbfcfe,64:0304060708090a0d0e111215161718191a1d1f222324#252728292b2e2f3031323335363738393b3c3e404243494b4c4d4e4f505153555657595a5b5c5d5f60616263646566686a6b6c6e6f70717273747576777b7c7d7e7f8081838688898a8b8c8d8e8f90939497989a9b9c9d9fa0a1a2a3a5a6a7a8aaabafb1b2b3b4b6b9bbbdbebfc1c3c4c6c7c8c9cacbcccfd1d3d4d5d6d9dadbdcdddfe0e1e3e5e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,65:01020304050607080a0b0c0d0e0f10111314151617191a1b1c1d1e1f2021#222324262728292a2c2d30313233373a3c3d404142434446474a4b4d4e5052535457585a5c5f606164656768696a6d6e6f7173757678797a7b7c7d7e7f8081828384858688898a8d8e8f92949596989a9d9ea0a2a3a6a8aaacaeb1b2b3b4b5b6b7b8babbbebfc0c2c7c8c9cacdd0d1d3d4d5d8d9dadbdcdddedfe1e3e4eaebf2f3f4f5f8f9fbfcfdfeff,66:0104050708090b0d1011121617181a1b1c1e2122232426292a2b2c2e3032333738393a3b3d3f40424445464748494a4d4e505158#595b5c5d5e6062636567696a6b6c6d7172737578797b7c7d7f808183858688898a8b8d8e8f909293949598999a9b9c9e9fa0a1a2a3a4a5a6a9aaabacadafb0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8dadedfe0e1e2e3e4e5e7e8eaebecedeeeff1f5f6f8fafbfd,67:010203040506070c0e0f1112131618191a1c1e20212223242527292e303233363738393b3c3e3f414445474a4b4d5254555758595a5b5d62636466676b6c6e717476#78797a7b7d8082838586888a8c8d8e8f9192939496999b9fa0a1a4a6a9acaeb1b2b4b9babbbcbdbebfc0c2c5c6c7c8c9cacbcccdced5d6d7dbdfe1e3e4e6e7e8eaebedeef2f5f6f7f8f9fafbfcfe,68:01020304060d1012141518191a1b1c1e1f20222324252627282b2c2d2e2f30313435363a3b3f474b4d4f52565758595a5b5c5d5e5f6a6c6d6e6f707172737578797a7b7c7d7e7f8082848788898a8b8c8d8e90919294959698999a9b9c9d9e9fa0a1a3a4a5a9aaabacaeb1b2b4b6b7b8#b9babbbcbdbebfc1c3c4c5c6c7c8cacccecfd0d1d3d4d6d7d9dbdcdddedfe1e2e4e5e6e7e8e9eaebecedeff2f3f4f6f7f8fbfdfeff,69:00020304060708090a0c0f11131415161718191a1b1c1d1e21222325262728292a2b2c2e2f313233353637383a3b3c3e4041434445464748494a4b4c4d4e4f50515253555658595b5c5f616264656768696a6c6d6f7072737475767a7b7d7e7f8183858a8b8c8e8f909192939697999a9d9e9fa0a1a2a3a4a5a6a9aaacaeafb0b2b3b5b6b8b9babcbd#bebfc0c2c3c4c5c6c7c8c9cbcdcfd1d2d3d5d6d7d8d9dadcdddee1e2e3e4e5e6e7e8e9eaebeceeeff0f1f3f4f5f6f7f8f9fafbfcfe,6a:000102030405060708090b0c0d0e0f10111213141516191a1b1c1d1e20222324252627292b2c2d2e30323334363738393a3b3c3f40414243454648494a4b4c4d4e4f515253545556575a5c5d5e5f60626364666768696a6b6c6d6e6f70727374757677787a7b7d7e7f81828385868788898a8b8c8d8f929394959698999a9b9c9d9e9fa1a2a3a4a5a6#a7a8aaadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,6b:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f252628292a2b2c2d2e2f303133343536383b3c3d3f4041424445484a4b4d4e4f5051525354555657585a5b5c5d5e5f606168696b6c6d6e6f7071727374757677787a7d7e7f808588#8c8e8f909194959798999c9d9e9fa0a2a3a4a5a6a7a8a9abacadaeafb0b1b2b6b8b9babbbcbdbec0c3c4c6c7c8c9caccced0d1d8dadcdddedfe0e2e3e4e5e6e7e8e9ecedeef0f1f2f4f6f7f8fafbfcfeff,6c:000102030408090a0b0c0e12171c1d1e2023252b2c2d31333637393a3b3c3e3f434445484b4c4d4e4f5152535658595a62636566676b6c6d6e6f71737577787a7b7c7f8084878a8b8d8e9192959697989a9c9d9ea0a2a8acafb0b4b5b6b7bac0c1c2c3c6c7c8cbcdcecfd1d2d8#d9dadcdddfe4e6e7e9ecedf2f4f9ff,6d:000203050608090a0d0f101113141516181c1d1f20212223242628292c2d2f30343637383a3f404244494c50555657585b5d5f6162646567686b6c6d707172737576797a7b7d7e7f8081838486878a8b8d8f9092969798999a9ca2a5acadb0b1b3b4b6b7b9babbbcbdbec1c2c3c8c9cacdcecfd0d2d3d4d5d7dadbdcdfe2e3e5e7e8e9eaedeff0f2f4f5f6f8fafdfeff,6e:0001020304060708090b0f12131518191b1c1e1f222627282a2c2e30313335#3637393b3c3d3e3f40414245464748494a4b4c4f5051525557595a5c5d5e606162636465666768696a6c6d6f707172737475767778797a7b7c7d8081828487888a8b8c8d8e91929394959697999a9b9d9ea0a1a3a4a6a8a9abacadaeb0b3b5b8b9bcbebfc0c3c4c5c6c8c9cacccdced0d2d6d8d9dbdcdde3e7eaebecedeeeff0f1f2f3f5f6f7f8fafbfcfdfeff,6f:000103040507080a0b0c0d0e101112161718191a1b1c1d1e1f212223252627282c2e303234353738393a3b3c3d3f404142#43444548494a4c4e4f5051525354555657595a5b5d5f60616364656768696a6b6c6f707173757677797b7d7e7f808182838586878a8b8f909192939495969798999a9b9d9e9fa0a2a3a4a5a6a8a9aaabacadaeafb0b1b2b4b5b7b8babbbcbdbebfc1c3c4c5c6c7c8cacbcccdcecfd0d3d4d5d6d7d8d9dadbdcdddfe2e3e4e5e6e7e8e9eaebecedf0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,70:000102030405060708090a0b0c0d0e0f1012131415161718191c1d1e1f2021222425262728292a#2b2c2d2e2f30313233343637383a3b3c3d3e3f404142434445464748494a4b4d4e505152535455565758595a5b5c5d5f606162636465666768696a6e7172737477797a7b7d818283848687888b8c8d8f90919397989a9b9e9fa0a1a2a3a4a5a6a7a8a9aab0b2b4b5b6babebfc4c5c6c7c9cbcccdcecfd0d1d2d3d4d5d6d7dadcdddee0e1e2e3e5eaeef0f1f2f3f4f5f6f8fafbfcfeff,71:0001020304050607080b0c0d0e0f111214171b1c1d1e1f2021222324252728292a2b2c2d2e323334#353738393a3b3c3d3e3f4041424344464748494b4d4f505152535455565758595a5b5d5f6061626365696a6b6c6d6f707174757677797b7c7e7f8081828385868788898b8c8d8e909192939596979a9b9c9d9ea1a2a3a4a5a6a7a9aaabadaeafb0b1b2b4b6b7b8babbbcbdbebfc0c1c2c4c5c6c7c8c9cacbcccdcfd0d1d2d3d6d7d8d9dadbdcdddedfe1e2e3e4e6e8e9eaebecedeff0f1f2f3f4f5f6f7f8fafbfcfdfeff,72:0001020304050708090a0b0c0d0e0f101112131415161718191a#1b1c1e1f2021222324252627292b2d2e2f3233343a3c3e40414243444546494a4b4e4f505153545557585a5c5e60636465686a6b6c6d707173747677787b7c7d828385868788898c8e9091939495969798999a9b9c9d9ea0a1a2a3a4a5a6a7a8a9aaabaeb1b2b3b5babbbcbdbebfc0c5c6c7c9cacbcccfd1d3d4d5d6d8dadb#95$,30:000102,00b702:c9c7,00a830:0305,2014ff5e20:162618191c1d,30:141508090a0b0c0d0e0f16171011,00:b1d7f7,22:362728110f2a2908371aa52520,231222:992b2e614c483d1d606e6f64651e3534,26:4240,00b020:3233,2103ff0400a4ff:e0e1,203000a7211626:0605,25:cbcfcec7c6a1a0b3b2,203b21:92909193,30:13#95$,21:70717273747576777879#4$,24:88898a8b8c8d8e8f909192939495969798999a9b7475767778797a7b7c7d7e7f808182838485868760616263646566676869##,32:20212223242526272829##,21:606162636465666768696a6b#97$,ff:010203e505060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5de3#95$,30:4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90919293#106$a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6#103$,03:9192939495969798999a9b9c9d9e9fa0a1a3a4a5a6a7a8a9#6$b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c3c4c5c6c7c8c9#5$,fe:3536393a3f403d3e41424344##3b3c373831#3334#104$,04:10111213141501161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f#13$30313233343551363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f#11$,02:cacbd9,20:13152535,21:050996979899,22:151f23526667bf,25:505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071727381828384858687#88898a8b8c8d8e8f939495bcbde2e3e4e5,2609229530:121d1e#9$,010100e101ce00e0011300e9011b00e8012b00ed01d000ec014d00f301d200f2016b00fa01d400f901:d6d8dadc,00:fcea,0251e7c701:4448,e7c802:61#2$,31:05060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829#19$,30:212223242526272829,32a333:8e8f9c9d9ea1c4ced1d2d5,fe30ff:e2e4#,212132:31#,20:10#1$,30:fc9b9cfdfe069d9e,fe:494a4b4c4d4e4f50515254555657595a5b5c5d5e5f6061#626364656668696a6b,e7:e7e8e9eaebecedeeeff0f1f2f3,30:07#11$,25:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b#13$,72:dcdddfe2e3e4e5e6e7eaebf5f6f9fdfeff,73:00020405060708090b0c0d0f1011121418191a1f2023242627282d2f30323335363a3b3c3d404142434445464748#494a4b4c4e4f515354555658595a5b5c5d5e5f6162636465666768696a6b6e7071#92$72737475767778797a7b7c7d7f808182838586888a8c8d8f90929394959798999a9c9d9ea0a1a3a4a5a6a7a8aaacadb1b4b5b6b8b9bcbdbebfc1c3c4c5c6c7#cbccced2d3d4d5d6d7d8dadbdcdddfe1e2e3e4e6e8eaebeceeeff0f1f3f4f5f6f7#92$f8f9fafbfcfdfeff,74:0001020407080b0c0d0e1112131415161718191c1d1e1f2021232427292b2d2f31323738393a3b3d3e3f4042434445464748494a4b4c4d#4e4f505152535456585d606162636465666768696a6b6c6e6f717273747578797a#92$7b7c7d7f8284858688898a8c8d8f9192939495969798999a9b9d9fa0a1a2a3a4a5a6aaabacadaeafb0b1b2b3b4b5b6b7b8b9bbbcbdbebfc0c1c2c3c4c5c6c7#c8c9cacbcccdcecfd0d1d3d4d5d6d7d8d9dadbdddfe1e5e7e8e9eaebecedf0f1f2#92$f3f5f8f9fafbfcfdfe,75:0001020305060708090a0b0c0e1012141516171b1d1e202122232426272a2e3436393c3d3f414243444647494a4d5051525355565758#5d5e5f60616263646768696b6c6d6e6f7071737576777a7b7c7d7e808182848587#92$88898a8c8d8e909395989b9c9ea2a6a7a8a9aaadb6b7babbbfc0c1c6cbcccecfd0d1d3d7d9dadcdddfe0e1e5e9ecedeeeff2f3f5f6f7f8fafbfdfe,76:02040607#08090b0d0e0f11121314161a1c1d1e212327282c2e2f31323637393a3b3d414244#92$45464748494a4b4e4f50515253555758595a5b5d5f6061626465666768696a6c6d6e7071727374757677797a7c7f80818385898a8c8d8f9092949597989a9b#9c9d9e9fa0a1a2a3a5a6a7a8a9aaabacadafb0b3b5b6b7b8b9babbbcbdbec0c1c3,554a963f57c3632854ce550954c076:914c,853c77ee827e788d72319698978d6c285b894ffa630966975cb880fa684880ae660276ce51f9655671ac7ff1888450b2596561ca6fb382ad634c625253ed54277b06516b75a45df462d48dcb9776628a8019575d97387f627238767d67cf767e64464f708d2562dc7a17659173ed642c6273822c9881677f724862:6ecc,4f3474e3534a529e7eca90a65e2e6886699c81807ed168d278c5868c9551508d8c2482de80de53058912526576:c4c7c9cbccd3d5d9dadcdddee0e1e2e3e4e6e7e8e9eaebecedf0f3f5f6f7fafbfdff,77:00020305060a0c0e0f1011121314151617181b1c1d1e21232425272a2b#2c2e3031323334393b3d3e3f4244454648494a4b4c4d4e4f52535455565758595c,858496f94fdd582199715b9d62:b1a5,66b48c799c8d7206676f789160b253:5117,8f8880cc8d1d94a1500d72c8590760eb711988ab595482ef672c7b285d297ef7752d6cf58e668ff8903c9f3b6bd491197b145f7c78a784d6853d6b:d5d9d6,5e:0187,75f995ed655d5f:0ac5,8f9f58c181c2907f965b97ad8fb97f168d2c62414fbf53:d85e,8f:a8a9ab,904d68075f6a819888689cd6618b522b762a5f6c658c6fd26ee85bbe644851:75b0,67c44e1979c9997c70b377:5d5e5f606467696a6d6e6f7071727374757677787a7b7c818283868788898a8b8f90939495969798999a9b9c9d9ea1a3a4a6a8abadaeafb1b2b4b6b7b8b9ba#bcbec0c1c2c3c4c5c6c7c8c9cacbcccecfd0d1d2d3d4d5d6d8d9dadddedfe0e1e4,75c55e7673bb83e064ad62e894b56ce2535a52c3640f94c27b944f2f5e1b823681:168a,6e246cca9a736355535c54fa886557e04e0d5e036b657c3f90e8601664e6731c88c16750624d8d22776c8e2991c75f6983dc8521991053c286956b8b60:ede8,707f82:cd31,4ed36ca785cf64cd7cd969fd66f9834953957b564fa7518c6d4b5c428e6d63d253c983:2c36,67e578b4643d5bdf5c945dee8be762c667f48c7a640063ba8749998b8c177f2094f24ea7961098a4660c731677:e6e8eaeff0f1f2f4f5f7f9fafbfc,78:0304050607080a0b0e0f101315191b1e20212224282a2b2e2f31323335363d3f414243444648494a4b4d4f51535458595a#5b5c5e5f606162636465666768696f7071727374757678797a7b7d7e7f80818283,573a5c1d5e38957f507f80a05382655e7545553150218d856284949e671d56326f6e5de2543570928f66626f64a463a35f7b6f8890f481e38fb05c1866685ff16c8996488d81886c649179f057ce6a59621054484e587a0b60e96f848bda627f901e9a8b79e4540375f4630153196c608fdf5f1b9a70803b9f7f4f885c3a8d647fc565a570bd51:45b2,866b5d075ba062bd916c75748e0c7a2061017b794ec77ef877854e1181ed521d51fa6a7153a88e87950496cf6ec19664695a78:848586888a8b8f9092949596999d9ea0a2a4a6a8a9aaabacadaeafb5b6b7b8babbbcbdbfc0c2c3c4c6c7c8cccdcecfd1d2d3d6d7d8dadbdcdddedfe0e1e2e3#e4e5e6e7e9eaebedeeeff0f1f3f5f6f8f9fbfcfdfeff,79:00020304060708090a0b0c,784050a877d7641089e6590463e35ddd7a7f693d4f20823955984e3275ae7a975e:628a,95ef521b5439708a6376952457826625693f918755076df37eaf882262337ef075b5832878c196cc8f9e614874f78bcd6b64523a8d506b21806a847156f153064e:ce1b,51d17c97918b7c074fc38e7f7be17a9c64675d1450ac810676017cb96dec7fe067515b:58f8,78cb64:ae13,63:aa2b,9519642d8fbe7b5476296253592754466b7950a362345e266b864ee38d37888b5f85902e79:0d0e0f1011121415161718191a1b1c1d1f2021222325262728292a2b2c2d2e2f3031323335363738393d3f42434445474a4b4c4d4e4f505152545558596163#6466696a6b6c6e70717273747576797b7c7d7e7f8283868788898b8c8d8e909192,6020803d62c54e39535590f863b880c665e66c2e4f4660ee6de18bde5f3986cb5f536321515a83616863520063638e4850125c9b79775bfc52307a3b60bc905376d75f:b797,76848e6c706f767b7b4977aa51f3909358244f4e6ef48fea654c7b1b72c46da47fdf5ae162b55e95573084827b2c5e1d5f1f90127f1498a063826ec7789870b95178975b57ab75354f4375385e9760e659606dc06bbf788953fc96d551cb52016389540a94938c038dcc7239789f87768fed8c0d53e079:939495969798999b9c9d9e9fa0a1a2a3a4a5a6a8a9aaabacadaeafb0b1b2b4b5b6b7b8bcbfc2c4c5c7c8cacccecfd0d3d4d6d7d9dadbdcdddee0e1e2e5e8ea#eceef1f2f3f4f5f6f7f9fafcfeff,7a:0104050708090a0c0f10111213151618191b1c,4e0176ef53ee948998769f0e952d5b9a8ba24e:221c,51ac846361c252a8680b4f97606b51bb6d1e515c6296659796618c46901775d890fd77636bd272:8aec,8bfb583577798d4c675c9540809a5ea66e2159927aef77ed953b6bb565ad7f0e58065151961f5bf958a954288e726566987f56e4949d76fe9041638754c659:1a3a,579b8eb267358dfa8235524160f0581586fe5ce89e454fc4989d8bb95a2560765384627c904f9102997f6069800c513f80335c1499756d314e8c7a:1d1f21222425262728292a2b2c2d2e2f303132343536383a3e4041424344454748494a4b4c4d4e4f50525354555658595a5b5c5d5e5f606162636465666768#696a6b6c6d6e6f717273757b7c7d7e828587898a8b8c8e8f909394999a9b9ea1a2,8d3053d17f5a7b4f4f104e4f96006cd573d085e95e06756a7ffb6a0a77fe94927e4151e170e653cd8fd483038d2972af996d6cdb574a82b365b980aa623f963259a84eff8bbf7eba653e83f2975e556198de80a5532a8bfd542080ba5e9f6cb88d3982ac915a54296c1b52067eb7575f711a6c7e7c89594b4efd5fff61247caa4e305c0167ab87025cf0950b98ce75af70fd902251af7f1d8bbd594951e44f5b5426592b657780a45b7562:76c2,8f905e456c1f7b264f:0fd8,670d7a:a3a4a7a9aaabaeafb0b1b2b4b5b6b7b8b9babbbcbdbec0c1c2c3c4c5c6c7c8c9cacccdcecfd0d1d2d3d4d5d7d8dadbdcdde1e2e4e7e8e9eaebeceef0f1f2f3#f4f5f6f7f8fbfcfe,7b:0001020507090c0d0e1012131617181a1c1d1f21222327292d,6d:6eaa,798f88b15f17752b629a8f854fef91dc65a781:2f51,5e9c81508d74526f89868d4b590d50854ed8961c723681798d1f5bcc8ba3964459877f1a549056:760e,8be565396982949976d66e895e72751867:46d1,7aff809d8d76611f79c665628d635188521a94a27f38809b7eb25c976e2f67607bd9768b9ad8818f7f947cd5641e95507a3f54:4ae5,6b4c640162089e3d80f3759952729769845b683c86e496:0194,94ec4e2a54047ed968398ddf801566f45e9a7fb97b:2f303234353637393b3d3f404142434446484a4d4e535557595c5e5f61636465666768696a6b6c6d6f70737476787a7c7d7f81828384868788898a8b8c8e8f#9192939698999a9b9e9fa0a3a4a5aeafb0b2b3b5b6b7b9babbbcbdbebfc0c2c3c4,57c2803f68975de5653b529f606d9f9a4f9b8eac516c5bab5f135de96c5e62f18d21517194a952fe6c9f82df72d757a267848d2d591f8f9c83c754957b8d4f306cbd5b6459d19f1353e486ca9aa88c3780a16545987e56fa96c7522e74dc52505be1630289024e5662d0602a68fa51735b9851a089c27ba199867f5060ef704c8d2f51495e7f901b747089c4572d78455f529f9f95fa8f689b3c8be17678684267dc8d:ea35,523d8f8a6eda68cd950590ed56fd679c88f98fc754c87b:c5c8c9cacbcdcecfd0d2d4d5d6d7d8dbdcdedfe0e2e3e4e7e8e9ebecedeff0f2f3f4f5f6f8f9fafbfdff,7c:0001020304050608090a0d0e101112131415171819#1a1b1c1d1e20212223242528292b2c2d2e2f3031323334353637393a3b3c3d3e42,9ab85b696d776c264ea55bb39a87916361a890af97e9542b6db55bd251fd558a7f:55f0,64bc634d65f161be608d710a6c:5749,592f676d822a58d5568e8c6a6beb90dd597d801753f76d695475559d83:77cf,683879be548c4f55540876d28c8996026cb36db88d6b89109e648d3a563f9ed175d55f8872e0606854fc4ea86a2a886160528f7054c470d886799e3f6d2a5b8f5f187ea255894faf7334543c539a501954:0e7c,4e4e5ffd745a58f6846b80e1877472d07cca6e567c:434445464748494a4b4c4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717275767778797a7e7f8081828384858687#888a8b8c8d8e8f90939496999a9ba0a1a3a6a7a8a9abacadafb0b4b5b6b7b8babb,5f27864e552c62a44e926caa623782b154d7534e733e6ed1753b521253168bdd69d05f8a60006dee574f6b2273af68538fd87f13636260a3552475ea8c6271156da35ba65e7b8352614c9ec478fa87577c27768751f060f6714c66435e4c604d8c0e707063258f895fbd606286d456de6bc160946167534960e066668d3f79fd4f1a70e96c478b:b3f2,7ed88364660f5a5a9b426d:51f7,8c416d3b4f19706b83b7621660d1970d8d27797851fb57:3efa,673a75787a3d79ef7b957c:bfc0c2c3c4c6c9cbcecfd0d1d2d3d4d8dadbdddee1e2e3e4e5e6e7e9eaebecedeef0f1f2f3f4f5f6f7f9fafcfdfeff,7d:000102030405060708090b0c0d0e0f10#1112131415161718191a1b1c1d1e1f212324252628292a2c2d2e30313233343536,808c99658ff96fc08ba59e2159ec7ee97f095409678168d88f917c4d96c653ca602575be6c7253735ac97ea7632451e0810a5df184df628051805b634f0e796d524260b86d4e5b:c4c2,8b:a1b0,65e25fcc964559937e:e7aa,560967b759394f735bb652a0835a988a8d3e753294be50477a3c4ef767b69a7e5ac16b7c76d1575a5c167b3a95f4714e517c80a9827059787f04832768c067ec78:b177,62e363617b804fed526a51cf835069db92748d:f531,89c1952e7bad4ef67d:3738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6f70717273747576#78797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798,506582305251996f6e:1085,6da75efa50f559dc5c066d466c5f7586848b686859568bb253209171964d854969127901712680f64ea490ca6d479a845a0756bc640594f077eb4fa5811a72e189d2997a7f347ede527f655991758f:7f83,53eb7a9663:eda5,768679f888579636622a52ab8282685467706377776b7aed6d017ed389e359d0621285c982a5754c501f4ecb75a58beb5c4a5dfe7b4b65a491d14eca6d25895f7d2795264ec58c288fdb9773664b79818fd170ec6d787d:999a9b9c9d9e9fa0a1a2a3a4a5a7a8a9aaabacadafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9#dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fa,5c3d52b283465162830e775b66769cb84eac60ca7c:beb3,7ecf4e958b66666f988897595883656c955c5f8475c997567a:dfde,51c070af7a9863ea7a767ea0739697ed4e4570784e5d915253a965:51e7,81fc8205548e5c31759a97a062d872d975bd5c459a7983ca5c40548077e94e3e6cae805a62d2636e5de851778ddd8e1e952f4ff153e560e770ac526763509e435a1f5026773753777ee26485652b628963985014723589c951b38bc07edd574783cc94a7519b541b5cfb7d:fbfcfdfeff,7e:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839#3a3c3d3e3f40424344454648494a4b4c4d4e4f505152535455565758595a5b5c5d,4fca7ae36d5a90e19a8f55805496536154af5f0063e9697751ef6168520a582a52d8574e780d770b5eb761777ce062:5b97,4ea27095800362f770e49760577782db67ef68f578d5989779d158f354b353ef6e34514b523b5ba28bfe80af554357a660735751542d7a7a60505b5463a762a053e362635bc767af54ed7a9f82e691775e9388e4593857ae630e8de880ef57577b774fa95feb5bbd6b3e53217b5072c2684677:ff36,65f751b54e8f76d45cbf7aa58475594e9b4150807e:5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081838485868788898a8b8c8d8e8f909192939495969798999a9c9d9e#aeb4bbbcd6e4ecf9,7f:0a101e37393b3c3d3e3f404143464748494a4b4c4d4e4f5253,998861276e8357646606634656f062:ec69,5ed39614578362c955878721814a8fa3556683b167658d5684dd5a6a680f62e67bee961151706f9c8c3063fd89c861d27f0670c26ee57405699472fc5eca90ce67176d6a635e52b3726280014f6c59e5916a70d96d9d52d24e5096f7956d857e78ca7d2f5121579264c2808b7c7b6cea68f1695e51b7539868a872819ece7bf172f879bb6f137406674e91cc9ca4793c83:8954,540f68174e3d538952b1783e5386522950884f:8bd0,7f:56595b5c5d5e6063646566676b6c6d6f7073757677787a7b7c7d7f8082838485868788898b8d8f9091929395969798999b9ca0a2a3a5a6a8a9aaabacadaeb1#b3b4b5b6b7babbbec0c2c3c4c6c7c8c9cbcdcfd0d1d2d3d6d7d9dadbdcdddee2e3,75e27acb7c926ca596b6529b748354e94fe9805483b28fde95705ec9601c6d9f5e18655b813894fe604b70bc7ec37cae51c968817cb1826f4e248f8691cf667e4eae8c0564a9804a50da759771ce5be58fbd6f664e86648295635ed66599521788c270c852a3730e7433679778f797164e3490bb9cde6dcb51db8d41541d62ce73b283f196f69f8494c34f367f9a51cc707596755cad988653e64ee46e9c740969b4786b998f7559521876246d4167f3516d9f99804b54997b3c7abf7f:e4e7e8eaebecedeff2f4f5f6f7f8f9fafdfeff,80:020708090a0e0f11131a1b1d1e1f2123242b2c2d2e2f303234393a3c3e404144454748494e4f505153555657#595b5c5d5e5f6061626364656667686b6c6d6e6f7072737475767778797a7b7c7d,9686578462e29647697c5a0464027bd36f0f964b82a6536298855e90708963b35364864f9c819e93788c97328d:ef42,9e7f6f5e79845f559646622e9a74541594dd4fa365c55c:6561,7f1586516c2f5f8b73876ee47eff5ce6631b5b6a6ee653754e7163a0756562a18f6e4f264ed16ca67eb68bba841d87ba7f57903b95237ba99aa188f8843d6d1b9a867edc59889ebb739b780186829a:6c82,561b541757cb4e709ea653568fc881097792999286ee6ee1851366fc61626f2b80:7e818285888a8d8e8f909192949597999ea3a6a7a8acb0b3b5b6b8b9bbc5c7c8c9cacbcfd0d1d2d3d4d5d8dfe0e2e3e6eef5f7f9fbfeff,81:000103040507080b#0c1517191b1c1d1f202122232425262728292a2b2d2e3033343537393a3b3c3d3f,8c298292832b76f26c135fd983bd732b8305951a6bdb77db94c6536f830251925e3d8c8c8d384e4873ab679a68859176970971646ca177095a9295416bcf7f8e66275bd059b95a9a95:e8f7,4eec84:0c99,6aac76df9530731b68a65b5f772f919a97617cdc8ff78c1c5f257c7379d889c56ccc871c5bc65e4268c977207ef551:954d,52c95a297f05976282d763cf778485d079d26e3a5e9959998511706d6c1162bf76bf654f60af95fd660e879f9e2394ed54:0d7d,8c2c647881:40414243444547494d4e4f525657585b5c5d5e5f6162636466686a6b6c6f727375767778818384858687898b8c8d8e90929394959697999a9e9fa0a1a2a4a5#a7a9abacadaeafb0b1b2b4b5b6b7b8b9bcbdbebfc4c5c7c8c9cbcdcecfd0d1d2d3,647986116a21819c78e864699b5462b9672b83ab58a89ed86cab6f205bde964c8c0b725f67d062c772614ea959c66bcd589366ae5e5552df6155672876ee776672677a4662ff54:ea50,94a090a35a1c7eb36c164e435976801059485357753796be56ca63208111607c95f96dd65462998151855ae980fd59ae9713502a6ce55c3c62df4f60533f817b90066eba852b62c85e7478be64b5637b5ff55a18917f9e1f5c3f634f80425b7d556e95:4a4d,6d8560a867e072de51dd5b8181:d4d5d6d7d8d9dadbdcdddedfe0e1e2e4e5e6e8e9ebeeeff0f1f2f5f6f7f8f9fafdff,82:030708090a0b0e0f111315161718191a1d2024252627292e323a3c3d3f#404142434546484a4c4d4e5051525354555657595b5c5d5e606162636465666769,62e76cde725b626d94ae7ebd81136d53519c5f04597452aa6012597366968650759f632a61e67cef8bfa54e66b279e256bb485d5545550766ca4556a8db4722c5e156015743662cd6392724c5f986e436d3e65006f5876d878d076fc7554522453db4e535e9e65c180:2ad6,629b5486522870ae888d8dd16ce1547880da57f988f48d54966a914d4f696c9b55b776c6783062a870f96f8e5f6d84ec68da787c7bf781a8670b9e4f636778b0576f7812973962:79ab,528874356bd782:6a6b6c6d71757677787b7c808183858687898c90939495969a9b9ea0a2a3a7b2b5b6babbbcbfc0c2c3c5c6c9d0d6d9dadde2e7e8e9eaecedeef0f2f3f5f6f8#fafcfdfeff,83:000a0b0d1012131618191d1e1f20212223242526292a2e3032373b3d,5564813e75b276ae533975de50fb5c418b6c7bc7504f72479a9798d86f0274e27968648777a562fc98918d2b54c180584e52576a82f9840d5e7351ed74f68bc45c4f57616cfc98875a4678349b448feb7c955256625194fa4ec68386846183e984b257d467345703666e6d668c3166dd7011671f6b3a6816621a59bb4e0351c46f0667d26c8f517668cb59476b6775665d0e81109f5065d779:4841,9a918d775c824e5e4f01542f5951780c56686c148fc45f036c:7de3,8bab639083:3e3f41424445484a4b4c4d4e5355565758595d6270717273747576797a7e7f808182838487888a8b8c8d8f909194959697999a9d9fa1a2a3a4a5a6a7acadae#afb5bbbebfc2c3c4c6c8c9cbcdced0d1d2d3d5d7d9dadbdee2e3e4e6e7e8ebeced,60706d3d7275626694:8ec5,53438fc17b7e4edf8c264e7e9ed494:b1b3,524d6f5c90636d458c3458115d4c6b:2049,67aa545b81547f8c589985375f3a62a26a47953965726084686577a74e544fa85de7979864ac7fd85ced4fcf7a8d520783044e14602f7a8394a64fb54eb279e6743452e482b964d279bd5bdd6c8197528f7b6c22503e537f6e0564ce66746c3060c598778bf75e86743c7a7779cb4e1890b174036c4256da914b6cc58d8b533a86c666f28eaf5c489a716e2083:eeeff3f4f5f6f7fafbfcfeff,84:0002050708090a10121314151617191a1b1e1f20212223292a2b2c2d2e2f30323334353637393a3b3e3f404142434445474849#4a4b4c4d4e4f505253545556585d5e5f606264656667686a6e6f70727477797b7c,53d65a369f8b8da353bb570898a76743919b6cc9516875ca62f372ac52:389d,7f3a7094763853749e4a69b7786e96c088d97fa471:36c3,518967d374e458e4651856b78ba9997662707ed560f970ed58ec4e:c1ba,5fcd97e74efb8ba45203598a7eab62544ecd65e5620e833884c98363878d71946eb65bb97ed2519763c967d480898339881551125b7a59828fb14e736c5d516589258f6f962e854a745e95:10f0,6da682e55f3164926d128428816e9cc3585e8d5b4e0953c184:7d7e7f8081838485868a8d8f90919293949596989a9b9d9e9fa0a2a3a4a5a6a7a8a9aaabacadaeb0b1b3b5b6b7bbbcbec0c2c3c5c6c7c8cbcccecfd2d4d5d7#d8d9dadbdcdee1e2e4e7e8e9eaebedeeeff1f2f3f4f5f6f7f8f9fafbfdfe,85:000102,4f1e6563685155d34e2764149a9a626b5ac2745f82726da968ee50e7838e7802674052396c997eb150bb5565715e7b5b665273ca82eb67495c715220717d886b95ea965564c58d6181b355846c5562477f2e58924f2455468d4f664c4e0a5c1a88f368a2634e7a0d70e7828d52fa97f65c1154e890b57ecd59628d4a86c782:0c0d,8d6664445c0461516d89793e8bbe78377533547b4f388eab6df15a207ec5795e6c885ba15a76751a80be614e6e1758f075:1f25,727253477ef385:030405060708090a0b0d0e0f101214151618191b1c1d1e2022232425262728292a2d2e2f303132333435363e3f404142444546474b4c4d4e4f505152535455#57585a5b5c5d5f60616263656667696a6b6c6d6e6f707173757677787c7d7f8081,770176db526980dc57235e08593172ee65bd6e7f8bd75c388671534177f362fe65f64ec098df86805b9e8bc653f277e24f7f5c4e9a7659cb5f0f793a58eb4e1667ff4e8b62ed8a93901d52bf662f55dc566c90024ed54f8d91ca99706c0f5e0260435ba489c68bd56536624b99965b:88ff,6388552e53d77626517d852c67a268b36b8a62928f9353d482126dd1758f4e668d4e5b70719f85af66:91d9,7f7287009ecd9f205c5e672f8ff06811675f620d7ad658855eb665706f3185:82838688898a8b8c8d8e909192939495969798999a9d9e9fa0a1a2a3a5a6a7a9abacadb1b2b3b4b5b6b8babbbcbdbebfc0c2c3c4c5c6c7c8cacbcccdced1d2#d4d6d7d8d9dadbdddedfe0e1e2e3e5e6e7e8eaebecedeeeff0f1f2f3f4f5f6f7f8,60555237800d6454887075295e05681362f4971c53cc723d8c016c3477617a0e542e77ac987a821c8bf47855671470c165af64955636601d79c153f84e1d6b7b80865bfa55e356db4f:3a3c,99725df3677e80386002988290015b8b8b:bcf5,641c825864de55fd82cf91654fd77d20901f7c9f50f358516eaf5bbf8bc980839178849c7b97867d96:8b8f,7ee59ad3788e5c817a57904296a7795f5b59635f7b0b84d168ad55067f2974107d2295016240584c4ed65b835979585485:f9fafcfdfe,86:0001020304060708090a0b0c0d0e0f10121314151718191a1b1c1d1e1f20212223242526282a2b2c2d2e2f3031323334353637393a3b3d3e3f40#4142434445464748494a4b4c525355565758595b5c5d5f6061636465666768696a,736d631e8e:4b0f,80ce82d462ac53f06cf0915e592a60016c70574d644a8d2a762b6ee9575b6a8075f06f6d8c:2d08,57666bef889278b363a253f970ad6c645858642a580268e0819b55107cd650188eba6dcc8d9f70eb638f6d9b6ed47ee68404684390036dd896768ba85957727985e4817e75bc8a8a68af52548e22951163d098988e44557c4f5366ff568f60d56d9552435c4959296dfb586b75:301c,606c82148146631167618fe2773a8d:f334,94c15e165385542c70c386:6d6f7072737475767778838485868788898e8f90919294969798999a9b9e9fa0a1a2a5a6abadaeb2b3b7b8b9bbbcbdbebfc1c2c3c5c8cccdd2d3d5d6d7dadc#dde0e1e2e3e5e6e7e8eaebeceff5f6f7fafbfcfdff,87:010405060b0c0e0f10111416,6c405ef7505c4ead5ead633a8247901a6850916e77b3540c94dc5f647ae5687663457b527edf75db507762955934900f51f879c37a8156fe5f9290146d825c60571f541051546e4d56e263a89893817f8715892a9000541e5c6f81c062:d658,81319e3596409a:6e7c,692d59a562d3553e631654c786d96d3c5a0374e6889c6b6a59168c4c5f2f6e7e73a9987d4e3870f75b8c7897633d665a769660cb5b9b5a494e0781556c6a738b4ea167897f515f8065fa671b5fd859845a0187:191b1d1f20242627282a2b2c2d2f303233353638393a3c3d404142434445464a4b4d4f505152545556585a5b5c5d5e5f6162666768696a6b6c6d6f71727375#7778797a7f8081848687898a8c8e8f90919294959698999a9b9c9d9ea0a1a2a3a4,5dcd5fae537197e68fdd684556f4552f60df4e3a6f4d7ef482c7840e59d44f:1f2a,5c3e7eac672a851a5473754f80c355829b4f4f4d6e2d8c135c096170536b761f6e29868a658795fb7eb9543b7a337d0a95ee55e17fc174ee631d87176da17a9d621165a1536763e16c835deb545c94a84e4c6c618bec5c4b65e0829c68a754:3e34,6b:cb66,4e9463425348821e4f:0dae,575e620a96fe6664726952:ffa1,609f8bef661471996790897f785277fd6670563b54389521727a87:a5a6a7a9aaaeb0b1b2b4b6b7b8b9bbbcbebfc1c2c3c4c5c7c8c9cccdcecfd0d4d5d6d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedeff0f1f2f3f4f5f6f7f8#fafbfcfdff,88:0001020405060708090b0c0d0e0f101112141718191a1c1d1e1f2023,7a00606f5e0c6089819d591560dc718470ef6eaa6c5072806a8488ad5e2d4e605ab3559c94e36d177cfb9699620f7ec6778e867e5323971e8f9666875ce14fa072ed4e0b53a6590f54136380952851484ed99c9c7ea454b88d248854823795f26d8e5f265acc663e966973:b02e,53bf817a99857fa15baa96:7750,7ebf76f853a2957699997bb189446e584e617fd479658be660f354cd4eab98795df76a6150cf54118c618427785d9704524a54ee56a395006d885bb56dc6665388:2425262728292a2b2c2d2e2f30313334353637383a3b3d3e3f414243464748494a4b4e4f505152535556585a5b5c5d5e5f6066676a6d6f717374757678797a#7b7c80838687898a8c8e8f90919394959798999a9b9d9e9fa0a1a3a5a6a7a8a9aa,5c0f5b5d6821809655787b11654869544e9b6b47874e978b534f631f643a90aa659c80c18c10519968b0537887f961c86c:c4fb,8c225c5185aa82af950c6b238f9b65b05f:fbc3,4fe18845661f8165732960fa51745211578b5f6290a2884c91925e78674f602759d351:44f6,80f853086c7996c4718a4f:11ee,7f9e673d55c5950879c088967ee3589f620c9700865a5618987b5f908bb884c4915753d965ed5e8f755c60647d6e5a7f7e:eaed,8f6955a75ba360ac65cb738488:acaeafb0b2b3b4b5b6b8b9babbbdbebfc0c3c4c7c8cacbcccdcfd0d1d3d6d7dadbdcdddee0e1e6e7e9eaebecedeeeff2f5f6f7fafbfdff,89:0001030405060708#090b0c0d0e0f1114151617181c1d1e1f20222324262728292c2d2e2f3132333537,9009766377297eda9774859b5b667a7496ea884052cb718f5faa65ec8be25bfb9a6f5de16b896c5b8b:adaf,900a8fc5538b62bc9e:262d,54404e2b82bd7259869c5d1688596daf96c554d14e9a8bb6710954bd960970df6df976d04e25781487125ca95ef68a00989c960e708e6cbf594463a9773c884d6f148273583071d5538c781a96c155015f6671305bb48c1a9a8c6b83592e9e2f79e76768626c4f6f75a17f8a6d0b96336c274ef075d2517b68376f3e908081705996747689:38393a3b3c3d3e3f40424345464748494a4b4c4d4e4f505152535455565758595a5b5c5d6061626364656768696a6b6c6d6e6f707172737475767778797a7c#7d7e808284858788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1,64475c2790657a918c2359da54ac8200836f898180006930564e8036723791ce51b64e5f987563964e1a53f666f3814b591c6db24e0058f9533b63d694f14f:9d0a,886398905937905779fb4eea80f075916c825b9c59e85f5d69058681501a5df24e5977e34ee5827a6291661390915c794ebf5f7981c69038808475ab4ea688d4610f6bc55fc64e4976ca6ea28b:e3ae,8c0a8bd15f027f:fccc,7ece83:356b,56e06bb797f3963459fb541f94f66deb5bc5996e5c395f15969089:a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c3cdd3d4d5d7d8d9dbdddfe0e1e2e4e7e8e9eaecedeef0f1f2f4f5f6f7f8f9fa#fbfcfdfeff,8a:01020304050608090a0b0c0d0e0f101112131415161718191a1b1c1d,537082f16a315a749e705e947f2883b984:2425,836787478fce8d6276c85f719896786c662054df62e54f6381c375c85eb896cd8e0a86f9548f6cf36d8c6c38607f52c775285e7d4f1860a05fe75c24753190ae94c072b96cb96e389149670953:cbf3,4f5191c98bf153c85e7c8fc26de44e8e76c26986865e611a82064f:59de,903e9c7c61096e:1d14,96854e885a3196e84e0e5c7f79b95b878bed7fbd738957df828b90c15401904755bb5cea5fa161086b3272f180b28a:891e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3f4041424344454647494a4b4c4d4e4f505152535455565758595a5b5c5d5e#5f606162636465666768696a6b6c6d6e6f7071727374757677787a7b7c7d7e7f80,6d745bd388d598848c6b9a6d9e336e0a51:a443,57a38881539f63f48f9556ed54585706733f6e907f188fdc82d1613f6028966266f07ea68d:8ac3,94a55cb37ca4670860a6960580184e9190e75300966851418fd08574915d665597f55b55531d78386742683d54c9707e5bb08f7d518d572854b1651266828d:5e43,810f846c906d7cdf51ff85fb67a365e96fa186a48e81566a90207682707671e58d2362e952196cfd8d3c600e589e618e66fe8d60624e55b36e23672d8f678a:81828384858687888b8c8d8e8f9091929495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2#c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3,94e195f87728680569a8548b4e4d70b88bc86458658b5b857a84503a5be877bb6be18a797c986cbe76cf65a98f975d2d5c5586386808536062187ad96e5b7efd6a1f7ae05f706f335f20638c6da867564e085e108d264ed780c07634969c62db662d627e6cbc8d7571677f695146808753ec906e629854f286f08f998005951785178fd96d5973cd659f771f7504782781fb8d1e94884fa6679575b98bca9707632f9547963584b8632377415f8172f04e896014657462ef6b63653f8a:e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,8b:0001020304050608090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223#24252728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445,5e2775c790d18bc1829d679d652f5431871877e580a281026c414e4b7ec7804c76f4690d6b966267503c4f84574063076b628dbe53ea65e87eb85fd763:1ab7,81:f3f4,7f6e5e1c5cd95236667a79e97a1a8d28709975d46ede6cbb7a924e2d76c55fe0949f88777ec879cd80bf91cd4ef24f17821f54685dde6d328bcc7ca58f7480985e1a549276b15b99663c9aa473e0682a86db6731732a8b:f8db,90107af970db716e62c477a956314e3b845767f152a986c08d2e94f87b518b:464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061626364656768696a6b6d6e6f707172737475767778797a7b7c7d7e7f80818283848586#8788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9facb1bbc7d0ea,8c:091e,4f4f6ce8795d9a7b6293722a62fd4e1378168f6c64b08d5a7bc668695e8488c55986649e58ee72b6690e95258ffd8d5857607f008c0651c6634962d95353684c74228301914c55447740707c6d4a517954a88d4459ff6ecb6dc45b5c7d2b4ed47c7d6ed35b5081ea6e0d5b579b0368d58e2a5b977efc603b7eb590b98d70594f63cd79df8db3535265cf79568bc5963b7ec494bb7e825634918967007f6a5c0a907566285de64f5067de505a4f5c57505e:a7#3$,8c:38393a3b3c3d3e3f4042434445484a4b4d4e4f5051525354565758595b5c5d5e5f60636465666768696c6d6e6f707172747576777b7c7d7e7f808183848687#888b8d8e8f90919293959697999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacad,4e:8d0c,51404e105eff53454e:15981e,9b325b6c56694e2879ba4e3f53154e47592d723b536e6c1056df80e499976bd3777e9f174e:369f,9f104e:5c6993,82885b5b556c560f4ec453:8d9da3a5ae,97658d5d53:1af5262e3e,8d5c53:6663,52:02080e2d333f404c5e615c,84af52:7d82819093,51827f544e:bbc3c9c2e8e1ebde,4f1b4ef34f:2264,4ef54f:2527092b5e67,65384f:5a5d,8c:aeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebec#edeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,8d:000102030405060708090a0b0c0d,4f:5f57323d76749189838f7e7baa7cac94e6e8eac5dae3dcd1dff8,50:294c,4ff350:2c0f2e2d,4ffe50:1c0c25287e4355484e6c7ba5a7a9bad6,510650:edece6ee,51:070b,4edd6c3d4f:5865ce,9fa06c467c74516e5dfd9ec999985181591452f9530d8a07531051eb591951554ea051564eb388:6ea4,4eb5811488d279805b3488037fb851:abb1bdbc,8d:0e0f101112131415161718191a1b1c205152575f6568696a6c6e6f717278797a7b7c7d7e7f808283868788898c8d8e8f90929395969798999a9b9c9d9ea0a1#a2a4a5a6a7a8a9aaabacadaeafb0b2b6b7b9bbbdc0c1c2c5c7c8c9cacdd0d2d3d4,51:c796a2a5,8b:a0a6a7aab4b5b7c2c3cbcfced2d3d4d6d8d9dcdfe0e4e8e9eef0f3f6f9fcff,8c:000204070c0f1112141516191b181d1f202125272a2b2e2f32333536,53:697a,96:1d2221312a3d3c4249545f676c7274888d97b0,90:979b9d99aca1b4b3b6ba,8d:d5d8d9dce0e1e2e5e6e7e9edeef0f1f2f4f6fcfeff,8e:00010203040607080b0d0e1011121315161718191a1b1c202124252627282b2d303233343637383b3c3e#3f4345464c4d4e4f505354555657585a5b5c5d5e5f60616263646567686a6b6e71,90:b8b0cfc5bed0c4c7d3e6e2dcd7dbebeffe,91:04221e23312f394346,520d594252:a2acadbe,54ff52:d0d6f0,53df71ee77cd5ef451:f5fc,9b2f53b65f01755a5def57:4ca9a1,58:7ebcc5d1,57:292c2a33392e2f5c3b4269856b867c7b686d7673ada48cb2cfa7b493a0d5d8dad9d2b8f4eff8e4dd,8e:73757778797a7b7d7e808283848688898a8b8c8d8e91929395969798999a9b9d9fa0a1a2a3a4a5a6a7a8a9aaadaeb0b1b3b4b5b6b7b8b9bbbcbdbebfc0c1c2#c3c4c5c6c7c8c9cacbcccdcfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4,58:0b0d,57:fded,58:001e194420656c81899a80,99a89f1961ff82:797d7f8f8aa8848e919799abb8beb0c8cae398b7aecbccc1a9b4a1aa9fc4cea4e1,830982:f7e4,83:0f07,82:dcf4d2d8,830c82:fbd3,83:111a061415,82:e0d5,83:1c515b5c08923c34319b5e2f4f47435f4017602d3a336665,8e:e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,8f:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223#2425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344,83:681b696c6a6d6eb078b3b4a0aa939c857cb6a97db87b989ea8babcc1,840183:e5d8,580784:180b,83:ddfdd6,84:1c381106,83:d4df,84:0f03,83:f8f9eac5c0,842683:f0e1,84:5c515a597387887a89783c4669768c8e316dc1cdd0e6bdd3cabfbae0a1b9b497e5e3,850c750d853884f085:391f3a,8f:45464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061626364656a808c929da0a1a2a4a5a6a7aaacadaeafb2b3b4b5b7b8babbbcbfc0c3c6#c9cacbcccdcfd2d6d7dae0e1e3e7eceff1f2f4f5f6fafbfcfeff,90:07080c0e131518,85:563b,84:fffc,85:594868645e7a,77a285:43727ba4a8878f79ae9c85b9b7b0d3c1dcff,86:270529163c,5efe5f0859:3c41,803759:555a58,530f5c:22252c34,62:4c6a9fbbcadad7ee,632262f663:394b43adf6717a8eb46dac8a69aebcf2f8e0ffc4dece,645263:c6be,64:45410b1b200c26215e846d96,90:191c2324252728292a2b2c303132333437393a3d3f4043454648494a4b4c4e545556595a5c5d5e5f6061646667696a6b6c6f70717273767778797a7b7c7e81#84858687898a8c8d8e8f90929496989a9c9e9fa0a4a5a7a8a9abadb2b7bcbdbfc0,64:7ab7b899bac0d0d7e4e2,65:09252e,5f:0bd2,75195f1153:5ff1fde9e8fb,54:1216064b5253545643215759233282947771649a9b8476669dd0adc2b4d2a7a6d3d472a3d5bbbfccd9dadca9aaa4ddcfde,551b54e7552054fd551454f355:22230f11272a678fb5496d41553f503c,90:c2c3c6c8c9cbcccdd2d4d5d6d8d9dadedfe0e3e4e5e9eaeceef0f1f2f3f5f6f7f9fafbfcff,91:00010305060708090a0b0c0d0e0f1011121314151617181a1b1c#1d1f20212425262728292a2b2c2d2e30323334353637383a3b3c3d3e3f40414244,55:375675767733305c8bd283b1b988819f7ed6917bdfbdbe9499eaf7c9,561f55:d1ebecd4e6ddc4efe5f2f3cccde8f5e4,8f9456:1e080c012423,55fe56:00272d5839572c4d62595c4c548664716b7b7c8593afd4d7dde1f5ebf9ff,57:040a091c,5e:0f191411313b3c,91:454748515354555658595b5c5f606667686b6d737a7b7c808182838486888a8e8f939495969798999c9d9e9fa0a1a4a5a6a7a8a9abacb0b1b2b3b6b7b8b9bb#bcbdbebfc0c1c2c3c4c5c6c8cbd0d2d3d4d5d6d7d8d9dadbdddedfe0e1e2e3e4e5,5e:3744545b5e61,5c:8c7a8d9096889899919a9cb5a2bdacabb1a3c1b7c4d2e4cbe5,5d:020327262e241e061b583e343d6c5b6f5d6b4b4a697482999d,8c735d:b7c5,5f:73778287898c95999ca8adb5bc,88625f6172:adb0b4b7b8c3c1cecdd2e8efe9f2f4f7,730172f3730372fa91:e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,92:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324#25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445,72fb73:1713210a1e1d152239252c3831504d57606c6f7e,821b592598e759:2402,99:636768696a6b6c74777d8084878a8d9091939495,5e:80918b96a5a0b9b5beb3,8d535e:d2d1dbe8ea,81ba5f:c4c9d6cf,60035fee60045f:e1e4fe,60:0506,5f:eaedf8,60:1935261b0f0d292b0a3f2178797b7a42,92:464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071727375767778797a7b7c7d7e7f808182838485#868788898a8b8c8d8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7,60:6a7d969aad9d83928c9becbbb1ddd8c6dab4,61:20261523,60f461:000e2b4a75ac94a7b7d4f5,5fdd96b395:e9ebf1f3f5f6fcfe,96:030406080a0b0c0d0f12151617191a,4e2c723f62156c:35545c4aa38590948c6869747686a9d0d4adf7f8f1d7b2e0d6faebeeb1d3effe,92:a8a9aaabacadafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8#e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,93:00010203040506070809,6d:39270c43480704190e2b4d2e351a4f525433916f9ea05e93945c607c63,6e1a6d:c7c5de,6e0e6d:bfe0,6e116d:e6ddd9,6e166dab6e0c6dae6e:2b6e4e6bb25f865354322544dfb198e0,6f2d6e:e2a5a7bdbbb7d7b4cf8fc29f,6f:6246472415,6ef96f:2f364b742a0929898d8c78727c7ad1,93:0a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3f40414243444546474849#4a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696b,6f:c9a7b9b6c2e1eedee0ef,70:1a231b39354f5e,5b:80849593a5b8,752f9a9e64345b:e4ee,89305bf08e478b078f:b6d3d5e5eee4e9e6f3e8,90:05040b26110d162135362d2f445152506858625b,66b990:747d8288838b,5f:50575658,5c3b54ab5c:5059,5b715c:6366,7fbc5f:2a292d,82745f3c9b3b5c6e59:81838da9aaa3,93:6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaab#acadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cbcccd,59:97caab9ea4d2b2afd7be,5a:0506,59dd5a0859:e3d8f9,5a:0c09323411231340674a553c6275,80ec5a:aa9b777abeebb2d2d4b8e0e3f1d6e6d8dc,5b:091716323740,5c:151c,5b:5a6573515362,9a:7577787a7f7d808185888a90929396989b9c9d9fa0a2a3a5a7,7e:9fa1a3a5a8a9,93:cecfd0d1d2d3d4d5d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,94:000102030405060708090a0b0c0d#0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e,7e:adb0bec0c1c2c9cbccd0d4d7dbe0e1e8ebeeeff1f2,7f0d7e:f6fafbfe,7f:01020307080b0c0f111217191c1b1f212223242526272a2b2c2d2f3031323335,5e7a757f5ddb753e909573:8e91aea29fcfc2d1b7b3c0c9c8e5d9,987c740a73:e9e7debaf2,74:0f2a5b262528302e2c,94:2f303132333435363738393a3b3c3d3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6c6d6e6f#707172737475767778797a7b7c7d7e7f8081828384919698c7cfd3d4dae6fb,95:1c20,74:1b1a415c575559776d7e9c8e8081878b9ea8a990a7d2ba,97:eaebec,67:4c535e4869a5876a7398a775a89ead8b777cf0,680967d8680a67:e9b0,680c67:d9b5dab3dd,680067:c3b8e2,680e67:c1fd,68:323360614e624464831d55664167403e4a4929b58f7477936bc2,696e68fc69:1f20,68f995:27333d43484b555a606e74757778797a7b7c7d7e808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aa#abacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacb,692468f069:0b0157,68e369:10713960425d846b80987834cc8788ce896663799ba7bbabadd4b1c1cadf95e08dff,6a2f69ed6a:171865,69f26a:443ea0505b358e793d28587c9190a997ab,73:3752,6b:8182878492938d9a9ba1aa,8f:6b6d71727375767877797a7c7e818284878b,95:cccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7ecff,96:0713181b1e20232425262728292b2c2d2f303738393a3e41434a4e4f5152535657#58595a5c5d5e606365666b6d6e6f70717378797a7b7c7d7e7f808182838487898a,8f:8d8e8f989a,8ece62:0b171b1f222125242c,81e774:eff4ff,75:0f1113,65:34eeeff0,66:0a19,677266:031500,708566:f71d34313635,800666:5f54414f56615777848ca79dbedbdce6e9,8d:3233363b3d4045464849474d5559,89:c7cacbcccecfd0d1,72:6e9f5d666f7e7f848b8d8f92,63:0832b0,96:8c8e91929395969a9b9d9e9fa0a1a2a3a4a5a6a8a9aaabacadaeafb1b2b4b5b7b8babbbfc2c3c8cacbd0d1d3d4d6d7d8d9dadbdcdddedfe1e2e3e4e5e6e7eb#ecedeef0f1f2f4f5f8fafbfcfdff,97:0203050a0b0c10111214151718191a1b1d1f20,64:3fd8,80046b:eaf3fdf5f9,6c:0507060d1518191a2129242a32,65:35556b,72:4d525630,8662521680:9f9c93bc,670a80:bdb1abadb4b7e7e8e9eadbc2c4d9cdd7,671080:ddebf1f4ed,81:0d0e,80:f2fc,671581128c5a81:361e2c1832484c5374595a7160697c7d6d67,584d5ab581:888291,6ed581:a3aacc,672681:cabb,97:2122232425262728292b2c2e2f3133343536373a3b3c3d3f404142434445464748494a4b4c4d4e4f5051545557585a5c5d5f63646667686a6b6c6d6e6f7071#72757778797a7b7d7e7f8081828384868788898a8c8e8f9093959697999a9b9c9d,81:c1a6,6b:243739434659,98:d1d2d3d5d9da,6bb35f406bc289f365909f5165:93bcc6c4c3ccced2d6,70:809c969dbbc0b7abb1e8ca,71:1013162f31735c6845724a787a98b3b5a8a0e0d4e7f9,72:1d28,706c71:1866b9,62:3e3d434849,79:3b4046495b5c535a6257606f677a858a9aa7b3,5f:d1d0,97:9e9fa1a2a4a5a6a7a8a9aaacaeb0b1b3b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3#e4e5e8eeeff0f1f2f4f7f8f9fafbfcfdfeff,98:000102030405060708090a0b0c0d0e,60:3c5d5a67415963ab,61:060d5da99dcbd1,620680:807f,6c:93f6,6dfc77:f6f8,78:0009171811,65ab78:2d1c1d393a3b1f3c252c23294e6d56572650474c6a9b939a879ca1a3b2b9a5d4d9c9ecf2,790578f479:13241e34,9f9b9e:f9fbfc,76f177:040d,76f977:07081a22192d263538505147435a68,98:0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d#4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e,77:62657f8d7d808c919fa0b0b5bd,75:3a404e4b485b727983,7f:58615f,8a487f:68747179817e,76:cde5,883294:8586878b8a8c8d8f909497959a9b9ca3a4abaaadacafb0b2b4b6b7b8b9babcbdbfc4c8c9cacbcccdced0d1d2d5d6d7d9d8dbdedfe0e2e4e5e7e8ea,98:6f70717273748b8e929599a3a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcfd0d4d6d7dbdcdde0e1e2e3e4#e5e6e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,99:0001020304050607,94:e9ebeeeff3f4f5f7f9fcfdff,95:03020607090a0d0e0f1213141516181b1d1e1f222a2b292c3132343637383c3e3f4235444546494c4e4f525354565758595b5e5f5d61626465666768696a6b6c6f7172733a,77:e7ec,96c979:d5ede3eb,7a065d477a:03021e14,99:08090a0b0c0e0f1112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2f303132333435363738393a3b3c3d3e3f40414243444546474849#4a4b4c4d4e4f50515253565758595a5b5c5d5e5f60616264667378797b7e828389,7a:393751,9ecf99a57a7076:888e9399a4,74:dee0,752c9e:202228292a2b2c3231363837393a3e414244464748494b4c4e5155575a5b5c5e63666768696a6b6c716d73,75:929496a09daca3b3b4b8c4b1b0c3c2d6cde3e8e6e4ebe7,760375:f1fcff,76:1000050c170a25181519,99:8c8e9a9b9c9d9e9fa0a1a2a3a4a6a7a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8#d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9,76:1b3c2220402d303f35433e334d5e545c566b6f,7fca7a:e6787980868895a6a0aca8adb3,88:6469727d7f82a2c6b7bcc9e2cee3e5f1,891a88:fce8fef0,89:2119131b0a342b3641667b,758b80e576:b2b4,77dc80:1214161c20222526272928310b3543464d526971,898398:788083,99:fafbfcfdfeff,9a:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738#393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859,98:898c8d8f949a9b9e9fa1a2a5a6,86:4d546c6e7f7a7c7ba88d8bac9da7a3aa93a9b6c4b5ceb0bab1afc9cfb4e9f1f2edf3d0,871386:def4dfd8d1,87:0307,86f887:080a0d09233b1e252e1a3e48343129373f82227d7e7b60704c6e8b53637c64596593afa8d2,9a:5a5b5c5d5e5f606162636465666768696a6b7283898d8e949599a6a9aaabacadaeafb2b3b4b5b9bbbdbebfc3c4c6c7c8c9cacdcecfd0d2d4d5d6d7d9dadbdc#dddee0e2e3e4e5e7e8e9eaeceef0f1f2f3f4f5f6f7f8fafcfdfeff,9b:000102040506,87:c68885ad9783abe5acb5b3cbd3bdd1c0cadbeae0ee,88:1613,87fe88:0a1b21393c,7f:36424445,82107a:fafd,7b:080304150a2b0f47382a192e31202524333e1e585a45754c5d606e7b62727190a6a7b8ac9da885aa9ca2abb4d1c1ccdddae5e6ea,7c0c7b:fefc,7c:0f160b,9b:07090a0b0c0d0e1011121415161718191a1b1c1d1e2021222425262728292a2b2c2d2e3031333435363738393a3d3e3f40464a4b4c4e50525355565758595a#5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b,7c:1f2a26384140,81fe82:010204,81ec884482:2122232d2f282b383b33343e44494b4f5a5f68,88:7e8588d8df,895e7f:9d9fa7afb0b2,7c7c65497c:919d9c9ea2b2bcbdc1c7cccdc8c5d7e8,826e66a87f:bfced5e5e1e6e9eef3,7cf87d:77a6ae,7e:479b,9e:b8b4,8d:73849491b1676d,8c:4749,91:4a504e4f64,9b:7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9ba#bbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadb,91:626170696f7d7e7274798c85908d91a2a3aaadaeafb5b4ba,8c559e7e8d:b8eb,8e:055969,8d:b5bfbcbac4d6d7dadececfdbc6ecf7f8e3f9fbe4,8e098dfd8e:141d1f2c2e232f3a4039353d3149414251524a70767c6f74858f94909c9e,8c:78828a859894,659b89:d6dedadc,9b:dcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,9c:000102030405060708090a0b0c0d0e0f101112131415161718191a#1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b,89:e5ebef,8a3e8b26975396:e9f3ef,97:0601080f0e2a2d303e,9f:808385868788898a8c,9efe9f:0b0d,96:b9bcbdced2,77bf96e092:8eaec8,93:3e6aca8f,94:3e6b,9c:7f8285868788,7a239c:8b8e90919294959a9b9e9fa0a1a2a3a5a6a7a8a9abadaeb0b1b2b3b4b5b6b7babbbcbdc4c5c6c7cacb3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a#7b7d7e808384898a8c8f93969798999daaacafb9bebfc0c1c2c8c9d1d2dadbe0e1cccdcecfd0d3d4d5d7d8d9dcdddfe2,97:7c85919294afaba3b2b4,9a:b1b0b7,9e589a:b6babcc1c0c5c2cbccd1,9b:45434749484d51,98e899:0d2e5554,9a:dfe1e6efebfbedf9,9b:080f131f23,9e:bdbe,7e3b9e:8287888b92,93d69e:9d9fdbdcdde0dfe2e9e7e5eaef,9f:222c2f39373d3e44,9c:e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,9d:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021#22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142#92$434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081#82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2#92$a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1#e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,9e:000102#92$030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e24272e30343b3c404d5052535456595d5f606162656e6f727475767778797a7b7c7d80#8183848586898a8c8d8e8f90919495969798999a9b9c9ea0a1a2a3a4a5a7a8a9aa#92$abacadaeafb0b1b2b3b5b6b7b9babcbfc0c1c2c3c5c6c7c8cacbccd0d2d3d5d6d7d9dadee1e3e4e6e8ebecedeef0f1f2f3f4f5f6f7f8fafdff,9f:000102030405#060708090a0c0f1112141516181a1b1c1d1e1f21232425262728292a2b2d2e3031#92$3233343536383a3c3f4041424345464748494a4b4c4d4e4f52535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778#797a7b7c7d7e81828d8e8f9091929394959697989c9d9ea1a2a3a4a5,f9:2c7995e7f1#92$,fa:0c0d0e0f111314181f20212324272829,e8:15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243#4445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061626364"),U2Ghash={},G2Uhash={};!function(data){var k=0;data=data.match(/..../g);for(var i=129;i<=254;i++){for(var j=64;j<=254;j++){U2Ghash[data[k++]]=("%"+i.toString(16)+"%"+j.toString(16)).toUpperCase()}}for(var key in U2Ghash){G2Uhash[U2Ghash[key]]=key}}(data);function isAscii(unicode){return unicode==8364||unicode<=127&&unicode>=0}return{encode:function(str){return str.replace(/./g,function(a){var code=a.charCodeAt(0);if(isAscii(code)){return encodeURIComponent(a)}else{var key=code.toString(16);if(key.length!=4)key=("000"+key).match(/....$/)[0];return U2Ghash[key]||a}})},decode:function(str){return str.replace(/%[0-9A-F]{2}%[0-9A-F]{2}/g,function(a){if(a in G2Uhash){return String.fromCharCode("0x"+G2Uhash[a])}else{return a}}).replace(/%[\\w]{2}/g,function(a){return decodeURIComponent(a)})}}});'},536:(e,a,t)=>{t(642)(t(145))},642:e=>{e.exports=function(e){function a(e){"undefined"!=typeof console&&(console.error||console.log)("[Script Loader]",e)}try{"undefined"!=typeof execScript&&function t(){return"undefined"!=typeof attachEvent&&"undefined"==typeof addEventListener}()?execScript(e):"undefined"!=typeof eval?eval.call(null,e):a("EvalError: No eval function available")}catch(e){a(e)}}},740:(e,a,t)=>{t(642)(t(481))},761:e=>{e.exports='(function(root,factory){if(typeof exports==="object"){module.exports=exports=factory()}else if(typeof define==="function"&&define.amd){define([],factory)}else{globalThis.WXXH=factory()}})(this,function(){const t=new Uint8Array([0,97,115,109,1,0,0,0,1,48,8,96,3,127,127,127,1,127,96,3,127,127,127,0,96,2,127,127,0,96,1,127,1,127,96,3,127,127,126,1,126,96,3,126,127,127,1,126,96,2,127,126,0,96,1,127,1,126,3,11,10,0,0,2,1,3,4,5,6,1,7,5,3,1,0,1,7,85,9,3,109,101,109,2,0,5,120,120,104,51,50,0,0,6,105,110,105,116,51,50,0,2,8,117,112,100,97,116,101,51,50,0,3,8,100,105,103,101,115,116,51,50,0,4,5,120,120,104,54,52,0,5,6,105,110,105,116,54,52,0,7,8,117,112,100,97,116,101,54,52,0,8,8,100,105,103,101,115,116,54,52,0,9,10,251,22,10,242,1,1,4,127,32,0,32,1,106,33,3,32,1,65,16,79,4,127,32,3,65,16,107,33,6,32,2,65,168,136,141,161,2,106,33,3,32,2,65,137,235,208,208,7,107,33,4,32,2,65,207,140,162,142,6,106,33,5,3,64,32,3,32,0,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,3,32,4,32,0,65,4,106,34,0,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,4,32,2,32,0,65,4,106,34,0,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,2,32,5,32,0,65,4,106,34,0,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,5,32,6,32,0,65,4,106,34,0,79,13,0,11,32,2,65,12,119,32,5,65,18,119,106,32,4,65,7,119,106,32,3,65,1,119,106,5,32,2,65,177,207,217,178,1,106,11,32,1,106,32,0,32,1,65,15,113,16,1,11,146,1,0,32,1,32,2,106,33,2,3,64,32,1,65,4,106,32,2,75,69,4,64,32,0,32,1,40,2,0,65,189,220,202,149,124,108,106,65,17,119,65,175,214,211,190,2,108,33,0,32,1,65,4,106,33,1,12,1,11,11,3,64,32,1,32,2,79,69,4,64,32,0,32,1,45,0,0,65,177,207,217,178,1,108,106,65,11,119,65,177,243,221,241,121,108,33,0,32,1,65,1,106,33,1,12,1,11,11,32,0,32,0,65,15,118,115,65,247,148,175,175,120,108,34,0,65,13,118,32,0,115,65,189,220,202,149,124,108,34,0,65,16,118,32,0,115,11,63,0,32,0,65,8,106,32,1,65,168,136,141,161,2,106,54,2,0,32,0,65,12,106,32,1,65,137,235,208,208,7,107,54,2,0,32,0,65,16,106,32,1,54,2,0,32,0,65,20,106,32,1,65,207,140,162,142,6,106,54,2,0,11,195,4,1,6,127,32,1,32,2,106,33,6,32,0,65,24,106,33,4,32,0,65,40,106,40,2,0,33,3,32,0,32,0,40,2,0,32,2,106,54,2,0,32,0,65,4,106,34,5,32,5,40,2,0,32,2,65,16,79,32,0,40,2,0,65,16,79,114,114,54,2,0,32,2,32,3,106,65,16,73,4,64,32,3,32,4,106,32,1,32,2,252,10,0,0,32,0,65,40,106,32,2,32,3,106,54,2,0,15,11,32,3,4,64,32,3,32,4,106,32,1,65,16,32,3,107,34,2,252,10,0,0,32,0,65,8,106,34,3,32,3,40,2,0,32,4,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,54,2,0,32,0,65,12,106,34,3,32,3,40,2,0,32,4,65,4,106,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,54,2,0,32,0,65,16,106,34,3,32,3,40,2,0,32,4,65,8,106,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,54,2,0,32,0,65,20,106,34,3,32,3,40,2,0,32,4,65,12,106,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,54,2,0,32,0,65,40,106,65,0,54,2,0,32,1,32,2,106,33,1,11,32,1,32,6,65,16,107,77,4,64,32,6,65,16,107,33,8,32,0,65,8,106,40,2,0,33,2,32,0,65,12,106,40,2,0,33,3,32,0,65,16,106,40,2,0,33,5,32,0,65,20,106,40,2,0,33,7,3,64,32,2,32,1,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,2,32,3,32,1,65,4,106,34,1,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,3,32,5,32,1,65,4,106,34,1,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,5,32,7,32,1,65,4,106,34,1,40,2,0,65,247,148,175,175,120,108,106,65,13,119,65,177,243,221,241,121,108,33,7,32,8,32,1,65,4,106,34,1,79,13,0,11,32,0,65,8,106,32,2,54,2,0,32,0,65,12,106,32,3,54,2,0,32,0,65,16,106,32,5,54,2,0,32,0,65,20,106,32,7,54,2,0,11,32,1,32,6,73,4,64,32,4,32,1,32,6,32,1,107,34,1,252,10,0,0,32,0,65,40,106,32,1,54,2,0,11,11,97,1,1,127,32,0,65,16,106,40,2,0,33,1,32,0,65,4,106,40,2,0,4,127,32,1,65,12,119,32,0,65,20,106,40,2,0,65,18,119,106,32,0,65,12,106,40,2,0,65,7,119,106,32,0,65,8,106,40,2,0,65,1,119,106,5,32,1,65,177,207,217,178,1,106,11,32,0,40,2,0,106,32,0,65,24,106,32,0,65,40,106,40,2,0,16,1,11,255,3,2,3,126,1,127,32,0,32,1,106,33,6,32,1,65,32,79,4,126,32,6,65,32,107,33,6,32,2,66,214,235,130,238,234,253,137,245,224,0,124,33,3,32,2,66,177,169,172,193,173,184,212,166,61,125,33,4,32,2,66,249,234,208,208,231,201,161,228,225,0,124,33,5,3,64,32,3,32,0,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,3,32,4,32,0,65,8,106,34,0,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,4,32,2,32,0,65,8,106,34,0,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,2,32,5,32,0,65,8,106,34,0,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,5,32,6,32,0,65,8,106,34,0,79,13,0,11,32,2,66,12,137,32,5,66,18,137,124,32,4,66,7,137,124,32,3,66,1,137,124,32,3,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,4,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,2,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,5,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,5,32,2,66,197,207,217,178,241,229,186,234,39,124,11,32,1,173,124,32,0,32,1,65,31,113,16,6,11,134,2,0,32,1,32,2,106,33,2,3,64,32,2,32,1,65,8,106,79,4,64,32,1,41,3,0,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,32,0,133,66,27,137,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,33,0,32,1,65,8,106,33,1,12,1,11,11,32,1,65,4,106,32,2,77,4,64,32,0,32,1,53,2,0,66,135,149,175,175,152,182,222,155,158,127,126,133,66,23,137,66,207,214,211,190,210,199,171,217,66,126,66,249,243,221,241,153,246,153,171,22,124,33,0,32,1,65,4,106,33,1,11,3,64,32,1,32,2,73,4,64,32,0,32,1,49,0,0,66,197,207,217,178,241,229,186,234,39,126,133,66,11,137,66,135,149,175,175,152,182,222,155,158,127,126,33,0,32,1,65,1,106,33,1,12,1,11,11,32,0,32,0,66,33,136,133,66,207,214,211,190,210,199,171,217,66,126,34,0,32,0,66,29,136,133,66,249,243,221,241,153,246,153,171,22,126,34,0,32,0,66,32,136,133,11,77,0,32,0,65,8,106,32,1,66,214,235,130,238,234,253,137,245,224,0,124,55,3,0,32,0,65,16,106,32,1,66,177,169,172,193,173,184,212,166,61,125,55,3,0,32,0,65,24,106,32,1,55,3,0,32,0,65,32,106,32,1,66,249,234,208,208,231,201,161,228,225,0,124,55,3,0,11,244,4,2,3,127,4,126,32,1,32,2,106,33,5,32,0,65,40,106,33,4,32,0,65,200,0,106,40,2,0,33,3,32,0,32,0,41,3,0,32,2,173,124,55,3,0,32,2,32,3,106,65,32,73,4,64,32,3,32,4,106,32,1,32,2,252,10,0,0,32,0,65,200,0,106,32,2,32,3,106,54,2,0,15,11,32,3,4,64,32,3,32,4,106,32,1,65,32,32,3,107,34,2,252,10,0,0,32,0,65,8,106,34,3,32,3,41,3,0,32,4,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,55,3,0,32,0,65,16,106,34,3,32,3,41,3,0,32,4,65,8,106,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,55,3,0,32,0,65,24,106,34,3,32,3,41,3,0,32,4,65,16,106,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,55,3,0,32,0,65,32,106,34,3,32,3,41,3,0,32,4,65,24,106,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,55,3,0,32,0,65,200,0,106,65,0,54,2,0,32,1,32,2,106,33,1,11,32,1,65,32,106,32,5,77,4,64,32,5,65,32,107,33,2,32,0,65,8,106,41,3,0,33,6,32,0,65,16,106,41,3,0,33,7,32,0,65,24,106,41,3,0,33,8,32,0,65,32,106,41,3,0,33,9,3,64,32,6,32,1,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,6,32,7,32,1,65,8,106,34,1,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,7,32,8,32,1,65,8,106,34,1,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,8,32,9,32,1,65,8,106,34,1,41,3,0,66,207,214,211,190,210,199,171,217,66,126,124,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,33,9,32,2,32,1,65,8,106,34,1,79,13,0,11,32,0,65,8,106,32,6,55,3,0,32,0,65,16,106,32,7,55,3,0,32,0,65,24,106,32,8,55,3,0,32,0,65,32,106,32,9,55,3,0,11,32,1,32,5,73,4,64,32,4,32,1,32,5,32,1,107,34,1,252,10,0,0,32,0,65,200,0,106,32,1,54,2,0,11,11,188,2,1,5,126,32,0,65,24,106,41,3,0,33,1,32,0,41,3,0,34,2,66,32,90,4,126,32,0,65,8,106,41,3,0,34,3,66,1,137,32,0,65,16,106,41,3,0,34,4,66,7,137,124,32,1,66,12,137,32,0,65,32,106,41,3,0,34,5,66,18,137,124,124,32,3,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,4,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,1,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,32,5,66,207,214,211,190,210,199,171,217,66,126,66,31,137,66,135,149,175,175,152,182,222,155,158,127,126,133,66,135,149,175,175,152,182,222,155,158,127,126,66,157,163,181,234,131,177,141,138,250,0,125,5,32,1,66,197,207,217,178,241,229,186,234,39,124,11,32,2,124,32,0,65,40,106,32,2,66,31,131,167,16,6,11]);function e(){return function(t){const{exports:{mem:e,xxh32:n,xxh64:r,init32:i,update32:a,digest32:o,init64:s,update64:u,digest64:c}}=t;let h=new Uint8Array(e.buffer);function g(t,n){if(e.buffer.byteLength(h.set(o),a(i(0)))}}function y(t){return t>>>0}const b=2n**64n-1n;function d(t){return t&b}const w=new TextEncoder,l=0,p=0n;function x(t,e=l){return g(3*t.length,0),y(n(0,w.encodeInto(t,h).written,e))}function L(t,e=p){return g(3*t.length,0),d(r(0,w.encodeInto(t,h).written,e))}return{h32:x,h32ToString:(t,e=l)=>x(t,e).toString(16).padStart(8,"0"),h32Raw:(t,e=l)=>(g(t.byteLength,0),h.set(t),y(n(0,t.byteLength,e))),create32:(t=l)=>f(48,t,i,a,o,y),h64:L,h64ToString:(t,e=p)=>L(t,e).toString(16).padStart(16,"0"),h64Raw:(t,e=p)=>(g(t.byteLength,0),h.set(t),d(r(0,t.byteLength,e))),create64:(t=p)=>f(88,t,s,u,c,d)}}(new WebAssembly.Instance(new WebAssembly.Module(t)))}return e()});'},762:(e,a,t)=>{t(642)(t(27))},815:(e,a,t)=>{t(642)(t(842))},842:e=>{e.exports='(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.jinja={}))})(this,function(jinja){"use strict";var STRINGS=/\'(\\\\.|[^\'])*\'|"(\\\\.|[^"\'"])*"/g;var IDENTS_AND_NUMS=/([$_a-z][$\\w]*)|([+-]?\\d+(\\.\\d+)?)/g;var NUMBER=/^[+-]?\\d+(\\.\\d+)?$/;var NON_PRIMITIVES=/\\[[@#~](,[@#~])*\\]|\\[\\]|\\{([@i]:[@#~])(,[@i]:[@#~])*\\}|\\{\\}/g;var IDENTIFIERS=/[$_a-z][$\\w]*/gi;var VARIABLES=/i(\\.i|\\[[@#i]\\])*/g;var ACCESSOR=/(\\.i|\\[[@#i]\\])/g;var OPERATORS=/(===?|!==?|>=?|<=?|&&|\\|\\||[+\\-\\*\\/%])/g;var EOPS=/(^|[^$\\w])(and|or|not|is|isnot)([^$\\w]|$)/g;var LEADING_SPACE=/^\\s+/;var TRAILING_SPACE=/\\s+$/;var START_TOKEN=/\\{\\{\\{|\\{\\{|\\{%|\\{#/;var TAGS={"{{{":/^(\'(\\\\.|[^\'])*\'|"(\\\\.|[^"\'"])*"|.)+?\\}\\}\\}/,"{{":/^(\'(\\\\.|[^\'])*\'|"(\\\\.|[^"\'"])*"|.)+?\\}\\}/,"{%":/^(\'(\\\\.|[^\'])*\'|"(\\\\.|[^"\'"])*"|.)+?%\\}/,"{#":/^(\'(\\\\.|[^\'])*\'|"(\\\\.|[^"\'"])*"|.)+?#\\}/};var delimeters={"{%":"directive","{{":"output","{#":"comment"};var operators={and:"&&",or:"||",not:"!",is:"==",isnot:"!="};var constants={true:true,false:false,null:null};function Parser(){this.nest=[];this.compiled=[];this.childBlocks=0;this.parentBlocks=0;this.isSilent=false}Parser.prototype.push=function(line){if(!this.isSilent){this.compiled.push(line)}};Parser.prototype.parse=function(src){this.tokenize(src);return this.compiled};Parser.prototype.tokenize=function(src){var lastEnd=0,parser=this,trimLeading=false;matchAll(src,START_TOKEN,function(open,index,src){var match=src.slice(index+open.length).match(TAGS[open]);match=match?match[0]:"";var simplified=match.replace(STRINGS,"@");if(!match||~simplified.indexOf(open)){return index+1}var inner=match.slice(0,0-open.length);if(inner.charAt(0)==="-")var wsCollapseLeft=true;if(inner.slice(-1)==="-")var wsCollapseRight=true;inner=inner.replace(/^-|-$/g,"").trim();if(parser.rawMode&&open+inner!=="{%endraw"){return index+1}var text=src.slice(lastEnd,index);lastEnd=index+open.length+match.length;if(trimLeading)text=trimLeft(text);if(wsCollapseLeft)text=trimRight(text);if(wsCollapseRight)trimLeading=true;if(open==="{{{"){open="{{";inner+="|safe"}parser.textHandler(text);parser.tokenHandler(open,inner)});var text=src.slice(lastEnd);if(trimLeading)text=trimLeft(text);this.textHandler(text)};Parser.prototype.textHandler=function(text){this.push("write("+JSON.stringify(text)+");")};Parser.prototype.tokenHandler=function(open,inner){var type=delimeters[open];if(type==="directive"){this.compileTag(inner)}else if(type==="output"){var extracted=this.extractEnt(inner,STRINGS,"@");extracted.src=extracted.src.replace(/\\|\\|/g,"~").split("|");extracted.src=extracted.src.map(function(part){return part.split("~").join("||")});var parts=this.injectEnt(extracted,"@");if(parts.length>1){var filters=parts.slice(1).map(this.parseFilter.bind(this));this.push("filter("+this.parseExpr(parts[0])+","+filters.join(",")+");")}else{this.push("filter("+this.parseExpr(parts[0])+");")}}};Parser.prototype.compileTag=function(str){var directive=str.split(" ")[0];var handler=tagHandlers[directive];if(!handler){throw new Error("Invalid tag: "+str)}handler.call(this,str.slice(directive.length).trim())};Parser.prototype.parseFilter=function(src){src=src.trim();var match=src.match(/[:(]/);var i=match?match.index:-1;if(i<0)return JSON.stringify([src]);var name=src.slice(0,i);var args=src.charAt(i)===":"?src.slice(i+1):src.slice(i+1,-1);args=this.parseExpr(args,{terms:true});return"["+JSON.stringify(name)+","+args+"]"};Parser.prototype.extractEnt=function(src,regex,placeholder){var subs=[],isFunc=typeof placeholder=="function";src=src.replace(regex,function(str){var replacement=isFunc?placeholder(str):placeholder;if(replacement){subs.push(str);return replacement}return str});return{src:src,subs:subs}};Parser.prototype.injectEnt=function(extracted,placeholder){var src=extracted.src,subs=extracted.subs,isArr=Array.isArray(src);var arr=isArr?src:[src];var re=new RegExp("["+placeholder+"]","g"),i=0;arr.forEach(function(src,index){arr[index]=src.replace(re,function(){return subs[i++]})});return isArr?arr:arr[0]};Parser.prototype.replaceComplex=function(s){var parsed=this.extractEnt(s,/i(\\.i|\\[[@#i]\\])+/g,"v");parsed.src=parsed.src.replace(NON_PRIMITIVES,"~");return this.injectEnt(parsed,"v")};Parser.prototype.parseExpr=function(src,opts){opts=opts||{};var parsed1=this.extractEnt(src,STRINGS,"@");parsed1.src=parsed1.src.replace(EOPS,function(s,before,op,after){return op in operators?before+operators[op]+after:s});var parsed2=this.extractEnt(parsed1.src,IDENTS_AND_NUMS,function(s){return s in constants||NUMBER.test(s)?"#":null});var parsed3=this.extractEnt(parsed2.src,IDENTIFIERS,"i");parsed3.src=parsed3.src.replace(/\\s+/g,"");var simplified=parsed3.src;while(simplified!==(simplified=this.replaceComplex(simplified)));while(simplified!==(simplified=simplified.replace(/i(\\.i|\\[[@#i]\\])+/,"v")));simplified=simplified.replace(/[iv]\\[v?\\]/g,"x");simplified=simplified.replace(/[@#~v]/g,"i");simplified=simplified.replace(OPERATORS,"%");simplified=simplified.replace(/!+[i]/g,"i");var terms=opts.terms?simplified.split(","):[simplified];terms.forEach(function(term){while(term!==(term=term.replace(/\\(i(%i)*\\)/g,"i")));if(!term.match(/^i(%i)*/)){throw new Error("Invalid expression: "+src+" "+term)}});parsed3.src=parsed3.src.replace(VARIABLES,this.parseVar.bind(this));parsed2.src=this.injectEnt(parsed3,"i");parsed1.src=this.injectEnt(parsed2,"#");return this.injectEnt(parsed1,"@")};Parser.prototype.parseVar=function(src){var args=Array.prototype.slice.call(arguments);var str=args.pop(),index=args.pop();if(src==="i"&&str.charAt(index+1)===":"){return\'"i"\'}var parts=[\'"i"\'];src.replace(ACCESSOR,function(part){if(part===".i"){parts.push(\'"i"\')}else if(part==="[i]"){parts.push(\'get("i")\')}else{parts.push(part.slice(1,-1))}});return"get("+parts.join(",")+")"};Parser.prototype.escName=function(str){return str.replace(/\\W/g,function(s){return"$"+s.charCodeAt(0).toString(16)})};Parser.prototype.parseQuoted=function(str){if(str.charAt(0)==="\'"){str=str.slice(1,-1).replace(/\\\\.|"/,function(s){if(s==="\\\\\'")return"\'";return s.charAt(0)==="\\\\"?s:"\\\\"+s});str=\'"\'+str+\'"\'}return JSON.parse(str)};var tagHandlers={if:function(expr){this.push("if ("+this.parseExpr(expr)+") {");this.nest.unshift("if")},else:function(){if(this.nest[0]==="for"){this.push("}, function() {")}else{this.push("} else {")}},elseif:function(expr){this.push("} else if ("+this.parseExpr(expr)+") {")},endif:function(){this.nest.shift();this.push("}")},for:function(str){var i=str.indexOf(" in ");var name=str.slice(0,i).trim();var expr=str.slice(i+4).trim();this.push("each("+this.parseExpr(expr)+","+JSON.stringify(name)+",function() {");this.nest.unshift("for")},endfor:function(){this.nest.shift();this.push("});")},raw:function(){this.rawMode=true},endraw:function(){this.rawMode=false},set:function(stmt){var i=stmt.indexOf("=");var name=stmt.slice(0,i).trim();var expr=stmt.slice(i+1).trim();this.push("set("+JSON.stringify(name)+","+this.parseExpr(expr)+");")},block:function(name){if(this.isParent){++this.parentBlocks;var blockName="block_"+(this.escName(name)||this.parentBlocks);this.push("block(typeof "+blockName+\' == "function" ? \'+blockName+" : function() {")}else if(this.hasParent){this.isSilent=false;++this.childBlocks;blockName="block_"+(this.escName(name)||this.childBlocks);this.push("function "+blockName+"() {")}this.nest.unshift("block")},endblock:function(){this.nest.shift();if(this.isParent){this.push("});")}else if(this.hasParent){this.push("}");this.isSilent=true}},extends:function(name){name=this.parseQuoted(name);var parentSrc=this.readTemplateFile(name);this.isParent=true;this.tokenize(parentSrc);this.isParent=false;this.hasParent=true;this.isSilent=true},include:function(name){name=this.parseQuoted(name);var incSrc=this.readTemplateFile(name);this.isInclude=true;this.tokenize(incSrc);this.isInclude=false}};tagHandlers.assign=tagHandlers.set;tagHandlers.elif=tagHandlers.elseif;var getRuntime=function runtime(data,opts){var defaults={autoEscape:"toJson"};var _toString=Object.prototype.toString;var _hasOwnProperty=Object.prototype.hasOwnProperty;var getKeys=Object.keys||function(obj){var keys=[];for(var n in obj)if(_hasOwnProperty.call(obj,n))keys.push(n);return keys};var isArray=Array.isArray||function(obj){return _toString.call(obj)==="[object Array]"};var create=Object.create||function(obj){function F(){}F.prototype=obj;return new F};var toString=function(val){if(val==null)return"";return typeof val.toString=="function"?val.toString():_toString.call(val)};var extend=function(dest,src){var keys=getKeys(src);for(var i=0,len=keys.length;i").join(">").split(\'"\').join(""")},safe:function(val){return val},toJson:function(val){if(typeof val==="object"){return JSON.stringify(val)}return toString(val)}},opts.filters||{});var stack=[create(data||{})],output=[];return{get:get,set:set,push:push,pop:pop,write:write,filter:filter,each:each,block:block,render:render}};var runtime;jinja.compile=function(markup,opts){opts=opts||{};var parser=new Parser;parser.readTemplateFile=this.readTemplateFile;var code=[];code.push("function render($) {");code.push("var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;");code.push.apply(code,parser.parse(markup));code.push("return $.render();");code.push("}");code=code.join("\\n");if(opts.runtime===false){var fn=new Function("data","options","return ("+code+")(runtime(data, options))")}else{runtime=runtime||(runtime=getRuntime.toString());fn=new Function("data","options","return ("+code+")(("+runtime+")(data, options))")}return{render:fn}};jinja.render=function(markup,data,opts){var tmpl=jinja.compile(markup);return tmpl.render(data,opts)};jinja.templateFiles=[];jinja.readTemplateFile=function(name){var templateFiles=this.templateFiles||[];var templateFile=templateFiles[name];if(templateFile==null){throw new Error("Template file not found: "+name)}return templateFile};function trimLeft(str){return str.replace(LEADING_SPACE,"")}function trimRight(str){return str.replace(TRAILING_SPACE,"")}function matchAll(str,reg,fn){reg=new RegExp(reg.source,"g"+(reg.ignoreCase?"i":"")+(reg.multiline?"m":""));var match;while(match=reg.exec(str)){var result=fn(match[0],match.index,str);if(typeof result=="number"){reg.lastIndex=result}}}});'},983:()=>{function e(a){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(a)}function a(e,a){if(!(e instanceof a))throw new TypeError("Cannot call a class as a function")}function t(e,a){for(var t=0;t0&&void 0!==arguments[0]?arguments[0]:":memory:",r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{create:!0,readOnly:!1};a(this,e);var c=0;r.create&&(c|=n.SQLITE_OPEN_CREATE),r.readOnly?c|=n.SQLITE_OPEN_READONLY:c|=n.SQLITE_OPEN_READWRITE,this[f]=n.open(t,c)},[{key:"close",value:function e(){var a=!1;return this[f]&&(a=n.close(this[f]),this[f]=null),a}},{key:"exec",value:function e(a){if(!this[f])throw new Error("Invalid DB");return n.exec(this[f],a)}},{key:"prepare",value:function e(a){if(!this[f])throw new Error("Invalid DB");return new b(n.prepare(this[f],a))}},{key:"inTransaction",get:function e(){return!!this[f]&&n.in_transaction(this[f])}},{key:"transaction",value:function e(a){if("function"!=typeof a)throw new TypeError("Expected first argument to be a function");var t=this,r=u(t),c={default:{value:o(a,t,r.default)},deferred:{value:o(a,t,r.deferred)},immediate:{value:o(a,t,r.immediate)},exclusive:{value:o(a,t,r.exclusive)}};return Object.defineProperties(c.default.value,c),Object.defineProperties(c.deferred.value,c),Object.defineProperties(c.immediate.value,c),Object.defineProperties(c.exclusive.value,c),c.default.value}},{key:"loadExtension",value:function e(a){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;return n.load_extension(this[f],a,t)}}])}(),u=function e(a){var t=(i||(i=new WeakMap)).get(a);if(!t){var r={commit:a.prepare("COMMIT"),rollback:a.prepare("ROLLBACK"),savepoint:a.prepare("SAVEPOINT `\t_bs3.\t`"),release:a.prepare("RELEASE `\t_bs3.\t`"),rollbackTo:a.prepare("ROLLBACK TO `\t_bs3.\t`")};t={default:Object.assign({begin:a.prepare("BEGIN")},r),deferred:Object.assign({begin:a.prepare("BEGIN DEFERRED")},r),immediate:Object.assign({begin:a.prepare("BEGIN IMMEDIATE")},r),exclusive:Object.assign({begin:a.prepare("BEGIN EXCLUSIVE")},r)},i.set(a,t)}return t},o=function e(a,t,r){var c=r.begin,i=r.commit,n=r.rollback,f=r.savepoint,d=r.release,u=r.rollbackTo;return function e(){var r,o,s;t.inTransaction?(r=f,o=d,s=u):(r=c,o=i,s=n);try{r.run();var b=Function.prototype.apply.call(a,this,arguments);return o.run(),b}catch(e){throw t.inTransaction&&(s.run(),s!==n&&o.run()),e}}},s=Symbol("kSqlite3Stmt"),b=function(){return r(function e(t){a(this,e),this[s]=t},[{key:"finalize",value:function e(){return n.stmt_finalize(this[s])}},{key:"toString",value:function e(){return n.stmt_expand(this[s])}},{key:"all",value:function a(){for(var t=arguments.length,r=new Array(t),c=0;c + + + + + 下载 {{projectName}} + + + +
+

{{projectName}} 下载中心

+ +
+ 历史文件管理 +
+ + +
+
+ +
链接已复制到剪贴板
+ +
+ {{downloadItems}} +
+
+ + + + diff --git a/public/index.html b/public/index.html index 83d6552e..431f627a 100644 --- a/public/index.html +++ b/public/index.html @@ -14,7 +14,8 @@

drpyS(drpy-node)

常用超链接

更新记录

-

20260118

-

更新至V1.3.18

-

20260115

-

更新至V1.3.17

-

20260113

-

更新至V1.3.16

-

20260112

-

更新至V1.3.15

+

20260208

+

更新至V1.3.22

+

20260131

+

更新至V1.3.21

+

20260127

+

更新至V1.3.20

点此查看完整更新记录

注意事项

总是有人遇到各种奇葩问题,像什么没弹幕,访问/config/1服务马上崩溃等等,能自行解决最好,解决不了我建议你使用下方安装教程 diff --git a/public/sub/order_common.example.html b/public/sub/order_common.example.html index 99d5a641..788cdb87 100644 --- a/public/sub/order_common.example.html +++ b/public/sub/order_common.example.html @@ -7,11 +7,14 @@ IPTV [优] [盘] +[磁] [漫] [短] [官] [听] [书] +[画] +[M] [搜] DS cat diff --git "a/spider/catvod/yikm[\346\270\270\346\210\217].js" "b/spider/catvod/yikm[\346\270\270\346\210\217].js" new file mode 100644 index 00000000..864df648 --- /dev/null +++ "b/spider/catvod/yikm[\346\270\270\346\210\217].js" @@ -0,0 +1,431 @@ +/* +@header({ + searchable: 0, + filterable: 1, + quickSearch: 0, + title: 'yikm[游戏]', + logo: "https://file.yikm.net/logo.png" + lang: 'cat' +}) +*/ + + +let siteKey = "", siteType = "", sourceKey = "", ext = "", host = ""; + +const myGame = [ + { + name: '斗地主(人机)', + url: 'https://www.haiwaiqipai.com/games/doudizhus/index.html', + pic: 'https://www.haiwaiqipai.com/img/DouDiZhu.jpg' + }, + { + name: '五子棋', + url: 'https://wuziqi.hongton.com', + pic: 'https://wuziqi.hongton.com/img/stype/init-bg.png' + }, + { + name: '俄罗斯方块', + url: 'https://v2fy.com/game/tetris/', + pic: 'https://i-1-uc129.zswxy.cn/2023/0223/5d809bdb026646478a97a938f7b3300c.png' + }, + { + name: '魂斗罗(美版)', + url: 'https://www.yikm.net/play?id=4137', + pic: 'https://img.1990i.com/fcpic/sj/436a.png', + header: { + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36' + } + }, +]; + +async function request(url, obj) { + if (!obj) { + obj = { + headers: headers, + timeout: 5000 + } + } + try { + const response = await req(url, obj); + let html = response.content; + return html; + } catch (e) { + console.log(`请求失败: ${url}`, e.message); + return ''; + } +} + +// 2. 全局默认请求头(匹配request函数,复用至vod_id的header) +const headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0' +}; + + +// 公共函数:返回统一的 header 配置 +function hdr() { + return { + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36' + }; +} + +async function init(cfg) { + siteKey = cfg.skey; + siteType = cfg.stype; + sourceKey = cfg.sourceKey; + ext = cfg.ext; + host = "https://www.yikm.net"; +} + +async function home(filter) { + const classes = [ + { + type_id: '定制', + type_name: '定制', + type_flag: '2-00-S' + }, { + type_id: '/nes?tag=0&e=0&page=', + type_name: 'FC', + type_flag: '[CFS]2-00-S' + }, { + type_id: '/nes?tag=&e=5&page=', + type_name: 'SFC', + type_flag: '[CFS]2-00-S', + }, { + type_id: '/nes?tag=9&e=&page=', + type_name: '街机', + type_flag: '[CFS]2-00-S', + }, { + type_id: '/nes?tag=&e=2&page=', + type_name: 'GBA', + type_flag: '[CFS]2-00-S', + }, { + type_id: '/nes?tag=&e=7&page=', + type_name: 'NDS', + type_flag: '[CFS]2-00-S', + }, { + type_id: '/nes?tag=&e=3&page=', + type_name: 'MD', + type_flag: '[CFS]2-00-S', + }, { + type_id: '/nes?tag=&e=6&page=', + type_name: 'DOS', + type_flag: '[CFS]2-00-S', + } + + ]; + const filters = { + "/nes?tag=0&e=0&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "FC高清", "v": "/nes?tag=&e=4&page="}, { + "n": "动作冒险", + "v": "/nes?tag=2&e=0&page=" + }, {"n": "小游戏", "v": "/nes?tag=8&e=0&page="}, { + "n": "飞行射击", + "v": "/nes?tag=3&e=0&page=" + }, {"n": "格斗", "v": "/nes?tag=4&e=0&page="}, {"n": "棋牌", "v": "/nes?tag=5&e=0&page="}, { + "n": "射击", + "v": "/nes?tag=6&e=0&page=" + }, {"n": "运动比赛", "v": "/nes?tag=7&e=0&page="}, {"n": "角色扮演", "v": "/nes?tag=10&e=0&page="},] + } + ], "/nes?tag=&e=5&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "汉化", "v": "/nes?tag=汉化&e=5&page="}, { + "n": "平台", + "v": "/nes?tag=平台&e=5&page=" + }, {"n": "策略", "v": "/nes?tag=策略&e=5&page="}, { + "n": "混合", + "v": "/nes?tag=混合&e=5&page=" + }, {"n": "动作", "v": "/nes?tag=动作&e=5&page="}, { + "n": "角色扮演", + "v": "/nes?tag=角色扮演&e=5&page=" + }, {"n": "射击", "v": "/nes?tag=射击&e=5&page="}, { + "n": "运动", + "v": "/nes?tag=运动&e=5&page=" + }, {"n": "格斗", "v": "/nes?tag=格斗&e=5&page="}, { + "n": "休闲", + "v": "/nes?tag=休闲&e=5&page=" + }, {"n": "冒险", "v": "/nes?tag=冒险&e=5&page="}, { + "n": "教育", + "v": "/nes?tag=教育&e=5&page=" + }, {"n": "赛车", "v": "/nes?tag=赛车&e=5&page="}, { + "n": "模拟", + "v": "/nes?tag=模拟&e=5&page=" + }, {"n": "其他", "v": "/nes?tag=其他&e=5&page="},] + } + ], "/nes?tag=9&e=&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "动作冒险", "v": "/nes?tag=动作&e=1&page="}, { + "n": "射击", + "v": "/nes?tag=射击&e=1&page=" + }, {"n": "赛车", "v": "/nes?tag=赛车&e=1&page="}, { + "n": "格斗", + "v": "/nes?tag=格斗&e=1&page=" + }, {"n": "体育", "v": "/nes?tag=体育&e=1&page="}, { + "n": "益智游戏", + "v": "/nes?tag=益智&e=1&page=" + }, {"n": "其他", "v": "/nes?tag=其他&e=1&page="},] + } + ], "/nes?tag=&e=2&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "动作", "v": "/nes?tag=动作&e=2&page="}, { + "n": "冒险", + "v": "/nes?tag=冒险&e=2&page=" + }, {"n": "角色扮演", "v": "/nes?tag=角色扮演&e=2&page="}, { + "n": "运动", + "v": "/nes?tag=运动&e=2&page=" + }, {"n": "策略", "v": "/nes?tag=策略&e=2&page="}, { + "n": "格斗", + "v": "/nes?tag=格斗&e=2&page=" + }, {"n": "射击", "v": "/nes?tag=射击&e=2&page="}, { + "n": "赛车", + "v": "/nes?tag=赛车&e=2&page=" + }, {"n": "体育", "v": "/nes?tag=体育&e=2&page="}, { + "n": "益智游戏", + "v": "/nes?tag=益智&e=2&page=" + }, {"n": "模拟", "v": "/nes?tag=模拟&e=2&page="}] + } + ], "/nes?tag=&e=7&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "角色扮演", "v": "/nes?tag=角色扮演&e=7&page="}, { + "n": "动作角色扮演", + "v": "/nes?tag=动作角色扮演&e=7&page=" + }, {"n": "模拟角色扮演", "v": "/nes?tag=模拟角色扮演&e=7&page="}, { + "n": "动作游戏", + "v": "/nes?tag=动作游戏&e=7&page=" + }, {"n": "冒险游戏", "v": "/nes?tag=冒险游戏&e=7&page="}, { + "n": "冒险解谜", + "v": "/nes?tag=冒险解谜&e=7&page=" + }, {"n": "策略战棋", "v": "/nes?tag=策略战棋&e=7&page="}, { + "n": "模拟经营", + "v": "/nes?tag=模拟经营&e=7&page=" + }, {"n": "体育竞技", "v": "/nes?tag=体育竞技&e=7&page="}, { + "n": "赛车竞速", + "v": "/nes?tag=赛车竞速&e=7&page=" + }, {"n": "格斗游戏", "v": "/nes?tag=格斗游戏&e=7&page="}, { + "n": "大乱斗游戏", + "v": "/nes?tag=大乱斗游戏&e=7&page=" + }, {"n": "射击游戏", "v": "/nes?tag=射击游戏&e=7&page="}, { + "n": "第一人称射击", + "v": "/nes?tag=第一人称射击&e=7&page=" + }, {"n": "益智游戏", "v": "/nes?tag=益智游戏&e=7&page="}, { + "n": "养成游戏", + "v": "/nes?tag=养成游戏&e=7&page=" + }, {"n": "音乐游戏", "v": "/nes?tag=音乐游戏&e=7&page="}, { + "n": "恋爱游戏", + "v": "/nes?tag=恋爱游戏&e=7&page=" + }, {"n": "卡片游戏", "v": "/nes?tag=卡片游戏&e=7&page="}, { + "n": "桌面游戏", + "v": "/nes?tag=桌面游戏&e=7&page=" + }] + } + ], "/nes?tag=&e=3&page=": [ + { + key: 'class', + name: '分类', + value: [{"n": "角色扮演", "v": "/nes?tag=角色扮演&e=3&page="}, { + "n": "动作冒险", + "v": "/nes?tag=动作冒险&e=3&page=" + }, {"n": "策略", "v": "/nes?tag=策略&e=3&page="}, { + "n": "棋牌", + "v": "/nes?tag=棋牌&e=3&page=" + }, {"n": "射击", "v": "/nes?tag=射击&e=3&page="}, { + "n": "模拟经营", + "v": "/nes?tag=模拟经营&e=3&page=" + }, {"n": "战棋", "v": "/nes?tag=战棋&e=3&page="}, { + "n": "格斗", + "v": "/nes?tag=格斗&e=3&page=" + }, {"n": "动作", "v": "/nes?tag=动作&e=3&page="}, { + "n": "解谜", + "v": "/nes?tag=解谜&e=3&page=" + }, {"n": "模拟", "v": "/nes?tag=模拟&e=3&page="}, { + "n": "休闲益智", + "v": "/nes?tag=休闲益智&e=3&page=" + }, {"n": "体育", "v": "/nes?tag=体育&e=3&page="}, {"n": "音乐", "v": "/nes?tag=音乐&e=3&page="}] + } + ] + }; + + return JSON.stringify({ + 'class': classes, + 'filters': filters + }); +} + +async function homeVod(params) { + return null; +} + +async function category(tid, pg, filter, extend) { + extend = extend || {}; + + if (tid == '定制') { + if (pg != 1) return null; + + const videos = []; + for (let it of myGame) { + videos.push({ + vod_id: JSON.stringify({ + actionId: 'browser', + type: 'browser', + title: '小游戏', + url: it.url, + header: it.header + }), + vod_name: it.name, + vod_pic: it.pic, + vod_tag: 'action' + }); + } + + return JSON.stringify({ + 'list': videos + }); + } + + if (extend.custom) { + return search(extend.custom, true, pg); + } + + const classz = extend.class; + const targetUrl = classz ? host + classz + pg : host + tid + pg; + const html = await request(targetUrl); + if (!html) return []; + + // 提取所有视频卡片 + const videoCards = pdfa(html, '.row .col-md-3.col-xs-6 .card-blog'); + const videos = []; + + for (const card of videoCards) { + // 提取图片链接 + const vod_pic = pdfh(card, '.card-image img&&src'); + + // 提取游戏名称 + const vod_name = pdfh(card, 'h4 a&&Text'); + + // 提取游戏链接 + const gamePath = pdfh(card, 'h4 a&&href'); + const gameUrl = gamePath.startsWith('http') ? gamePath : host + gamePath; + + // 提取标签(如果有多个标签,这里取第一个) + //const vod_tag = pdfh(card, '.table .label:first-child&&Text') || 'action'; + + videos.push({ + vod_id: JSON.stringify({ + actionId: 'browser', + type: 'browser', + title: '小游戏', + url: gameUrl, + textZoom: 100, + header: { + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36' + } + }), + vod_name: vod_name, + vod_pic: vod_pic, + vod_tag: 'action' // 统一转为小写 + }); + } + + // 添加固定项(如crazygames和poki) + videos.push( + { + vod_id: JSON.stringify({ + actionId: 'browser', + type: 'browser', + title: '小游戏', + url: 'https://www.crazygames.com' + }), + vod_name: 'crazygames', + vod_pic: '', + vod_tag: 'action' + }, + { + vod_id: JSON.stringify({ + actionId: 'browser', + type: 'browser', + title: '小游戏', + url: 'https://poki.com/zh' + }), + vod_name: 'poki', + vod_pic: '', + vod_tag: 'action' + } + ); + + return JSON.stringify({ + 'list': videos + }); +} + + +async function search(wd, quick, pg) { + const p = pg || 1; + const url = host + '/search?name=' + encodeURIComponent(wd); + const html = await request(url); + if (!html) return []; + const videoCard = pdfa(html, '.row .col-md-3.col-xs-6'); + console.log('【调试】网络数据:', videoCard); + // 提取所有视频卡片 + const videoCards = pdfa(html, '.row .col-md-3.col-xs-6 .card.card-blog'); + const videos = []; + + for (const card of videoCards) { + // 提取图片链接 + const vod_pic = pdfh(card, '.card-image img&&src'); + + // 提取游戏名称 + const vod_name = pdfh(card, 'h4 a&&Text'); + + // 提取游戏链接 + const gamePath = pdfh(card, 'h4 a&&href'); + const gameUrl = gamePath.startsWith('http') ? gamePath : host + gamePath; + + // 提取标签(如果有多个标签,这里取第一个) + //const vod_tag = pdfh(card, '.table .label:first-child&&Text') || 'action'; + + videos.push({ + vod_id: JSON.stringify({ + actionId: 'browser', + type: 'browser', + title: '小游戏', + url: gameUrl, + textZoom: 100, + header: { + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36' + } + }), + vod_name: vod_name, + vod_pic: vod_pic, + vod_tag: 'action' + }); + } + + return JSON.stringify({ + 'list': videos, + page: p + }); +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + search: search + }; +} diff --git "a/spider/catvod/\345\223\251\345\223\251[\345\256\230].js" "b/spider/catvod/\345\223\251\345\223\251[\345\256\230].js" new file mode 100644 index 00000000..91cecaa6 --- /dev/null +++ "b/spider/catvod/\345\223\251\345\223\251[\345\256\230].js" @@ -0,0 +1,323 @@ +/** + * 哔哩哔哩 - 猫影视JS爬虫格式 + * 调用壳子超级解析功能 + @header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '哩哩[官]', + lang: 'cat' + }) + */ + +class Spider extends BaseSpider { + + constructor() { + super(); + this.host = 'https://www.bilibili.com'; + this.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer': 'https://www.bilibili.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8' + }; + + // B站Cookie(需要登录才能获取高清画质) + this.cookie = ""; + this.isLoggedIn = () => { + return this.cookie && this.cookie.includes("SESSDATA="); + }; + } + + init(extend = '') { + return ''; + } + + getName() { + return '哔哩哔哩'; + } + + isVideoFormat(url) { + return true; + } + + manualVideoCheck() { + return false; + } + + destroy() { + // 清理资源 + } + + homeContent(filter) { + const classes = [ + {type_id: '1', type_name: '番剧'}, + {type_id: '4', type_name: '国创'}, + {type_id: '2', type_name: '电影'}, + {type_id: '5', type_name: '电视剧'}, + {type_id: '3', type_name: '纪录片'}, + {type_id: '7', type_name: '综艺'} + ]; + + return { + class: classes + }; + } + + homeVideoContent() { + return {list: []}; + } + + async categoryContent(tid, pg, filter, extend) { + try { + const page = parseInt(pg) || 1; + let url = ''; + + if (['1', '4'].includes(tid)) { + url = `https://api.bilibili.com/pgc/web/rank/list?season_type=${tid}&pagesize=20&page=${page}&day=3`; + } else { + url = `https://api.bilibili.com/pgc/season/rank/web/list?season_type=${tid}&pagesize=20&page=${page}&day=3`; + } + + const headers = {...this.headers}; + if (this.cookie) { + headers.Cookie = this.cookie; + } + + const response = await this.fetch(url, {}, headers); + const data = response.data || {}; + + const videos = []; + if (data.code === 0) { + const vodList = data.result ? data.result.list : (data.data ? data.data.list : []); + + for (const vod of vodList) { + const title = vod.title ? vod.title.trim() : ''; + if (title.includes('预告')) { + continue; + } + + const remark = vod.new_ep ? vod.new_ep.index_show : vod.index_show; + + // 处理封面图片 + let cover = vod.cover || ''; + if (cover && cover.startsWith('//')) { + cover = 'https:' + cover; + } + + videos.push({ + vod_id: vod.season_id ? vod.season_id.toString() : '', + vod_name: title, + vod_pic: cover, + vod_remarks: remark || '' + }); + } + } + + return { + list: videos, + page: page, + pagecount: videos.length === 20 ? page + 1 : page, + limit: 20, + total: 9999 + }; + + } catch (error) { + console.error(`categoryContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + formatCount(num) { + if (num > 1e8) return (num / 1e8).toFixed(2) + '亿'; + if (num > 1e4) return (num / 1e4).toFixed(2) + '万'; + return num.toString(); + } + + async detailContent(ids) { + try { + const seasonId = ids[0]; + + const headers = {...this.headers}; + if (this.cookie) { + headers.Cookie = this.cookie; + } + + const url = `https://api.bilibili.com/pgc/view/web/season?season_id=${seasonId}`; + const response = await this.fetch(url, {}, headers); + const data = response.data || {}; + + if (data.code !== 0) { + return {list: []}; + } + + const res = data.result; + const stat = res.stat || {}; + + // 处理封面图片 + let cover = res.cover || ''; + if (cover && cover.startsWith('//')) { + cover = 'https:' + cover; + } + + const vod = { + vod_id: res.season_id ? res.season_id.toString() : '', + vod_name: res.title || '', + vod_pic: cover, + type_name: res.share_sub_title || res.type_name || '', + vod_year: res.publish && res.publish.pub_time ? res.publish.pub_time.substr(0, 4) : '', + vod_area: res.areas && res.areas.length > 0 ? res.areas[0].name : '', + vod_actor: `点赞:${this.formatCount(stat.likes || 0)} 投币:${this.formatCount(stat.coins || 0)}`, + vod_content: res.evaluate || res.new_ep?.desc || '', + vod_director: res.rating ? `评分:${res.rating.score}` : '暂无评分', + vod_play_from: '哔哩哔哩', + vod_play_url: '' + }; + + // 过滤预告片,构建播放列表 + const episodes = (res.episodes || []).filter(ep => !ep.title.includes('预告')); + const playUrls = []; + + for (const ep of episodes) { + const title = `${ep.title.replace(/#/g, '-')} ${ep.long_title || ''}`; + const playId = `${res.season_id}_${ep.id}_${ep.cid}`; + playUrls.push(`${title}$${playId}`); + } + + vod.vod_play_url = playUrls.join('#'); + + return {list: [vod]}; + + } catch (error) { + console.error(`detailContent error: ${error.message}`); + return {list: []}; + } + } + + async searchContent(key, quick, pg = '1') { + try { + const page = parseInt(pg) || 1; + const encodedKeyword = encodeURIComponent(key); + const searchTypes = ['media_bangumi', 'media_ft']; + + const headers = {...this.headers}; + if (this.cookie) { + headers.Cookie = this.cookie; + } + + const allVideos = []; + + for (const type of searchTypes) { + try { + const url = `https://api.bilibili.com/x/web-interface/search/type?search_type=${type}&keyword=${encodedKeyword}&page=${page}`; + const response = await this.fetch(url, {}, headers); + const data = response.data || {}; + + if (data.code === 0 && data.data && data.data.result) { + for (const vod of data.data.result) { + const title = vod.title ? vod.title.replace(/<[^>]+>/g, '') : ''; + if (title.includes('预告')) { + continue; + } + + // 处理封面图片 + let cover = vod.cover || ''; + if (cover && cover.startsWith('//')) { + cover = 'https:' + cover; + } + + allVideos.push({ + vod_id: vod.season_id ? vod.season_id.toString() : '', + vod_name: title, + vod_pic: cover, + vod_remarks: vod.index_show || '' + }); + } + } + } catch (searchError) { + console.error(`搜索类型 ${type} 失败: ${searchError.message}`); + } + } + + return { + list: allVideos, + page: page, + pagecount: allVideos.length > 0 ? page + 1 : page, + limit: 20, + total: allVideos.length + }; + + } catch (error) { + console.error(`searchContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async playerContent(flag, id, vipFlags) { + try { + // 哔哩哔哩有自己的解析逻辑,直接返回播放链接 + // 格式:seasonId_epId_cid + const parts = id.split('_'); + if (parts.length < 3) { + throw new Error('无效的播放ID格式'); + } + + const seasonId = parts[0]; + const epId = parts[1]; + const cid = parts[2]; + + // 构建播放链接(原版B站链接) + const playUrl = `https://www.bilibili.com/bangumi/play/ep${epId}`; + + // 调用壳子超级解析 + const playData = { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '哔哩哔哩', + url: playUrl, + header: JSON.stringify({ + 'User-Agent': this.headers['User-Agent'], + 'Referer': 'https://www.bilibili.com', + 'Origin': 'https://www.bilibili.com', + 'Cookie': this.cookie || '' + }) + }; + + return playData; + + } catch (error) { + console.error(`playerContent error: ${error.message}`); + // 即使出错也返回超级解析参数 + return { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '哔哩哔哩', + url: id.includes('_') ? `https://www.bilibili.com/bangumi/play/ep${id.split('_')[1]}` : id, + header: JSON.stringify(this.headers) + }; + } + } + + localProxy(param) { + return null; + } +} + + +export default new Spider(); \ No newline at end of file diff --git "a/spider/catvod/\345\226\234\345\210\267\345\210\267.js" "b/spider/catvod/\345\226\234\345\210\267\345\210\267.js" new file mode 100644 index 00000000..c3fd1380 --- /dev/null +++ "b/spider/catvod/\345\226\234\345\210\267\345\210\267.js" @@ -0,0 +1,173 @@ +/* +@header({ + searchable: 0, + filterable: 0, + quickSearch: 0, + title: '喜刷刷', + lang: 'cat' +}) +*/ + +async function init(cfg) { +} + +async function home(filter, param) { + let classes = [ + { + type_id: 'shua', + type_name: '测试示例', + type_flag: '0-00-S' + }, + ]; + + return {class: classes}; +} + +async function homeVod(params) { + return null; +} + +async function category(tid, pg, filter, extend) { + if (pg > 1) return null; + + const videos = [ + { + vod_id: '美颜怪$美颜怪', + vod_name: '美颜怪', + vod_pic: 'https://vcg05.cfp.cn/creative/vcg/nowarter800/new/VCG41N910289498.jpg' + }, + { + vod_id: '短视频$https://v.api.aa1.cn/api/api-vs/index.php', + vod_name: '短视频', + vod_pic: 'https://vcg05.cfp.cn/creative/vcg/nowarter800/new/VCG41N910289498.jpg' + }, + { + vod_id: '酷音视频$https://api.suyanw.cn/api/kysp.php', + vod_name: '酷音视频', + vod_pic: ' https://vcg05.cfp.cn/creative/vcg/nowarter800/new/VCG41N910289498.jpg' + }, + { + vod_id: '刷刷刷1$http://xjj2.716888.xyz/fenlei/mvmn/mvmn.php', + vod_name: '刷刷刷1', + vod_pic: 'clan://assets/collect.png?bg=1' + }, + { + vod_id: '刷刷刷2$https://www.hhlqilongzhu.cn/api/MP4_xiaojiejie.php', + vod_name: '刷刷刷2', + vod_pic: 'clan://assets/collect.png?bg=2' + }, + { + vod_id: '刷刷刷3$http://api.cc1990.cc/api/video/ks_xjj', + vod_name: '刷刷刷3', + vod_pic: 'clan://assets/collect.png?bg=3' + }, + { + vod_id: '刷刷刷4$http://av.npcq.cn/pc.php', + vod_name: '刷刷刷4', + vod_pic: 'clan://assets/collect.png?bg=4' + }, + { + vod_id: '刷刷刷5$https://av.npcq.cn/api.php?action=next_video', + vod_name: '刷刷刷5', + vod_pic: 'clan://assets/collect.png?bg=5' + }, + ]; + + return {list: videos}; +} + +async function detail(tid) { + const vod = { + vod_id: tid, + vod_name: tid.split('\$')[0], + vod_play_from: "测试", + vod_play_url: tid, + vod_tag: '[SHUA]' + }; + + return {list: [vod]}; +} + +async function play(flag, id, flags) { + if (id == '美颜怪') { + return await 美颜怪(); + } + + let url = id; + let title = ''; + let res = req(url, { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36', + }, + redirect: 0 + }); + + if (res.headers.location) { + id = res.headers.location; + } else { + try { + const d = JSON.parse(res.content); + url = d.url ? d.url : d.video_url ? d.video_url : url; + title = d.title ? d.title : ''; + } catch (e) { + } + } + return { + parse: 0, + url: url, + header: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36' + }, + shuaTitle: title, + errorPlayNext: true + }; +} + +async function 美颜怪() { + + let pg = getRnd(1, 30000); + + const res = await req('http://81.70.181.238/hot_video/getList', { + method: 'POST', + body: `page=${pg}&limit=1&sign=${getSign(pg)}&device_id=ffffffff-ca78-c55b-ca78-c55b00000000&auth_code=NotLogin&pro_id=2`, + postType: 'form' + }); + + const d = JSON.parse(res.content); + + return { + parse: 0, + url: d.data.list[0].src, + errorPlayNext: true + }; +} + +function getSign(pg) { + let txt = "limit=30&page=" + pg + "&key=f6113acb6573d8b98502335e06f0c857"; + let sign = md5X(txt).toUpperCase(); + return sign; +} + +/* 生成指定范围的随机数(最小数,最大数,进制数, 是否大写)*/ +function getRnd(min, max, hexNum, isUpper) { + var r = parseInt(Math.random() * (max - min + 1) + min, 10); + if (hexNum) { + if (isUpper) + r = r.toString(hexNum).toUpperCase(); + else + r = r.toString(hexNum); + } + return r; +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + }; +} \ No newline at end of file diff --git "a/spider/catvod/\345\244\256\345\244\256[\345\256\230].js" "b/spider/catvod/\345\244\256\345\244\256[\345\256\230].js" new file mode 100644 index 00000000..478e4b10 --- /dev/null +++ "b/spider/catvod/\345\244\256\345\244\256[\345\256\230].js" @@ -0,0 +1,290 @@ +/** + * 央视大全 - 猫影视/TVBox JS爬虫格式 + * 继承BaseSpider类 + @header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '央央[官]', + lang: 'cat' + }) + */ + +class Spider extends BaseSpider { + + constructor() { + super(); + this.host = 'https://api.cntv.cn'; + this.siteName = '央视大全'; + this.sessionStore = {}; + this.videoCache = {}; + + this.headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer": "https://tv.cctv.com", + "Accept": "application/json, text/plain, */*", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" + }; + } + + init(extend = "") { + return ""; + } + + getName() { + return this.siteName; + } + + isVideoFormat(url) { + return url.includes('.m3u8') || url.includes('.mp4'); + } + + manualVideoCheck() { + return false; + } + + destroy() { + this.sessionStore = {}; + this.videoCache = {}; + } + + homeContent(filter) { + const categories = [ + {type_id: "栏目大全", type_name: "栏目大全"}, + {type_id: "特别节目", type_name: "特别节目"}, + {type_id: "纪录片", type_name: "纪录片"}, + {type_id: "电视剧", type_name: "电视剧"}, + {type_id: "动画片", type_name: "动画片"} + ]; + + return {class: categories}; + } + + async homeVideoContent() { + // 央视首页推荐 + return {list: []}; + } + + async categoryContent(tid, pg, filter, extend) { + try { + const page = parseInt(pg) || 1; + const videos = []; + + const channelMap = { + "特别节目": "CHAL1460955953877151", + "纪录片": "CHAL1460955924871139", + "电视剧": "CHAL1460955853485115", + "动画片": "CHAL1460955899450127", + }; + + let filterObj = {}; + if (extend && typeof extend === 'object') { + filterObj = extend; + } + + if (tid === '栏目大全') { + const url = `${this.host}/lanmu/columnSearch?&fl=&fc=&cid=&p=${page}&n=20&serviceId=tvcctv&t=json`; + const response = await this.fetch(url, {}, this.headers); + const data = response.data; + + if (data && data.response && data.response.docs) { + const docs = data.response.docs; + docs.forEach(it => { + videos.push({ + vod_id: `${it.lastVIDE.videoSharedCode}|${it.column_firstclass}|${it.column_name}|${it.channel_name}|${it.column_brief}|${it.column_logo}|${it.lastVIDE.videoTitle}|栏目大全`, + vod_name: it.column_name, + vod_pic: it.column_logo, + vod_remarks: it.channel_name, + vod_content: '' + }); + }); + } + } else { + // 处理筛选参数 + let fl_url = `&channelid=${channelMap[tid] || ''}&fc=${encodeURIComponent(tid)}`; + if (filterObj.channel) fl_url += `&channel=${encodeURIComponent(filterObj.channel)}`; + if (filterObj.sc) fl_url += `&sc=${encodeURIComponent(filterObj.sc)}`; + if (filterObj.year) fl_url += `&year=${filterObj.year}`; + + const url = `${this.host}/list/getVideoAlbumList?${fl_url}&area=&letter=&n=24&serviceId=tvcctv&t=json&p=${page}`; + const response = await this.fetch(url, {}, this.headers); + const data = response.data; + + if (data && data.data && data.data.list) { + const dataList = data.data.list; + dataList.forEach(it => { + videos.push({ + vod_id: `${it.id}|${it.sc}|${it.title}|${it.channel}|${it.brief}|${it.image}|${it.count}|${tid}`, + vod_name: it.title, + vod_pic: it.image, + vod_remarks: `${it.sc}${it.year ? '·' + it.year : ''}`, + vod_content: it.brief || '' + }); + }); + } + } + + return { + list: videos, + page: page, + pagecount: 9999, + limit: 20, + total: 999999 + }; + + } catch (error) { + console.error(`categoryContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async detailContent(ids) { + try { + const id = ids[0]; + if (!id) return {list: []}; + + // 检查缓存 + const cacheKey = `detail_${id}`; + if (this.videoCache[cacheKey]) { + return {list: [this.videoCache[cacheKey]]}; + } + + const info = id.split("|"); + // ID 结构: 0:id, 1:sc, 2:title, 3:channel, 4:brief, 5:image, 6:count/remark, 7:cate + + const cate = info[7]; + const ctid = info[0]; + const modeMap = { + "特别节目": "0", + "纪录片": "0", + "电视剧": "0", + "动画片": "1" + }; + + // 获取选集列表 + let playUrls = []; + const mode = modeMap[cate] || '0'; + const albumUrl = `${this.host}/NewVideo/getVideoListByAlbumIdNew?id=${ctid}&serviceId=tvcctv&p=1&n=100&mode=${mode}&pub=1`; + + const response = await this.fetch(albumUrl, {}, this.headers); + const data = response.data; + + if (data.errcode === '1001') { + // 需要获取真实的ctid + const videoInfoUrl = `${this.host}/video/videoinfoByGuid?guid=${ctid}&serviceId=tvcctv`; + const vInfoRes = await this.fetch(videoInfoUrl, {}, this.headers); + const vInfoData = vInfoRes.data; + const realCtid = vInfoData.ctid; + + const columnUrl = `${this.host}/NewVideo/getVideoListByColumn?id=${realCtid}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json`; + const colRes = await this.fetch(columnUrl, {}, this.headers); + const colData = colRes.data; + playUrls = colData.data?.list || []; + } else { + playUrls = data.data?.list || []; + } + + // 构建播放列表 + const playList = []; + if (playUrls.length > 0) { + for (const item of playUrls) { + const title = item.title || `第${item.index || '?'}集`; + const cleanTitle = title.replace(/\$/g, ''); + const guid = item.guid || ''; + playList.push(`${cleanTitle}$${guid}`); + } + } + + const vod = { + vod_id: id, + vod_name: info[2] || '', + vod_pic: info[5] || '', + type_name: info[1] || '', + vod_year: '', + vod_area: '', + vod_remarks: info[6] ? `共${info[6]}集` : '', + vod_actor: '', + vod_director: '', + vod_content: info[4] || '', + vod_play_from: playList.length > 0 ? '央视频' : '', + vod_play_url: playList.length > 0 ? playList.join('#') : '' + }; + + // 缓存结果 + this.videoCache[cacheKey] = vod; + + return {list: [vod]}; + + } catch (error) { + console.error(`detailContent error: ${error.message}`); + return {list: []}; + } + } + + async searchContent(key, quick, pg = "1") { + // CCTV搜索接口较复杂,这里返回空结果 + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + + async playerContent(flag, id, vipFlags) { + try { + // 央视视频采用直接播放的方式 + // 根据GUID拼接m3u8地址 + let playUrl = `https://cntv.playdreamer.cn/proxy/asp/hls/2000/0303000a/3/default/${id}/2000.m3u8`; + + // 也可以尝试其他格式 + // playUrl = `https://hls.cntv.myalicdn.com/asp/hls/2000/0303000a/3/default/${id}/2000.m3u8`; + + return { + parse: 0, // 0表示直接播放,不需要解析 + jx: 0, // 0表示不解析 + url: playUrl, + header: JSON.stringify({ + 'User-Agent': this.headers['User-Agent'], + 'Referer': 'https://tv.cctv.com', + 'Origin': 'https://tv.cctv.com' + }) + }; + + } catch (error) { + console.error(`playerContent error: ${error.message}`); + return { + parse: 0, + jx: 0, + url: id, + header: JSON.stringify(this.headers) + }; + } + } + + localProxy(param) { + return null; + } + + // 辅助方法:安全获取对象属性 + getSafe(obj, path, defaultValue = '') { + if (!obj || typeof obj !== 'object') return defaultValue; + try { + return path.split('.').reduce((o, key) => { + if (o == null) return defaultValue; + return o[key]; + }, obj) ?? defaultValue; + } catch { + return defaultValue; + } + } +} + +export default new Spider(); \ No newline at end of file diff --git "a/spider/catvod/\345\245\207\345\245\207[\345\256\230].js" "b/spider/catvod/\345\245\207\345\245\207[\345\256\230].js" new file mode 100644 index 00000000..89ce8472 --- /dev/null +++ "b/spider/catvod/\345\245\207\345\245\207[\345\256\230].js" @@ -0,0 +1,403 @@ +/** + * 爱奇艺视频 - 猫影视/TVBox JS爬虫格式 + * 调用壳子超级解析功能(壳子会自动读取json配置) + @header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '奇奇[官]', + lang: 'cat' + }) + */ + +class Spider extends BaseSpider { + + constructor() { + super(); + this.host = 'https://www.iqiyi.com'; + this.sessionStore = {}; + + this.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer': 'https://www.iqiyi.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive' + }; + + // 分类配置 + this.classes = [ + {type_id: '1', type_name: '电影'}, + {type_id: '2', type_name: '电视剧'}, + {type_id: '6', type_name: '综艺'}, + {type_id: '4', type_name: '动漫'}, + {type_id: '3', type_name: '纪录片'}, + {type_id: '5', type_name: '音乐'}, + {type_id: '16', type_name: '网络电影'} + ]; + + // 筛选配置 + this.filters = { + '1': [{ + key: 'year', + name: '年代', + value: [{n: '全部', v: ''}, {n: '2025', v: '2025'}, {n: '2024', v: '2024'}, {n: '2023', v: '2023'}] + }], + '2': [{ + key: 'year', + name: '年代', + value: [{n: '全部', v: ''}, {n: '2025', v: '2025'}, {n: '2024', v: '2024'}, {n: '2023', v: '2023'}] + }] + }; + } + + init(extend = '') { + return ''; + } + + getName() { + return '爱奇艺视频'; + } + + isVideoFormat(url) { + return true; + } + + manualVideoCheck() { + return false; + } + + destroy() { + // 清理资源 + } + + homeContent(filter) { + const result = { + class: this.classes, + filters: this.filters + }; + + return result; + } + + homeVideoContent() { + return {list: []}; + } + + async categoryContent(tid, pg, filter, extend) { + try { + let channelId = tid; + let dataType = 1; + let extraParams = ""; + const page = parseInt(pg) || 1; + + if (tid === "16") { + channelId = "1"; + extraParams = "&three_category_id=27401"; + } else if (tid === "5") { + dataType = 2; + } + + // 处理筛选条件 + if (extend) { + let extendObj = {}; + if (typeof extend === 'string') { + try { + extendObj = JSON.parse(extend); + } catch (e) { + // 如果不是JSON,尝试解析为key=value格式 + extend.split('&').forEach(item => { + const [key, value] = item.split('='); + if (key && value) { + extendObj[key] = value; + } + }); + } + } else if (typeof extend === 'object') { + extendObj = extend; + } + + if (extendObj.year) { + extraParams += `&market_release_date_level=${extendObj.year}`; + } + } + + const url = `https://pcw-api.iqiyi.com/search/recommend/list?channel_id=${channelId}&data_type=${dataType}&page_id=${page}&ret_num=20${extraParams}`; + + const response = await this.fetch(url, {}, this.headers); + const jsonData = response.data; + + const videos = []; + if (jsonData.data && jsonData.data.list) { + for (const item of jsonData.data.list) { + const vid = `${item.channelId}$${item.albumId}`; + let remarks = ""; + + if (item.channelId === 1) { + remarks = item.score ? `${item.score}分` : ""; + } else if (item.channelId === 2 || item.channelId === 4) { + if (item.latestOrder && item.videoCount) { + remarks = item.latestOrder === item.videoCount ? + `${item.latestOrder}集全` : + `更新至${item.latestOrder}集`; + } else { + remarks = item.focus || ""; + } + } else { + remarks = item.period || item.focus || ""; + } + + videos.push({ + vod_id: vid, + vod_name: item.name, + vod_pic: item.imageUrl ? item.imageUrl.replace(".jpg", "_390_520.jpg") : "", + vod_remarks: remarks + }); + } + } + + return { + list: videos, + page: page, + pagecount: 9999, + limit: 20, + total: 999999 + }; + + } catch (error) { + console.error(`categoryContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async getPlaylists(channelId, albumId, data) { + let playlists = []; + const cid = parseInt(channelId || data.channelId || 0); + + try { + if (cid === 1 || cid === 5) { + // 电影或音乐 + if (data.playUrl) { + playlists.push({title: data.name || '正片', url: data.playUrl}); + } + } else if (cid === 6 && data.period) { + // 综艺 + let qs = data.period.toString().split("-")[0]; + let listUrl = `https://pcw-api.iqiyi.com/album/source/svlistinfo?cid=6&sourceid=${albumId}&timelist=${qs}`; + try { + const listResp = await this.fetch(listUrl, {}, this.headers); + const listJson = listResp.data; + if (listJson.data && listJson.data[qs]) { + listJson.data[qs].forEach(it => { + playlists.push({ + title: it.shortTitle || it.period || it.focus || `期${it.order}`, + url: it.playUrl + }); + }); + } + } catch (e) { + console.error(`综艺列表获取失败: ${e.message}`); + } + } else { + // 电视剧、动漫等 + let listUrl = `https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid=${albumId}&size=100&page=1`; + try { + const listResp = await this.fetch(listUrl, {}, this.headers); + const listJson = listResp.data; + + if (listJson.data && listJson.data.epsodelist) { + playlists = listJson.data.epsodelist.map(item => ({ + title: item.shortTitle || item.title || + (item.order ? `第${item.order}集` : `集${item.timelist}`), + url: item.playUrl || item.url || '' + })); + + // 处理分页 + const total = listJson.data.total; + if (total > 100) { + const totalPages = Math.ceil(total / 100); + for (let i = 2; i <= totalPages; i++) { + let nextUrl = `https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid=${albumId}&size=100&page=${i}`; + try { + const nextResp = await this.fetch(nextUrl, {}, this.headers); + const nextJson = nextResp.data; + if (nextJson.data && nextJson.data.epsodelist) { + playlists = playlists.concat(nextJson.data.epsodelist.map(item => ({ + title: item.shortTitle || item.title || + (item.order ? `第${item.order}集` : `集${item.timelist}`), + url: item.playUrl || item.url || '' + }))); + } + } catch (e) { + break; + } + } + } + } + } catch (e) { + console.error(`剧集列表获取失败: ${e.message}`); + } + } + } catch (error) { + console.error(`getPlaylists error: ${error.message}`); + } + + return playlists; + } + + async detailContent(ids) { + try { + const id = ids[0]; + let channelId = ""; + let albumId = id; + + if (id.includes('$')) { + const parts = id.split('$'); + channelId = parts[0]; + albumId = parts[1]; + } + + // 获取视频基本信息 + const infoUrl = `https://pcw-api.iqiyi.com/video/video/videoinfowithuser/${albumId}?agent_type=1&authcookie=&subkey=${albumId}&subscribe=1`; + const infoResp = await this.fetch(infoUrl, {}, this.headers); + const infoJson = infoResp.data; + const data = infoJson.data || {}; + + // 获取播放列表 + const playlists = await this.getPlaylists(channelId, albumId, data); + + // 构建播放地址 + const playUrls = []; + if (playlists.length > 0) { + for (const item of playlists) { + if (item.url) { + playUrls.push(`${item.title}$${item.url}`); + } + } + } + + const vod = { + vod_id: id, + vod_name: data.name || '未知标题', + type_name: data.categories ? data.categories.map(it => it.name).join(',') : '', + vod_year: data.formatPeriod || '', + vod_area: data.areas ? data.areas.map(it => it.name).join(',') : '', + vod_remarks: data.latestOrder ? + `更新至${data.latestOrder}集` : + (data.period || playlists.length > 0 ? `${playlists.length}集` : ''), + vod_actor: data.people && data.people.main_charactor ? + data.people.main_charactor.map(it => it.name).join(',') : '', + vod_director: data.people && data.people.director ? + data.people.director.map(it => it.name).join(',') : '', + vod_content: data.description || '暂无简介', + vod_pic: data.imageUrl ? data.imageUrl.replace(".jpg", "_480_270.jpg") : '', + vod_play_from: playUrls.length > 0 ? '爱奇艺视频' : '', + vod_play_url: playUrls.length > 0 ? playUrls.join('#') : '' + }; + + return {list: [vod]}; + + } catch (error) { + console.error(`detailContent error: ${error.message}`); + return {list: []}; + } + } + + async searchContent(key, quick, pg = '1') { + try { + const page = parseInt(pg) || 1; + const url = `https://search.video.iqiyi.com/o?if=html5&key=${encodeURIComponent(key)}&pageNum=${page}&pos=1&pageSize=20&site=iqiyi`; + + const response = await this.fetch(url, {}, this.headers); + const jsonData = response.data; + + const videos = []; + + if (jsonData.data && jsonData.data.docinfos) { + for (const item of jsonData.data.docinfos) { + if (item.albumDocInfo) { + const doc = item.albumDocInfo; + const channelId = doc.channel ? doc.channel.split(',')[0] : '0'; + videos.push({ + vod_id: `${channelId}$${doc.albumId}`, + vod_name: doc.albumTitle || '', + vod_pic: doc.albumVImage || '', + vod_remarks: doc.tvFocus || doc.year || '' + }); + } + } + } + + return { + list: videos, + page: page, + pagecount: 10, + limit: 20, + total: videos.length + }; + + } catch (error) { + console.error(`searchContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async playerContent(flag, id, vipFlags) { + try { + // 解析播放地址 + let playUrl = id; + if (id.includes('$')) { + playUrl = id.split('$')[1]; + } + + // 关键:调用壳子超级解析 + const playData = { + parse: 1, // 必须为1,表示需要解析 + jx: 1, // 必须为1,启用解析 + play_parse: true, // 启用播放解析 + parse_type: '壳子超级解析', + parse_source: '爱奇艺视频', + url: playUrl, // 原始爱奇艺链接 + header: JSON.stringify({ + 'User-Agent': this.headers['User-Agent'], + 'Referer': 'https://www.iqiyi.com', + 'Origin': 'https://www.iqiyi.com' + }) + }; + + return playData; + + } catch (error) { + console.error(`playerContent error: ${error.message}`); + // 即使出错也返回超级解析参数,让壳子处理 + return { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '爱奇艺视频', + url: id, + header: JSON.stringify(this.headers) + }; + } + } + + localProxy(param) { + return null; + } +} + +export default new Spider(); \ No newline at end of file diff --git "a/spider/catvod/\346\236\234\346\236\234[\345\256\230].js" "b/spider/catvod/\346\236\234\346\236\234[\345\256\230].js" new file mode 100644 index 00000000..ea5846fc --- /dev/null +++ "b/spider/catvod/\346\236\234\346\236\234[\345\256\230].js" @@ -0,0 +1,365 @@ +/** + * 芒果TV - 猫影视JS爬虫格式(第二个版本) + * 调用壳子超级解析功能 + @header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '果果[官]', + lang: 'cat' + }) + */ + +class Spider extends BaseSpider { + + constructor() { + super(); + this.host = 'https://www.mgtv.com'; + this.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', + 'Referer': 'https://www.mgtv.com/', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive' + }; + } + + init(extend = '') { + return ''; + } + + getName() { + return '芒果TV2'; + } + + isVideoFormat(url) { + return true; + } + + manualVideoCheck() { + return false; + } + + destroy() { + // 清理资源 + } + + homeContent(filter) { + const classes = [ + {type_id: '3', type_name: '电影'}, + {type_id: '2', type_name: '电视剧'}, + {type_id: '1', type_name: '综艺'}, + {type_id: '50', type_name: '动漫'}, + {type_id: '51', type_name: '纪录片'}, + {type_id: '115', type_name: '教育'}, + {type_id: '10', type_name: '少儿'} + ]; + + const filters = { + '3': [ + { + key: 'year', name: '年份', value: [ + {n: '全部', v: 'all'}, {n: '2025', v: '2025'}, {n: '2024', v: '2024'}, + {n: '2023', v: '2023'}, {n: '2022', v: '2022'}, {n: '2021', v: '2021'}, + {n: '2020', v: '2020'}, {n: '2019', v: '2019'}, {n: '2010-2019', v: '2010-2019'}, + {n: '2000-2009', v: '2000-2009'} + ] + }, + { + key: 'sort', name: '排序', value: [ + {n: '综合', v: 'c1'}, {n: '最新', v: 'c2'}, {n: '最热', v: 'c4'} + ] + } + ], + '2': [ + { + key: 'year', name: '年份', value: [ + {n: '全部', v: 'all'}, {n: '2025', v: '2025'}, {n: '2024', v: '2024'}, + {n: '2023', v: '2023'}, {n: '2022', v: '2022'}, {n: '2021', v: '2021'}, + {n: '2020', v: '2020'} + ] + }, + { + key: 'sort', name: '排序', value: [ + {n: '综合', v: 'c1'}, {n: '最新', v: 'c2'}, {n: '最热', v: 'c4'} + ] + } + ], + '1': [ + { + key: 'sort', name: '排序', value: [ + {n: '综合', v: 'c1'}, {n: '最新', v: 'c2'}, {n: '最热', v: 'c4'} + ] + } + ], + '50': [ + { + key: 'sort', name: '排序', value: [ + {n: '综合', v: 'c1'}, {n: '最新', v: 'c2'}, {n: '最热', v: 'c4'} + ] + } + ] + }; + + return { + class: classes, + filters: filters + }; + } + + homeVideoContent() { + return {list: []}; + } + + async categoryContent(tid, pg, filter, extend) { + try { + const page = parseInt(pg) || 1; + const baseUrl = 'https://pianku.api.mgtv.com/rider/list/pcweb/v3'; + + // 构建查询参数 + const params = { + platform: 'pcweb', + channelId: tid, + pn: page, + pc: '20', + hudong: '1', + _support: '10000000', + kind: 'a1', + area: 'a1' + }; + + // 处理筛选条件 + if (extend) { + if (extend.year && extend.year !== 'all') { + params.year = extend.year; + } + if (extend.sort) { + params.sort = extend.sort; + } + if (extend.chargeInfo) { + params.chargeInfo = extend.chargeInfo; + } + } + + const queryString = new URLSearchParams(params).toString(); + const url = `${baseUrl}?${queryString}`; + + const response = await this.fetch(url, {}, this.headers); + const json = response.data || {}; + + const videos = []; + if (json.data?.hitDocs && Array.isArray(json.data.hitDocs)) { + for (const item of json.data.hitDocs) { + videos.push({ + vod_id: item.playPartId || '', + vod_name: item.title || '', + vod_pic: item.img || '', + vod_remarks: item.updateInfo || item.rightCorner?.text || '' + }); + } + } + + return { + list: videos, + page: page, + pagecount: json.data?.totalPage || 999, + limit: 20, + total: json.data?.totalHit || 9999 + }; + + } catch (error) { + console.error(`categoryContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async detailContent(ids) { + try { + const videoId = ids[0]; + + // 获取视频基本信息 + const infoUrl = `https://pcweb.api.mgtv.com/video/info?video_id=${videoId}`; + const infoResponse = await this.fetch(infoUrl, {}, this.headers); + const infoData = infoResponse.data?.data?.info || {}; + + const vod = { + vod_id: videoId, + vod_name: infoData.title || '', + type_name: infoData.root_kind || '', + vod_actor: '', + vod_year: infoData.release_time || '', + vod_content: infoData.desc || '', + vod_remarks: infoData.time || '', + vod_pic: infoData.img || '', + vod_play_from: '芒果TV', + vod_play_url: '' + }; + + // 分页获取所有剧集 + const pageSize = 50; + let allEpisodes = []; + + try { + // 获取第一页,同时获取总页数 + const firstPageUrl = `https://pcweb.api.mgtv.com/episode/list?video_id=${videoId}&page=1&size=${pageSize}`; + const firstResponse = await this.fetch(firstPageUrl, {}, this.headers); + const firstData = firstResponse.data?.data || {}; + + if (firstData.list && Array.isArray(firstData.list)) { + allEpisodes = allEpisodes.concat(firstData.list); + const totalPages = firstData.total_page || 1; + + // 如果有多页,获取剩余页面 + if (totalPages > 1) { + const pagePromises = []; + for (let i = 2; i <= totalPages; i++) { + const pageUrl = `https://pcweb.api.mgtv.com/episode/list?video_id=${videoId}&page=${i}&size=${pageSize}`; + pagePromises.push(this.fetch(pageUrl, {}, this.headers)); + } + + const responses = await Promise.all(pagePromises); + for (const response of responses) { + const data = response.data?.data || {}; + if (data.list && Array.isArray(data.list)) { + allEpisodes = allEpisodes.concat(data.list); + } + } + } + } + } catch (episodeError) { + console.error(`获取剧集列表失败: ${episodeError.message}`); + } + + // 构建播放列表 + const playUrls = []; + if (allEpisodes.length > 0) { + // 过滤可播放的剧集(isIntact = 1) + const validEpisodes = allEpisodes.filter(item => + item.isIntact === "1" || item.isIntact === 1 + ); + + // 按集数排序 + validEpisodes.sort((a, b) => { + const orderA = parseInt(a.order) || 0; + const orderB = parseInt(b.order) || 0; + return orderA - orderB; + }); + + // 构建播放链接 + for (const item of validEpisodes) { + const name = item.t4 || item.t3 || item.title || `第${item.order || '?'}集`; + const playLink = item.url ? `https://www.mgtv.com${item.url}` : ''; + + if (playLink) { + playUrls.push(`${name}$${playLink}`); + } + } + } + + vod.vod_play_url = playUrls.join('#'); + + return {list: [vod]}; + + } catch (error) { + console.error(`detailContent error: ${error.message}`); + return {list: []}; + } + } + + async searchContent(key, quick, pg = '1') { + try { + const page = parseInt(pg) || 1; + const searchUrl = `https://mobileso.bz.mgtv.com/msite/search/v2?q=${encodeURIComponent(key)}&pn=${page}&pc=20`; + + const response = await this.fetch(searchUrl, {}, this.headers); + const json = response.data?.data || {}; + + const videos = []; + + if (json.contents && Array.isArray(json.contents)) { + for (const group of json.contents) { + if (group.type === 'media' && group.data && Array.isArray(group.data)) { + for (const item of group.data) { + if (item.source === 'imgo') { + // 提取视频ID + const match = item.url.match(/\/(\d+)\.html/); + if (match) { + videos.push({ + vod_id: match[1], + vod_name: item.title ? item.title.replace(/|<\/B>/g, '') : '', + vod_pic: item.img || '', + vod_remarks: item.desc ? item.desc.join(' ') : '' + }); + } + } + } + } + } + } + + return { + list: videos, + page: page, + pagecount: 10, + limit: 20, + total: videos.length + }; + + } catch (error) { + console.error(`searchContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async playerContent(flag, id, vipFlags) { + try { + // 调用壳子超级解析 + const playData = { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '芒果TV2', + url: id, + header: JSON.stringify({ + 'User-Agent': this.headers['User-Agent'], + 'Referer': 'https://www.mgtv.com', + 'Origin': 'https://www.mgtv.com' + }) + }; + + return playData; + + } catch (error) { + console.error(`playerContent error: ${error.message}`); + return { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '芒果TV2', + url: id, + header: JSON.stringify(this.headers) + }; + } + } + + localProxy(param) { + return null; + } +} + +export default new Spider(); \ No newline at end of file diff --git "a/spider/catvod/\350\223\235\350\216\223\350\201\232\345\220\210\347\237\255\345\211\247[B].js" "b/spider/catvod/\350\223\235\350\216\223\350\201\232\345\220\210\347\237\255\345\211\247[B].js" index a699e4e1..2b940d50 100644 --- "a/spider/catvod/\350\223\235\350\216\223\350\201\232\345\220\210\347\237\255\345\211\247[B].js" +++ "b/spider/catvod/\350\223\235\350\216\223\350\201\232\345\220\210\347\237\255\345\211\247[B].js" @@ -8,4 +8,4 @@ }) */ -22DAA16367B92CC030386164898346DD1C0609E5E4216F8DBCAECCF35EF70F55C25CE8F59EEED9164FDD8C3D453708E101E456346123448D658C94C0869AB7674CE7EFBD6258F1FEC412F8941F11017D143F2C4CF0FCBC32A3002C132208834E3C52CBACA4AED32A44BD9B515936CC0394704099B511F18BF3F35BCDBDD1A76E49A45DD5DA51ADB54014B3284BA7A5BB9A69A60E35966EE12ACFCD5D4AC4EC96CFB44F8B15BEA6CEC221FF7B90DA9FC027927A6505257C3E562934F81FCF81CF6ED36E981BE2A608F5B01B6138500D16C720B560EAD4D223A280E54FCE22838AD070DD8219D1D5FC6D6AD204C5E584B49F419C954E944CD189340FC87B01EE424399AA4ED45AAF0B35E10D9B36BA9B493951BEC257025998ACC0C8115CCED73BFCBDC1FFD3DFD6DAFB1A065DDF8B7CCED61C73FFCC02076BA49FF37719AC60E60F11A75716A0BD6AB221A413F0E80A7A8F58C670B5ADFA7A914CB105EB8364CBF5763D2A8BF14072D906D715FC779F36987910C29832E56A9573762ACB105EFEF5119892059422CD79300EF60408F05A1DB8AD64E6B53C76AB9A82BA2DB9F266EE5ED7C00401B63AF4709C71C16D11E0E451D79F96CDC0F5A9379988B6413C9F6B41A0343DA3869AE33A69312BE6DF05CD5BDA8783AE53E3843A45A25A4A4C2FFAFA48285F419EA9909B3B43A89C53CC5F5F58EF747023111CBA085BF16F8C48A46FDCCF1324F2160C433AC7092ED6966E7B84D49C2809F015A45A2B2F8326C0066A247F9A630D74BAEB068918CF7FA09EEA004412A9314D6078C4157C566523EFFC85678C063A36FDAA9A189FAACB6CCDA183D4DE30F4094CEF3CC9613D5596017206BCCD7D9EFFB34203B7C3E60F33E96D5A288E494E030C79E81CD6698C494AEE621467FCC615DF4B4CB587BC4BD594EA26668C06FEB662676C5110D7A14D90F8D1B0921964E6F48BC738B160F3AE0658CB9487DDC904BBC7CD80451975FDCBBBF5CB59F7DFBF034F8B738375A99D1416696BEAB17C924E5D2D37B0FE5C116FABDACB5C99F245C867A96D21792FF7C5622CCC88C2EA0BE56B3DF069731B22C9116DA6A9D75AA8CA50E9555FE32EC237A48A5364F3E09E5D63961EEBF795139CAD4D79810EEA914B014D0A05E4D9CAF883EF7D94E2432FAA0B657FF9B71049D7EA0959096B941DC18792A1746ECBF91F5837728DD7DF365E7D168FA39C61DC65F7351A35D7172E55F10F03197686ED6AF14D8C5AEB941A35DAD514D58D84C3D920DA793D5CFEE71C25E53EC21C1936FCE766119A6492CBB9B67925B9DB30B75E2391D8DF684AD5DF08B64F3DDFC31FEAAF35B5CA72BB73BCA95CA621C9D0C03E877B115EF2B3988078E83AF83D2AC691092C7AAF8D45557CF4003D4FA67A81C880EBCD8318B37C5219808335CB7F8664E176769B1BACCAB58E0E1C8B2F7B06D339FB38718653D352F6A5B2FBEDF758F2AD54B2548DE7A343B23B8E7B437C1AE212CF0DE693DD84ECB9D922B756FCBF8F67C143A90671A7A0DFE8A08063F24539FCD1579DAAC2A6DF8BA36B6DC901FF6AAA05763A10334ADF0381DF233A56FD68CE17F5B3279C1F5CAB7CB1D8FB5C490DD23B77D895061426287D96106858325AA4E666605D6A1488781190781DE82AEF9EFD0700D189757D64634844EE0EB21F152401DA3527A75B29519B9115B9A5CA3F2C181551079205A874877E61BCF897C2444DAE41DDC644D8F2635211C6A1851AF13DAFDE63DD929ED87434B8C2E3BC0AF17040ABC43EEE2F7170680AD7194194EA04AA55905F746778DB544F9F06013E3276BF535FE4255383F31BDE7F5F9CC241A7EE71CC839F57C6A8356B89BEF0C43975A1381FAA09D7CB1C0ACC85FDFACE418B994FA2A86E0F4B55E17E40830FF0236F140455E44063D126F164DAEC12BA124D2D212D52311103353E5D7E6BFCA89923F294673BD43CD5DE58C051BE48F7CBB49687F675A4EE1A407738713130868401CF272324A3675A1DDE2960FFDA9065B1E5479E0B62B5E36C8440FE50C2AAF5EDF7FE1001869A9A93763B9B805B2628E6759A1DBD1A68465709D52B808701E5ACB87E68AE661BE8B3CED65D6D57D23F3680E21C2A3766D0A13AEE06E042C973A04A1D13AB4E320AE59DBDCCE0A5DE7B3E6B6D70DCA38910B3267F21CFC6D5457B9C119E1AEA4CE2DEEBDFBBAB3731FFF612426DF576D4A82AC5B2C043346B152E1632635C708D6BCD21902666EA42CDF002D526D91D37576197FDE6721211C47EED3F1D753219E8730EF4299D321AEBA5115C4C99DD532B34044CC1C2E65D4421BB4F28B9A10A0FB6B1D23B1C1867B698C9DBEE0600FB29AEE4D5661D128BA572B285AD7E646F689D93CC5C1F41DF28CA28A402BA3036745A6FE8C783E4E5104CBFCA723F28FE4377A5C9398592BE67BB2F5E8203C76DE5197B39B1477DF3FE167E667B77D6A965F98B2CB6AA37C8AE5707AA27B3D6367277EF6D4F90DF1E60929189CBD8215AB3510D1A62935D672D7DFC2BA16E204B77BD2E8BF34FFD6AB21A87D147349F58BD8F9245B8B396E0DFCF23DB0DDEE7A32DFAEA6E1ECF18DDC32E868E1C2A54CABCFE97D08CAC8856F9353A21E3E840AF5DD56450F474F1AFFA6A4AA5538B903C2B696618BEBD087612F0800B22A721993A7285F462F30CA340A093A37E70F49FCA876A3096C23A2E08DB4619B5C48DB7F3FEAC8DEBD60642424511DC26DCE6B749FFF58C3905D0E095768771EF820E0944BCD2DE87F551E388DAE0CC5E81653CC9F82411E6B84B358B6F2FCF3933A356AEB2E799163E163C6A7D79FBAD027291C77D630A652270F10ED4DC9F7F6AF04FD21FC7257C6079DF692170E0065674323313911BE264C40A0070BDD9E631C5893070134DAD54D53C9A47694B3F731AD6A6D45D9661A9A5A0C89614352199E3E38911D7649D40ADC731DBF8B100644BB3D836CD15A534CE876D98E92810E0A22FC410F6CB55D224689E2C7FC6C6A357D2143105CAD937916EBBE2B2F683237566EECE0CA25A2FC51283E43C0DE5A9875BD13C94E3EE9FF71B77E5A410CECF084565F535DDAC9D1023BAF6F43D3850FC17E916B277477FA144978694FF61852C77805C28D29C8A19AF52CF3C0666C324AF9D77034C1FD1581179C063C0B9AC1D72A038F4B85214B2226ADF8702B940EC21F6F5C25A432E317BEFBDCBD735D60F8DBC1AB833B969E351C113114E5AEC7982BAC7F4B095C77EEBE42FBE837426409DAC0EA79589CB07B0437AEDF524D14BAF59A3DCBC8D4BF2B1C4F45C6F1B9D8C76427D1E242DF18A4AE933A8A3A4CED486BD0EA9277E093D36463C61E0DDE2CF5BA7E103689FBF7184F547BECD5AD25C635FA921FAC0ACC9903B448812BBDF67DCE05C28D29C8A19AF52CF3C0666C324AF9EDA4202C51A8100878F0259D6E78D637545421AF73DF114174D1B0CDAA710C0D4671D9936E9CD93307F8F879CB2CF3C50A1A7BC177FD4D38ADF97ACB09576F2BD91EF1D4A4244F665BD0969D372749A305C28D29C8A19AF52CF3C0666C324AF923815C408A0E8F777CD2583FE2062F438A7682D6690478D1BED2315E3F00E866C1AB833B969E351C113114E5AEC7982BF0730EF2A4111EA27B25DAD22F3FB483820B695209A37DDFC70561A7EF86EDD22EF31B834BF01F50873B6913E72AB018D5093F092F2C2BCD802D2B91EF5E179416255AE46B97E9145579367068007662EEF1ACFB3BFB7B5E448FFB764115BE3141FF35528F85373C71167B13AD82167895F6029FE858CD17B9B43029811056962059891A700283696F8175F1D805F14855A406C54CF4749D4E2BD5C00F782BB870946FBAFE98A4F2DA1EC3F4F1016FD105C28D29C8A19AF52CF3C0666C324AF902FD756803F2B47F0C54594198D273D6E2BEB8A2CCBD33215C84EDC6A48412AD9219952C8FBAE7D669357BC7B422785F41FF35528F85373C71167B13AD821678DE1AEDC5C49478E0D029291F3A98D315825CBEC7A61D64012300CD34FE07C5CA54BF1E91E1D1C6310035B73DA20D0D9F05C28D29C8A19AF52CF3C0666C324AF9C11E23C1D743CBD139E1E1F0459D4E4A0B58B7D69D72AA79325628E1B31F394F05C28D29C8A19AF52CF3C0666C324AF9B9EE558BCD2C3970430837EC85D734B87F95E78BF6A283BA9988B521A47A41BDD9C0EC00939DEE2574F7875B3B098A408873C10ECE16421EB807A62B13FCAE375BB74D628AECA3BD9C19C2A8D50946E805C28D29C8A19AF52CF3C0666C324AF9FD3CE85280A2D758C01B7D8CE889B8C24991041F9FEAAC90A271E9DB4D0BED2958E3028B6D30B2014121B7897A48A428E06B5A60EDFE80A70E8B1222310C7CFC1A639AE85B45F3836BB04BAB585541EBA80FC5AC5FB7FA13C70EB0D1CA40190FB5F8FE78C8D283EE9CF7E26ED287E1B27E90D85B3C77C0FCC49FE301727799C5E626F6BD4395121CEE9D11BD9160BF2201EA2D833D9F9150418E77D3A3B4F860505443C633C4EE6C4CBF3709FB8FCC64F400E3A9B3F97F282FB88C60808D00293C1F733615FDAAA5C180D12C073A8412BBD380BB60DC4773AC037BBEB78103C8121F28FE4C8E17F6C6F4B19A7DC5B9B5A189FAACB6CCDA183D4DE30F4094CEF3587A484338B82B7DD143C594401E4D0DF7FD608DB622A03F3C0EF94269EB2C7E93CB72F3764EA83AFEB2E062C34D2354BA63582E8187F1487390D031EAF2C8EBE6415815F5ECD7C4E72FBE4228B3E12F635136D3BCCA82845D49649510BB90BA43A333792275C86A0372ED608A6E22882FBCA2C83A2F7ABD0DAA21D443EE0BCFD4722C78C61A06445CCA2807FF3130A291AE10029A084CFE86851602EBFD2D1BE17240295A7E69C1DDA7BB48462DC9F3B19066037BA4316A1FF10788AB670D604FA812C6110E0C68D4B6BBA93A6A95D7F098755DBB6CF7E2CB170A3F6B271C5515514FB01789F488B28BEB235BE05CDA8D83D971DB00CF7A845EE904833D261E41FF35528F85373C71167B13AD8216787D57B4BA5CBB54BC268E0E20ABE595AE56F2387DA935EB666F3D77C59FA73CBBEA676F58A843E809AD48AC102EE1DB12A206898ED94D69EF0D868FDF7355E993862D80DC04D43382510D88042A0555CA259A39CAF911836214412D0C49A0E5AAD65666C9BC11F34999A4BA5902BF243C56F2387DA935EB666F3D77C59FA73CBB826FCD5378B557CBC1EBF7C22EB8631896C695C97340A74AB3740075489901A94722E419647BBD294C83945AE9EBB1FF5501AF1AC28CA9F579D326D8BE740DFB686F6F8EC98D1E5B61EC1123698B323DC0EA79589CB07B0437AEDF524D14BAF5545C9BCFF3DB20040F337F1F3906CD73CB78210273B70CC470466232637EE18E5E6AAE9AD4359D929AC271390946C40530B78BC343E2FFEE8A800D6F977C4056D2D37E4D6435E6A379CC5D16789CEC38097DC23581CDFA72EA11715CF62460D2D0DD57862A8897B5BA6D4F865FF0C7CE155CB1B0940975ED691FF19D416975AA26BD2A8FAF6D3BF47B4A005CD37F9C67A5E3D42D6F12CEE01091123478F0FED450BA1A701C48D9DF1D2C7088230F6BE5C8A4C1D328CF4CAF9B1B4208F8D295ECB44E688B0A2A646B2A1B815602DBD3EFFADE9A221E638E8515F390BD45EF8EFC0CB5D4C724E34BE62176CBAD3B55BAE0C40EC1CEFB0AEDF4F020CEFFF037BBAF92FEE5FDA909FB5F3351B903D965D6A45D9816F1139D03D9B9B53B69FAD5A31FDDC0666F1C85261F319EEFCA4D133CAFBFE328F8D948E781D417768D73F4EF360A4E5231E1C0881E56399787A22C2F68E27C6E3512E8D648B6D80ED3DB9166F41459DB9CB42BB93B94E626E3D2265C43E37FA3BB1FB8FEC735906EE68269E6F62D80AC7470E0B37B370AEBB2D49A4873E985EE3FECCCB41DBB671215B494FF18FE4EAA26A773012EF68EE008312BF91670BF13F16911D60DEE8AD0322286C713CFBE728A7FADC8C9381B8538018D173BE01CCA64D3E40645572AFF0F752AAF9F9659382845A813E9D4B9BFDA2D4B16D2E24332F42FCE55C031F02ECFFAC89B6AA504B445AD91C0709DC904A1530B2D7375226D33C653C73A18C7D752BA298969EBCD743654E9CC95C0A937E256F24D36148E9D70FA86C2C01CED746800DB5CA1B54A6F8D5892E1C270732993632595921DE2663DC0A230CCC13B4DE3312F941857E28E162653A9970AE066AF1F596122DBBDBFB30819FE0EC4A2C71E74CF86B3AE9F11A4162F96AEC0BF88D79A2A2E9D049C1F290EE19169E42E461C32F3D48DE379AF02366B794D540CEF1A6E3896492BAEA61CB5032811D9D0DD1FC788D2056093F3E14D5B1CD6CA96C7543F8D80E19E4C467306743F21A7D7D2C18DFC2DDEDE608F9E1DB7C4AC7CD6FE94A3C64C24E17240295A7E69C1DDA7BB48462DC9F3B19066037BA4316A1FF10788AB670D60C3DF3306F19F62A69D88778E3EEB1A55C83404C7B4F1157290630B5A5A6AD34BBE48F26AE5115FC5842736DC21532D402EEA945DE13F572031A713FEE8E3D47CAA36F7B05174FF2D631FFAAED61D4E56A4BB744185C2F984E76570E7DAD0C237F30B15B68A590B5BC9697A257A7F7EA1006E6D266E3754859E3795529DF707AF3617E0A47399AE2DB893FF654E1253277FC916C5FFAB6111CDBFEE42BE653FAB9B78EDBA81BD1F48A9203788E42D1BEB781B84292D84455D1D17611E6D910C1389EED1588D9EAEA076ED82F2151714C92D7C04F94AC6FC18146CA1D3F994CC7AB79DA78175FB9DADE105FFB7AB95A2054286EF4C678568C78461AB5D51B4417F787C8DFB3ABC1E8A3CF0AD326DCC7AB3EE029463C9FBC16EFDD9017813901A39ECAC0C74E271B71E42660BF576A264245B3F4377D43C9098D14931988DD6B6823362A524DA3F8A0D6AA52D7D1558FB31536876B8E507E34E01F7AC9BA1743A8DCB91E2D46D1886067EDACE62F948771B226DA87F5FC43CEA2E8290108C3B5BBF64B83285E79295D88A1F304A380BAC17300FB2FD64E1FE0CE92BE4CB18715E9FC0EEB2EFFDA222433188E99B05CEFD251F92CBDF22E35C7EDF719ED0D6BE20D6C0EA79589CB07B0437AEDF524D14BAF5CE226292D40B24F01CB97C7712832F9729F02DEBA2C6FC6C0A226DD6D6CAFE160682713ADBB2E18FE4F8906C6F8B768A88B58AA4259142AED2333AE73650E773BEC40015B91171B1B4BF9B8C68686C647BDFC2B4AA6596AF36D882FC3464D6DD6FEB6ADD5BF224ACCAF68D2E341BE4F041FF35528F85373C71167B13AD821678839FA87F83B69C43DFCE83C4CAF2363519BE2624D4E5842393EFF18C8037383A84E2231B6D8F27B045B05DCF0EB63ABE8BF5EBFA3D338B22261D067A93A3788DFAA3EA2E7E0AEFDABF5688CCD0CCD0CD91A99CC73FE6AC204988E24A3B04833083D8AAFCAE69DB47846B1CE94908CDC97938DB809BD048C42A7C658F79A1934683252CCC8BCC788E65B6D4193D146DE58C0B34DFF1C76B4FE156845B0B813FE2D236FA51F75C464985542B9ACBE0028DF379158CA32475A832BD2CCDF52C3062B373A8F1886BC03AB1A094DD4B6F27E7963D71503922CE66A690501DEE9BD3367CC60FD3E30978829A3984400CE1AE34EEF8CD9EC26A0156E18A178835C6832154195F1472B7F22741067B06F04977491B80CB2C5860F0E378BF71A9AE4B4A0E740FD06E960762AAE6D40029E471A4079837A27733314C53A43FF2D09C81CEAFE05B9C8C503F854AC91A08D2E69F30945FE450867D1838A593A118163576A28303DF9B1B530BE2CB3B00F56C38F4B62E3C3289C6AB4C1BDFE060318CC46CFDB5CACAFF5619000C9FDD8922D513366BEBBA2A4CD70DE94582FE2FCF63EDB67AFB3289F1ADE014A195CDEB1E8567B2B68FA7CF291DE2B654DA293C98F6153E68037FC5071BF0638FAF5E4F840ABB09884238973DB688602C293D9F34D93996FBD26757C352E58DB8BDD9611AF68206B58C6F051C8705C63A7D84DB01D67CBCAF2475464A6A2627F7CC9FD0DA332C5B3E94E22497DC844432857CC5897F887585AD4F31FE8589766FF04B0727331E6083B806358790AE1F73B7047E5A7841A20693B384885CC142AD514C525256BE613C81922C38649DB4D8549E36A1480A56098BB86BAF4D2DBAF5B822624B0F8971D96C9605176997D8E5BBF1A50B35002A949FEAD7DDA87F829357FAA35839D66F45BBF9D8E8037F7EC7CEFC5A18D3ACFCFA94473A78DE0BF2001A7DA4183F150AD0D83C818FF5209EA012ABB739F60DB45A475962260BDD0B104C571FF651B3C1F11A8A08063F24539FCD1579DAAC2A6DF8BA36B6DC901FF6AAA05763A10334ADF0385E56C949B1B206ABFBF60262FAB9102B05C28D29C8A19AF52CF3C0666C324AF9B1F2A113F06D7604992431034279DAF7753FA6773CFE21D73B9E92A71EF550CC87AB3605FE7814973B70D8CE3CAC6F6EE863FB281EDC96577EBF427359A2D830E6ADFD29D0D88B3A35000DCEC23F09E37F6A7AFBDA97DD8330204EBAAF12D7171A9F69E8FD6C892880BF6B4DD8ECAE16B5A5D8A7A199200C96C845E979332EB08CC1FA5E47BB51E37B997167ADB099CFA2F5D29DB3E9EB3B5108F077A9FB201746569D8739F572E393B2540CE575EDC8B4C6F63370EDF14C2358EA5C4B093D0E0772CECA542DF9C788BA436D257E27531715DD6A51AF22FAF0106700C6AA25B7C7F70B246C1A0B1F1DD0E52FAA3F6CB399BA5EB3C2E2ECB93839359980000E4774E93436950CEB9A63D50D9456D2C7ABB517904BAB1AC82DFC3DF59A368F326C206EA092B7B0082446391CBD569B27B2045D8CB3A893F585DD6C388D8F42FA956D0D58184FA0C49524FB3FB5DE394EE84D8C8E43111C101B5AD062C1B32E4C64A7CF291DE2B654DA293C98F6153E68036872A5B756D2C33E7C7648B0968A4B75C0EA79589CB07B0437AEDF524D14BAF52E4E8E86F5EEA7E42585F3B0454043EE82DB3B526DA6C9086CA46442AA138D33B5134B996B001789553E8884B19AA1B002E84A5AA060C9F3067FDA1C4F7BDCC0393B19646FC6FDF78F92E9C9A297E196BD85CABE3173FCA65B31925C0C243FA2C1AB833B969E351C113114E5AEC7982B89F742F9DDFFF15846D8E44E4648BED04F14C6DC450E8D9B88147E78BFD30F800C9340A2D88138AAB98F9A40EFC416D19B4AB5C66E214C23A74331E3FB2D2F2856ABD0D8FFDFAEC9992B63BACFA0748BC804FA53CB9BE97C9D3B9359A159A1EED0E4DBEAB5409E58E29F1DD3F317CBE28A7615C7DA896DC451E665826D72BAA27532D536869DF2F45B6A1B93A7CDBDA65E2F214AF4C1A8C9B25AC39F4E2F4853E892E7A4F0D809064795567CB2E5DBCCB3FB0DCC82525FB3776CDB6132A2EC199AA70D2C558AAE430C3EA1DFF8F09BEE865E5ABF07563C85686B2A0C1A8339620E729CDCDE0A844B547C703BE49E58A0154E00664F661D9C3612B81C429F813E76A8A0486D27CC7EAC0E6946979E9402B2B073F873FA0C7BA888BA197636D87C4C7D4741FD68FAD1CD265EE3E89F32C0DB015701A09068F04BD0505C58EE239217A33414E568A610D2D4AC946BD4AEB5453F958F832284D359AAFF3D6F39C788E78D4A9664A0F4D79F5AFEC0610ADA27E26E348FF3A480F16C1BFB74B3C41F825075C8FD9A15796E0033893A586582CE11A6392FF8581A3E5C6D7EF6E9DB08833A043AC0179AECC2505242C84DAAA4AB5C7B4CFAC3E17EC0FBC234E07824AA7333CE7988DE6CB3EDD47CF06FBD9D4264A57D5E4F0CEE8E0747EDB5D18463E1FDBDA91C1D938FBD135B898D1AA3B7E49A2D6BEA6D0DEA07BD614AB017B023798C560F6335316D580E3F5CD95610A0C0E40256D779B3761E4C2DB2422CEAD6B6D7F068BC077CF2E7EF42C6DE30BBE493903BEAE8D8020FCDA5ECAFFB23D956FEEDA8F90F22A17D68706FFE5FFA975865A596D21792FF7C5622CCC88C2EA0BE56B3DF069731B22C9116DA6A9D75AA8CA50EA84F41E8DBC5757D262C8B3FD0F741CEEFB3CD3FF9E0F1B2834C91ED3D15A5B9DAAE04502130C16A67018A6248B37DE47ED0282E1C7133C4D2E626F27A2827A93CBCE1165DD40EF6A5A26E373599D8DFE2C3F775CA41A279D23CBDE07D697DD52462D2C1878FFF629CFBE70051D6846C78ADF01716BB691DEB014361FA9DC313301BE423F410A9BB92455EA8B20485147D2A3C365F51B1186419FD93D6F533A2474B12B829320C5F150A0A9078D9FACEA7CF291DE2B654DA293C98F6153E680375ACCAAE2BD4315F4C6FCE216D4BD6FA8AE38F0A188A6AB7A85CA96A8C17DBB2D97006C47FF07C9EE493BCDFE9D6819F8269696B470AE1CF022E5FFF6241FB55C0EA79589CB07B0437AEDF524D14BAF5F22E864B47782819500AC78EC26597135BB583190F189109338A27D0055791A0DEA5D8261F9A9C26C7DF1F7C02D3B8DC03D4FA67A81C880EBCD8318B37C52198D048BD45A8752310D42229E002EAF0F5C0EA12CC647B10403890D06BB4B47699E3964AD473FE9A0D5C20EE43F6C5C96C10A160DD7C97BB3701FBE3DA9EEFFCF0A510A7FF543A110CADECF6AECF5226134F2807AB2607799395CB4CDB670A3F1F487AF2CE4CA3F0B8EA4D3CFCD65729436C1F24A4344C7D0C1713C0B5529322880AEC64FFB4E3A8B1BD1EA28448571D7F603D0AC30A62910F8005BCD5BC29389DFF962925A666C3DD3D5A05206C251693A6B34BAFAFAAFE8A0BF96FEF695480E9AA332F6ED7F5D0EFB6016F0B593F0EDA2B066FC8AE05E37AB549335A999A26A6976588E3531355AF21A831E9DC31D12805C28D29C8A19AF52CF3C0666C324AF9F6A1CF5650D72BC9A883E7ECD3CB2DF771FD39AF6231B388F0F85966FB60679641FF35528F85373C71167B13AD8216781C3A081649A1E1D792766F30AABEC2BB335EE6A610585CF074CCD43F7808EB0304BD91D2E08EEF33345845CC663BDEDE97159CCC8FC5BDFDE3CA339EA2BEE2850457595E9BA24D7ED422735137F568BCE1B7E6F16D0F94AFB195AD43F1951920001609DBCAB43F4DB6CBB10CDE443018EE611A2BC475BF8AF96684AAB99D24E03E8209EA0E064526FBE616C8C527390E48C81DC2354C7DDE3CD9673EBE57089706E70D3B1EA3F642D83A4FB1A5DC356801B86DC75C24EE218EB2D3ABDF645DDAA1424777EE01052B997ED1D38F2A55FC269E565E8F2FDB674E3F508795584BC599BA5EB3C2E2ECB93839359980000E4774E93436950CEB9A63D50D9456D2C7ABB517904BAB1AC82DFC3DF59A368F326C206EA092B7B0082446391CBD569B27B2556899FC3E8B23080B9F1B1315B1B95A6E27F970072EE89FF3CA4D257B1AAFED3B610410B441BF7DD4F95FCB5B364673281F5AA47781636D39A82A4D8141F90C206E149C0DA5A64C48C3D3248F289C798B7EAB6FDE3015E7E28CA8C44DC89C2F23380057E3403D1300D4E7E8E38545449D10BF0C51E3349AE060081BE4F8D0FDBD0F1924678D037AC5F8AC2EC2576ECEC0EA79589CB07B0437AEDF524D14BAF580AB402C591CA94B316A5D209A6508D2097DC23581CDFA72EA11715CF62460D2D44017D8F04DB3BF574B1B84D4CD5606F12F8D3D1B510C28F8788951B52BC8252E072C363FDD7501072B469952067E65B6795052B572F5600E60CD4CEBAEF82A3914A2E06BE2687EE90D6EF094CE7145BAF8DD868805957C029495953816A29BF7B90DA9FC027927A6505257C3E56293CB587BC4BD594EA26668C06FEB66267633CE7988DE6CB3EDD47CF06FBD9D426479495D471A32AD1AFADC22D5BACD44477591D872D394BE10DC917FF63FF3CC3F5B41EA00EDFF64C150C7531F5634F1360CBCD6BC1611E2CE07C987FED0DE1D454E871BD31E6AF9849779CBB330D871CE21448D2A59F2ACE7EA9548CA988B301D3343B2107D777E332B8ED8BD043D8808010686D809368529AD43586963474551A8999261698C57513E637B5E6EFDABCE21FC7257C6079DF692170E0065674323F710E763EE3A89423B6E0204514440C1434C196A670C732FF338328FF27E712305C28D29C8A19AF52CF3C0666C324AF9EB2E3F88B7413C33F736278C74D89B3C4CFCD8FCEE0DD7AAAE06C3F0AFB7775BE190FA4FBCBB19FD1484602AFEBA5972C38E8B132588FCD6BFB8CB7B8E67F8CD6E9747CB7AD4FF463C6CAB9C3D9AD1A467E88BDFBAE0B4F29F6BBC1861986E0C69181B1086D21429466DE1593B293FAB41EA8E892D050A3878B679562A2FF4C4B48F7AB792C504BA921893AA271F7780657D9141C04C46DB34A34BFA4C47F1F53EBAF8746BC1FCCFFB43F0616ADD0383DC3A6E8B770DFD7CBBA538BCD210914CEB2E799163E163C6A7D79FBAD027291C14C0D5C2ED3D5699C1EA5344DD3A56E64508927480B25B4D36D1ADE3E08B0E5E41FF35528F85373C71167B13AD8216782DE60F6E81CD5E10D3D279E87B8EF46A36B6DC901FF6AAA05763A10334ADF038DDB6C968FC3CEC9BC9CBB9A6F3B2F097906E179EB4FDFD328EDA28B47FCF14E4CA3C8BF264AD455A817857CC7136A0E4518270B935D52B6AB3EE5CE7593CFA4AE1F9E274B9C918F2A9FBD323E83F7A8B0C9340A2D88138AAB98F9A40EFC416D1F37977067C59B4ADD60C2D4343A6DF95DDA0E83B40CED0D291D073D53DDE5B5BB48BFCF8D3A53ABB3FBB27B19D711A547DB695A2B3576C9129C9A29219E8535B5DDF3A71F1B30AB7C933219AC3E9F9263B083A12CD055F55F9B54FDBFF3272AC1B36CC7B3C67DA8A559E42E59D9F92A443AA39F890F6704590130442378F82919DDE5DE7C7AAB44DE9B4E3F5A57B31CBC19C95BC788EA483D88A87B7A0365A49AFEAF09B249945E02557FED97996FBBDAC3FBB12E58FDC2B770756BD0D34466904F612918E104478A44FA5830D4D8D4E8014F570B78B86506571095A78ECE65E60DEADAA9954354B17693D82E87CD6D232A71165A471703EB3DD25285BCC83FE651684DD5ECCDBEA7B530ED7763A68D9008BA197AF7F2061F88E4B30AE4313D735102ABA1C46D78202A0FDB667E0030E75EB6491F5EB653ED8758F003A915C896CA393E0E89C2BD1C4A16615C13F399B90B0EF15B26D742D5A8D9D3C2E5C77A1C5473858B50B0AF63F51DDB1A0C332DFACB228275DEFD66C1DACACCE720ABB62BA538C7FF8C32892939ABF9C659EAEEDE9F6A7053444F4966AE58910458DAF509D2527E3CCF57906FC8AF79561DCCE0721FC7257C6079DF692170E0065674323151C0D90564EBB08D471152227280A72BEE1237DCB03FB5A1DE7C1005789F56593D23AF9467EB995630A2590DF26B7F6B4620A03DFB1328E73FFACFC1DC96592C0EA79589CB07B0437AEDF524D14BAF5F3CFBD702B465312D1385A50893132EC5F609D7A6E3406D1460606A6FAA8E62A38E46BAB9F0645F70447212E94AD982B8512C0682EE2EB6C8AF927CBF3E4FAFB5C582D1D0AC354928FAFF8CFC4A8557474DC19A842D1CAC1A70E5211488813C44A69F14277C3780FD8CBA3636B8DB30B89256CC5F519746BC7D78F7A3712D413D5C9B1C49C1BF7D657A4727AA51C3FA2DCBA2A665EE752EA78C9780925A06A0DD5C9B1C49C1BF7D657A4727AA51C3FA2D6A8225E2D5F3BB74DBBD7EBCAC7D2017E90D85B3C77C0FCC49FE301727799C503D4FA67A81C880EBCD8318B37C521989E084A2A7AA1ED67B4D5B002028E786807A54FD7F2034514A0C3E601335FC3BE39F1F093355C1EBF82539386158134659753E1704E464B4C4DCC79646664FE8E7E0F7FB6D72FFB97550E301A5B2B83928151EA935C11E8CA3A19BDD6BDD8F5C547548D7EDFDDDD7A0985C0083B46010005C28D29C8A19AF52CF3C0666C324AF9AEBE964003215D9B42BBA0235C008E83AAB3017060DA4D3869E70B6F13A4921C52E734C5E0532CC5CC9E4F37F2439E94C13A9942AB30E124CB2406182332A7944BF181CE62614AD5C88B64693131FA91AC587D400978887B1B94DF79F580DAEE39BCB505DF889E8FCDD5156A0BD29FC605C28D29C8A19AF52CF3C0666C324AF9801A6A9362FBF4B57F8929CB0965044A5E812B96D9B5B313ED44171F241A61C04E909771F2A1F84176DDEE101718233A95C0B73D71A39452816FCCD452BC6C4CE601A2CB72C1B7CCF748EC4D882E64A817D729A8A9C8E792CA61A83DABE3CA321E38D1611DB9E0035EDB1C7E7BA812E9E326388BEB807A41ED24A1E06EE33A593B643FD5B2B881BFC21AED48BEB3A8BA15053C7EB28CC4011D4CF66CD8A81B5039EA667BCDF93E8079741F5BF4EDFCCDFF6AB481CB5CBA2601A9A1F172D022D1AF8CD20DC7090EA4852F04E4C282828731E93D4068B8D85621C75C35C27081EA1EE50ED98BB7EFD8E9B7E2072F7E1E4581C33530F536AE0482D898FB005200ED602AE6E6460103BB2FD2999A5738D209FAF98C72961D5F873F8A4634BB1F3BAD24104435C96AACE15780099B6642F3D511CA9064D021FF37C4F90EF5CF75762DEA546B7762117FBAB99E2A92ABE34711FFFC9EE1183059EBEA0A45FCD91681CEE77006425DB8F2F2F499C6840C8D431E4F417E03F6602689BCC51B685C4C8E5BD3BB00D2379EED5B016E970203105654B5A5D8A7A199200C96C845E979332EB05894B4AB36C89E3BF0FED36F97A7A55B44C3E11E54CF6BB7F95C4020076E991B9478175E22A6EE6B67AF3E8291E303DE2C5C93FBC9FA0566623557A2B41FAFBA18AF9A5FB463C9E9C9A70BD7FB0BD16E5B103A734B7BF28BEB0A14A589BD3192C175E5B509EDC382FEF3A6A99302D0DAE3FA1DD5B49E837DCB1826EC9921A19B16CC76876C08296B4EB21E2DDBF49BC0B2A20A027C3C69994ABD2361200FFA8CCFB6A8C6643EEFB73FA2A24F4319FE9205EB689B285F77A25B7946572A4E9C9F09CB871AC2FB4527E3B7B8158C5AA8B997D6CD152BF45A3315C3C2A524865BBCE0542A5CE1DD6A71B1D08F448E5BF4CB920B64CA874FDF1FED718ABF65FFB008436053D1E9B0AD97001EBAE0B87E86AEF14DD5F9AC13F20B0CB2C4F50F7EC22116DE98C45BCE676AF3D6D026B03B39681CB92E94ABD921F99E71BD2BEC222154052A3BD454B5C92C39285F915649A3EA817735AB9ECDA654C49DF5EEA9084B30BE9F3D2D2513D0209C0498BC89D4AD116312D4B446DB811E0239C2ABC783B8FA00FB26A635FF754B76B1C2679862851743E2E5BF86CD44061D5958E05467AD6E4DA3BABC8754A5FAFD26BB892F3CAD2D5540F908C8DBE46737EAFAFE49D195F60CEFFCABEBF4380285B21CC44241D38CA1F3C5BEF4CF3FA66DF25C5EF5CC67737E14366134D0DD9631DF2C5B245E1017EDD0DC8646A873ED364E2404A40EE35DBD1A1FB03093DF6C1D47442F0C5F47518869EEEBD1555A52660E18D06BC7A66EE62A83735EB75A503E0A612504FCCA9A5DEFFFE51F0764EFDB4636D7FCD9B7A3F7408C8F5C974DED30DC7B447D1EC863A218F1FCD54F6DE7339705260205F979650AC27241C4D98DBE075497A204AF9EA62CDC5562E768BDCBA0665F77B1FC3E5F7D9680A5979110B65453E0E5D773B0E6E5200A6636F2E59778403DCBAB7BC43A0F3CDBFE6A21ED6EB5DC617C55FD4819A2EB7C5D7E99D4613F918535DE881E38C7DCA21FD3E84AD5734EC91D72FCB9E9001641089E6274FAAE7806E68E3A86A1C8A5F29540ECA3521A9D3D11AE42B3EF3A67E951A85893F578B8203F555DC683BAC144574E5A9B0B09F7ED0282E1C7133C4D2E626F27A2827A9327BB42185E5330D4D6A22FA30B17313B2B636BD505BA703F3A43F1225D49ED941FF35528F85373C71167B13AD821678AFA0B5A629A0C6F7EE91A057375360ABC0EA79589CB07B0437AEDF524D14BAF53CC11A9D3763AB0ACD845EE72CA3E7418E252CE748F4D11DB71F3283707522C113D2B9B030BCE6E5060EEC5BECE25EFB1E217DE1D2FBA5C925A36C11A45A39264D828C04AB8D65F478CD4DDD9A1B99905456DF0FB6842B1E33FDACF1162402076D62551DE6477CFDEFA748F54F49B6C58A0A8B7A925B9F6D2EDCA5F70CCA9446694F4F2A70CE82075B89078AE10E3083622D9EC658712AB9F8F98EE0919783C121FC7257C6079DF692170E0065674323130AFEA5E8DC8C4B3119B319A83E817B85F756DBBF3E3FDB925C2628F2294D38FBA107752E8609C83B3BB3A67E77A769B56F17EA3D468E0BC78A7D47B82C676C05C28D29C8A19AF52CF3C0666C324AF99C5BCB71071114010CE5500B358BF4CFC0EA79589CB07B0437AEDF524D14BAF5D602520B5C3CEFEBED377C19F7C2AF3B13CDB7A1959747F253545FC81C19CC327FBADC0C930ACE112A5173B116D438CE66248B7DDB757E7A1792787F77357CE3046EB305F533ECD50C813AE29E2608A748C81DC2354C7DDE3CD9673EBE570897493CF9E38A4E3DE62AB18B1A692FC19A24067C1CDE5F420437CEE20C18CF40E18CEB5957C2F46F11AE115A92C708BCAD15DBCB43D375A62B25532434C9CB797207455BA4517EB8A4254F2B2A0C54F8CEC8BFEE2036E9B1CBA72037B8AB699BB72BD4D322123D437B2A4C5341A9ED7B89EC91D72FCB9E9001641089E6274FAAE7206E61EA76C697795B53E14D1830F0EADD8F4A924BDEBE254DD7288F66C43BE7BC76F1471096AD3AA6980CBD2B0154E146C990CA0B52C562AD7CA21AD936BD5A32348CFB29058CBFEA91C9BAAA09B54334A9D2206858A323EE752BA209BF075E1BE9E92DD43DA126704227AD6C8ACEC9C07D0167CC59994D73253E78BC50327EE2383A0D49B7CE1810102876625151FCFF0C4B8263FC912299D0593B924639F4ADF3F280A832B20B79BE51F51781010C833E21F464BF3C84CD5B8AA17B0DB6846F92E92013193A7192776D562A15740FF046B622351C2FD5FB1ED9C3A84AB4F49C9DF4A3E47947E36464863DBCE729586EBBA0DAE7282DE6459936D55336BD0110E5EBAB11167188A455736035747A94792F41582B1A13DBF325CE1451C30DF141FF35528F85373C71167B13AD821678773B0B220363D11B26EC7FF0CA206252BDCF073ADF9C1F7C69BF5E2CF9D088E147AC62094699DDB977F08722CC1D1A471064B1FDF47E1B6B15F1289ABA62F62057D87A0998745AFF3D9AD7C6A05605D3DE39E5FAE2BB95A3C84943A78945B855168E68CFC94B64A7EB1B698B3CFFF5DEA189FAACB6CCDA183D4DE30F4094CEF3582126877B4DE65BFAC092CC12B2BAEDB2C31D10703E570AFBE62578528A27859C7DE619BDF17733F7A80FD26D9861621EB27FA30B79A7F66B146F3090B4F42E8E65C181182E4C57E7DE6A6979158016C933A7A8BC571FDB93463510B0295DC9D633FCDCC6639DD8C1B60837CFFCB39916D0D7F23CB8FC26E2DC26FDC34572154C932B56BE7C884321BE1D37852064E1DC863053C0D1247C3AB4633D7D51A1DE50CB505AC71D99C1D57B2437FCE495BA0A078CE9F9CAFE2E946A2BAB340A1C5B1591C199D28D761CD11803413555796599BAC31C61E20732898D50C2A1F4A818B79DA78175FB9DADE105FFB7AB95A2059DDC4ABAD841EC39B5C80AD70F43515BD9756F809DB3CFEF7E0F85F0D50D2B54DC9A1149C68DB091A5F5B6B8F4F8CDDBC2EE58A460D826E8032AB03360C03C21F10D3BAACC2F83C4DA443D6D6FEDCF2D95391FCF84C681E5342E974FA0824112B122182B86062A7239E2CA146FD1D0887EC0236A96EAFA216962E780AA905967E1F72A485661B990DA62CD535E64E6B17176DEF76C248B1DDF33A991D7927E62348CDE86AD987CA06D9D87110CDCCB3108D374DCD493C4D25B060E21B6353B561A6C970543D9DDBF94C3225E91C11AE157E502C3DF2D4E693B54D0F99C8F11EEEDE8B6DC427EEF408C0B35C8D6A48B1366F1D54BC29AFDA8296AD7B828C3813496CB0A7C094F05FC1EC5B0D4789D4917CBB0518571533B17C89D4D501B7DB15ADEC2099E66CCC7290B7676DBC08435F622C1518289042663C95E83099DFB0AEBD2A094759CFA6B924E9645F407AF56D95F0168522F92379D4A235D9274B128D5D20BEA2E33AA199A26BE33692438F35AE3E4887596A7B21A4F46286E5F94F4A829450014225B9D5B18536A8042D3EB9FDEC68EC2177001A10DCDC94D1D047F4138E9ECE19F179BC105FE39A4DBE86B4564878A04083C82C5B91181540A0AE1DCFB1B92289D8AC15FBB504360E6136FD7924242964139BB14D59D25A0B999930E0D30990D98376ECF101E3510E2DD605A64A7301804DDB1454D479D208A4D5929F311FDA8D7E8F0A38FCE7AC22AA98524138EEBFD64049F667EC1404388EFD6F760862C71EB223A18634BBB0D3147254041C640F1037FC5EE323B8F9C6BA18C16D7A1267C6D9DF871F377DF4A8783D3FB3519A06E0467A9FEBF999122E7693B82015B9A915EAC60C4A4F5D8112EF27C3AC9DF4AF759AD04E0ACFC3F9D793634F295391FCF84C681E5342E974FA0824112F709FAE6142DA20DD0AF802C36A45FF0CFEE0D0C299526927E2569D98EA2FC6D5F0168522F92379D4A235D9274B128D5D20BEA2E33AA199A26BE33692438F35AE3E4887596A7B21A4F46286E5F94F4A8E01FB5F41E32ACFA14B1AF7263FC1E172BE960574941FE14256E0748B0C6EB49D21AD9286E1A610C27677305D31B34252016F50620496A77111828928368584361FF012011CC89EEEF5A47D58144A524665A9DFECB48F654E8B8CB66C0F9323B8B396BC7B0C10ED0FCFEC94883A6F0072F2E6ACD78A2BDAAAD627DFCE7CC78AF457DC2CD01884CEA34DAF8F95D67665AF1D899A3E0B2616ADDCEEF8D29307CD057A6DF8E43779824CBAB01AD16FD96F5B1EF471D2FB5C53388F5951A07E0C18A3251394491DEE06996C196DF89A50B459B9C7587381A2CBFA766156A174736DBF2BA82DBDF197C6AF6BEC5D28ECE2EBDA283E5429EBD694D5CB787BD47D42F6AD3145F0D50F7617D77AFB8035AE4953E35CC60AFA48D9F06088912135461AE8CD43012DD69F11637827590E2125B808AE5E458F1475C71406F28D5428A648824B9547B082C0BCFDF2D157289B32078D39CCBACEB8F28D3430FC5C9ACA1926AD60737595D7AA2F30B59C790F2C3114BCF7E2E6DEAECA3C502FBF573DFC856F3DE8423F21CAC9285172F85A6F44FD75529A8DF30E57BF156B9C4B7AC614438A312886E8AF5A63B297F390D5B217903B24DCF15FCA0E9BEE5766791688315522AB6F11C7B947B8F6F8670C05D2635C31C9635C7891C322ED398C0FFE0D2344E06280AF0A0FE97DC1A63021722B5D4D5A6365F0D0733E3CF94E5784561C9708FA804F588322031662A1D993FBD2B7CB0F2CF7FF6BA1C4D0C86EF58D66912C617139AA44ADA01E266EDB2C8B9D0A25643A5F526413FAB629DF09AAFC836A2FD56227428F2E7085188F8962CF391E4CD265523D6E89D4F131DC43A5212CBF58B0A80BF65D84FE5D9759862C753E63356EAEDB2676A8470F61040C40B5E6BBCA6F01FBE0851A892C409ADFAD3097B6F7F631F84B1A006400F30AE24D888468A9867BCD3E4C07D51BEDB6A52F5B86F054F60DF061BC63C43BA6176E4A5B02E6E669D46189404B709C2D37D4D92213149E02A3B285B29E41D5CE24ACA35CBA3E93831A6E75104374369AFF531C4B5B7405E711EFFFCEADCE1F1CCA5C7AD3E4CC89D2581CE12907EB337AD8CE4E54CB222C89C20F539F1161485793B5807B441045BA843C007FBD664A153E764C7DC242448719B2BB202D98A180302D1B083DC08E12C94C7AF80A13D0ACC2F98260408651DF7239F286D5C5ACDA1A5EBF78F016A5A7221F226561971E33A5F20556D04E41354024427146B00281B54223491855304A4AC1173999CB9E8818BCB8EE4E2D9DEAA51E495EF44AAC1637A6B45D199B5C9A45EAB8B83C4B374BD432BEAA545ECD974971F12304DBBC1C8823164996F030AF245BAD586C958657ECEA362BBA51C384D99E3B43F24AD84697F965D0E5EFB22AAF36241C7773724DAC4CDB77F4E6C87D2386D6DFFC552DA47A0FB32F5342BC4A0294AA9066E291451F6D7A6CC428BDABE898428A29223AD003EEB642978AAB9D8DE9808970B442121831D04356C3161E8251B13BD6F6D0ED50E2440C7A59B3FAE513B6FA16D7345E17FAAF1A381D0ED1A7754BDED1F58EDE6989E7F9DA13B8CA136C50BD36638B3C3BBAA81F883885F5449E3D595B9CB309D5D607C37A91D26B09884104443917ABE1C8255DD34A77FAA3B746C038EAB5FD3C5499836A35D7D1550666E02CC4D9317680854094E107C71D961E2586F64C1599B2026C0C67C9DDF38B6CF5D4E6FA8A381F652743316BAE79062E2380DC916B7F0496A4F67A9655E7A7290D3296E619B14B370291B7A2AB2B3295260205F979650AC27241C4D98DBE075497A204AF9EA62CDC5562E768BDCBA06FA1803CC024FF9295EB3A7DA79B4F0ED83466F8CECCBB40621708BA729FE75376A7B53C2B1E001E3BE5047DC553B7405DA146FB10ACFC73697C9C4C7018A19583B1557093DD466B8AAF1F8B5058165779EAEA59BE175C862B83B87264990C039A3B77D8B19E3A6CC22C70087E90C1ADB3B51B1664870371DC8207A3E155B1836E626F6BD4395121CEE9D11BD9160BF221F80BA0C6F0DA424022893A1B583C7088C0B261CA36E37B40B84540B4DFC25E55D8C2E8C6E03B5D487CF09662503394EB700FAA44529B9C30E9419A2E26BD6B2E676A36A9A8EA37E580F95CCD4A38B4E651CE9690794F463913732718B569A8501B35FC0E90CDCCE1724507AD13D5ED7F07CC41D1BEFD1598FDDCC83F37980544E360A3FF34F0B08CA1184B3FF5219A04C43E9B68782F9594758F49A6F3D342B0D812C1B13A620343F15D9ACE0022E33BA499744DA27F277F914D56DE45698A741FF35528F85373C71167B13AD82167834B08BF720F2414C19F67E427EBD2D4D14CC2ACB24F7F5F8FE32B35982FC5E43030188027E824EA2910708DECAA47ECFAED42413FEA5137E8257CC0D4E949B116C89784C4BC37F5B02614941CED6639676541AF4DDF436980BB7693C813E6A9C44D6FBDC48E2B9A40AF1EE67463180D4705709E78F7FE799EF00ADAD414F83FA3BCF391E82F3C26B90520C4DA669ABF7DFBFEA315006F450397CFAE72EB9BD2B41FF35528F85373C71167B13AD821678F3E62A2C6306C92EFF436518FEB2B887130AFEA5E8DC8C4B3119B319A83E817BC1AB833B969E351C113114E5AEC7982B45FA2222959403E2B2FD6BDD4A56D2A4E9568BC1830E91577F2683A424773DA230505E197890167509F51FEAC10D63A3DE33CE61E2834CCDE976EA39B9C9CEF30751FDD33271D05743FEFC045A975D0205C28D29C8A19AF52CF3C0666C324AF9606C3600C5BA9C05735C636212AE00EE05C28D29C8A19AF52CF3C0666C324AF976C444D691CC6363DF8F5E1ACC4DEB90C3C84BB5C3DA8054A0CDC1CAA68614ACDE33CE61E2834CCDE976EA39B9C9CEF3F6D0C7D315C24F7007700992215EE31C0E5FD4E6C8B7DC718A3F26D468931CB9EDA4202C51A8100878F0259D6E78D6370C9340A2D88138AAB98F9A40EFC416D165A9099F3D8C2E642328B84175E18524DB2F5136BDFC63A5C442B4EB71D67074DA31D80A6AEB5A00155C73FAB3B374888A08063F24539FCD1579DAAC2A6DF8BA36B6DC901FF6AAA05763A10334ADF038EFD40ECBE72A1BFB9CE8AA8774DD4B1F21E2F1B6CB1966010B97E2265B473A29B38D3406B97830B6FCD9410DC3FBF7BE63937B0129B949454EE9EBEC761CF05ECE5C9A603709C2BBC9561D22AAB0827C5C800BF685ACAB3A15FF9EF4627B5A045FFE3C7A552B8FE7458E4D4A55F66F531686B69A0E65F120FFBAF1D63A65619DCBEAC05C344A9770422194F87CB362906887515FB87A9CA56CD78E1D860C8E5C49DC50567382E2AFDF9D9F2698FEAD057725DF33429580C2960646228417535575DE843733BFDBB31CD65842DD19DC82F1835E2BA29F02049756FB11606EE7BA393D385150A4E2A381CA50EF60E503DE0E7D3CD8AC1118872321C99BCBEDC9C1317C96F91A8437983D36E782336B61FC36AC68825B1D1DE26C17E4B98F8549E206AA207DFCF13B366F018F1975ACED82EFC606F9904B45FBE22C4483A243C2F3CC533ED020D9F2D5D8DE507E9AC031BEF72A03C4D5FADB162B4BC0D0348CB30CA16AAC4A7E287D7C407373AC0FD88B66C6E0FDE0B9BE374900FDCB68DC218F755659A7038813EED7479A1FE0337BF067EDDED5D58FC3F762650B152499C57499447F4B494815F722116584213F98D4846D7E934B259916E66453C3CE956B7D9DCA92879312BEEEB5BDA5A45E9F7444250EBD8982591D5CD660E22D5F46972CC19DF2799F369EF0535CA58CA18F79BAF5C60EDC77819352C12053B113AE113D95750E232C756DD91DB44FDD225C2F1DEAE14DB82FA214F3094E2B91B1AE2EF8B5F71DE619AF867CA6223135F2B596C406D22C4C626DC15300BE20052DE1E49E61204862D0814D2A7F145C778039BF3CCD6F6C5F739F073A0313C72284411D65A28A08063F24539FCD1579DAAC2A6DF8BA36B6DC901FF6AAA05763A10334ADF0385D020126A63F933CF3B92F5C797A127821FC7257C6079DF692170E0065674323A086F526DD55CBE899B1DEC4E53DC71B11A08C2726D1B3575B2278203F56348316F4CCC3EC7CF487AE61DB84F935E878B05313C3F0613C5BEE7E2E566567353A5F25648F5E15CE28B3A1B9C6E3E2996295391FCF84C681E5342E974FA0824112C4D500B99DC2C206A62CB88EB33395B774EAC744B0C62097D8D50F43FCFCFC41DE33CE61E2834CCDE976EA39B9C9CEF3EC58EB2C60384394FBD3FD23B138E8A0BA526F6F4C1964F83C03951BC8FEBB788660015823BB51255EC74DC1974A29126E9747CB7AD4FF463C6CAB9C3D9AD1A4AC74610134768DCE49DD211F29129D57B0104B4F52ECA43EC8A4E54F09894973CF7A0F958EB82E785FD669E0D0723F1A954DF1605DF2EA6EB30A9E3C7E5853AF169AA32069728E4095D817E6BA020DA2321047D5516EECB6DE17E17F47819B3D5E1B9D5B07889AD00A8CAD9F86CD5753A6D49D45FBAE46B936E892A12B162A1278D0E675578B8FCCD3762E1CC97649EE81E07CCB0E02F5743CD9454618F6244628BEB08C56DC781560F27E90641217BFF41975CDD8F88180FB6749B364D47B7BD9064B4F85F2773BBA45DC865A9D32CFA2A470527A6497EADBD968068D02FE57336FB7E8CB29DB344343255DD767E4BB846CEFBED4266EFBCED105319701EF66D1AB82ADBA18220DEF216EB52D909D7C8213BFCFE93D437F89B5CF0D4E621AD15F25648F5E15CE28B3A1B9C6E3E2996240ED516BA9AEEF92ADB29F3A7A69B70D05FD3D7D38193717FA603738A8BCA29E874BADE9CE6DB4E54DC7CBDBA5E1B390DE1AEDC5C49478E0D029291F3A98D3154F25CBF61558AF773958E31D38F11B321B7301CF4A26B6C5D30EFDE6C5FE5D89C0EA79589CB07B0437AEDF524D14BAF556005612042EFEE55F230EE97B65725F2271413966089C1BBA0304E7D6A475C3DF9A626AB43D0CEABE5945B40BAFBEC9AB26A8367BCA6FE12782EAB8DF36D3859061B9C14F3690A5186B07C0AE5D73C9080B431BEEA1D327767A180F2AA72F2A6104B2311E7A841115EB885BD0723319CA8A9A487744D6945EF812978B3A7E73A325B22744F920EA44DB0256F6DEB659A08A4CF56135622370C8A1E9381601956A4C2383D8242B3437E4765C62594E2FBF52571A07CEC204879590B7BAB7FDE405C28D29C8A19AF52CF3C0666C324AF9FD3CE85280A2D758C01B7D8CE889B8C2C5082D3F0D2E4FB1255E093D2FC7B81F692AB419C5AA1D32A030CDB7DDFCB256DA4F3A5E3D890E785195DB37FC0516B4111C47EFA76539A16E198877A26C5D3DFA000787AA7191C3311B4F1E7328B32E0D5D1406B4647450A8D69D26431D184DC3931792B68CB5F619AF5E45AB94CF3EB9B628AAE240F71D8F81942615A62618DBDCF3290826EE9B97FC3AC3B23BA5FC7961F38BED9E636873621A9FFB90A50784AD1CEE38CFFCB9AAD3E6F7BD78DC75143A5CA235CAD1106F80B91D43A669C5DC95704ED70EE462BBFD35957AFC94DEA876A3096C23A2E08DB4619B5C48DB7F3FEAC8DEBD60642424511DC26DCE6B744EAA34F4D0FFEDCB4C8A1AD244FEEF54C1AB833B969E351C113114E5AEC7982BC2AC15EDF8771A6077FCDAE3526187AF2CA7927920D77B85197B8E1D302B7BBE3E0531BD69AB22A4A19D51F7C795668FDCFFF5B5865360B6423275EEA9CD092462F87F8E1103E18228E314AAA7C3290BAE6A688DE97740003190CAF86A630BE75751EF136C99509A91A6BDAC5BD970B598876D4BB387E241CF866159815B4578F1575685BCB7722E3B87E7B934B75344599AF72259507AA1B3CCF43DD82248BBA22082958DE6E9FBF7B1A3F9D4A4058DC1AB833B969E351C113114E5AEC7982B8671DAF4A2D18DD92C974D2356C611D605C28D29C8A19AF52CF3C0666C324AF9A3D4630228F96E7B25D0ACAFF6A6ECFDB75F97F8C7A81155C2956FC44E079E545DF647E3985E9C1151D134F0E6350073E06B5A60EDFE80A70E8B1222310C7CFC1A639AE85B45F3836BB04BAB585541EB2534BE065AC456A902904463FF609EFDE674436BE5D8BA29198F3E2E9C905569D131EB3A99CBF5B66270D1FDB90B1642653E21E9863AD5BB6D94BFA52C86696A3DC10C1F9E880E5A2BF4840DD77A5BB0D4C5472495C369FE7E96358A2FD4AEE2B079A652121D68B59E09078BC05D425F704B0BFA9579F877FB82CDF89505C18E111A3E63D3FDF2060D6D6583FF6F251CA7C65AE869512237F68A70FE61FCFDF762A5478716F27529D07481C9F55C9A09C7BA845CC367D7959D4BD76341D4132C91C827DBE187AB866B6AA49C6C2882F20F328A1C2B5B06BE47B9A310CBFDDEEC5075C8FD9A15796E0033893A586582CE345C01AFBA363CA5E13C9FF0A357E0F5FC7D14E0F11CADEC794EBF788210252643A665395B5E11ED9E6096A49E6C943776BBB2B4F211FC11560C311EC44D4C2EC454266A1F9077EF38EEFA2C3F00B34B25914A671D127426AB198B310C858CCCFA621259B75DA53577AE2E9A6752348EC0EA79589CB07B0437AEDF524D14BAF55EFBEBFE72B69CE04E1C72A52D3F621CBBAE44321C30D3320A3FD5672E3B2E062BEC43AC9FE9810C1BA1790C0F0FFBECC0EA79589CB07B0437AEDF524D14BAF5F1575685BCB7722E3B87E7B934B75344E318B6BB4703C76D2723767BBAB22098AE2BE03B82A583CB6A66A8442663AEFB05C28D29C8A19AF52CF3C0666C324AF992EADA4C79EFE9BEF1273F0458E398A5E85FD04E7C7A67588A9345E8B43098FA835CECB4804B953B65800B2D34B0954D152D19F241CC50800094FABC437A7250FD2253D3129C1B32D8B45EBC7F1FADCB5F25648F5E15CE28B3A1B9C6E3E2996220200FE661CD3ABBA9891AFC80F217CDD640082B8077A1E4AEF8D023F91B5BC63F31D4D52077EF3EA6AE62786396548866B350772F6CB61A98188F869F21A144A7CF291DE2B654DA293C98F6153E6803EDA4202C51A8100878F0259D6E78D637545421AF73DF114174D1B0CDAA710C0D4671D9936E9CD93307F8F879CB2CF3C50A1A7BC177FD4D38ADF97ACB09576F2BD91EF1D4A4244F665BD0969D372749A305C28D29C8A19AF52CF3C0666C324AF9A754E6E19DDBB9F189686F1F35C34F0EA7CF291DE2B654DA293C98F6153E680311F71EC57235C517F3522784A6C0F8E1BCE10A712970838484F7BDF1C21ED3DE7CF40C8501C0585F274999EB132F9148C0EA79589CB07B0437AEDF524D14BAF5BACA99811B5E905C8C61EE4A94E212A40C19BA269A4B8F14765A30030B7892C18203F555DC683BAC144574E5A9B0B09F7ED0282E1C7133C4D2E626F27A2827A9285F8935E3588D61BA19C7C429F66A771CEC73C324FC53CC73542B5EF06719779D321F8B0DED862E02AA603521C2AC4947AEA081E2B1F68D73DBADF72DE65C97C1AB833B969E351C113114E5AEC7982BE9B96C30B931E8648050DDE9E16A53B48596249E2869DA7C56246EE74C5719C5BEEC6067020C3EC17CDC8E8953B66F6EC1AB833B969E351C113114E5AEC7982B27EA0E176BF6D98E156E907826154E5B790AB955204CEF0A527C6C705CCC7CF80C9340A2D88138AAB98F9A40EFC416D1CDDFEF3D4F3F0C82F6D09267FBD21C7B929D02085F5EC0D83A0E651C3863CCB3035D8CC452B1884E60C9C58AB79AE0537E103689FBF7184F547BECD5AD25C635FA921FAC0ACC9903B448812BBDF67DCE05C28D29C8A19AF52CF3C0666C324AF9B9EE558BCD2C3970430837EC85D734B87F95E78BF6A283BA9988B521A47A41BDD9C0EC00939DEE2574F7875B3B098A408873C10ECE16421EB807A62B13FCAE375BB74D628AECA3BD9C19C2A8D50946E8ECE0CA25A2FC51283E43C0DE5A9875BDC04E908E0C7902E6AAE0A5B0D65E5FDF1C08146E6E6AEDB3BAA00D3AC5B9DE05232FFB1AFED78C4276558BB008B6C6D9AFBFC143E524263DF4D1BB5D5777F3AF41FF35528F85373C71167B13AD821678F83137E352B0FFEF259E50E333C633E78E7385148F1AAC58BBE4F90A0C948003C3F5E82C58C968A4210D09F2CC641BD2AA332F6ED7F5D0EFB6016F0B593F0EDA42F3BC800C4A5BE02F7D79C637195077C0AC19B39F674ABFC6045E6BA6FF3ACEB3DE1BC658F2F3FD502A8FA21FB1D9425FE1AD724AEE24B5B109BEE072659D0F04DEC773F47D102E132259B565131930F80C3EE1ECB1F16D087A491490472FB5E585E1DDE753D745B4A310655470E695E816E7AB922962924F2CDB0755AEB923A4AFAA64E8BA1176C1A96C3DFE3AFAEC2E3DE96005A0EFF6A7550C4A9B19E509DD295598454DF0B0EE35C1597F8E58015142824F1862C2AAF25D8D62C94DD48231029748140F00642435198C33FDD941129186904549B4CDBBDDDED5A93AEDEA3C2BBD0E9F231ABC8F3B038ACA0BA20F839458E3498653DA238E25A10DC152360095D9A524CE121ED9C0CAC42E76B0390B22E243567E4B7CDDF1E1FBDA6036361F7DB11E6ABD76113536595937372DA9ED714F146CF900DA05140D3094A383EC13021C8409480180F61F473429A59AD961CB10FE4C1C71311C8AF77448BD550F4BA4505ACD43CE1A46D1831B22B78940F652A8D0C5DD7B9F55D0ADB8F9F77770487B016CBA338DFC8FD79B792F67F43E7B67343A19CED970E3BDB606BA72FCE627E8E47911A1BE129811EA9A958EF6952FA7D78A12DCB98FD7978991837D72A16122CE0182D22002982AE940C2022A35356C731CC5D511B6F199BC1E49F783D64879846E5301180138DF9B54285F8BF636D1F431813E6E5D3E0E7C3B63A922FE1838B26A744E21A049512F991A00A41D2ECCE85FAF34D5FD2194A54D12563A4FE31420B9C4228FA379EC22B7AF8214686A2F3B16E93BD32ADDE9DE899FFEBC494C6381F6ADF39A8C2C4F2EA000F9276D5F32D2F263BCD7C1E547C9F386DDDC6407DAA7C52F14A18C57B1F87CF0FDCA5FFBFD6E9D07BE8E82C4D75D9519824200FEC8667ABB0B63AC4D10060FE925B6000364DF0E30FB4F1FC4237FE8679807954921EB72F1BC84B5BDB65F6BF9BB7D74BA3AE6319FB682F26C3A91791DB1B1CF9F33517D61C766E41FF30DD64385DD5379BEDFA7BF0A04328513472DDF52003518676D425B9F932692150E43D5778DE2D6A54D2256C5ADF872B516C41354EFB82DBDC1812C561A499BF951FDF94A5C87B137947231B9A9543E5356CDF43BA11D78F59FEF29F4B474D859BFBF16C0ACE0D0E51F9132BBB98D0C69EE385D6E45792501B80BC6C0B01626DFF645304DA433A37D311BC70BF53E0AF1AEF81963E6A3CE7B2E4450B87B01C84B0BDD220597EE8B17DD072CD2E4FCB995CD4E6E5AE54FF80E939E6E40241DC7B93E2C118C3DDBC9045634743B298AA00862A4EB4188429659382845A813E9D4B9BFDA2D4B16D24DF0E39AF7F399F27B25F3B8612228F8B12050C844D5F2720F28CED22FA9E5161C02EF2F373CB45A1383B09E05B11DB86CF1C3B662D9D6C023EA07FE6F2C2C6946C23EC9A45A066AD3D184B8D5D500D846EDD46C8C61710B5817C576EDAC0453DA8BFDB759955DEB87E6A5F75D95AB0B43EF8F0848242966234CF315C1951E91B2C55D1EDBE3EC1419AEFE2347C59D649E14035D9899B29F14AA6085C5CFDA25BAE52F86F4D1BD4556DEA43CCDC0A944AC903F2F89A3FE4D229AE349B5509B140FEF64826092BB018CE69EB7685AF2A6A57665488FC4A5B6B5A0D61A7948FE41675E1B1EF2E090C9E162284635F3F9E412C1C846996B909917F121FE4FEE786083B60A40E74ACF84D7753B5B38E328A98203F555DC683BAC144574E5A9B0B09F7ED0282E1C7133C4D2E626F27A2827A960129571A954CBF4581E484C9D4B7540826FCD5378B557CBC1EBF7C22EB86318B3A58E460D8CEA593F35575BAA35B60EE27C6E3512E8D648B6D80ED3DB9166F4FF4AEC892B61EABFAE1527066FA081D8689C05803406FAFBC563D7B7D50344964B3A96FA3AA3DCB8986FC9FE7154242E65FA35B99852A1BFF4B4487D2ADEC6A62059891A700283696F8175F1D805F148390BA30F9A7B2EC8F0A29654E82C3EC3035A77040D192FF717D9692446F873FCC058DF1A77D1C460094501F243A53CA0457FDEB92D231B67F5BC2F348BD7BE75CBEAC05C344A9770422194F87CB36290ED3A6D5C2F793D82F9EADC940F1868AF4B5D40E4AE44F7BEFD5E8551763945A19CB734FD19C7C33F1E1E587C89CC326A9D577ED83AF8AA358F08BA8740C4A7607E6309879257BE3C28F7E455E54BAC25F2A750C4EF89370BF3D22C1C268A5658AD549EBB3D500FDB5EFB67CC1C0E0179B2A92487C83406684D110A05BA7E42B8F0670DFED89AE3175BDEDE6285A47156470C276F611DB5C5485AE19F2E5BDD0F7AE4C7D7E9B9DC1A20255D5AE205937A0C0AAD448D018590D2D73773D2B07652889446CB4110CF3BA8A67660EC52DBA3ACF77AD8CB61197FB327C50969A4BAE7E1C567E4A2E101791ED2FB4A1594DA62A2DAF059A79218BFA512E063E39B06869118CE92EB2DB978402D9840AEAAA0815F46F913F80D8ACAFE4077F09C7A86B8A9F95AFA2FA234B494F0BDBE62EB275F9118CE92EB2DB978402D9840AEAAA0817D7B82E550B2AAF97414B9371612C339D2FC624B8446E010155228FCB6F013741692C99E51783B6A4C74E9468B3D22C8532A8D2E89B718C467E54B152401E3AE3D48CCB08722394F6C3F92FA387BB30CDE566D5B0C654CAAD9094D2997D38707DBA1F34D2E86B76E665F725FDC445E7CF8E68FAE2CBA5A887993189FD5024A91BD83A679A01DF8A425CF9FE85DCD7DB3EE6FB2A067A8FDB88E937BE5269206ACBED470AC6AA8591EC72AF84EB82BA6957C509F3BC280C9D4F6E18633433BBB0EAE3C23C01FB9626BFCC0B93A45D7EA6AFE9F5EC69F089746290BCDC6B889079474DF112181B090EFB0CBAD3B5874AB76FD5D7F550A784090FAC8841D464CEE04877FAD49E75193D04DB983A954ABB64ECD8EA20C8C5B0CA47C95B79C876E52C9A63A5A6E42734CF22E3FB62F5EFBF7491C0CDE6484F537D898D95D108A4A0AF9AA3CCCD0CE01369D8E5F15757854BDA1E1C8B2F7B06D339FB38718653D352F6A9AD791A057BDBD2FACFB606CD8F1B6BBECA6E384E67956633FD30C1153715EABFD983FF6ADD0B29B311E83F05E311066FEB942A9D92DB2B6466463C4951873C5B98DDCFFF96DE3FD7E6FF153ED1107301751BA0CA9B92D66D68A4C209048AC688565EA5538EB8A850EFBB2116C3D13D4C3D44D60451BE14E6E2D47BD24F25C3C4F32E5BD830545DAAD67278E0BD151982BC25C6585181290C7CB686D101B55D4C36226021EA0B7D19D671C70150878FD9086795329C771A6B5202C76BABDD3E373939AEE46D891AC09A4885284685A90796B1E3DA316321E73B2C84BABDE10E7930322F8E5826F8C190CA0E2C26AB733D4EA70BCDCAC0CC5F3E3480BB20CBE7BCC307BDDA0201A71C4C2C8FDCECD16C33DCE3A7663DD5E7A71BD024C5507CB14AD70F9A184E23DF02E222B579A21F51D3FDD9CD220F279074BD409DC74250CFD036EDDC7CC22156BBE4E9F2A39097E2171C084AD4D8CD202A133486BA5105C162059891A700283696F8175F1D805F148710291134431D15702D6861338DD5A6E7F91702EDA2B7D88D736EA3B5CD9599A0DE6A2F200F050CF0880C9D092EFD9334BDDA3913B7D8D2E724AB88EE96B97B3C241D19091565023D5BB654E89598D61DB1B3F94DE628CF5D4CD058E8602C8B12DF0EAF355304C0DEAA4D6C34F9F2C7F5A0587E3AD957AE120B0E4C30AC434ED7E2E6DEAECA3C502FBF573DFC856F3DE2C4155681AF334978090535ED8EEF66A9979F5B7227493EB335B836B5DDB0A332D849963ADFAF82F4FEAFA747F89ED33BA7248E099E474F13ABE4F7355E3661D2FE30ED31C4FBA9C5448D026121B4733ED1304AC60F124F372971A9407A37E3CE2B9A1EEBCE1A42F9E02E0CB1BCFA2F8AA507EACA8847D09BB64B30AB86F5E7A2830A50B50AF20DBCD0370F9286B942769E51B324B3D1963163D6576740F5021BFE081309746B8CE8721265CEB9267B6A6077121FB3B38CFBB908C39C8B40A0C493BE661FD95658909AF6B825053470BB45526B10EE3245936BA4E4276A8E2A01DE832D59AEEF3D84712F4B90F0632AB5BA0652B25D49A308FB06C29AE1F3F3F7F4F3894A81E0B3CD487F68D56ED3844CD2FCC9D208E0B7CF03FB95C09DBCDB1CF7C727A6332F5D3A41C7BF7EE1D267F60FD900211B4737A7345EE80003AF8B5843882AF006A91DC9EDB22170639012F0B9FBE87408EE4EFD94E256AE44B178F00971569FA2445428616D7701F4F303A803C0DF98B6313D3A376085111AD6BE9F98965481DEBF4CDBE81A662557E462F4B8BB7B95FDA36BCBEA490DD7BEC2D8E4C2E4FF0F500B34D82849155347790A763A0262EB5C0B10288B020241255290CBC39EE01D3C8D108F59BB936BDD8F2A82DA5689157FA44BA9B6B302169EE573E66D93654419EAED3CD626905DD77F178AC394BEEDF43723F3CC60E77827AFAFC7CCD8450B6E7A680CB3E2CFFE05B7D5B5B039EC9D39405030052F78AF68972E168923302197F5C1313A29022991707EECDFD5245988934A50FD769BA2FC9C2018374ADB90888155BDA52F280D1C19ED98D0DF197EA99BF15EED8A270C8668A145C2E9B7F5B8D6D3F9E07908519CBB1F58FC0B4E00E1348F69CD4FCAB62585B43587707B28725A48EF5AD8B35ADDFBE0C2E84CEDC518D0F9A9FB24FBE68CC296CB93C5408837934DD9D492D989DCC669563DA0B757A5A0DF78C4D42DC18D76BFC35207CDBD322270493FAD01D50021678E251390A364FDF03FBE0CD9C9FEF5C161EE58854E27E51924083FC38C06C45BE0ED27345373646FF28992121329C857534D74B014352E95060B8EBD533D2F2D14C33FA2288C8286358689FD4989B089EBA04B8AB0CDD1EEBD3B477B0E39CD761F16FBBCB7CF4A9D83A73D6ADEFAC1B3CE386DB4B71E438D8E9662C0F9BB4B0AB6A132CBAF3C9B6EC08DC62C37CF2150F8A1A1283A8C40396AA935270DC868A51CC5580D5064F293B939C203AA4F82EB7C8B744F091D68223EC42584CADEC7D6B41CEC0910AC4B7D61546F4B24825AD32254F1B48EDEDCD762D9BF980BCB6E4012D8ADFFE29A6839BF18557C7FE4E5A28D3DEA7CCCCAF48246E29D3CDFACF584226AC8639318B98CFCB577B67247FB9496FE6128BF388AB5F8E4869E70110469D297C5194A32A22CD9007F6CF269EE5AEB92F1D462D72D26F9FBB4AA7FA914D5E4998614C605F3CA8D3DA86D8E4F86228A469929EC20F5CF8E82EDB8858550581942BF3BA3096107E41FB66EFF45EC3FE5708D5B2DF000530A80E72E8FAFB4DC0CB41CD2892539EBF11C7CCE918CD2529CF35A47AC3F384BF3D0ADC7CBA41725F802D3B05A145DD9D3E9E69CA69978EE47FCE294C68BD611B87AA7127565EF89A6713D583C295B3DF8D5F42C9D4E4A90EA3E7ECAEB7DC4292897ECF3A496D10B4AFBBBDD35AC46AD59CFF0ADAA99C2020A45257A87D4E4FD61061526EDD0D556CA195BB0D043D6099BC0600F5166F58849FAB80FF8E7D77B9E8E0DC8F958A07E309526C6A79F80C2779A9CE4D2EDF5A0AB0886510C5F68E01F2C736CFD8500EBD56187CDDF414C35C7E6A9AA9DB75D613AD579E18BCF416C13BA7B2526FDEDD4F0684412AC8DB5695D858162A4DED6F32102EE1E4A40D8E5BFAFF88A75623B5CC9510C4C53FAAC04850E605F4FD8DDBF81A04BC57D69613D16B83C374FC1A7EB3449A29584B6DF7A05F5F53F599B6BB05D0F96E671DC2DF943E6B031769706A92F73C2C737392341E15A3D5FF9994EFA4F20C0FB8120DA1F2659931E5264F3B961E4C5583E54EDFAF3C639F2ECF6704BD3E4D6D1EF5E822576636482C5E7C3ED6BEB67E1F1C7099DA9D14FC6431CCD1DBFFF96E1D1553D81F4C1E9F805D7D3FC67FFFFC9B1D8E3C8A6707F26590FEC1288356D053587C08DEEC0F29DCB4C7B3027D67027B5AD0A16A16371FCC0A0836FC10A386645F32EECD44E189B9F860C7C27466AF154C169D2393850BD6FB7368A6BA807A2790663255790006FBAB132476BE7351259DE171DB13C33A95F34DE9218733D2CA358CA0354E745BFDF07F1624878D5E2EC6958C430B093FBA511AF5D1599C421FCEC5624C3E6B025494CDD67328A3E7ACF189A894C81094ED0DD26131311B5E1A2DB1E7665F631AE4E9AA87540D0D03CFEC23C18FC0E8CAC8CE7C57065D1BF1DC9D1DE9B07A83227481BE92C48F37CEEF579C7F48C0536F634C64EFCB6F3801D8DC75F1C203CE49C9FE47EDF0A0ED6F5A322AF9F84CF7F89AA510B621F54C05FC30F53163D15DBB14767E4D14FBA76E28DE123041E9F5A23F9F2E705C59971F62E9B2D4D0E4C8B4552B8BEA499C0A6607D21D93CCDD540F16849039C78E5A44C55DBBA1AEAABAD700918BEB5E134C3445AB953D2585E7F5811640CB4C64FE0EB310AE6D53652D5C6F9081356E69AE9B29BA30C5E7BE59B776EE45F2D8B3FE517178F266DF5DCE2BCD4C097EE1F1B72F8808664768948E7D8426B9CE17F1B1D22A3AF0E4FA1DD768A5E641B51D0CBACFD3030585461A6ED99A3451A6E2C415CDC760E7E1A3AEE6BF0A2DEED6DE7223EE46D6B45A38B22DD5FCFD4D474322292522E3BEAF7F48F31339A4C81A21441AF438A20628FA55BC0802BD70B1700C541E357FD2F6F00961921CB0E8E9CFDC80C3E9F9EF6227D1F7A02414530BFCFEE950F2D350C7C59A0A18E77FE8A6F76A528CEBCC04A409F73263BAF858AF8A7E1A4FE63E0F14F6B22C15D4D0370C93693D12ACE67645EB17C3EE378C3F1E00B3EAA675DE35B9EC0B04352A1A672FBE52C00258464AC42275384BF9F1057876435A9276E3F736E3AEC7C19146F71BB87565F519A63EBA44FEE1CEC9673ADE2E7E5E394816798ACD2724BDFBDE93BA190B7C6D308205659C0EF3F0163AD42596C39BBF8C92BACCDA7EAD63DE7C68A072A61FCA33AB0EB533ED4A182C4B09A7EDD439C4C54F0F9D59D24C42E767A5053D7E12148ECD0126E5F77731B4BE6F50DF189A74A4927CA038C8B3C269E61DF7E5A3AA93C0A572AAA218988F5FF8DAF042F7A58CA36A069292812F8810374B768EDCCFCBA337AF9C362353942243146BED5F62921A252C378B5F9B41B6934EDBF6ECD7123273818276870CCE45B81AEE4C971FEC69A75249626821F05651786E952F3C533F525FC8B7B5F6EAB33C320532774256C42C01B7205AD6265ECB324F364F6785D5D54BCDC595B252BA48F4933C74F262A680E908D6217F81FD778E240D7ADD32B568DD00B1A2FCB2CB70D39CD39632D4883546D2FBAD0748DAE6D34E090694DFFFABF7705237947C6C8BBFE827F97009544A56B3FC94F67A972F2C5EFB19748EFF9B8ADB783E114470A7ADBBD1595E490846DF2E9531BEE796F63A1166850BB6B1FC2C14F429282EE3AD5B213835E8445F50C8711E06296371F912EAA6CBB756BEFEBE373FA80A7CE147BCF84A12E39FF68EFF2A0D40F44A304D170F20D49EE207611E67EC1AF24C244C49F842039DEB0728CBBDBD50BABB546CCE699E32FF668603387FCA38ACE7C89BB8BA142F52706D6898888D6BF4F99999E0003FBB62CAB4536B066517C8F7D022DAF488FF6617304B96D9CF3D45C1845E2443C59C3A6F6B3ECB173E9F193421AD77C449D96218488343F9E7AFBAEE2139078547CEBB62ED5FB12024C936E9D1F56C1852B392346F3B92219D09CA83E89F7EBD613518B29DCE4BDE26686F75617CD7E7690BFF58D520B16D87F5522F4F8BCBFF808BBFCDEB16CA08FBC9E71104E2923602D027C1C3B8AB9EF1F550C138970A9C21AB3A416A23BEFDF57D8091BAE54A636CEDD05596901D581308048CF77C5A0270D8AEEFCEC454CC1879C0B59C03AA4A2526DB6D3C2BF48482C8B47969C49E480F11ECD54C6A121CBBF1EEEDA08DF309F919EB484C1718CC373C8F29E9A8DFBDC3A2B64E3E3C91B5E5819F7EC4F8EA190F40374B43CF65D8EF9ECD2430C171123CE4FC643D3B1296A8B35DC8F29C4939BDA0D6BA4E5681A0304FC0B131D9CDD4E92FC48AFA4C6771F34D762CF151616F7E44C00EB5A553144FF3D2214DBFECF85205B8E060C4B7A9039A5A99C62A2C95B5FCCE42888BA45EC5FB1A8E6D7C49786445552370E45116E7DCA0129838B9909648A6F20EA06315FE643D660DAAAEC5167B5C204AECE536812E798F69E14F634CA9EFD711AC7B1A887DB433AAF6AB84BC2650ECBC864894B6B01413530163B9A0E0C93E7D67CBDCA1AAC561A35E4605BC37B452D469E01BBAF52CCC9E08329875F1558D7293BD73D7992CF3FAE2BA844CB4C3EA1BE890375ADC66CB1406E6AC043C02FB6CCF652186E8D6A59ACF284C0A4ACCF7DC8F7E57A94E3321B3839E2CF469EA737EE6E8980549B76829B29234BD5F877ECB298B21DEE6B37D5E46CD3403AC854FAE1BB3E08DC7096491F6CC7BB937FF4BE3FEDF68CD6538745BE7E760FBA1B9068DEC40641C3F3897DD9F90EDEB78530F6474539A9F61A0EF0528BE905CA2A512AB8B3E98CA0F0F7860E14B9412266F74B88CFE88A99935B38C4CC18BFA4791CD3B5575005419528735F9DACEFB259D167C250F782FFED6992436F35D8BAC8DA7BBBD8BB525F614A9CBC54CE98DB4367290035D15E957CEDCE9C84F52468ADD40F1788B45792FBEA514FACE15787C6DF86867A7E9BE693DB1C8876CEFAB16E60AFB71D66693D046C0C7ACBF96FE51188E61F63289DB8D1FF68FAD8E979F44E9C4907ED2DD3B9A11C23B1E7D472DCA16C3117E29333C1559AAB77B6F8A2928F9CA8D8677969D41668D7FD849ECC63EA7887A097B957AB6A2EFED953F2401402C249863E1A48015763F426CD818764B87A5EFBC29FABC1420FD78852A1BD727A634E2D7631B4E8D13F653E9AE9CAF331C27179E42233EDE93BE998524A329BEBB41E16943FA0B425B3184E6D756DAD71E4F185A601C5AA07468CFEE447841524999292C6A366EAF6525E276DE21F2FAC66F938015652AAD176419143762FC7C8C67FD0ABC69443C5AB5728183205B8C4C2197A9FD0EBCE2BCABCCB92ECE940534C4308E93F3850072784A16D4AD96220C1E824EF9053CCDD76CA78D834F1085D91AF9195A029E1E7445A67709E9EE2919DE4BCAC1C0FD2126A432D54E8736BA51CE906D9B784F43275F88E61B5E05A488BD7B2613E38D832166F4D236136FF3B88D0795B9937667F523BE902BC11604EC956A00CA3EDE437DA9BF9EF64C8F2288E92CA2B72587FA01E0E09D4AA606C8BB2F92CB9AE4C8753E910716EE44FC3DB7FF5884866463B0D0264019B39C4065CAD037911A44C0EF78402497E3CF7318F12FA666F555511A68AEE13448C3D7AB7E45480DDC241E0CBDCE2299E497382DFC84B7E455965CE6F9C9B64AFDF6C89E31482A0844C525AFF963280D1E00B4DB9079EC897E3DA2AD187FB81006F09E35E2A00B7B5DDBF6383A0F722233D1C8EC17B17537CA446CB9592AEE7CDB6D76C9B56A73E0B90C521BC745221FD083800899BD6CDF33ABBCA54235C522A744EF6F3A990853958B0C8C25320E3246BC43691379018A93CA28F69FE315C20E583C58BF0748C4546D89760E314F2E9A3222D641D6B4B963E4949F5BF278A1551BE72B2565AAE225D877EE56409CD9DA5D0CAC8E191822623B4E4AEAF5FC3E3700096E0C5942C3B97E65D138AC6DBCE6D8788535100CC0089017545E5697924F3A0EC31FFBFEB19679F1431DD0A607F173DD466F98266B1248D425B7E32F0360047D58BF1340BECB1126C3818DA4360175C6D7D8CAD5FCC1BCC9DC36C4E74C4FDFB70C62A98CE75466278AB3AEA9E6B79D16C4C75432317B375417DEFFD5846584F0AE5B05ECDCE6C99B3A03C216BF45866B1EC12EA0A5F7587C6DE1C497A2E08B85F385B71569EDBD406D93B3B1D0DB2E7F557561CD36E737C2B43E298AA9739004BCF55D7C96CB1438F2BD7BEABAA9C64B58932AEA808BBDF30FA4D363DC501741CF8A43AA1039E7B46576B8B299AE300E7BB49B61FFD29D0F814857931FC2AF69E6988F12C6731D619C20DF1D7096DF5C656C765A35DBF099EC5AD97B54F5A40128182B0508A6C5E6C7BBE9958F290D76637909E4759AD778E6924255A7AE038296127F00124B51F5F3742475E68C2BA4B2BBCBF39CE7463999A2C95569FADBAE6F80CC073BEA063CC5587D9DB5962C9B392E6FCD143F95EFAB1F619EFF7FC7CF64FE3D195916CB53AE2A956880EBD9EB199B87E5FC55D497FE261DE93C129564E90DF1BB21DCF41A53EDFD430213C35BF4D85E860C04C06A7CFE8126CE520C1286A24D6872949B702F09581F8351658CB86D44C1629B4A0F24BE07CCD39DD21D9AB8DD1B082462AB67338D0326B481CB4BD541D25DCAC6FF5A98AAD4B5C6B50BA0F7177C4D40964B46E96EF198309222D50005E1ECD5458C71473466EB2693B3EA732309B19A3EA582099C4A16BADE4850097BCD495BFE8DF97E1FFBBA29638CBFD8D841549FB213EFB29AEA862D6AE28FD152820CD0AB67965C2D08956016C4DD6D24F94BFCC9A396BAFD26AC085709F5899C4A6831E8DF072A96A05DD8B854963BD0F2346F13E5FDB7050BEB13B4DDDE99B2A60BB4550129FE53E049CA595351E29A07CE7828F387468AD313E11040C3C0C2F42DA056A311FEB561DBBB9910F080485FE31B1A3C46D44000BC619C578B387C7521E4CB99000AADAC71BC2FE7CE8EBEBD9EEDB294702EB294E0E2CF639B6766C12416A3EEFA84CE71C8D630851B5FC5C57F80F92A119F07CF623DD1CD80988C7CE6B6E1688EFC9226050ED3494822309E2049C0ADCA80327165283C8C8028769134D9FF9DDC6769E4EA422C5777215EF7006B8EB54FE5298D5386EC0AAC906B03C5E1180C5E2AC69E1C93053DCC41921E66F976CA35EF242F2A26C86F311F7B998BBDE5B83C547B4039F44B0DF8ACFD24F2928D4CFBE7DB647ADB1FBC95A7E2DB518A5A370D9A74619283F4F77F159BF5CAA1A489631458F8030C645731078489A144DF87BF847318F420FF5A611B4AB8D7E3E4DC2DD74F3C91E7ADCA32E4D4EF99C0410664A32B50A85D2AF82006C859378791E86DAF7C94ABC34C166B9AD0EC287AEB8F8E275E45C51FCB7EC3EC5B65668A07F8D8DCDF19F201DF1FD0134B3309212EA13CDF3E0A25CE16201B0A69C2830B7AE5F7F8A95EB38399BDE01FFA81D642147D2686E56C213C42F3866445F567252A47A95AB5AC483F2BC6C9E269A9F75349768567159BAF00B0A5809ADA84870721AFAABC61BB2263167D70B916871208C64794C929BDADA24D89393E40747EB6F5E4316FF5360AC876F726224F5319332B90CFDDC2AF9F401EEC6BCC002940EBDA36AD04905B209DCAC11C943DC58565D53E260494E7A3266533A0B6B98D21D1AAB449F08981B01B2EFBAB0ED4E7126E54ADF47F5C8C45D3DCF2C65ADBC8989FC7D5263040CA402B76897811D409D1B04A0C80A078ABC9C4567AAC088F1C20210FEA960236D485728326EE3515B05BECDDE2D1FDB5549260FC023A5BA9BB775C14001F88A4C5B35BDCEF9858BB8660D1ECDB0A780FA439E5E38E96BF36C1E97BD80CC0B11A704EB2A73A2E6C0495FE780397C157CC378F2BFFB86014DFA7774B0558CA6AACDAA5D29CBCEDA55CB321D2B635EC312BDAFB30C45C006D8248D6F8F80D037EFCF94692F5DCA16E244D665DDABEB2C39F0011BD0F624E30D2F6853DD46DA56AF67E51D0B34C9D075F5BC3A90393FC8F5E96CD8E029A7901191E66CF79D64651CD6B95870F483A39AD9CFB53B5D1C66FF7CDDDE4FA42405350DAD3B010F645C5175461EBAC27731FA4466461AD472A0F37B531227A59AAB687D14409213EEA815AE4F925BFAF43276947B28F965F99AB15948FE8074F7130F929E481041DC9372B7B249E1B8A5AE6FEAE678C1DDE54520167ACD1CE98EB678DDA102E7F3B8B432228EDD58AE4AA4CF9A24ACABE95DB1F544421B177E30F32443B8A13732A45DDEC167958C4E7A35E65391C59617EFA7E4A18E0BFFF6ED42A494B070D7298944A7A2654663BF1BE91C73F023B6439BBD4368A191E1B31B4229329E2E81949E46F2AF68609AD9A72DE6F75FA4725A4B50012BAF07F460B04FEB6B15483EEE459135B6EBE5EBA62493F23B4B046F1D712FBEC4E9DBA3FA5D0CA2DBCFCDBAC154771B39385F0F0834779D6B35F888FC881A47BCF4C074FF9195B18685B30DDDE3899D3E0D553FBD6EF5F8788BA2E977C93551E92288E2149804A3B64743ED730D265E82534D123563B3D3311264DDFF1B884043EAF01B8333944BAF360F09FEC910F44AB8AB1406FCF4286F04C88250CE337995994A9BE19B0599307A0557F722233BA3D325EF2F2C4504ECB5C157CE80267942DDB304A4CEDAC15915639B162776C4C853B98E9B45CB12D03144696C1DC81F3EC75DCB2C0D7947683F1E39DF344E30DEEB7F21552AA6720D81ECC47EAB91988223172920012B0DA85B78F80027C3FDA06F30E60CDF1F9DAE40188F51DB83767E6550FC97C66BF183E580F55A9FA682504A86C4DC29E81C6AC3973D24731C630180AD764938B71DEEE937DB1682BBE9A4C7D49349B615B688E13F3DB8AE25439F4686217BB31974584320FAFE77E167386C6D54CD8F9F0082DDF6CEEA87673F2D1B5F15A12A8A844D2BD55E7DB2D842E456B321432C62623C4E304B20230E7837D67F4DDF293F46520BC957FD952234605126FFDDEEF94FC5E77D2013D1A49CD7020DDEBE9E8A704BADDF4EC56E2A47AC02E134BA6B1CDA80FA5A0E2530E4866C99B163C3E378CC770FA5E3855CE5C146583D3783474F83187F9AE5936FCD448BC8CB91E2E29965368A9F3B140D9DFA6E95CCAFE976A93C6730B6149CA739816C02F4EEC2600216CEECC8C14073CB6C93A6472119345BB0E0BD96E557FF284DCC19F90784E39AD0EA77708A37DCA7AD9403FDC6655A45028D0393804F7A4249D3F592998DB96950F78EA46592CC53A0C197E141F930F58DF7BE81FED9B158E3F232D4DA926135CE6CD8D16FD88C43623D13F2401587323B822F581E3D2935AF2ACF3E4FDBB682CD9E0D2C09C995B47AC930AAC36CFF6C556B94E1DA0561151EC280806EAB7F9ADD950B791B720DA3442BCD0BA3528FAD1E11997E3FE8DDD4496981630F6D664960E5DA44984A2025499BAAC975C12F57CA9F5F425242BE527C64CC062463D03F93530157A256DA1575AB97EA7528986AEA76D5B1EFC34A81B809F5979700EDC9D3BE902C28EBE1741E1F1109F5FB1BED02631FB0F25867119BC63D6443A22013C6B38B1E2231DF0C5E1CCC1B05E34917285B003AA9E13011458B2F776A64A63DB3BFE85675F8DA25C8E697D594A6F801E78EB375472F039DF556B74FA908260F1CFEEE87B938CF6ABC4717039DF7F29E31F503CD38BC3FA6441A0CABD24C7EB9685C1971EA977CC7166F3C6FC857227F04C47B239BC9B66AB1C900CBA95637A72948037601877D8AD8CA60D935C2D4A7175ECE306AFC15344DCBC568EF74EEAF981B4C28AB56B2DE51A8CC9D3F2D61B06189CDEFBE371C3442DCAFFE1C0555ECE43523C5D00DEE2C4AF477C773A4CC02CA63337489DDCCD645E6D10268083B3A148E09D4902A3E15813EA8F6E27FFC022B144454667BAA3A67D01FA2DC0F26FF7721EC6F0FC13AEFFB2F7111D181B57978277BDB694F6F34076895F91A078F2BEC3303AEF47F5EEB898164CD4D80426B9593B0F5473C6B2BB947FDDB377A957521F9417DBE4162A1A3842218EC2F87FA87D4702E2A4766B1A8D14CCE492DB85F7D028D0E77CD29CA26221E1A738376378E1D7D611CA9064D021FF37C4F90EF5CF75762DF8C79474D1CC66D1B655BD5E30AD8C0B7676E8E96F8BC155CC94584E4D5FCB1BDFCED948F8ED31F5241E591DB6C53EEDBAD4B167299DEFD4A9E7C64EAC9AE5B1BD4C0C8BC29915E7B2E0F32407040F390DB5DE243106CD74B6FD02554FCD534D9FA3DBB34133FE1AB83162B5BFA019B27874CA226AB586DB37DB60EEE066E2E71A74321D15254C8611EA500329A050526E6EB229745EBEC1B2E01AAEFA4D4A839DD60C5B5B4F41AFAAFB3DA78DFE5A3108B837BD3F5EE623D878D39AE6C77F4CCFAB9D0F572018F46BE01E0CCA9DFBDD6C6438F7240CFCF1C0C25231D72A2C48EB9B553275B299DE98D02B15075398A837EDE94A85BB834089F969C06B45D5A33C548ACA31CB006D5C7B4B2CEE7E346F96415161D79721A22A29D8B2EDC3ED99B08557F5A170BC0EBB6526821BC5DB7297AF25DE46570F975BA8B5845CC7FFA5A74C201B51A90A2293B45D77D52F90A2B9AB42922379DA819B6B54138590D7E8ECD8B50F455C492B85ABDC1042E1ACFFACA92B0CCC629C1135DF4DB0FC76330043CCDB2E26DE48574113DADFCE23B146BBE8F5C22D501778AA5DCBFEE4FCD0173BB60EB93A0796E6E2E4A13676B39A5BB5E4D7A3E292DD20917CA3CC170E12C5C933036A31D80080BAE9B45574D70B5282859401145DE90810D0DB0D6E76A222D36E483C3CD9DFA0923696B024F0D06ED83DE0E56F662C3F0884CEACAC9A95F6632889420E3E57224F43C6EB5888DCC95F14041B66096219D49B3186C94477432D47F605788B34F491FBA6657772651BA8F9DB8F469898315AF514FCFBCE15FB82AC5B2C043346B152E1632635C708D61A000E420813C3A31BC1DC1352EFD258244DC3454157882E19486D6A74DA1021A0431A2FC43D03B7FD8993422D30027183E8B2BC727DDE4AECE3DAF454D6FB7BFAA4BAEC281100C2A59D2530AE67421DF76D8E9D01EA507544BC8799DF19FD2FBCB214D3B80435B5BC9040051048B02390EBF080DE1111178A915BEB018E3EE5A43B483A1526532CCFB48221A7319EAF8AE2215C37D7A7684FB7B924D9079EDACA56BE7B57AAFA979BC20992A69745E91A3B74911D238033AD90B8C6241F1D72C1CB8B2672841F61119B5A6298D5B34213384DDD540B965A600C682540A3BD8F466AA25E75CA34883E68EEEFEDE502BA7665AEDC133707A380627F92A990E2E27874CA226AB586DB37DB60EEE066E2E71A74321D15254C8611EA500329A050526E6EB229745EBEC1B2E01AAEFA4D4A83E93CC0DD26A8D770423D127B762AF62E3F3BCA02510AA7C67E14B038F075C5C0511878324C47DE85C28C9CCF10245325D5D52717ED331EDEDBBDFCD4DC61A9E63100328197EAAA40DD8600B21E1C8AD0AD1187B69E29FA78FA7D618F7F82B6100C102EAC3DA727F9E322543355DD0128F5420E17564BA6638CD784D9E9A83814EC0F46C6F2113F35D12117F0C93948F7C789DDC46EF7842E95FC78CB961DC312ECB58ACB1EF0D614F2E9F144D464124EFA9E06148E78DEC111050B93F0BEAE9BB408392FB4506F350DB7ECBB6E026DFC61336D130B1EBFC9A702DCD80915BE21C7372A3B767068E27040CC5679158C166C159794AC3FF300F5F9E53C1F84420B692A14500F98C72FE2E52D6643D185D217CFAB3CD9EF9C41BB297F65E164B654EEC777CA127A0173197179A3003783C2D48F7C2E1B257FF189D40030776CCA162E0F130F1798BFE2B588CEFC16BE109B03BF936AC25A28785951E49DB104AB23BBC121C5D5D6BF441E2A49B61E8E201346EFDC76ADAA55CCEFE4BAA25C540483096F36D767112B16009FBF74708EC3E38982DFE9B31B75C09B524B91E64461865BDCAAD85445D208E013874415964E448929D746C1AE26A6B932F919577F60E836BCD7937E677BA4D8134820696116850AB81F22EE3691BB4F306FB9F4029BD87D3C9BDDE5ADC1B3578F099809A8FC88BF3866E9428B6DC2FA66C8FF172A1283B3FA3D76561CFAC53DF900A895DA44CCC9A34D99C46C056243CF601CC3D2E300B32F60C74E739932A2F2CA0B2C91B9C539B885A4A33A164D35B6949BD5EE060BA28D02DF6672F5FAFD386C1CC5FC2E3AA8D1B1E91B8CAA7A2CD45EF78E822DE10B4AB0C4941DC4C01A4FC21BCA8BD1843ED0914E511350ADB4DF15E4561083C59754749A5726637CB64200BB665511F59B37408568F438D8FD0F8BD34BE5B2B789F5C733E1DE46DE6808F06311152E94DE6173B784FF6E0B367FE993993E0C3065925CB492F60F023B00423670119FAE218E54A5CC0C73E08BD6E8883F0FBB83F243868D609889B0220D7997728ADACF39B885A4A33A164D35B6949BD5EE060BA28D02DF6672F5FAFD386C1CC5FC2E3AAC3B31671BE86983D2FC361C7576CB730B4AB0C4941DC4C01A4FC21BCA8BD184BDC143E13AB03AFB8B460FC86564C5797068DDBE9995E5B798B9993DA100D94B05C0EF0620AB84CCD3DF8FE4261FF4BE168B4B471FA03FBFDB59820DFE0BB59532D82B88E154C051AAE9F7C2DE99D8F348F3682415A25EF8BB1DC0A5EF37015CAA780A19678FB2BD1299374BC9F7CD3F416A96D70D0C8BC84ADE068F1CC158D89A2073F04DE9A442482670A75534D8E2075929024AAED011EF7B27582DD1DB65B3EC7CC7FDED33037E8F56BBCE353156F268D9BE25F3982EBB4731646FF7E5A2854A243B2381D6AC9A8B3C40476B25CBCC362DCEE464C4099972FD4A763F48D21A6FE20FD68323E6DEF620F656CF28E41D8E14E6438405C2B22728B7AB9C088E413074EEDE5E07B4994CD2049A185678BDCC0F26D406900C223E3D3D73966F8935A072AFB238380778EC85DED3E9736DFCCF9C7A402173B8C5C616CD8E7A8EC8B9CC837A6C7CBBA15E62E6C50A7A828FA1C0FC246AA805F1E4CA1B6FC1138CB35211FAD88DA43E92D5C73201AEDD60B54F5E5F3875B6B412B6ADC0980821E0A16DDF2202C2097C974C14BC490612D356682CE5CF702E6CFB49F4F8B6318322DECDC96FEDD79B47848B2B517A6A0A519C46A0F17A81817F1E50D36B94BE0A2D1D018602B2645F3C0EF839BF2874DFB5AB6BD5F8B4EBC52574E2E6FBBFE698523E6F84E8906CEB38CFE07B333AAB0DAC201878E9171BB0EB56A753E4FDF8A4504241484214D578263A3DD058187C4EA24ABDFA707BD1004A9125CEBBC8913F250CCF4EF4AA2D4B89D689CC8E99EA12BE553B8DD975350EAE6D121543C13BD03DBDD57ECA97C16E6461C710CF4052B84C5821BEA529C55227F401E4BA9EB584C7EB26648AF893F20E3C73E038B2C76DF8C2CF35EF70F55C25CE8F59EEED9164FDD8C3D453708E101E456346123448D658C94C0869AB7674CE7EFBD6258F1FEC412F8941F11017D143F2C4CF0FCBC32A3002C132208834E3C52CBACA4AED32A44BD9B515936CC0394704099B511F18BF3F35BCDBDD1A76E49A45DD5DA51ADB54014B3284BA7A5BB9A69A60E35966EE12ACFC9780CE7EA0CDF220AA0E3E7FE30E110704BD91D2E08EEF33345845CC663BDEDE1801D04A682119B6E7184BC8EEDEEDCE8F8D62191BFFDBF4852356845FDBABAA4FF5809935FC86BD21360285E6F6081AEC6B26EAD2CF3B038A05381C7153DF51C7A36B3EC1A54AA44F52B80B855C90167BFA28C21458F4715EEEDDC9E43C5D9C23668C7615484FA19E9643032D91CD34E8BDFC213DF36EEAF780C444E50E01E9AC6804CFAA9F2E0BB0115F7A16179A22D5853C765B6A6DD59F2393DF15051182E6EF1700227477F25B4F74B99D0064AD68682D5120D932D827A4C795468440BBED354761639E22B305346FD74EA67E9D1D4A6F4B9F08ED159A62A976180874940B891E189B2412190C85BD0162382767EED017D37D9AC7579F3136D3D38DDEA358FC0D7DA7E2D81AC17EF82DE6BBE5065B37FA86470F50933A143841658747F47779B3C321E4CE4C22363C3F31F2196B65DF63C56FD9E2EC886CADBEAEF65CC4A8A00EC3792AC5FF6691EA37CF4B7C7EC938D0DE37CE22A11E186C13299EA4803085FF7BA0771D7B376E5866FD45B64B9045936BA753A71E2B10FC7B21579FF84069562AC291E11D35235DD45C87A6F7A016F9C24F7D60C01B7E101155C90F0D3808EB0528FAA080769E5E3F6E29F7C4F97626E202A32C47C7F62732DA5BF0B6F492F4BC56550DA1A9371BBBD7D0B1F026BF41573CD2EAFF09884CD0071BCCD5156F849FE6F8545CF58F14B969520ECDA988E531A36CDB5031EAE88850E1F33C4FE43E61B0482AA0B198EF0194EF383ED964DF673FBFB6096C0844087D47B39D57ECE891059A623AAF24921B9BCCD56818F43462E88FC0415F58370CFD8C9087C23304AB7F39B3E1CEF3E035B93AACACF681610BFEA6F41FBF1A8D0D18E91252E938120EA579EE755F8735AE26E651670D344A7178D0327166931E90B35D1ADC9F583D225B1001E50CE8B0791F2E02FC1A2B5631D9D27A79F2E0DB0073CD029F40A2D73AA495F3F844CF22B1C383D70C43D90530F59922BA8D297A3B38B92C21321AC900999094B6E49F5F8E67E8D3986A8D6B1F7F31507288508615F0EF11143344A74B798845BCAE5C3880E01014A37FD18A1025B51A3EADC8A927A52571A2845CB71AE284A8E4802BE0B8944104E858CFDEB9146634E9A40DAF7E7CF3D3D4319C9E6B0D2B0ED90BE7EA0AA987F8A58A9D5BB6B36FE0A6BA8F1388CFA586376672795FB3F22E743C55F099DE488217F97A9BCDF74289975DDA109F0A7530D03AEC15D57DA947AC0C653EE57887745655A5DDF88B9B40B8C576F244209047F6CEE2A0E714F8DC883232E283F9E217C6E66AC6AAA17285BEA81B15DCC5872435720C2E462F1127C3B41BC1405B80FFD4E3B5C652DC22DCB3BD0389D7E2D2168208CB590EB7302FFB1EFB037CEB53E5D1830507C1572D6110161E921E6FFC49DB4445DAD603FB4E83220DA450CB9EB4D988DB3AD6C9D36FD7D50BB8052DE49AC9B174098FF92189817CFA066D9A4098ED588C45148A54D67F8DB0B0C8A17FF049AA87EE8DFE2F4F8B7284CC0E041758F4419C11005EA764B84575FB61CE2E09EDF2BEFD4BBE5AF2ADFB2437B10717F396F899D3E30431779A2B492DD6B578B9B458A8B11AD1E8DFADD3834DA854FCF95F81C4FA2D8F96D3E4F860BECCB4C2C3D10A6FB7750D09D4DBC28FEF753B16F536C5FAA942D7C30B9BD0BE62AA96DF892EC27B8B16A54947441C30EE4E0180A19E8FA094D3DCCFE591ABA767AA6F7962DE2E2373DD45C3C88A7B2915 \ No newline at end of file +EFE643AF98362E004A0B709FC1212F80BAB72360DC142D08F4004331133594E20668B50E06A5B790C162021C4BA45A5150DDD680E8AA307A3BCAE59F8EDAE2F3F3BE296208BF7BABEAEAEDE0CE8792AFBE8A31AD3BAA5F06C5F6EF7962292BEBEF612077D2C54BF73609030AFA17C3DF7D78119051188791EBB4D79F2FD9488E81061B730D1F58F0054A00880AE7604B8CA1145F2690D2F3EAA18F933258BC3923C608BD8C16DFA2992BD82C9FDE1628C94D31A8B3D3F98CD71665FCB0AE232FEB47447B8ABE33344078508644FD8BDBCC177176D506F77609B94A8AA1558BF53F365DFA4234C7DEA81A08D9A578058B28D2ECED0A902CC0666D001BBA6E441A56A5A430E0F2EFFED4515B1EC7032739BAA30F57868E734BF72594331E00EAD996AA23D50A59A2D4151B4B041E9400D595CAEE695F68298535F14B29271C1C54CC26B74DFB34B311B3620001A4BB13D3773F250616F48D2548F5C24222BB073C50336CB9A87481CE86B59D3377E99B2306459D775DD67DA3EE957C3740B6F777D198B88A27CE86CB795FD742350A7F39F5FE058B3A382D1B8FE121EF236415EFFDEB739B19A6EF285262C1F0C845C9A7B1FADABA3E2E87AE3175A1C610FFDCAA148B32D726D741F7DBB8AC0D0E4D8BCDA02F6F44F1A2171999237D22EBD42977D2C4CF4E1C8069E7E7056B7A9157AEFBE82B4DE2655C6E75C8BB68A9AE2AA2F97E66B30B14F9C79C3D192AD03376859365CDD172CF03EECD2D99AA720EB15EA176F757009C01525768919D35D71FBC6F807BEC94482214C91FC07F2AA22F31C56C750A0F88DE5C67A312BEFF4DD509798ED7E030492A4EC1ADA3052442845FE367871E2D52B4595016F1F8375788BF6C3816DFC8567C5B8106CCB8DED71D8C2E216FD5B2E9E4C74CC1E835239156FE6A580B57B8C279556345B70A0683BD6CF046039D1D5F3D98383B3DDFDB43DC1394ADF3C401FA87D11AA011DB68CA3C6FEC41538D807C94D7CE8EDA12A5C734543227D9522683C155B54ACE43FB50B2A7559E4783F5F5F796504B389210B8A304B7F615522AC2A27EC188F5569B0FCB2AE006B22B1DD0FD8FA24FE43F24B62371F795752D026CC5CBE80133BA8DD60B2585ED3CC823C34568EFB7C669117FE8520B21FE4B66640EB747BAB87AD132DBCCAA1142E82CEAABD687A457E800D2DE96D43047C165B71DEF5FEC951EE192414667E370585895303C3606EBA44F97315F2EB2B3B95A0235060BE892C0B3FD7EAEFFB3A9E04568162B0ACC8BCBE37B201CD3CE326745FFB4668943C2CEF294512381BD15565F799477B0CF15BFE9A60630006705661079639C81CF5A0F6966831DC548D5EA19282A980BCE863F35720BA54104F7D0CBD8D7909C06DFBA397021DC816DC1AE686E5736B88F0B6D690B95E538F0383F8E85E2B1F8B47C569E751F5F597136AB1364DCD069722037D084688E54325B3D4D54C52A8B7286A581B72644F2B549974DDA1FB0439DE1C52427F6B1CA847BB206C761A2F3A3CE20847DB98F74F49FDA0FDBE907F54E60D5081148609905E4B2E09EF4350FA0362AF990D14AE43D2CBA05AC537DAE102498B52B4F0ED2FA0F43CCDA991BD4BA62BF1DBD7F460B39DC225BCEED3203BCCCD276F037C15B39408E07A7A37302F266DD3CBA633CD602828E512B0C5DA5ACC5209048FB92AEE387DF95C3C6F05242DB2227129AA235D569360A31E2BAC02865DEE3960400830C56AEBC052CE37C76D5824A84EAC91081D5ECE718839E4066AF477750308FF8E8B783A035AD99EE7F2EFF0753B9555DC13127AA3298373DCC394164176BB991E9FDDB7602058E53AF995C7CAD3D2EAA1B80FAF63FC240735B0CA6B9BC4ACB3C40A37A3F3CDAC9E3B1381ED9F21F7CE334D87887A0BDECD8F1F231830CB580E5DC301B03018D899514B93787BE604D7B9B5B41E13EF492A8886C50B01791154071DC11252127EED15348AF2AC9A3A1C66CBAFA68E709A2E0B1F32B2DAA5D21E7C4831ACDCBFCDC80F27A29F30731039C403F561B8D99992E3E0F03E6179291D1BF086753B46B24865EB05E5C5AFD7498F949E92413D905EF9564FB09373391AA0475E2BA9AD6CAD5F316CA79096C59660B2941DBCA200F25E344DCF2B71298766892EAB62E3E72B47778FA80334D4093C2E5BE133B9C13E1754C16C3C31EA17AC5B1E9D6A82A84CE7537F2C16870AD6DB7EF435BD027D255F04F074283E759DA2452CBE19E99BADF3A0D6D50E32F36A61B05DA1C8DA466B39282E142E69A00B871C872FAD02993D2468CBDD3AC7FF7283996C8AB9FE864DD783D3529AD101AA7FB8A4178CCE6861C980508A847FBE06051F7852187CA86C14373BBCA898A8E937453884F5DB723646B9152689B4E2E18164B5B793DC6F15B3FD4191B1462E9FC9B19759D534CB09F6F125C4E33CE208C94E43AE4FC4D051CE15EE294B52F5028266CE310FD83F504DA7C0ECE14FC8D1E3E4A89C6A7D638FB4D68354A92546B5C7C4636C0D635A520FAD72C8AA97BC1EBA2895FCBB3FCDEB62246A3D9F386928F97EBDE09A8743B4505492FF3E90ABAB32654959880E4A291F010B2DBF6EBA8E64952DB68AFED3774D563F9925D368F6EE0F94EE3337EF9EF80931AEE947C115718A1148111D2E69CFBB9518FF48368698C089AE59BFD38F9B69F4492B81FFAD9BCE4FAF291E08AFD68C828E30F692E613BEC38EF52F0B4E855DE72DC964B0D49D3414B3B67092AE1E8A862BE39B890F427833BA4ED53D42C1EFDC329CB3062BA3EDFD921240D304BA96D3348DF612CDD24AF7E4A85972CD448AD0A44B00814B9578DA2EC0BE52D0F8305EBA9DCF2486186F0F930E10A2976A942DE435344893548872117EF5215673695784460B9942C9549B6833E7630FD5B8ECDD8C15F0D8EB42664BFD471A00BD2C73B50119ABB0E2B3C9997BE136AD5495D18933B802F5D9CC21A9D02081A622524D1AF44AD3C36FF281F317A16683D35320769632AE5D7BE73D9CE8E2D4A04677D521C0C3BEB2DEBDDEF1500A91DA3BF261B8F3E450691E602BB4EE704E2B16BBABCCD35E06131E894A53C41DFBCC4A46434F616A109DC664FED07E6FE8FA0C6FF0B7A11DE2AB65446633DE634B9588073C905809BAD32DF3676866F23F6CB9DE77A83E8FCA52F012E56A7764FE69A0A84ACBE85F549FBBC4E8DA632FB7E70C2035C772ABD5296AB4C872DE25843CF93EF8A502938480C323822EEADDAEE431DF3711F7043AD4DDBB4704D243B6DFCD7EB629267AA43894A11085F07645EB03A32B61FE29527CD222994A21BE8F108F7672E220EC3BDE02F7FCB0A65591941489CD7727B981A9F46637789CC40EDB0608DAC79947DEAFBE2CE39EAC9A9C0D4895C0FF0E5E7588125C03B9CC6741950FFB209CC38024489C4CB41A52A2551F649D742D781270544ECF2171DF580F62E3085FA1C8AAF44EB05C4BED224BDC08E52B8E699CAE7B5902F7FCB0A65591941489CD7727B981A9D81E01481AA205D1287903F849507EFA77D0B39D1C198B0838604DE94148E3EE9CF098D5AAFF3A4270CF5E4251AE2C324D3C39E648D55ED4C77139E5BEDA260CD6BC67D1048DE879B19DCF56E4363973402A6B18DA8CC744AA90A843767BC4359BBC152EDF0D61406BC69F8B63B934FA08AF925BAD881B25B104B8CA928C154513A1E59DF691CB46F76FB75CB277F3CCBCD85F97B44B97DA63B68DED02A86F7602F7FCB0A65591941489CD7727B981A96252FA784009C02A59C68ECBBEE349E20A96B9E399049DB5F2A4DF8B498EAB0657AD7C2D2C0FFCDD5FF44B93C94DA8277AE570EF93EF0BAE0B417B89A2D5ED8DE9ADDAADF40D3E576D532B4D3267B5FE02F7FCB0A65591941489CD7727B981A98CC207739970C8F2937B4A07FC77ACA384F054A17C2A6BCD0418379393223D743C06564982B14D753CCB7829F134E6F09557B191B95F66E76426762CBEE31889FEF3E74932D04119DA3EC992D336C36502F7FCB0A65591941489CD7727B981A9FA91688618D3A422FACE6FC0018B3DF108AF925BAD881B25B104B8CA928C154520576A4A462BA3A96C90BCCB76ED3A560C8A762C9188D02E07C5E9AB26C6364D02F7FCB0A65591941489CD7727B981A942D4B8309F970B5496F9B3C2864F6E3CD68BC9C6EF87278FB4BE5016989260D002F7FCB0A65591941489CD7727B981A96DE46850C2E498000B9AF897171AB98902F7FCB0A65591941489CD7727B981A9B44EA8F4AC8C08E6A854AAE0FDD94D980D76D0C12A318268690089B241AECA731F51D75BAA8EDBD39EF13D490C2C257C18236FFED80489848B2BEEA20111F24102F7FCB0A65591941489CD7727B981A9FD99A4410D814A6DD9761FFD09D55DAF8129619707A564D8947C1293221E826E4D2BC8C70583C5E744C74D0FB141AAC502F7FCB0A65591941489CD7727B981A9F691A50DF3653EEB0987909E1E355858FD055BCB1D3D5DFBF739AF033715740CEDB7B27D96D6FAB084CC5A592F5D345A02F7FCB0A65591941489CD7727B981A94E237E414F7DDD7D613C1F731458453CC83250B907675C7C8257D07CC03DCAEF1B263AA437DBE5C2B9E8490CD26F3C2FD277625A9B58528E9D31E7921B0B443536574806CFEC4EA6090F072E480AC37381A75E6954C0171573D728FFD4370AA6D642848B685185EC81127A19F4664AF570E43631628876CDBA595103A7FAD5BCD6BC67D1048DE879B19DCF56E43639738A1B8C7453FC349723B7BA45A241F5BADC4C71D1CDF742B73545888CDDE215B4B89F905EEB0BBDA8B4C4FF093A132C42AB967CD8487A1F3DCEB8F530E3BC735894A11085F07645EB03A32B61FE29527C90ABA4377743B0F3FA6B76B5BCCAA102899D4B0352F1DEE1FACB4DC4F2E62952E863996D5DF73058CF3CF153708A99271DB3B6B3467639B93051FA0A53D82355F256C402AFFEF6C3A2C807CE4A3975D5FCB522AA2323C3CEFF53D8F63E92F7169D4932D319D430247BE60465D34A53F27C276B9A5F12A4121791F16F3369FAA0DB08DD4F8E8C27A3EA3CF7C6310EED53DEDB4D0ED7E211FBBBC602054E2701211BD15565F799477B0CF15BFE9A6063006B2C2CF5AB7F8B43666153A8A16EFB52B045BFBD7EAB60B9AB0C518140E3C2AAA6F35613C6D8F3BAFBBABAE48EFBDE5D299BFAE30FD8DB6EA8DBE1117195673A057B467F1A80F6C2CC818324FECFE8ED21B93FF1DC1DBC1D457779EA74E50EF14E6D1A3F30DB024233330AB14B2D061FB02C848E91CC16E5D887850ABCF458B2EA4325ADF366592A8A67410392F4F256D179FFA511FD1C44FAD45611C5D573EC2C4E1AD9D9EF734A89D91D64860DAE91D5161E440E4E7212B69C621E6B24F46499B5511134793476AA5DF9C1E5D5461422692AD80A82565452591AFED50F5513CC67B0C106F8D24F45C46F751F5C9E878A158E4B50B86495F2CF37D80C726BC46D10A41408E34868F8CE0F7852C6A50C5F79DD02A76EE733D478FE4C91B5B0EB1876E1E58C4F8597C421D5BE7FC859D13B849398FDF4A45F9DA57DFB698ABDED94A11085F07645EB03A32B61FE29527C9E4AE53B099CB8513CB5F2E9D5DF254E02F7FCB0A65591941489CD7727B981A9D5DB5F056BF83545055AC63A2E9564661876E1E58C4F8597C421D5BE7FC859D186117924E6E576EB0C4704CBA69E6E3952AC346432C704FE09A657BFBC896CCC02F7FCB0A65591941489CD7727B981A9BC677745E2182B983D9D63DB0C08DB778BEB4DBAA246D99247108D9FA08154ACC14502F8A717D3A09F24E3AB1383CE0E50D6C3C414568EE3FC2902DD64E654FB0A49F2C7A668C8445947C917A3271D633BA68F7C47EFD7A2C079A992264A504D249657EB6706E569D6702616961217CB669E97C474E6BAE9AA66A6114EDDF7CC4CB0ACB61CF24AB11AF0940E03D210A32226CEFE79AA6831C46A0C5B8710326B23D9711328161C20B46E9299A8919063A82D5ACE11E3B94FCEBD37480870FFEE9FE0482B2506DA714EC6D18AED572D3D85A1262D45D1A93B7464C5EB833B6FEBC8653C56085C3B9F12FEE612188FB8BA6559A5749F531BD56EEB34F9F12E6440EF844E42553AE6590C6ACCD31D2F840F56565D69B2FB38A43D3505DDED4B49477F53024252E555EA80ECB52381AB5869D6400DACB4778E1D9F306F542601AE68285EA143512C9F84D51DA724D7D4E44D30BAD85D73FDB52E0225A942788E393A77955519B971B0E52939A55A6EAAA7440BC24EDDEF1A50ED8DEC7BFA5C7C0F63C336423B75D125BE0363318B8B433B3624EA3AA0D06A78282C7F4199D45A41147A778CB2B4DB69A9D7052CEE172F17435BA495D8DA92E29776B11C80D8437B993C0558CECAC7FA1ECC861913757886D20420C3E9EA2EC7B5DC78FD036C61BC9860EB91492799F32AFC13CE89F4701C444B0D53E212F2B9A3AB1789DF8120CC1B00BC89BBE3FB70554914D1029A51A47B896936AC1DA446756B502F2E6B9CF2175F1531600B24D5106E8A29A3EBB7D4D4737EC1FF30AEBCB175570B5F59F8AE55A74259944F350A37E4B6153B4A1C24C7EBB93EE80FD5E24A08801F5BD981A5AB34B3CC236F59EFA5A8F5263A440E52F919D7851B0C81C09DF6232D731774FAA90D0A9379536DE15C568A8D5D304C529CA886FD8841BBCA1DDBA389F1DA9D7B098A5DD1DD34E4B65D550BEEDD00CA0B7D9D55F41768F0130E8A3BC03914F9BCF5FA6BCFAC72EFD777F56F812D30FBC8B576DF5729B028426EF39A7D9595393AB794F3080ABBE44F197BC6AC77542D4FF7E82C5F3DD0936266DA4BFD82E3A0F54B9FDC2C3E73D1DF7796527DACA91366B2299427CE395BD8FE561B3C37C321318C2C4E1AD9D9EF734A89D91D64860DAE91D5161E440E4E7212B69C621E6B24F464A8873832843774C6DDBB76147C010E017E5EE54257287CAB02086170A792205A277E2A18F9C2A7E37A3FE01722BAF063981B1986594FC52D86E778B53FF61206E04487125BE2900FED0C8044B40B69E7AFA1D07194123D66D2225C2B975DE8BF09A6849FAAE7DDE1440139C20801C391F49906ED4504FBDC80CC3BA5FFDB4CFAED5C688BD7697ECD7417FB5791F3608DBFB465606A80AF120377ECB30E8B5CFF31B8CA83B73FCA2716590305314429A79B53AAED5DA007C7B3D96BF99A296CFA05B5B4661DF78B131A4BBA57B64ADDF2BF30FB867541D12B91BE54A25AA27E47FD6EFF63849D92EDEDAB03FD065D25CFA7F97BA269F0909F02DB614BD573414602F7FCB0A65591941489CD7727B981A996FC994205128AD0071D6661C193FB2C438ACB246D7CB7F7DB7484AF0526696A99FBA6AE8C33252E187AB9F7A84ED0AD2428290CA197A9DB90D8256D7A5BC26704A1EA860D075A6D4C35E5F9B05A85F3A6E76812EA801DA832AA17CFEFC0FF6E65C063A09B87553D0A35E4E48D6FC2CF26D916425BD03DE883AE3D2F865D96D294A11085F07645EB03A32B61FE29527CF3C710DD7CD27247A79485B3692E9EABB3998040859B9933D0D4FB173ED7AF272B6B72894FA2A2D9CF38C70E3FFCC129AD1DD14429DCBED050431F990B6688554535F6E7BAFFD64C909C3ED96AED4AAEC3047E2BE71372CF3EF076AD49F5336C438ACB246D7CB7F7DB7484AF0526696ABB37F81AA73A9F1ED2BBF0AD03040EC880AAC9E1C43379236F4B8718EE45149E591F4B965BB384215160546E6E329140CFE0F1C2157DB5372A8E44D501C6D1BE3820973D39B9E9DC47B1AC43440B8F510C614075670898E8A29713A939B6F62937B939A13321B466D47CA080BD7452CB10355739F83EE47FF1D15516C6F0787DBC3BE0D039713A4C4ECF9EA1E29CD611B014E0C208460FAAC757ECC1C16728A99C7E1618BD00EDF037C4938ED14E2057B15F0392A16DF0F9DD894F945453C0EC06E3096811662E5309858D89F3B49FCEC8016021E2DD868BEA5DC0EC892F81041221E8AF1EF3C27D7192D096F7E323235F1B62DC2D3ACC91BCCD742B6527400153E5E0FA106F47CC113F796BA0A499090DDFA5A3401C1551D21A3F24986EFEBF08AF925BAD881B25B104B8CA928C15457DC1EB66C2EB0332B720AA4B5C3FF1DFBF2CD353DC8307DCFB935C1B6C34048604491390F6568FC034D99D4127F14426A20EC2137E4BD7F317F90334A6F50A61DBA243109A2368AF7229B724D84A00C377955519B971B0E52939A55A6EAAA744C1DF3E31FFD083D6BD59DE4919E99B4BC1E42D9FB041D119BD39FA38369BDB6B9EFCE417184364814CD45A8369AD3FDFFDF4D784EF483E1F0FDF81FCAF59C378DBA5BC2639F83B230A23F41CA4B2D52617E0F237EEB29DC76303AB6FA05DA83602C0C17B921537DA0FAAB07872F80BE4905D42FFCDDF9A3E2CC27C20F764229B77955519B971B0E52939A55A6EAAA74490DEFDF99D1833F60B7BD3EB1D7669680BF43F0D598A85C25483108E21CD1002F96BB9CDAF8F5F948BC8CC10803AAD47AAA1524C1DA0811957CD156EF83F30108ED854495BDB982A734BA01D8B963C44E2921F622A9479F347299C9456146979889E9A1E78E66627806EAFED304CD9B412A505F9DC3014EFA2E85AB377FE8C293F60BD0714165D33E435E4A3D9AC66DC6E7ED54A766716CF5BDE0DA614A4EAEBD68B804CED4F1F64D870F1B34EFC5060D4C7931B4DB876A2EDBFAE7C5454D47C277E2A18F9C2A7E37A3FE01722BAF0633F40ACA29BAE89F1BF78C883C244A806B77DF4E34506962C94CAE54A7C6CD6BB7C4831ACDCBFCDC80F27A29F30731039C403F561B8D99992E3E0F03E6179291DC7FBDF6F37D58726E208D9258B06BE2D142E451DB92BD6ADC3014A4E3B900B59F18016A1C132ACE7C9FCC05E42D8689A9E20653A9918A036B9A74E7723CA0A63C37E622674864DA026C1A6BE275B801FE9F3D83F051AED7DEF71BCD798615FCCD7FB1C9A337CBEFBBF750D586B8CC22C67A298CE2E3828ED8D3A97F00235D8CBD1FAAF0088EF29C3C878DCA87969DFAB3F514A8E265D39F0615AB9ED3508510FF217B431737C2CE217656571914DE975F4D5540CB8690E5493DFA60D7B6A97ACE6AC4A1BB61FFCD44BFCFE093930C861B5D28F4D14D1D640FF2B10F8939E262DDD6D403F9CFF4E6A6CBB9DBBD307BC03FA76A6902C8D56BC553A5C12CE1F0B4485F91488AA86B269EB03BBD16664786F24BDC7B8A959C2297FE059D713FB6971BD325C19A4B1E85D46E043778F4306814597174436C5EB2D8BE05DB17276814FFE495C0FF57DD462853AD61961CB449963ACF54F8FD896CB2D2090DA246D71C977955519B971B0E52939A55A6EAAA744E8C4F3B23A7F4D179B5FB1B52404501707690BF8240081EED3019CD109FA3B5D54EC8C6F9D15EAFD84BF72C93366F3BB387052BA9101D9C1444197BBFE1688195AD746666A27258CBCA7746F6B7B2D4DB09136F5F2ED5EA9D48B6355B5982B6594A11085F07645EB03A32B61FE29527C30734FA275F5601A6E7964A5F8EA2A3EAB3B078D56021BA54E0EF60B00EE3C556AC666BB20DE57E1F899389CD636F418A110F832C18A27929D4FB532C13EB740928EEEB0487263A6FDD4EA63C9959F81FE3C7C1F7DE0DB338A706B010E7C857577955519B971B0E52939A55A6EAAA744EE26DFB48EDF8E5D0999EA64FB807E02EAA7A897CF05A01D81537459BD2CC419AFBF67032353567AF1875E195FF996BFE53E85D5508F70E9F79158397D9B70E48604C35CB285A24758A5BAB173C3A7A31A8489015AF8E740BD6F152C01EB250023FE5823AF84C65744942B71DFD42F657D08E877659143AD61DFF1F4268FA374A82509151427E743557B9F8D56262D29A18AFE294133F60F336D1949320D05E871A4E6C86A0F08CC13E0C9CEA8E2D5EFF6C7618382BA3BF9271D53DDF8837E918F5C0EAF12D293C6BDA9502268502587C10FB1B064C0040F69F42D91EF5971B067452FA91D314E709EA7DCEDBC0DEA29B5CF12C3E84E1D09FF855AB1328BCDE724B03D50287A9B52656D9B5FC4368D8BD68E8A2632EF2B2B7B3ADAB00A912AEDB4789FF4EBE69F0B3800A6D92DD6EBFE6802B73AB166A70F811FCDB939D97498FA428487CDFAD5F3CC1E180FAC5CC7A7D5CC5F44031C99030AB0311F3AD20EC6416C64F13E8D6AB3C657D59D1ACAC9FF667B575AAEE7465520E45DDBE275A2518157C0C655659FA6E529A1E117A128651C0C427C4F432291D628116C2A709AFD68C4B0319F429AFF99D83AC7AF81713D1B1C463B83CFD3EB16B3DE367AFADA404628ECF5DC2728BEA13FD86D9803DCDABC200175214D3942F20F43BC9FA8A678EE547F599C634E04C0F34653FF1B8856DB98752CB4410A386BFE3AA01107400FA934FB393A8DF3F78710467D53BA62FFEF8DCF365D54715FD7A1B6DB23E854614F49FDA0FDBE907F54E60D5081148609905E4B2E09EF4350FA0362AF990D14AE94D98580354019DFF27240CF4D35355B63D4F6BA33AA91AEE2677BB4A38AC9E932BC5511B334A4DF6E88ECD47434E7201747BB3C09BE4440BC47642B08F86FB7AA0A8A319136321D46793984483F887368C4B0319F429AFF99D83AC7AF81713D77955519B971B0E52939A55A6EAAA7442CAD5D698776A2EE9B20F1F34873C3FD20932811169CECC7F3C85495CBFDF40D02F7FCB0A65591941489CD7727B981A9015AEFCDAB2E93E71574C7015E643F7A07690BF8240081EED3019CD109FA3B5D5965E6AD14F81B950EC08A7F1B65EDB0557EE36F7A520B06F90CED8A61C9DC4177955519B971B0E52939A55A6EAAA744E937588583F30F0A4B989077D740C528114EFBF4AC8FA3D928A360474DDE632671AB84C90C2DDAFA48EE69ECEBA987BDFFC351C2199CEF4DCBAF7DEFFBA22D24C9CF29C6B4E460D470C32339359D40C9C40A37A3F3CDAC9E3B1381ED9F21F7CE78A84A1027654278EA3A8217EAC2330E00D79AE2D9FD36B483C27C2D98FC12534CC52CBE0843C0586873BD80538EF3D8531A89906BDB07A5112B4E290673749C653C077FD85E70F718F40FF91F15FF9BD7BD30E051C723EF617B6A8F2BDA143C49D468E58DFCD265E9310DE1E7F377F7E5B35557960DDDD73ACD1940BB1A65C4B32FA9890263339E88512EE5C2900DD468D6934A90C05A6CA7844D7423E7B4A7AD304BDD5D8CF00EC5040030DFDB8B286B5C249CBB7E342FD45EFF6621F5121D1DCA6DE016CA4A124C09CCB8A486233594A11085F07645EB03A32B61FE29527C6F18957EC6A3EA46135A12E91C751CC51747BB3C09BE4440BC47642B08F86FB7DEEFDDB809EE0DEF230B0E238417FBE8AED58521F5C44D171F0B299E2FEB6A9A02F7FCB0A65591941489CD7727B981A9F7BC328FB0A0FDDCE8CC8A4202E7EF8A94A11085F07645EB03A32B61FE29527CA6FE94BFBABB8A21EFE55FBE761DFA03BED52DCBFB01C68F417D70B65CC31F9FD9330203DBC29F316EF1A4E35E3C2A8092D86C2D178B8361A92276FCC8197A2D0520E2E2800E7E5D257DC0CC6AF39F0CFB9B5EC47A19A6DC2D95C76BFE20B9B03202707DC779D7DD4049528135B6741D5C00FB2F706B64A84E96B0EA4D9104F7098D29E303513CD55FAA868D43DD04377AFD502374153ED7883F4B1AE7DCDDB7E216703A22A30EB12DCE6FDEB63729F54C7DAFFB652D2E48D8F041030961E81224BDC7B8A959C2297FE059D713FB6971BD325C19A4B1E85D46E043778F4306814597174436C5EB2D8BE05DB17276814FFE495C0FF57DD462853AD61961CB4499CE467EFEECA3EC033AB96B0556BD307A3981E5E7597FE270C982E93E5C6BF3E008AF925BAD881B25B104B8CA928C15458AC0BC7469FFA16B384556F5D45160F677955519B971B0E52939A55A6EAAA744A01BF36EC12D71E1FDC25820C53A697594A11085F07645EB03A32B61FE29527C394D636C8236ABCA0D24733C92C703662ACEA44489EC6158F99B3E88B960E90EC6886467C203FC948FD48220398229C116FAD82E69BC1C7E0FCBF6565B448F024CB0ACB61CF24AB11AF0940E03D210A3B2B3430C919F1792C033470000B76C6E09AE97095E2F539FF174CFC77517F4AC4B727628A1D2113E7004D02856647A9D97856A7AB3B798E2DD8DF93AEB69F3909D34983E64B67603EA58F56CF7895F2DDAA9C0EEABB040412ECD8AD9D686EDB186EEF992F24AF9F4E0BBB48EC1260AFD86F05F48783456D28C6FC72303BA1291D098D7A475CD49C0FFD839EEAFD92F93330595D7DA040B8EFFDD27E70743598FC510C3FF75F4C7665F153EBA393626E4F11E6C0EE7D481CEBF7016039B6F37B9918B9315EA8A5FBD5D1DD0D380C0AC8CB6FB5BB68239F07D9D9296B0F57BB5954BFE3846C46CFDCA0154CF9E59F7483395B1B20F79C224D7F409106AB4EC78FC0D883A73C619CDE930F4B5B45EB20A0F29C6BF53FFF16010EE0E9917DE15B3B0624C553EBEB82D87F460F9A573ABB4E5C5E53D66C6AC5A7742BB34AAED558AAF68ECFD0E7B313F69D92B9455EB7FB8934B73B06B9F56B57A9E6249FA3421430E8B7DE0E3B6EA0B38ED84E6433C624474F88D088770CB3A270F93738A8660EED28F7B26C33DEA14269F6E5CA79333107EC336423B75D125BE0363318B8B433B362CD3AE62F42CA809C23F529BF43C6ED07B0D8196B4B9B66975305A3226C5CC4C2E7F4145F6E5B20A590FE2B9D42629A95E06ADCD84272841F18303F474B054B8FE3399251F7DA19FEEF2E9BF5F533219B91BAF2656C4B0FA17659B11E114D6C2747533BBDEE382C00F881EF03700D38E0C9B564B5025548B6055FE60C035C85594A11085F07645EB03A32B61FE29527CFDD06961DD0F313C4B64A4E7B616F12BABD90B3851942445DF2E8DDA4D2C9E92A526F33DE27808D086F797AAE5F8B26BCE02CE050555B48621F7C453FABD0BA1C403F561B8D99992E3E0F03E6179291D14762DCEDBAF5B00049803F5308712777F16F61C25F85BB79CD4C6AF9B657119E970DE64C6AD6C7FE7F1B14745EA2ED1BF056F333584FD98D883F0F68CE0B397F304A4C33BDE9E21F1C8D21E1E370BE177955519B971B0E52939A55A6EAAA744F7B03B6636B85B4C227C2AD90F28455502F7FCB0A65591941489CD7727B981A9B32B69113650BAE4EC2753C54C01614E7EC3B1C968DF606368608EEE9AFAA0F7DB06625E64BE0609C490726F2DA291F7F49906ED4504FBDC80CC3BA5FFDB4CFAAE7C06E18856A48CE62AA0F5CCE999513C0DA2E91755A089DD189AA4D61DF8C72D68BC823CFD9B5106F1A2CBE6E2704A646F403C5D4FBD31B24984E1D357BD9E3BFB8A919F4B4B12FBDF7CBCC91263480AEE3934D757EE43A3C86BE482A49BD8127573B57BDCA4EEBFB759C3A40A40C5411409CEC072F643B804C0D4C339D5E6BCF421CBAC85AB9B81FA802C91E902F30AFE8B736F3931EC11C6DB30D926CBD18BB012AE427493EF4A0754FBE891CA99AF30A8E12F28BEC0E61CA3B9B99D59228040B5EE1C9DE64D1B1B66239D6C1C6BB4335340D681B35D05534EA15B482CB35949514358ADA32009C3AE92A6D20F5BFE79A3147026318E10225D2C9D356B85BB1E200FBFE9DD30A3F160FD25D91FCC0513986060CC0B785A55D12C36EAFCE8DC0EED4FB88F9FA4972CED5D79C090ECE407CECC087F9D62B778F9532E63298BCC37478A4264CF16FAFF6ED661D3AFE602F7FCB0A65591941489CD7727B981A9EA4E7422AD5D258E4C40411C9B709257CEAA4FAB862DBE2677C4B06D7BA161C58BD9E8A3A9C2D961CAE4E1E266A46749F74A26D9AF2CF144E949D0F09AB61DB802F7FCB0A65591941489CD7727B981A9320A13E6C31A435F446B87754955978C7BA5103570240FA4242D8C1E77BA99D894A11085F07645EB03A32B61FE29527C872D4BF6D24FAEF3449A7DE7E999DB59F4A348A1255C7BFDFC26F49C68713F4294A11085F07645EB03A32B61FE29527CFB27FFA7003BE08F96F6A40B7623AF1F86C216F21CBE5FCF9C91A334A1EA9EB0AF47E40A0B417A1ACBD0B1D1D1A2DF91A06594D8423336350E6A8C76D029C4D3AF47E40A0B417A1ACBD0B1D1D1A2DF91516D57FA491921516CBAEF605BD08090E863996D5DF73058CF3CF153708A9927C40A37A3F3CDAC9E3B1381ED9F21F7CEFCF4B5966E8001E796CFC9A898FD1932B3E29D662540AFB704900F2AE81CEA704684C9388A68F182A0CC0002248D52CB11C37FC03395050550AA37A2616DEDAE2E0042386789C8FDA45D48597ED23A1F949B25747B0DEC43F605DA11796480DFE4613D47BE536FCB57BF2A5F07FC8A3A142E451DB92BD6ADC3014A4E3B900B599FC175084E735F135F18467DB19BF397A1719B8089078CA3F247E87EE23C9D008C713CE7B0AEF1999AEC58AAD334F1188379653CEC1964D8FED2A907B196E7C194A11085F07645EB03A32B61FE29527CCB0E38140D1B74DF0D9B332E79F7ACB45996C1C6E206A2258FD169E24A8E8CE19A0D233653C9F5D962F49A415A9C690D40BED0CCA9BBD5AA2883E8796AD932E90EBB81CBFB39BB02E08AC24FD9B4AEB85BE67491C504D91026B2A67F770DC8C594A11085F07645EB03A32B61FE29527C8045FFBF27F9C4F3C37C2F348499D20894A11085F07645EB03A32B61FE29527CDD3A9E07D5A46CFFE45ACA668FDA27C194A11085F07645EB03A32B61FE29527C7C084B295332A48406892597430A101394A11085F07645EB03A32B61FE29527CCF24B3BCEBCA5BF6D6D7AC1F37E97BA373ED797A94949BD144F20A913A7A2C9D0562303813636F18280E99B8632F399EFE891A8B10F21BD2F1D8EBF665325D49A41D286D4C5CFB6B3D88131BB7AFEEA5CF4116DD5C3914608335792CDB3B7169E3841454D79C59B9A5606B37098E979C11BA3D25210285B09539743E8FC6DA8900AC8FF5CF0B5D838C532FB804211FCA5BC7C93E0803C0E49FCB0312A883C3E069E8169AA98303884CB6C3FDC7D9153157A1D4D1BFF387B57CD1555F7A0C72F4FB7F0DB5EAA8FF791F635863FDB7BD671FFCED925272D4D35F284F37E4A4E3357FD34A01B379DDAF209E2D4CF5ED3D4DBB6FEC3BA80D0EB4807FEC1F1BA3CB5CEC2B4A776732A7E36F8DDA12ADD28473CBE6E7539A0155CF70E7CDA0B0F6BB08F99E0161BC8B1566B319400B0E119AAA57138B26B0CFBBB0FED8FDF26D8FC3BC7A899B60F371FF4154192854D9CAC31AEE0793C8498D64BC8CB06FA021E0FB865D323ABA26DB5A6A742871FC1EEF443D7179B325352E5AC4788481B2A6E1FBBEC02370B22E4E649D6542AAE25C270F1A74C078502270C88F0D19309B97705230969609CF7B711DDD4D515CDB8153ADD6A418AE8D1223559D56D4957117FBEBA67324DB52A67C4B57B00D935C6CAE79705FA8317592E1405AE491840338BCC62B02F7FCB0A65591941489CD7727B981A9E01561CA2391BF8E34F2646DD263ADEBA674091AEDD376CA674B457A8AACA61ACAA188178680E5194C9B7FA52E4262DCF0FEE50EBC63B2C8A1B12F5C5359F322AD566BBA51B253A8170E26EE97D684E9CFC97514A385EB93DD989E1AA1E0C4621F4181E85935E26DA85202530B6AF3BC3DE4221930CFF59813C889BC239EF77FC84999CC5B2E62311CC4415DC7510AEAB26209B994B55BD6EB89CF8ECB87A2CDD5C42E0344629B7DF0C599819307E1B3240E86B6BAFB44B8868894B344E958E9257C246B5E07D9B06AEF3ED62562F2753F22692F1ADC582DE6D5ADB0C291164F9A95ADC280FB835898236EEEF254054D5D5ADBC0B8228CE8508F06311632A776478F2B7044DE266AF9CF8B6EE777E95A721D775CDC1D0007E187C6562F9EABC936AA03540422B3CFD2E458F1FA0D170F82A637ACC809922C9837C6D298995133BB326F17755F47BF7C59D2AEA5094FADBB07F2F9B6D409A0EE17C406025BEE4186287EBA1A5B4DB347B1C5B5FB1CE188396CEE1F774EC4895CA9F4FD7B1CF269394F8583064EF15DFDF443170DD0C2C9A1BD5BFFD82A5A8AA9D64B1B742BEA8E845D805A82AB369D6E253A71D5EE1DE10B0EFD5412C04749E511EAB080F83A67C1A3C8EE9C89DEE11541F6A2285E936EDA2E6CF8804DC1E9407D15D364F4829DD41EA71D19BF2FE9390EFB100A67E600E361370F87415AFA7C5D8DD3C252B9F20AC3E4149AE9B6AF9160BA4B9E04E83AC81F9AE1F1341511C0474D04222873F3498EDF5A54BC69F035E618E1C6AEB5ACEA76431B117825202D954A60872B3F661BFEC2F78526260010FC41C36CB381B6827831DDFD9D0EABC84769EAE8ADCCAD92A08ADFA74FF1DAE16C03079378955A6E39022E6A13EDCB0E5CEBCE8E52672120B45282B3DD8D2469BAEBD5A46F9541AD2B1C9CF542C066E3824364E48CFC47B202943631FAB79BA379EF7DA3B2936FE27BB19467A0C5AF90420095E6E18129458C30C879D1794D3C00EC17BDD26C6BCEE23BD88F3C100BBF1AC6874F60C1CFFA3D48031584A89FC3FFF09D5D63F675496A96604B2DD6DEA46B40DAAD887E19DB42EF62F9283AFDDBC48FDA99CAE5F1D41112096DABD90B3851942445DF2E8DDA4D2C9E9221CDAC11EA02A1A133A2650541AF724F0BB0BD2A4D427CAA857DFE82645BEF1DF5B935B1B57F9A686A54EFFA95E28B8510D009B57782E0845E10B99B9FD7A46545D0B8156FB7C5464014FF41F49E681C9F86E480AED2C88D667EE8225B03A390D47BDE505A99114EBCA79D59D56BBC4755109ABBD01EACE19186F857C2A8C03C51BB34A980C4CE79FD9919D14D74ED9A5107BEABB42780621CEF4385D09CE105354D1A035F0C6B75E31D2E78767BF848FC5242FF30EFC7B6DA05BDA2D1392FBE923EDB3A4E4B055A7171D96336A130D303EBBDC710D0BD8F42D6AF10CAEC2E1A86A0462DBFC732ECB85CB44309000A2732B7AAF0390EF6E65A2337ACBFFCA69B39021196DED7AFA1B03DD7E83A023623424E5A71CEFEA4AE86A0B1DB314669FA3B2634629F01C7A15A03108084C2BFF335E7ACFA0681219AD8B7DFDDE63C80B5B3D9FE83B6B03F63AD9AC4C732617D0EBF4B362F945E82CAC9D399448C28C7A69727E3569224741DF56D26DF00DB17ADEC07EB2BDECA872E90178838AFA552200571B958FB17D57DBBA721EE1830659D8F66CC4AF8DA1AAD23DDA7AD1461F01D3B03FB9698A28DF0A5D09144331ADD9C5D909B8F9A511A2FA3837ADEC42C25D7052F1F3BF0DC47768BA87634A36CFB7567642A5BDE90D39928FD610EF6CF7553AD365F0B2612F8BED1E2261676B6863514097D309EB6734BB1ED45F6EC261AC5D0EB0BB31DA966BF39CA58795E36C0B328B4F44E7206F2A604A80D25F49AA6D24A67622F593A01172C5F639C877339E46A800354A14BA1FB1038E9A637D5502AAED88DB00BEBC4D33221C0F938317209B4B413BED072290489D09189C63B7F92AF3D3EF93D122A6B0409C5CD5206B2F567A9037F46003DBB10EE21B5B34D3DDCA937E4D838EEFE3D5FADDBE575668181048B2D366E13FFB8C48FE47E8A3505CA6F27F2C00ED92A89AC74E3725B325F3A0BD2289F3FA57D5213F8B549D94381DFE4668FA7BFA882F7599A2AF6A6FDD962A2F53BF7D4C5C43456DFFE9A8CF9BBEB313976E9650F5C72EC46AD81A9C3146621A52A4BBEB9A0F4FD6D2D0F5665A6101210853E25E188C09D80AFD1C2C7FAB268270AB2AAF0A7D6E40918F2EFE5D2BAA18AFE294133F60F336D1949320D05E8B4D1094CC8D43D5778B5933266CC43FA41DB18D3E60057E7D7424433FEF0A7A7125016B1CB289CFE641BEC566385259807690BF8240081EED3019CD109FA3B5D3F0C0A2DFF28F9BD8779399B5B97C6F37140E0CD88E56D1D4F4E365A874281FC0EF4DB1A154DF8766F16633F5D501580A3A69161A027F983BF9F9EA42E0FB2ED84B1CD1A09C849E268A80A47F8E1BF5935F0407D997E54F731EFE2EA0FA997B8D0B69324BE2F93BF55ABDC488E0088F89606347464978F68FE62B8845C15DAEECCF58405389877F0B6EE840606E557907CA8E60F1CCA3F46B0EA6D0995BEB51BA57B03D9DD0626A49F8AB1288E2079FA0F0C5131D9756C8C9356DB1CC675D37A7DF1CA3831BAF1FD5945762180BA52A910D8D9AC89746F816BC4D3B697B3159A68E1A0928D6C2C4F985C5CAA1C861BE19A0D233653C9F5D962F49A415A9C690DF08C4842A2108CBDAD961C40B8D0FC811FCB0555053A333E905FA1D27C4796BAB6194E2E5C85C1864BD0A70F9C3300F6207678318CCBAD5FD55DF927CADEFD370EF4DB1A154DF8766F16633F5D5015807FC63E529DF0EB8B8D5E09FAFA2FA3A702F7FCB0A65591941489CD7727B981A91321FB1F424BA934386F6852CF705B1621F703C34EF202029A329A2679E560D383F5EC836F9C30A78AAD5F45665C5C311286653F8682979E00DF3D8DE9D3E761D9330203DBC29F316EF1A4E35E3C2A805B3F843A3A3BD25A5A680047E0B4655FE73147B3121F93F6B60407892D4A99ADC3265FD6CE6D4086DB260EF1DDC031B422B365698E0D264DA76B977E0BD0CCFBAA80F426229C53A7ACCAF834674CC0D5B833AE1A8B6AB8C6270B30302A1D1A0B9BEA926C9D58AF4171E5C62725F6E9F83CBED7FAA7B47A2C014F3CDF9FB3E5C12E0042386789C8FDA45D48597ED23A1FAE4401EE188D83D32D1C4935464193D47161B838FC9A901A04C481D933C8DD6F8FDBDB54A8834467532741C75D264860EA5F7456395233A1E5A0EE93290503B628E140462C699C68F0ED0C1DDC94D9DA2AA2D6F8403BC16F03B45DB889E3C57C8801D6153218F9E9CFF4A46EB3C470BE0A6734249275DFB3D6B47813548441B1D2D04B20C9F9E4D7932C14F4CDE8EF53E76D193EB0CF134E9C843A826CE361F9A49E712DBC4F36F9F78A9CB250B9032D35822ECDE1217AF26822304C44B12062216F750E18AA6609A132E96AF44A776546E431F3181563557C7E9632CF1031D33EF833D7E51A8A8A4A2CFFF32192C52DFB1C2ACA30F7EC5B0DB51B03840C2BB966C7E9BB5A824F789533E6CB9E16425DDA580F77B747F9DB25DCBBBCCEC2A6D90EF4DB1A154DF8766F16633F5D5015806BE6BEB287D5297B1BBCE463946A195CDCE76CDCFB0FFEA5B2A3CC889AE54AD3D8B9254327E5FDA6CC43562898F861D21298EC7EB7DCF1523348B82B055A055F1DD55B0497AE6860F07BF2DB7AB5B99FC336423B75D125BE0363318B8B433B36EC0D287E805DEF355F30193060DCB9FF1BD15565F799477B0CF15BFE9A6063000314EC0EDE977039CB2BBA790C0A9B53876C8D4EE140DFB2C12EAE54A2725225ABF968223AF4489DF2AD94635A55E1DC5267D80BE8691AC7933976DB26F72FF8E21F795CC085397040077A6E7EC97013E5564DFCAD048CD678661EF08AE6AE028E4E7AB7CDBFEFE5D8CA97C5B1FD9DEA7C7205911DEA120C5328E5610A0F62BC8FF03633D8E1B38CAF05E20ADB40AB3A187DF321EB68556D0B9AC60DA842590DE913B94B4A69D9772F58FEFD86AA4B2265C37F653DCC935E683D30C31A78F8DC89E88B2FB93C364CBE0B5E9F76B3C9AD1DB3B6B3467639B93051FA0A53D82355FD6EFF63849D92EDEDAB03FD065D25CF64DF8B2439EF36A03927E54C2FC545D3C83329BD658FB39B2B6CCAD2DEB0E458CED581CF1AAD07602092B32BAE60F3713652E6CB0F6E8D1E2172FF7A307CCE7FCA3F0B3018EB970028D83FACFB0949A53FED00FE3ADBC6AC58A7E86985A6543BCF108582463FC12784CDAF58FBEC76F03A2752C13EB82742AF0C6F6E197AC8D3C336423B75D125BE0363318B8B433B36D4F220B8345D7464A5268456D3C3CF050DA49437333BA4E66CD76147F46336DA4657191A302FCD04546D262F66D200A1C97C86150C6CB051D744A2D2D7F7EC8EE6148E895D5CD2FFC21E208F6E676C452C8CA1A41B063246A595D6D82F3B68D0AAE2B8B16EED703FC881B71C1EE06A83385EECBA6DF391F892B2368EC2FB59CA6601AD5F821DB53BC08EEA8F1DBEE9B233B57EF4941FF46A41299D8AEE52400043E4AA1DBCB1650529262DB4796BE48F9014EE0798321439BC552B42559B7588CC28E8E255669F33F375D26FF98806654632C4A661D3EA3866E00F9C23CD51E31729476347748879FDD8B7704F80420FFCA749AD9DD6BB9F80A0D9AA953E9B03F17E479D7E92EBB7DB60C93D4B1A26FDC8E0812694CE0A8A5C8C2305E6D5519865265E776528B944CBA67F283858154981FB5AEE9F8057581065C8A8F75886964C3BB5311ED21E173B3EC11FBD0171946C86A77C290F55D27D6B23915837BEE67E2D4498715CB4E4BC4AB2CAA43A4555A874C6B60800E4972901ADA61226C287505E87D216DB31FA6524B4A8629F95D88C35DEEC70E63BAEF917B7119BCDA924D076BD339ACF3054217DCB011973D0A664D8A6C38B654A4CF37336DAF2A9E45DD81E01481AA205D1287903F849507EFA63ACF54F8FD896CB2D2090DA246D71C9C3EBA3A06FD856BCFFD6F4B75BF7BCAA3FED00FE3ADBC6AC58A7E86985A6543BEF6CDB8AE75E457610359445BD83EC95D26A76C43B5AA04C7F6BD7350BD14CECCC28E8E255669F33F375D26FF98806654632C4A661D3EA3866E00F9C23CD51E3BD532BC8CE0E52794E132AD97A65FE95D502C649953A58FD8B9AAE7C233FCAF092A35E278F83BFC662EE5CA1D0EF258B2072D38E7CD182B208CD0DB1CD3F5EF6467B4C621C54C095AE7840AD4DC084981D5057A473C1A1326389CD7883761192999CB5779DEED8C9BBE8A0BA9CF2644B83735091CBD6B99CB62F80A175F767AF17DBCAFDCA8026E0ABB380AE9938E8DA9C0DC802B4168A2F5F24483830D3B31203677B4A035E7B0D828EC76A68DEED70974142781EB8709CABDBA967E0D9750AF5423C4F219BA82DA61169F0937215FB9EBB97C192032072A01292E7A88B0F8368C4B0319F429AFF99D83AC7AF81713DAA43E7EA6823A13E3C445080644B8D4D5A4FFC05CF3C7CD93273C287BC3C7B61D8ADB7FD648D75B5179516DF792006A70F0E510C2E8CE00A5EA084F3CDC5ADC97ACB3316730C31658425314471FB57BF852279589A9CA273CE2FD5C56136B11C7B7DF38E5B0FE09BAAD132D105B395DE1D94076D707EB8A970BC3F291C5CA784B2CA51BA8C7FD32BB0A26A0AD45C811E9BAA30F57868E734BF72594331E00EAD746AC1450601CE95C29C5293279C89C13999996E7298372D17F2DC5697F2242F0F6A26AD61F67F60678151671562797CC30076D7C3433F6FEBEE789B0DF739EA61A1A8D884B0A4CD64D87E2A9062B99357F58759716C07692DE093D90338E1D9E61923917DDE422B536AFA6580D87D2F745265C6BA8099C5DB51C8BEAA5E2CF5D974143D5EFBE4EBA42A941BAC54749095033E3D31B2EF8E2B41DA36A7D61325B50D39E7CE91F0CA3B6F422A6B813DA4667B79376E820BE711A34F6DA54A6B921B1A30B3FF22CFB32A83CA5EE0F3F94FA0D03E58ABE36BEC05260CC4E5A77C83015BB04ABB1A7641A9B0298B791F7E28371BAE546B6162975D938737DC5A3DC6316204F7F51D3ABEA111E62462A887FB4B746100A707D9D5C6E300E903CDB227F28CA6FF40ED0A658A5020E1D89052A4C2C9F2382035FAD667FFB5C2BFBA6BC9A2A11DFAAD5C2679DF3A230E6C90879D11FA136A38228ADCE2D7A56A1C194175E6D7EBC310DE31DE4C327E5E7B2D1F57F7C3E9D04838D25B04A68862A94EA076B844A6635835B435A2AB68751714D263A92C673F7CAD73C02F5DBB040037885751E81127218AB7A69E5403A1060B5A6AE6CF7E04F82B9DFB5A093D13F827BD8E4F1B758E9764FA921BEBBAAF9F39B7C3DB08DD4F8E8C27A3EA3CF7C6310EED53D005BA3B8E8AF82D74964A6D88207F8FF654F1162A9CF206F6139F60369397DC2FD18B9EC99A512B49474805B842B5071C3F962DDD801E9291E553A93D9AD0AEA6EDE03806D980BF308113A210C91F935579296900B5B1AA95530153B691FEC9F3863CFCDA88F976DD111F77992A3DA12CD01F6814B91162205D8EBA4535D2DEBD2183983A0B27F59604082ABE597041C1F2E858735FF382BD3D0E239450DECA635535F5356BDBC4D76644F307485352089DA33B82779791B6EED268B034E76DBE5E850595FFE0A346DAACA1A3BEBFC75BE1564A6A9E8094D7605E8021EA20649601B98361E793C62BD2ACC6A5D6E3C52025CB35EBB425AD3B001CD6FAA542D8432BAE0148D0A9EF8C4ACEEB5D2A640A4C5EDD01695EEA92D40B0664FC1EA05D64BFA378884E1F7EA43E7B3BBFC52F480E7231ED64EBC994F0CBC2C937B5D6FD74311F7ABF7A81ED042276B525E5B2C47D3CA2D856E61DB0BD364A66D10F5C9152EA6EAE3C2AACA0DE40EAE1C30722A06A38884EECF9CE95B4C7721E3572C47F782A5B5C75A5D4D374395C8F2DBA59E00BD2289F3FA57D5213F8B549D94381DF64633A491780A9A50C950A59ADF990D844E42ED80D05BA26A38D2623190984C44014BE89A7F8F6A88EB527B24FBCFE607BBCCE735E2E0D8047969A3BFF25128B6599DCD6FCFB219082DAB7D57CD16A6D584D3F3ACBF333C892AF9E69A3543319F0F0983D2DF69F2DC06BCD24B4BF0A4604D6AC865698491C06DD6E28B99077823D5DCC28EA18224587995A116C5DB8D6580C5C528C9EB4A6A944EDEBE8F5029EB590D4B67C39027EF147E955AD9C2641355DD3F1BF90F69CBED42AA6C9653025B874AB32040BFEDAA511202D4F8B46D307A3B7C6AAED4D9BCD89BB35C1DD0C14ADF9E56E84788B6D7EF98CC9A9C82701B0DBDFAD92CFC867FED418B370605C6102F7FCB0A65591941489CD7727B981A9C84BAA75457502D397CB71241EE3EB49C20B9857352667E6004DE64BC9D6806002F7FCB0A65591941489CD7727B981A906B08985CA1CD5646D2208AA5163C8A557D1764112717D26803D541217330C5B0414561040BBF6A5E7BF077E850B55BC4AAA4E20568474253187F30D9C1C099C850DA4F2F41D90D6547DB2A53DD16CFCEB42664BFD471A00BD2C73B50119ABB0B1941DC74E2EE9DAE7729CA4DFD56C1D67CDCD14247C1670ACA4DDF3C9B6B9665FBCF95EFF25D69070F897009D80D4B502F7FCB0A65591941489CD7727B981A9596EA4721A298975C3D4371EB5B8E7BD0A8A92640422425196615C8C53C443CE8AA5E34FBD6ED314376A2743932A9D87544ECABEF060E9ECAEB58EB06C6584429A0D233653C9F5D962F49A415A9C690DF08C4842A2108CBDAD961C40B8D0FC81AA3FD14D8D828B03CA96893A3D19671F3025F8417168F7088CE70B7B5A52069330A1E0937752A7D2A7E4B0945313A0E7FA6A2D3EC998E68662AC037FBFFAF0DDCED581CF1AAD07602092B32BAE60F371E21E4ACBDE363EF2FE9C194EC71E48C95D8DC03AA012CBD583BA44BF0E5444F172DD47E5A6ADA03B40D884D0D54671681B263AA437DBE5C2B9E8490CD26F3C2FEF46FEC3F626AA32831BECF9BC3F16568BFA798E95052DCA90A804FE79CAA02895453CB17AE8433576C1A9AF05F83FAC25FB0E941CD8F09AE927F37D70B0F28D02F7FCB0A65591941489CD7727B981A9C01CB65ECF4338777517367B9D76E81177955519B971B0E52939A55A6EAAA7446954D35688780F09BA71914D9E70D18EDD7B2E149B83028A6CB318DEB18946BDCBFAC860D5148FB84B57CEBE9DEEE4EE7C4831ACDCBFCDC80F27A29F30731039C403F561B8D99992E3E0F03E6179291D834BC4D7A85C55690850636BF57DA46A403CD181F58AF86CD70EF98BB7DFEBBB416B58E66F3DEF2A340F714017D6439F4422E0025A6F6F3FE937306DDB7C25B2A6A91896CA241736C0651268E1884565F8C54D39CF395714A0816595E73E5C008DB304D5B74D8A988261E52AD0DDBF688A865AC3B974CBA8BFBE516589971E02758718B86D6C098908EC64CF691E049C8540EC519C0F6230D50F7D5590AFC1B7CC3C0C1B07782B631138F021EFAFBA684B1D3E516EDAAF0398656B1FC4B88B34E4418691DC72379280FC4ADB805F007E1CEAB838534BA40D61619F15FBA5C875D4B8F5420377E7A22A88B78298FC29DDB485874B12F40A1634A0DCE789448ABE99FCEFF58456453A89D306E21469C2B374D684F75194B457EA825EC19F72F8A672A92AE6FE40B7D586EAA6AD522AF5065863627AC79B81DEBD12E71116177E0F3BE82AAAAA63C06FC282C023210BFCC84AFD474E64D27639488D35AF21C526FB40316EFA4FFE8422B38EF5943D5E96FB7386C39AD98B8D10B99C6F9B25A233B35A9164AC35D42B1152D4F42D071AA8820CA133E2A5D7257E1538165CE0F2686D7D4DE8AAB63446258DACA3AD1F15294EB5E15FD22B73C2F73E001D1B293DC1F770B6B9617AF5851C0232376D42506E661A72C13D03B78AEE93A7EAAA0CFE6935769CDCF1995AFFBEA094F2621453985F5D5BA281C74227D6BAF8AD822C45E3B0A9A65C9F995AE40B7BAD2A969BA31B0BF791BCF50B34349BEB07B922DEDC5982BE5AACB0D2E2391629DA0C3EB6AE3854EFC3795DCB7AF7EA43A64D1B2C0742BFB6DBA7A62AB01DC9F4A2FB795C369E8E4EBD84378E28BC13B92BB15806D2754B7C4831ACDCBFCDC80F27A29F30731039C403F561B8D99992E3E0F03E6179291D6507794B2505E84EE8013E40612EC5AA52FC73F152A7F2E2C9B9DF47B8A2D98C907CDD2061EB77713B1F99942207D92F64217523B2B73DD89ABC13C7B26492A3ABEBC0067F39DDFB94C114A5B00EF2089EDB8B9FC7BE43B8D0546913FC32EFF2CA3F0B3018EB970028D83FACFB0949A53FED00FE3ADBC6AC58A7E86985A6543B1291127B80244FEC574B94FCACB514A2CDC95FB8FAFD18EFC14AD1D29D30AD8172EF771830CD80D1EC1CEFA21270F4208EE37F4AE53A5B9EAEFD0C824D6F0AFB1D20E4F359A8FCB0DC7A1508952C1B0C045C449805F6A0552053F24AA1727C6B8E6899815687D5D03E1B4F4BE57AD5766A770B5A2EB9B8D61DDFD326B26162EBB5399F3837611F4FB0C1F1C915EBAC205E06ADCD84272841F18303F474B054B86DF05272F7C882ECD6808C61AD403597E22AFA59E41277D19F75BE7D734503013F54FFB8E91ED4F1C66690B132A6F95765F98FEC6317BC679E026BC1F894B37586494B4A6382578698EBDF7AE26D740CBBF063F0AB9D5ECB0509D10F668B83F254F731E4ED3730E75420416988077CB75BFFBFC1B25ED615C8BB3134FD69346000F5DCF08121D16B950F53112D5B5770A836B458CA037FC5B9A6A0FA7D0E0DD894A11085F07645EB03A32B61FE29527C4A5064DD2A10B96A59ADA8C33989EAA21A84314A4FA3CFA267B8CB75FDC4D3EBBE78DD60015A76B91B9D96F3EF2FAAC65A4E9413C36B093FE577F50598421619C481817A0675F36E1B90DB899FE2269FBE7B9CAB9E215508383BD9369205719FD76D93FFD3F25AEED04A87D2B4D46CC79CCBDECD48E2EF637707DC593A1E11D9F691A50DF3653EEB0987909E1E3558580C47F44AFAEB4CB61FB37CBFC05DE5D417A90F6AD49C5121892D5661E290256152FC73F152A7F2E2C9B9DF47B8A2D98CDC6DB0FBCF4168E65F0B433E8BE2886EF030DA36FF62BBCFB93D49CE60CE5CB0013BB4EA2F31256957238CE41305D595F65BE655D76C7F104EFD12289AE3EBCFA2DE16164DF46159DDCD399B7AC373D527E77B6871B7D5E4F7653E426861C8ECB5D571D4581BC90219FE0B1C7EDCBCE9BCAEF232AFF3E9D801EA418F5B4E77B4C24EEB7657A69009F9C8B7AA5C3F6755D8E845B1A04686F27CE3721AD4A901BC8341FDEBDE4B9ED0AB6C4002A3386462C2A534C5148AA8F8F663B23D8F9D85FDD6BC67D1048DE879B19DCF56E43639731751BAAF5F36005A432CC7B5174652E41B80747A93FB375ED9641883E71CC5ACE13BA04643BADE35B0C4ABABB8ADACA0A7918C31EAD8D2D839C35BBD0879636EB337E3549227A9780CE27D7888769B1DBA26BE2B6DD7C7B8918E9C52A6EF5F37BC081A56EF8F01AB93E9E0001BB29D292654419B5D313BBCA6B7620AD14AC364B37BB5F15AE07681EA720937380C294C3422EE8838FDC0CE0FF4043D8023C8B38ED854495BDB982A734BA01D8B963C442A0F0EE3B18E327DB06AA22E06B2FDA115E05550314077D3CF405B0B770950A794FF8D6C44B99EFCEB51998CEED8052BF012E56A7764FE69A0A84ACBE85F549FBBC4E8DA632FB7E70C2035C772ABD52954E6680242CFA8E85D0AC9B3C228D026AA3FD14D8D828B03CA96893A3D19671FD335FBC1D6372ADF051A9C1ED622E6AC3C79DEEFC0CAD6BC738ECFBEC86D845ED968C8F707DD1CA34A3679A692A9BA1D8AAA208BE2511F6CD6B9B24E59BF342D00A3F5D52FB1DD1A8F1D63865D7499F57C000298103D6312187A2FE57D6C91F1DED437C5DAD50EF1BDAF94D7F94745F177955519B971B0E52939A55A6EAAA74423A9685B43C4874F983F92658086CC6046F7B3EFBC3675DBB71F9127A360C4BC22C33833F3846A413AC79412E984BD2413A1E59DF691CB46F76FB75CB277F3CCA35154AB51FB65E64B5D181A25BCA532D6BC67D1048DE879B19DCF56E4363973B4AF7280D43926E10C924283585461AF00CBAD34A70B81C3D1A03472021A24B1B89F905EEB0BBDA8B4C4FF093A132C42AB967CD8487A1F3DCEB8F530E3BC735894A11085F07645EB03A32B61FE29527C0E1F9BD9B2BE9A774A69B1A31BBC6A4CD4DE9145319FA2EE4C0BA3364D3A2D10D7B22248F3F95CB4EE8284A7674833835A624A231E490DFBA3E7A553AE1C9A07D73A7279C10BA978FA9ABCF789D29C851286653F8682979E00DF3D8DE9D3E7611DD46D476140E3472F522914100365BF282DD2935220B3A9CE6057DC86F9CC331B019AF36EE99D8C8FA942AE7469F335B3F5B83A073A318CE695F3986DEA103FF6A7880C4EB5C08817DE703ECAD3F10FA9DC1225D1D5AE0A1BCC7D9FF7F3BB0B21ED55ED599CF936BBD88A6A63EAB3808156E0F1490442D8A40390FA1E0678FB48C7F12B8ADD9C5CC5E8118A8A61DC1602F7FCB0A65591941489CD7727B981A9FBC6AAB8C3AC54ABDDD455544C21CC0E355577E8E53A9BA703D57B83015EECFAB327F89BC4EA2F40E5EA554518E2F3BE294DEFB114FDB6A1FB6F2A33A2BB1340423BE56A6D1AEC04E92801C143755136920A920683432455D6F4B13EF828A6D402F7FCB0A65591941489CD7727B981A90A9B7EFB4CB7BF37CA0B4D505FA04251F1B620BB07C5CBA3691FF15F7A57A17AD4A43883B21B3C6FEFB8DC11123B4B4A02F7FCB0A65591941489CD7727B981A923A9685B43C4874F983F92658086CC6025B793FBDFBA572090556B432181AB57B387D76B90C57F8E9D8AB35144801B3C5D8DC03AA012CBD583BA44BF0E5444F1A3BF213671B9548EC2D4E9A885F1A4870C01A44D99A0F1BFC3ECFCB2DAB0C5533D9B44F5B64224E65F2B6233351DDFFA8814C110F85746FFA7C84B73CE66C3CEA06594D8423336350E6A8C76D029C4D3FAFE16444D4D940D940B696200629F5DB521CD444DAFC01B9DBF89641DC5741883A0C40DD62BE48EC878E4133772F2125DAA48676F047999067583C9C6722B7156462817D1E13CFEEF314229ECCAE38602F7FCB0A65591941489CD7727B981A98CC207739970C8F2937B4A07FC77ACA384F054A17C2A6BCD0418379393223D743C06564982B14D753CCB7829F134E6F09557B191B95F66E76426762CBEE31889FEF3E74932D04119DA3EC992D336C365D6BC67D1048DE879B19DCF56E43639739EA1F4E096DE9FED3F7E27E77D5C59EB02F7FCB0A65591941489CD7727B981A9D3F3FEEA2A9DC17A268BA148D5D835025B4DC7120E7465416BB44F2B5858818C4D367C712CD1B829EA1F9C4C848AE583F317157D32303D2BC584D6A0CE67357E5E61994EE68BB0D56500008D776134575878FFB80D412327651A3EDBFF4BC6B5125016B1CB289CFE641BEC566385259802F7FCB0A65591941489CD7727B981A92A0CF474606B19AF455B0FE76A74577DC9E786D0B6B4F42D54050680EDD032F420DF431912079BBC9B1A1A5873BF7AD259264E5132BD8218A6E21B83D3DF3937B250E21B3F7DC4D8232066990765E989898B0251FC925DB20A5899FCD65AD45B811F5370F1038A6A48BA678ABF5DD0383B18C7D7295C19FFEDABF798E5E8BA255B8B8E52DAB9BF0FDF5977E50D366E9B99D524A0F4C618ADAA0E9CB9D1A002F26523E108BFA77E3EE98BB2A45F2B31C577955519B971B0E52939A55A6EAAA744D7BEF2E2BF0730268D6346C36A7EAF803B980CAF1454D8CF356DBF8AAC8C06B78090BF49A48F81B5A893426FE297CE2B7AE570EF93EF0BAE0B417B89A2D5ED8DE9ADDAADF40D3E576D532B4D3267B5FE1B263AA437DBE5C2B9E8490CD26F3C2FD277625A9B58528E9D31E7921B0B443536574806CFEC4EA6090F072E480AC37381A75E6954C0171573D728FFD4370AA6D642848B685185EC81127A19F4664AF570E43631628876CDBA595103A7FAD5BC02A139A981E0FC14819AD97794432B354D94996A23ACB2CBBD3008A6B852003D22039F09B8FDDB60D427FA655DCD5E95CA26D3F99B538662A40320CA1E4A264F75AAE24B3DA2946EDC1782C6133CEEB458E14049410B8F094BC50C70BA3FC6591F3E9E02294C5B383BE6A55FCB0964F08B98DFC25C73799ECFD0D52EFD15C96040D659F73FBE9F4A8AD34DFAF3C1705F1DCA6DE016CA4A124C09CCB8A48623354CAA7C7C9AFC5E4BE64A5D1CBFCF07FEDA0CF68C859E2427EDB5B2F89FD79F24C470425C8F828EA6458CBF747B7562E97223B2B2C1FE9274AE04AEEFEFB6B9676E83FF646A1DAC29C61BAE6B17274A74DCABBEB4F46AAC46D0D59B4063752DCE4531DF3991BD51F378FA87F1BC0D8F60656A928DE314FCDB764FE306503FCF2BC45A8DE0AE36852B5DFBDF63D60E7397148939FD667DEF1D11D4485D0DE5DC15423BC4A62B48C292E92F65378BF39DF1EEBD6A3BB3058472C2F817CB29EEB3F7A2EFEFC3FF2D7A688E52894C5F7365F758E4122B1380F9C047E028896682378A1853E343F055566996CB5D6C60EADB5C92C7D480467437DD5620DCB7A7AA40331BCEA497260414E5EB6E174FE606D3BA8977E94D46DA4A779359A7A0F1761DC7FE471CBCB6506BD4A5E6B3C9B5CCA33979568574FB49E2A9CB8197A4E206E5CD55CFCA35AE6363B9A1EFC6F0F8C41D1080C2C7BC90F881582C51A7DEBBC418D9E337C27E72EB838608E281291E4EF69C7EDB305C896D98DF94AEC31AB35276CFB4A3D5E3A11878E51A3645C592D26988A6CA14A8438A84FC022E38E34FA15B2653EF129813B1777AB7236B6F5B603405C0C56ED91EB34921B15DA78AAF024D1A726C1539B4675AF8260D12C1CD211C06EC2FE76A6F6E6BE636D211442E3FF8775B07E63394A05E44E82E28AABF695E691725426E4F57C3AD7F102571DFEB169B1ECB64B9DBD14AF2422FA7369409A4D37E1D824DC18ADD3A67B086CA7DC05BC6F1DBF507D1B6DAE06582173315949523C56843E93F3662B0496BA8AA1C1917AD50589A1F766C72E4446BF8FB72AF5B8DD92EF721616B942B893C6DDA9C4C8B637572696DCDD81BB042A8F9CD2ACAC557FD4A450B74F84F54530B63A110694CC13AF1A248EA501304CCBAD1BF299EE112C8F0ED5CA8EAE2648B13689FC3223612C8D608D5B34740A25CAE922DE1CE5A6A0331B59E9B07E2AE35D05DA3DE49EE6217A08439859E6E33141E6BEEE308A39C668D0613CA8CD402721F039DC24A66389F3CB5E9380EBCE93DAEF0FAA6EFBD918CD3DF53090E4AFFD6B7EB3BDA93232C697BA6914D26EEB2B64B5C8939CE94FCAF5323244205DD5FDD2352142810274B9D31BE9FA99FF27DBD6502A4AE5F1D05BB8175570702C16D0DC3AF7F2CAE6BA85E09FC500AD177AD4B1BB53A8CF0CD3A6B5F5397CC445246A20F2D042E2CF5A8095AB988DC3100FFBED813248831560C6CAE7C6A067714DC3AC53D57DA86F68F09DB0E789285D98CC386860F07D80CB91F253265E64C4784E46E2B74CA1C8C6A896936AC1DA446756B502F2E6B9CF2178D339066C9BE085EE8C7EB9A92AA9393000F455F163C270E8E756183079874C5B61D1965E5087B7E527764DAF84353CA9CF263BD248F9603FFDCE2C0D95A007D525993BD1D9944D69EC4172836DC41A9D65CD7C2E24BAEA04B04AEE61889995FBACB55E240E2D29577766AD592B56B0789BDD7FD5B55CAC161D167653EE425818A5F795E62DA6125BAB6156777713BAB9773ADD664D74ECA8942FB37C99F85F0B24573C35E4390C00B56BB63F58D01EBD94BE76D8EE8848805126A723424A4F5846B6C9B040F82C82B1A8E24A5634271782A5B5C75A5D4D374395C8F2DBA59E072B6049CD9851FEE8ED4DFA2658D92FC948726C56B257B831A7571371BE1489682FB1DD1B0438D881F7F8EB7B35A22E7125016B1CB289CFE641BEC5663852598D1B9AF693490AC2D38F2E52B15780F2827120EEB0F36F9256A71C5F94E0485EBE9215E33B05F05946D161B579602DF463FE0338EE27F0D800C66C07ECA9A4230C336423B75D125BE0363318B8B433B36D335C8B9C9271F5E10EB40514874ED72DEF03250AF00B20A426E7028E821F536F16979CC0877708DE98FCD06901DCFBC954978D859CAEBA4C45767703B87D26407231540794FE71F782061D7BA81D097D79369E061B8E8373C7D09E3ACF500135D8A61E5B75533015399D7EC74CE8DBD24721BBB1DDC2D8ED198078FDEB842B3E25F792AA8CC44D1D6C8664EAC1CE2370BFD182B97EF270F6FAE61B7FD2F16403F1B124D71679103E66E9E8DB9573DD96443D2FE51678CDE250C9E644AE7A290EA75F57905716439F31B7E13F1FB0BB3E7C35C783067222ECC3B617BD67BCCA6C57F3AB6BC6B061FB32C487D90369A1639F6D50DF26D7C53351A26843AADFE5D87B30C1A948832AA058D2044F1B01DF1338A7C290BCCB42295FA9794C0C20AEF9BA5B707B7E2437F3264166B1AC7EA803B8F34C7DC2C177AEF41301584C0EE8F7B40D68F36256F3F957B65B967A71A7B6982B40BDD7855D11AB391EBEAB0E578D181D57C645BD43965156C2D779882A8C5577B3CB86ADCCED447B2BE74A97F7C64AD8F900D806ED6C6B3DEB749DCF91B6210A5D6ACAB1C48050B4BDF2BECB4308EBF12D07CB59610B24C84B9737E4E8AA76EF9F50C25ED1A9B2DC46F57FBB8216210A5D6ACAB1C48050B4BDF2BECB430E16EC610D14D53B1EA2BEA843E7C68ACA4D1493B494221D5BBAD78C1204C4CEE45A0054CF560CA3DEB3BC51ECDDFE99250D580A80E1FE6D07F2A33445232CA5F7BBCCE735E2E0D8047969A3BFF25128B4AAD70FD27C9B44D99A525F334194718385FE416805AD86832D9E6EA50BE32C3D579085BB7DD98B6493C25E0A289686B0DE2323A1365EDCBB5F7D4BFFE7A7591343AEA7D3ACB208D208DE8D2CACA0E5B3AA036489299EEABD2219A746165B60883AD764072B0645213ED78FCC5DF68E325AE5DD8CDAAF3A8CD0CFB1340AEB238005299251E9AC31D119C4A955A58CE9576245C44B638CD1E5BF58792BF80421C98AE2E701FA218C1A0AFA308499812D7CCB2AC2049DBE4AEFF5326EC53459653E86696C1965551EF9B00EDA3C698A00245541FFBA45CDA334551C5D791BA2A194F374FE2003EEDC5F5F4A2A078CB21F7F32C71ADBCC9A50EE7ACED604E74C815DECE98DDCF70DA73316E23391C4C76135DC301B03018D899514B93787BE604D70B5892CEFE1679B32DAE02D595F5AEF9FF263230C2C83A98A79AC4F74F292A63BC12DCFF0CE8D73244282D7C3582B745DCD241F10C375F9F765D1F41492FCD0116DC1AE686E5736B88F0B6D690B95E53F86EE35EE18D7AD24A0BC517E5119557C6EDCB559D2562BCDEAD98E63D7E406A5397D5951EBB5841659DAA03061DCED256B409F338F7319341513AAAAF611FC0AE9A233345E02C4F682E18E95CD7ECA7AF6282D4D2D9E8A9C36C95FFDFB947D102A139A981E0FC14819AD97794432B35452414759272796CD27CE6492C6B36400E9CB297A50A1A114D244246CC302C0064871352B45558A10DCD3660F0FD8AAB4FF91B3F2D1606E201930B6D2AA3C74608F5DEBFB67E1AA1F60A4EA0913C764660C0FA64B8D1D0B3F39FBCE6E54D97D26F3CB82EE26853CF2355F628A0B0CE9A015562B9805B94941345BEF64CDC69BDA04789FECE538292322FD7C3B9EA13855E602ED45DA7AF96ABCB880D6CDA6A108808A6625F3EFD2D5B7FC2063134F1E85C5633D81C37450E9A91C9403DA58740FD49E596B8DE72123D1DA81BEF10CD5B427B6175ABBE13CA534D7407BCCF3AC3179D262CEE73E7F8C5712EC4285F2B5B04536AB0454465BB3FD3A3570C77ABDF78A4B4B746E9605160073EFAF2F095CEB8600FFDFCCEA5F9CE1B9CBDD96F814AEAD4C74D0B162B200437DEA981561FF849A0C289E3BB640760235EADA017274A0E793A1782363369CFC3D1127B234E2EF0164987FC41BDC6E926EE671DF0F6AF17CB58D91EF453F5EE4CCBA0A6AC13F61B2FFB4AB9C6049E8B904B9FE468DD1484F7848BF729349A4C66442308F007AB66FC8A10E8D8D586BAD4A8B81B6B43AA6CC48E48D0EFC8865F984E33856742FB418B6776AF92A37A354341E0A1C2C62C27A76541799A4F15E90FF1452001EC58E26E14FC018F43F83AE1564557565555CFC49F4AA76F016606A12CFC91D935F165999C68DABA4651C1AF808278997EDE9C0EDA30185F4844573F10F0D9D297520F522657C3B42C784535CADBEAC69C219F7CF1E412A26E24BCD15AF604BA112DD5E22F644244C348F437C87AE31EC481C861CE582B59B3407DFA00CA2F94ED0EC0B5827CD1ACDC03EDB1D86E687CBE69D033EB4C36CCEA2B7CD881EBB0E0AE7402C02EBD00620AAB063213152456600E4A078FB4CED01BC671B851854EE2FB92F3C29798571BA34AD601F962DD450B07F8B4935F06734A6558664EB65BCCD6473D209D788B0181803FFBCF1FC53651C1945167998770E914F9FD6A9E6D20E7AD714A3C66D937275CB08E1B1122DD4A19390AEDB25CB3A4401E7B77D2B66BD9B2F34711FB756DBA8DB7D3365826AF8CDB12C6FFB775EC5E628FC4074B7FA5C5EEB5F566220B40CD32B9A8F360673F9480CF2E76910B2FE47EA7C364BCBD158833E35860C887C279CFC1E350EDBE1373888C9CA0FD1576869C92159FBE97178294A40A4FA4BFE980011DBD0F05BFC30C84091E8B10736F359C041B03850A5F7EDE25EA8725C7F3E3C64FA9A3433DDFA6FE9E266018B997B36B84A0E73F827DB6C08C93E86BE28344777D2AFFD238132AB8835D574CE9A1EF5BCB80BE9F56F7B03A215BB437166A0CD2A967A409256CA75D86E942BE48C1160279BFFE459EC93EE3C7538B3E7AE8A166F1B01DCF63FD89206CAAF56FE454B9D6D5D51ADD64EAEA6EE5FB89C554944DE9FCCC8173DB52E1E2736975CC251248629E98500BEDDC316C8C1DE9290466C1CF069C6C39EB80885AC5E0C8C7759D4FD2E06CFE1A389E2988EE2CD07F6161A232B5D69AAC893AFBD15C8EC5E1AAA3D8407C6D9CFC9DD1027476D89A4CB4F82C7E6005E3B3BE1A651832E6D3F0B47DAB9F8600A74C8E34A0B17EA74162A19F68705904E060A41046FFE711A090D2C92C95EE33024F9635CC61C6BEFD20AD64F5B4B968D3DA3BF9FC1B5DD69AAAE2F89E66E3D9A3C7E76BDCC209BB46716819662BF1C084BCBA0A237B518BA8145B0EE249A0136CEF181E112A9AE621F078613E7E7B8D5CE1E952F9E5F425300E22B9C62D0AAC3A7BDB7D0CE497D158B8802F10ACA99B2DF9691FE3185127B64D4081E11E27299BCF88B8DD3B64A921B0F24A09CC571005B41C76F8BF4F0D13F9C28F0DCDFA7D05309FA8E6B1E419C472EFA785A4E44A3F3719955B0331F7207D174CC9987F487049AF927301802B12667057E260F53CE5EF4F8CC845CACBE86E3AD6573EE36BF2625442A59BC57B0AD82AE28D0E29846F3CD9DE0A1E77EE99491D9E198146FC4F02CC45424803155F536015D698FB092E7AAD38891926E2279139C906DBA59CEE4E741C1B3C1BC5E00FB75233705B63934B7B4DCE5101C6D2E40D14755F356E4AEC3563B944F4D1D741C3747AC185043BA18B102B698E95CADB19226861BE181F3274223F9F23BD175E90DA8FE2193012478AEE0EC6C708CA7E0DA540D6A3D0F6B1B037E07DFF58A0E4564B2F311C0FEC16F2C1849F005D5A2A138D88F4C6D5064AE021A3DE96EBD8DA09D8311630AE0CA6C7D8508F7CAB060F8C7DBB0B36644453A2881CD6A7B5DB7C193B977AA7014A4C331C754D21F205E62923E22ECDAFDF5058942EE34AD9971F6EB0567949E00F23DAD36A531B1546894317D32702B9BECA963457606F59E68926F0CE72B0A9633F16147935C02C449F9F959755DDB45B114C574C90172F513C5093AD3D35CC3A3151E65B9946B8B5288A89E24CB3B02C030844F593B682AD283D6DF8C343B5A98177EDB44E2E56C4A6CCFB929F7B7163285DB7001FAC759DEC5EFB6104BCA8EA50E97ADCD094AC08379F75D39B88F3DDDF578041996E0AC12FC998B1C22D3B00770FD1B8B6F34AC2233E9688A437183101CCBDFDB51EDE666F89004938E1BDD4C4369058FD7B993B90E9C483AA9C42AA3091E1FFDE2A69C8CB96C39ED2586FD26772D8C54DE25BA7290272AAF946A903B078CBB4D14B517F209F9AD7164A0BD31E02612D3D342B879D4B4C03EA196D6F26A059F375108654842E33EA84C3DACBFE75B6C8EE2C7AC69492DE261CAFA8E677C1B27C3DD88C350B0833A9887E4DE9A4CC38F72F2A6E71DF5038E1EBB7ABB6CABD7ED458CA00BD452855C02F517AC999BF94282CF334713DCB7273E6C8F077A35D9212525C6E1BEC78E7103A00182D5EFD0B1292A179527E25F705FA7662E554AA0D684843D87675CB47CA12350F841E10855DCBACFE083AE39BA5E071BEE5174233C1CF8A703A8F2D737C02E3938C6EE1932A312ADCC08CA489F1E6261480EF79DBB8E78DBC50397F01D90D8810FCB3DB2C5AE1C8089B5A1BEDCFAB6E29A23F28B6F2B593B7CFDAEE43329E8F90C2281FC9D38EF1456D1C0601A50844462F4A0F9EAEEBF8989961EC25C8E48946690D2610E2EF0245B766BE00835ED230B86B71000C3A9515E25D95ADCC076F85D0F0783F557F479878328AB8E685B0C6E98A7BBBC97DB6AD9FCC3200C71A4B496EEA6E796DDE701A66206273F8B19D8E689F2A6FF1BB6203837415C038757C65D06A9158D069B08585FB6B6895F38A2B5E8045AE88D55E0E702722E9141BFCB6581F834C369D1F3DD7C4ECE254848D2B507D500C04FA9A644B184782159EC80354FD2FD724F531198CC782A1E34C0C1F5F001EC232F7364979FF1632497FD6C9FE806D024DDA77E92B9D5CD8FF0591E60C939F4F526837DF3E311D67E4CFE1DA8D505116DF401EAFCC86783A4B954823BA9BD089EB45EAC381968781D09EB214955C6F5305AB209E61A2593A31E282F3FE2C04C6FB0D738FB968458D1EA267F99BFBF6B280FA2568DD1D6A2D80C3A3D021F5940004D20A38F1E85C0DCF0B3D289AFC834956E41E50490F41BC41C6E0958447E1C3DD07D6ADA4CB2BF4AC11443F5698588DA27239EB2C01627398C8252403603AA224843928DB26D783347822707BDD5087B9CAD63C84A1E05E74D40D2C1BC324AE951C59A26F55A9A1022C5C76281CE4BF5B96EAB67063F5F1E02B5CE96AEA9033C949CCA87E8701C5E1D4AF8943E9040C0F1380E801DDEB679D356EAA1C9184564AD94E8AACD98E45163F03F2AEED789F7FBC00761415AAE2A331569853778CCBFF134671AF7A8C015628326BD20ECE436C126AFD5A73F9A649E0651B25959CEA30CDAA5B9222940203AD0DD25F9C6FE5FAE5E1AA4EB277678F0B6EE9EAD96195E1A443953664C02DD871A050370FFBB2AC9571744064225391309919913851A3BD1799B8C0333F22BFBDE47B05DA3BE3801B91CAAF95258BD1F743D39E6C6F11B2F363019F224BCFBC967CA677474A25EACFD7540D0759E2072699A657F621F25BBD25A95402CB3951A876FE114947DCAA035D6352349C7AF97194DCFC188847F8794EEAC4475CAAC4BE7921455979775669B51F0489643AAE52D8AC16CAD92B26EC9C0CCFD16FE0D207CC77A68449C971CDBD999FB21448312FA62A08A50545FFB9BE392F79C37CBE2D7F93E33D7AA4B4AA806309396F2F4DDEC53361FC11D874140C34592D6AAF3ED7659C6CC3394D5EF943F5F5A9016C90CA0BBC4B5F51E55BD0A1E2EB0AD44E624002CA7D8CB195A4E78C9FEF90BA83E613E4B1D563376DD12CCF257D0512BA9B7B9AFBADA306D0E34CFF6C16F3F9CB5581946AE746434067AC9D97A37D4CEAAC65FBBBD605B6555678FEAD1845C8A95B458373014CCC0599939CB50D74C4EA0C87B85D0515CC4D55228BB2928A6D59892ECAAA26006FC924F528AC42FD273379927AE472299D3CA24C71D531BA2DF833FBF5C4B404C178819789C430353EE2A2A3FCE8815F5F640650E7BE540D28817156F8C301B0F45C3373FDBC43A615B83987EAE6C722D7D1141E26F67D7C9698F4D8DC7EDD5E1CB128E2027F1DCE2219562E857F44B508AF20347483302FA7CFB086821CF4DA534504FAC4C3FB6B2A8F5342916C08D65B1AC04E30B16F5CDCD5678B339734D1FCD6EE0FBDE39060E08053390BFEC0DFC4E06454954093182538A42A9B098E7ADA41A51175DD8353D117E3B4A969643C186B8AF071266F8B0CEECFAF0AB76A8853D79C216524E4776F5B45A35ADBE1D526B9D93A425360A52AC08B4792A89251F1265801DC2EFD2D4B9B460FB615B6EAC6C0C5E6B5F2CD0C1DF0D20EAE796B8BB25B3DF4A639A95392B80EBA2F3AAD054C74A1884DB8A5347AFE1B9FF33CBC33CE4D20FFCA2BB0C7F4A131C85F0A0A091E26F99CDA40789B9906C890B90D55CC9589FA360AC35F66DB9A6FC8974FA836F30A3EFB935803A7847E324E6F1ECF54161055689160BCF07A0D0F83EF7422533E2FC340F553DF0F968BA5D5D339E70BFCEB41A6A2A17523C6C6F586A7D506442186D839F64D63AF74D93AABC4BAC05B751D683669E47CEC57A873DF7A31D2ED18712A9CBB98B8E35FB739A4F4CF2072E45F8A2885BF278BD0EFA275250B6901328AF2DBE07EB36AF1A17B622438A2A14B84A17E5AF049064EB2EC5791E0221D95B8DEB057E778812D095063036E0EF9392FBF11792ACAC14892F84E5D0990B727EE9EE58DB88B6CBAC22FF24D3F443D6F73792A41F87AB88332C40B61B3A4288FC88D8DCCD7FDAA042C62CA74D734294FECDE53EDDF59FAE9A7FE651565802CC957C8CC6D1709AA7C6D236605400AE4BDFE19C5BB9BB38F6B990A9F57F17253E623990905244E373EBD5D70A8106CCB68B3CF0326D92916F5B0F5A6CD00F7F13C060129E9272A4544351A073D307E990F627FD99E164108991AE89C7017190EABF0572747BA2673F7CFF9BACEF0A5E73E66AA66AD9C605375D122499E7A60981DCDB7544AA4ADDBB9D2806A35CDE65FC44DE0ED40576CA79023B6E4D996DA6A4823BC2EB933FB27A7C239E00B742209A7056E7982668D06F19AE0ABEFD44FC3EC15A4DF7BCD4F5BEA60802DADA0E8F72EEE72C005603749F3A9A3A675E7660C21640D647FB7A2F68E0280A43C2F4E16C0B62C1335B6DD9CD22A431215E7F1736C3FA887C3023969234B05F57749D721CA5E06A4022033D3A93B071287743AE370D560B3B05FC92B7328BAF1C81D72351CA05EF5EF230CE59D3FF7EAC2124E9720E78F4C286B592DB1B4536BF502C77B8F868582A2D059B8C4E5499480DC74B1581AF3C847D99CAABC7A277963A5397F0F0E3E1A5161134ADAF6BFF2719641394DAAA656478FDAC8337BBBB5F694C9650170D26F200537127A26CE5AA66E525BDB774CA62088FCC42FD2D700DF13440FD1F05BAE3FFBA5098F1B7CC28120639569AD1B4DE726BEF0EA6453EC3930F87A612EAECCF9BEDEAB231920D62916A22489A924E52A18BC66BEF7F125D4BA54F064F4C1F1090A0A1F3DA56BF843815B59DF8C8F6ED4CA3A2226F3DADFDAF64DD01189A98BA951D6EAAFE009694A51A86EDE9BBEFFC2F3157EA077BF3148C669E0D2BE4AD439629B469941ECEE71AE5686A850963FACD34B594029B8DE939D97E73AF9CB396D0CE8A5DEE1E142876B9ED482B8487CAC7AD5367510DD4F531C6C4E1418A1B1031EBED7E3A356771608C6A91DB9AFBBA55997B1959B815B344B465CA4C1D2ADE976F3AC6B764F4BAE21DCBED51D2F5BD16AE309FF92996399FB1E50BC2AE521D6EC2ACF1F93EFCD511A66CCFB2B0C8B9445FF5FD8E774E409C970610379AC3C22443EF82EF87758759A506796179D72F227911DD942E9C00AD8C37C7DC7811A545373EF103059C72A9D1A3D6CF3FFC6C64C77CE6EA687FD14AA8BA6B052E3FC07FB8C39BF7045076B5993487E9F4D25E2D1FD82D0A51FE7895E4D0DCC27698ED42F6BC56F6AECD2A3979A141275EDEE8CC02D2F829F3AB3F4F6C71B9BAC2DAF401996E613293BEB634DC4ED8F206D4F23ECC67954DB3BFBE041158C0B5FF055E51FC087150632FA40B18CDC41B715502CD0071FC98E660532C44340528B7D8252FAEF8B333EB8B46998F5DB67EF7A93AE400F3210F4FD6C3E3220CB35F9703AAB17847223A9BD8BAD1FB9BD049494100557F5EC90CCFA99A95C695BA9DC2888FD9F3DD1F3FA9908E699F02F8E34149E22963B8235B60ECFDF86DC7A03D9AFBDD9A7B55BE142AA9B54D68CCDF647AA441B115E70218C32006CE55AD91A2915D33317E8134F49FEA1DD6F4EFA0110B4862A7C18425AD3EB2ECEBCED908E4F13F9AEE7C9AD53300E0F01542BF7B7E5654D133C43CADE22DCF0257DF572E89B5F953325EE58646D8E2FB5A928400F767FDD794484E0A3483ED34EC6D9CF835F898910527A0003BD3D7699CD22F61E4479737247CBF1D91EEB0ADB38C7E6595C10045437090A3B20D3AD3A36BDB9AF75CCF6CD6453CD7665645D176C3895628C269DEEA0C18B0F0A78A5AD2CD1935835A62A9D53E49F00E31B4B9ADC7F7A48148657C441CF49E0CA57FAB3B6F4508CEC57B3BC42A3F04B076020D40D896BF3E0604237E4A7E661B7BDADA2E7B55DB294E88810C369739D3AE502405B5BF819315FA69B7B1FCE5A1C2C292BFF64639D0CA2D517D91DBB0F3F8A92D89AA3A315EE2BAF5E26DDE82AA332142AB0D5919DC75880DCA8F69E113DDABC635165C933285F118C7CBB8C1A9F71D7E2ADCC5CE52604731CBD6E6ABE886AF1F38E3EC54E21CA4E5F2D73D39DC9A76F1F8A2529AFC20088EFB3AD3DBB2CC00F609B56D4BA2D4C692D6320940B172A946C02329820FB99CD61AD1C938C96E1E88E8C7885D288B5D35BC0701E1FEDECC61808D486C90ABF38109EE0D3133D4EC2B50F0762BE8778B8D9CB9E8BC93AA16131B5722AC9E2E8CC92E1B461301EE73CAB9290F5564D6CA567A15FE408EB63330E6E71C68139F34F9BEE5AA43C09D4586E8ED13D88584225CFFB3DC636BAD330A14F628D406F16B15D8B34C4B66570684F1186C4D77EFE0A1B15B7723B2DEE59722F8239469B3C7AEA53FDFF2ADDF7348349795F5BBE2485FD1466D5ADE2265D6AF775DD03481B4B8D645DE335D3562E93A93CFEF1F1DA7E6B964EC6382912F1FEEE36053582960B13CF36BC20CBC31BB4406E7DED085006FED5D91B7AB2A684A0939325302B10652E4A4F29D19B629E65DC0B495970419237FABEBBF313CF0D3F90F1DCE2B6BE18DF96F362ACD78B23A156529891E0526BC351566FB135F3461AC0C46C91B23331C8FAFC886A5DFC27EBF8B8D8BE733398B894A1CA4F2C1B09610E1EAB2B5CFF674AFF27A80D63F4329D79A18579E2B873C300DB9CF226BB89925591B79DF9C2CF87886FD7898C3D545C6DDF38B8628D565E08CCF92040D0A261A430818C68DE1F1D65296D8AEE79BF7A3C00DDE2DD1215BF2F03E732FA4E92B9721A2CA29B284EFE07EDEE8462EFD997403A1D4DB6A6672199AFB870DB2BA46958055C508FFBF13C77A8FF93A88F18C78E692771EC7F3AAFBC9C9720ECD883ED3EB4DEC66BFBE3B83EE27DC9EA3DB6AD83030B826A1C74C67450B7726BD358EDB53B97461AC81ED299296668731E73E76BA3F65ECB46F23E57B4897027A2CBC030FC3546FD950DE96C75CF1F052EBB36A8EEBF48119E87C5995AD0E84A6815B7C903B2FAB897475478499ED821C972AA305EB3778D957AB11600D331C90682C3D07669622B05492940DCB194844E155603D45A0A791FB2C9ECAC176DE2B78BDC8C838C113E338D673848CBF55E8781829957487643275AF62C9E6B630B9B21CAFA507361D8BD39D0F5E32BE3B0D2EB820F4D06B67A1819DDF3142912E48F685B75583755982FDB530BCF29A1E2EE58CAB1594FA5E931CDC52B4FBD94F55972B44D123D6E26AAB1F60AC9167D579EB3810619765E13157BFA39AC2AC0BE772CA0EDD82F561CE893167ED4A4E61808F878B8CE364D36E4B3D1538B7DEDED012787609161EFD16AE65B389B39552DD1ACD3BC55904DB90FBCC807BA468A9A7941F861A317D958D089DE00BF376711295D4C96B6964C6870AA2B5FB78F726693E5220AA311C7AD24BF1D0FC50C7EEBA0B286CBAA1D3511326D76E4934322E6ED26F268E03E4306AB8D670FBD1D304B64209DE3F267E2996B732C07032E6787E5636F9409E9502C5B1B32C267D5997AE87AD6903F38BE9ECB0EC6D0F2E495502BBDBB9A4CBE12DAB09356D3C69EB074FB648AD1C54C277FDE4069D43C6ABBE5EA5E6AD483C16B6C3222EB499E053C77206F9C220C51338E31EE28A98A219FC1DF57DB399763883E6895DFC3C7A832DAC4A43F6D531ACE173740520C6A42936B21234948B31FBBD5A58F1B8ADAF8540F0E428C73676D4E74DD131D8A8B2369129AB146E364375290A1686E2A4A865C5D24ABF6658FEB50F18C3E45C0139D35AAFC523D95E0E06FC06613A7A764347318E1552E7C3868269B7C1697ED0148376F1F7B070BAC5B0E9ACC3A90EEB234124EBBF1D19C73447F7DA0A302BCEF9604281ABFDBBDE857683FD4B6EF613DF24154D34FA40F25837B471C3EB4EAF6DEFF1CBCB765F11D846BB684E816D93B2ECD85ACB3E9D1333EA84D8D224E88A78F6E67468341B7B0F23AD5C80FC7C72157EE732A4398AB95F395A63D5015787552F548A11DFA9BE245F1EE813CC7850A09EE035203FFA3366F0BC70F00367C85C695290A134BE6BA1EABBDCD1C9B4E0C14177BB0541DE87033699B0DEDFE5523CB9DDB331AEEA5F6BCF4BFACD9866D569AE43BC4C2D18C685C4FD132ED5920959279EC65ABE2B23B68FF349912D568EC3CC4AF6EDA4FC3561C9185BF40156B22E5A0CF195C8FC891C43F560BEC196FD760FF8964D332CC6572E91AFA9498E78E8ED43E4248B7EBA31952F7E7799D647A1BACBF106E0DFEAE953189DF61B1C940BB65AE1B70D1D9A510BD8432E9AE6025D3F531B6AA2EA77CBC49186BAC7237A288FEBF02304EF0B2EBF6273AAC3895802FB0E614B5C9E73167ABC2E833CCFE885E4129663F23E9A7ECF3D62F2583BCE67304812DA64234137BF67C4678B4E7FC10D5BFBF478BA29C1729E64231ABE0F611BFD9D40B48B99C37678073BA402885ED58A9824663F8DCE9BFAA709C473CFA28136926CC395F675EA2F34E9EFEF527279DC72D1964AC1B1E45C7F0256AC6C7FB83BE783CDC4317C8C7BA70E02F072969C3AA6CBC0346DA6C3B100E3BF60D52A4AEABC809D9BF55B74A51DC018115790BFAA5AB9FDD5BBB7CB0BF3C47E9A4A9AECD21ECB8F0EBD5CE2C06F262EF2F082DEE76B1253010BE670FA67AFF89059088AC776D30C2A03BCC612A6AFE0CC9003F435A34987D36D1F9DFA105167918BDC0B12D007DD96B99156F518ECF92D99795B5E0CA7D58E977CE35F87E8F6EA57663F9638A32E8D94EFB242DDF5EA767BE5EFAE3C2C7FCE1F16C55060A7D3DA23212CE709BC337EBE6C4DEABF60F7CF6E3AAC49CC561BF1759136009A454A2A0E5E665194E0063B6E5FD3983A45CDFB10D7F8119853804A4C3740F578E83BE95329FD1778B169BC94DAF1FD10D5417FE70011D4329B615E34C72F32A4C85AFF4E90028444EC41DDC9FE1E3C7964E1307A9457FE4A14DEFFCD14324C11BB3592529367C0CAA7AFA9874B0BA51140040316EFA4FFE8422B38EF5943D5E96FBA920CA7BB76BF7278DD9E9E95378E77F9AC079095032FF70414B97FB3D795B2B769CDCF1995AFFBEA094F2621453985F4DA2EC2D4F1E5F14D5D938C5E85B78F8B479943F3D93E66CB464536D2F35560448A7DFBC8D824BB2723B6EF271A681D85BBE0DF3433E93708A17EC56831ABCE174FE260C99E49C01FB11503B6327A8DD2E202429AC480392C0642E3A4ADD2DF6D3FB7D7622A8E23F9D082B6F2816A53E5F03614BE7665C093C9F9391563ACF934DAAC84F1C6C908CA83DB9D67C349869AFB3B6AE5384AA3E04E522874FA18D13266563482F795570FFD1443EFFE14AF86391442186FB7D4AA45EC5F9569C3D6EE38B35BF95E7354E8E5F40D62E35E815714F8730EFCBD9B064437B96E4322AB96570E225E45309A237B57BA7EDD37E73AB49FDA7820AC10DDFF2DE47BCD7960F64E9B2403BE63D50347C8D89A66A090C05DCFE7892F87D3984B64906934ADF36E3DDC3B664B350E2AF9157584F09B0506901226F2C566B85342F4BD1AE96446F40AE873BE92BC66A0643DD8C9286ACC8655B4C767377098B2EFA2D11382C0FD6CD83E57C7204F5A2BE5C7172DA69EBE79033DD7A30448C28B4AE78449A041FDC9F3381BA5E7A2FBD48F6743F487BA59C55F4FEE2504AE6D0DFA241801DFE79EAF470EFFDD4795E75F061DB6C53F97CCE537BE0ACDE2DBB4858C2F62A4313251D7502C145CEBC430BABC8922ECD5D1C2B78CD08820C8AA619FE98A1B15228323A392F523F1C7C448CF025CA57B58B532F6ABA0948AE3BE921CB0985C8A0394F6D8998E271F7E3BE9E707F4F43219CF3527008B126F4DB85983E83414919FB1882FAC4516CC276CF5F67D6E048170726F78BBCE782F6162043573FA8F3E3D6177173C9FD9052441503629C4D4EFCE68F6E3E967CC19D92923206E49590C20E4E5A38A4BF71992953447E27FD8FB4928E09FB3A9E04568162B0ACC8BCBE37B201CDB699E9E1F2269283B3B93FE2BAF621E4CC434C1DFA6EBCB58268958344D684CD40316EFA4FFE8422B38EF5943D5E96FBA920CA7BB76BF7278DD9E9E95378E77F9AC079095032FF70414B97FB3D795B2BBF811C9A99ACB8B32A9F904AEC602FF1E7FE4DDFB85F737F3E6BAC095E6E30D30FFB58CEE8C00A2E20B4BA4A1C6C057C6C634A675B9A542D5FDB862F92AF0F2A3DB5E63E6A747990B39898F4EBDC9DE7C649C45E1D0FC0098EC64726E929CE620A875B4B98539CBF6EFCA8EF192CB90FB65F9196AAC1D8A99CE268EA960B211D9A694B308C4EB02680C19212769CEB109E71628D4F9442FD076A1AED785263DA0885D0C7C1EA93D2C1475C402352F1317088507E0E4216F79CBE4A83E02456287D1400208A9EE8E1472508DC2715E028508FDDCAABE0563F3C036A75B42969F37C0C3D4C29F5553AEBA773F20D5112EA447E390667294F39EB328155010513111C044FB066263AFA6107F3C069F981A95C8DAC20A4E8E40E86B3135DDA286BF6EDE2392A7E71FD027122E2BC3EC5BCFAB833DEB56E77FBAD9F738F7032C6921C51657260C2CF05F3D1358587FC56EAAFA63ED0D2394D2B69FBC6C5B0F34DA7FA6092CC5C318705050C7EB7ED9E158E9599C53B36500027FEDAF2A241C530F7866DB36A0BDFFAEB74504EB0363FC783DE998103C3421172B4C40CC7B01607EB22E47C24D13A0A3BCF0739E38F359813808EF71255E01B90A0AFC619464BEACD0AA8969A2248B2D738B059FBA4643784E67221E6D0C6142A83048D7366E5D3D180ADE9E95A79EDFFDB2B892AB7B3A2F25DE0550EA4B52E9B1C5E2C4F65480B17797F28493C38FD23F3EEA21162E8297B86E49B5283477759DB53E9AD4FD869E7507C60876942968DAA8DF9B7808EC10F7F9C296F862038F8A1E77FF5E3D3A9309DDCF6BBA0660D19B292851FF123712D1E51E9D2F7616A8098D9DF00B0BCEBDD1E15BBFD68CB3C92707E190F7881BB9FCF928F8536F28C7804A00018AFA9BD1937F38BDD65B176BAB441D727C4D3071BDEFC170A84C615F8F53B0D42763519DDB491FF871AE4AC985C9647861FE883E2A0C5B24D02A52A639F8881E095FAF9067F57D4094BE5F2E0F191F7488CD3DD93CA1C5082B6956E6F62DE3061840D3FD8FC3CE5DBEC65E3989AE612D6796FF907F1C6A1891DE2235BB073CDDE9E47588FD4DCF6BBA0660D19B292851FF123712D1E771DF77A062186EDB5F7F030303B19D015BBFD68CB3C92707E190F7881BB9FCF26B2AEC7C1B95C2D2216CC6C134619176DC89CE3D25790BD0BE8E612220D607D52FD79CA26DAB4ECED9EBA35C3126E098FF81ACE1366037AB8D0C81FC87C6AF0C23627B5967B8CBBD27CC6C1A62EEF6DDBAF2F0CCB2CA2609AD9EF3C6EFAFD5416BD7EB16DCE8FC6E2FFBCB0CE080343C8F3E69A6C2C1E7C8FA67429E3B47A24E6CFC8476676A0EE5B9CD485A7752FB9653574640293B018BBEE15D1830C17D52E8401B67BE62F0B075BDDE27A922B45352ADA1D0A0292657CFF5B4E6BD6DAC617813C38D4A65A12C58CDD9FCC6A3953AD27274429EB34EDD58DCB0099E67F1B742EB7053B262155F1FD47E92B089A359E76C4D285AEFD0AF5B3CCD41BF7BA5DFD5CA9E29B212DFCFC02ECFBAA4775EBD5E697934CF243CD211E611FF9C54E2B43A21C8DDAA35E923E92F32CFC0F44A168E26E308F0D5C660DF601CFB6D9A35BBDCC16F5CA67ADF167F35313834A4CC8F7D2FA689C01C71D049E4856B1F10E79B35EBE3139CBED5AB4D1E685596BC92DCABF8DCD54852139AD03C811DF81F8176B417154C47F87EB13B5C783B0A1BEA70C6394F28DBC1C934F098FE167E1DCC7B695746C15E47E9DF2F390EB7ACBACE327EA0DC3E20A2D4E7F657D09E5D3CFAE463CC7CDDF7E38A978D5D5AF35B98C910DC2C79EB3A53B750C417C6C14AE6C16FD611B2A9A8BB91DF3418CE7EB7BA87F8B2C6FD26D1019E39DDF5DA802837457A98C9DEE2B6FB90AF4068DECA8F5E8B2AAF48645760584C602AD3BFAB4BD8369EB0D958EBC89C0FF2C4A73FE1CCAA802370863CD24ECB0A0A4933F2F02B5A707B5FBA9A778391055FC70CE6753DF36C2D62D49C91F62DF6E42DDA7303387E463C2C382A8A1B430A6262BD6C9D9838DA67F3631E7A8EB41E360F0CA190DA43C3AF195727B41AC51DAB4B8E2D6C58D242BF388B7738FC09194DCDDAF1F859558FD973721FB9F6FBFF748650CE4F2D12BCBE241C5AAFD717A20B80F6C5E53D3A5F50B8463A57AC39657B13D410BF975D5D65494648AB8826448E3053038D475CE42F72F27BEF2E36033F12F96AE8F719B1F31CE6AA346AFA23537D743DF5C430E96E8CD1F6CEB50E75174FA04A7AE8948EB89D46FC9BA233BA4AD8B9C57E7B357B06A7ACD29C8AB7D702F7BF18B990FECD15A3BB2AFA7B415EB09A3F48C341BBF7EEDE24B339C8515010E0C4CCAFCFBB2813EFA9A84FC466991FAE52715EC4C26A7E0E0063917E5BCFD96AE966AF846F25EE04CD6174E649CA7B8A52B803D3B4C26A14D4AA544CBE1D12020FF078947879C1B6D553545CEF2BF28C1807EF7216BB6DB3388F97A8DE80A5B921AFAA5D0EF8462A9F6B064E1186D3AC5EB012C28C10FAD0B58CFCAED362E6B9B6C47A7993602876E780F4A08665F65DD3A4C6B7BD24EF38EE86430A80820338ABA932A22AAEFE25D7BE2E2DC0C7D1ABF5055C7F52B63A2D9FAB474CC49C5F886890A9E90C094505635EE7E4F65C1C14B0410D1A14527679F0C88C11ED4EB528C3C7930B1D093F43CE6E5562A14F5CF151174AE14B98E90D2A8115BDA20174856B71406C627492190E8F8CB8346D5796C0BE39A19396A62521F19A76A36C0D7A0816AD5ED717B33EB742904C1E980BD425B3C936B3AAC9BF0FEC84369C8EB3ABF1072FF63AF5E6E8CADD00F0336624AFB714ACD524C0594F3080ABBE44F197BC6AC77542D4FF7EB10092227615640A3C03258794D96936AAA34CF32394EF04E29EDCCE2186081421BF595FAD4E7BC9725F4E9789C09E067C8C6F2F0C80F13FE43764CC3EE9A0D813F4107CE388CD20CB76298693F6D4D6CE65BBA32DF9B80F6FD32B8D53975A2B20C96151C63EEFE7EE3458E32942DE4FC775C3AE4BCF7AF90BD6028E5AA9BDAE803DB6C023589F664903EB889A8EF1BF27EEF271DAEEA0E398F68AF5078A9D33162503D240DBD16A8A8A7EE078D612857938A53E91DED56EBD9DC0E757F88FB48DEA48FF701406004461A9AB5125F189D882349F70774ACCB4D45D9DDD20329E4D856A77A5E644D73AA92585D2E286F41227B95AD050278E206477617DCE3DABBD72B81621D2BF3B8B44BC6FC55C99DBC610FD4630B9E868455ED5E69CCF18F48E90C6B2E613A06651769D0BB31A879A38C12A85138834043C6545A85A8E8317ACC7EBCC0BE492D7D6028E1BFD2BDBD1D515142FFA9B9BE42507DDF8CB202A2B9B6E8E13A61D11A13ED54326F73CE6A28BF1803B4E9758B07D19D9D30F3BFDEEEB79DD51B4D2C7A4D3C4187E8D402AF3B5F103411F992722B213362FA7A07619FC7A296EEA585382D1082C52E5C4EC703F3997DDB14745DAB7CBD9EEEFB4D0CE5F01FABDDFC31A9B0F0D7A8B2C791807440786E6D7E9A94EE6D67FCC70F40DCCBFEFB12951986159254BB422F86A54A5889BF871043222858E51CA4E41A8EC4487F5D41077DCB856E73B9410075D761E6613074AF1E00A9DFA7DB9146DB9C4833730800976C3AF55DCC50E70D52FDBDF082BE30BDA4AFF91F4FEF12C926BBA928B4F44E7206F2A604A80D25F49AA6D231FB0694AEF6DB4ACC8A6CC9B969581431CCE458B660F205CA117682050494324AE3EB758049F8EECF4A3073FB8FFEDF54C2420ADA9520BDC946112A64908D4A072394371341A42DFED1CCC2BB2BF55AD6138325F46FE8B8B61ABA0A7AB2679E3BBEE7CEB18E93FD5AD0710AD9B63548F268DE94E7A32D5BAF57F8E9AAD87017AB398FAF8CFEF856C4F376DB77B30F1E79E7E4F1D08DC72F0ADC6B8D79F7E1A9F262226758EBFF31E1011E02A1D3374B2C85DFD6D28A28984A84CAB3B36C07D589D48A1E59E0C0D4AE81B58EFD6C13D741A10F91D2FF77F33ADCE2653D96CE25A95C8340C1766D78E4A951C833D0F1F1C0C037248262EB18F4942D8F0EA40AB94A409C0854B3F8C078EAFA7E7DD6C2F4539B9076B150FBB4D8DE7D6E6D8AF0EA0AC821B1BFA57677FD1A2DB02AC05A1B306459D775DD67DA3EE957C3740B6F777D198B88A27CE86CB795FD742350A7F39F5FE058B3A382D1B8FE121EF236415EFFDEB739B19A6EF285262C1F0C845C9A7B1FADABA3E2E87AE3175A1C610FFDCAA148B32D726D741F7DBB8AC0D0E4D8BCDA02F6F44F1A2171999237D22EBD42977D2C4CF4E1C8069E7E7056B7A9157AEF4E0A3AA6B2673054ED50D7ABAE5D0052D08DEA4E7BC834DC440F3E5F2939DE72BEA396D9F4EDF8D588208271EF89199E28A74BEFBCEA0C66825FD6AD76A88D302E6C9716EA883101DC8CFE5AACE738A6C1F33B633C5E1869F7CCD3A26218965E407F661E3B589A2633F83EF46BCFA25785252710C7877DD8B93C9D0A28F1468AD3AF67C98FD20820AEFDF1842CA880390F22C7389D6676D77EF34203F024C4DFB539320869FD1B6214A697C5E9378B7E63B6F38A7D55CD9F5B5139A7B79AB6F3435DC2D7B432C3C325D604019E22CC16FCC52AE6E5C5784212309D5935C1DBC11B2BB22AAF7855B87131A2BD4FEDD6C921474CC89CDFCE7A8C91A8D49E7F892A78223E796A39C5132093A66BCD222E74B48CAD76A4A00551B6C5E1B3E9BDA986ABE914CDE65ED34389D4F601F73137A91F37EAFD91DA4F8BFD398ECFD423A930058CAF529D3BC7320EB6B6F7E18CF15C44986BA1CFDEE84049BAB3C1BC74851BD \ No newline at end of file diff --git "a/spider/catvod/\351\205\267\351\205\267[\345\256\230].js" "b/spider/catvod/\351\205\267\351\205\267[\345\256\230].js" new file mode 100644 index 00000000..754e204f --- /dev/null +++ "b/spider/catvod/\351\205\267\351\205\267[\345\256\230].js" @@ -0,0 +1,356 @@ +/** + * 优酷视频 - 猫影视/TVBox JS爬虫格式 + * 调用壳子超级解析功能(壳子会自动读取json配置) + @header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '酷酷[官]', + lang: 'cat' + }) + */ + +class Spider extends BaseSpider { + + constructor() { + super(); + this.host = 'https://www.youku.com'; + this.sessionStore = {}; + + this.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer': 'https://www.youku.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive' + }; + } + + init(extend = '') { + return ''; + } + + getName() { + return '优酷视频'; + } + + isVideoFormat(url) { + return true; + } + + manualVideoCheck() { + return false; + } + + destroy() { + // 清理资源 + } + + homeContent(filter) { + const categories = '电视剧&电影&综艺&动漫&少儿&纪录片&文化&亲子&教育&搞笑&生活&体育&音乐&游戏'.split('&'); + + const result = { + class: categories.map(name => ({ + type_id: name, + type_name: name + })) + }; + + return result; + } + + homeVideoContent() { + return {list: []}; + } + + async categoryContent(tid, pg, filter, extend) { + try { + const page = parseInt(pg) || 1; + let filterObj = {}; + + if (extend && typeof extend === 'object') { + filterObj = extend; + } + + filterObj.type = tid; + const paramsStr = JSON.stringify(filterObj); + + let url = `https://www.youku.com/category/data?optionRefresh=1&pageNo=${page}¶ms=${encodeURIComponent(paramsStr)}`; + + // 处理session + if (page > 1 && this.sessionStore[tid]) { + url = url.replace("optionRefresh=1", `session=${encodeURIComponent(this.sessionStore[tid])}`); + } + + const response = await this.fetch(url, {}, this.headers); + const resData = response.data; + + if (resData.data && resData.data.filterData && resData.data.filterData.session) { + this.sessionStore[tid] = JSON.stringify(resData.data.filterData.session); + } + + const videos = []; + if (resData.data && resData.data.filterData && Array.isArray(resData.data.filterData.listData)) { + const lists = resData.data.filterData.listData; + for (const it of lists) { + let vid = ""; + if (it.videoLink && it.videoLink.includes("id_")) { + vid = it.videoLink.split("id_")[1].split(".html")[0]; + } else { + vid = "msearch:" + it.title; + } + + videos.push({ + vod_id: vid, + vod_name: it.title || '', + vod_pic: it.img || '', + vod_remarks: it.summary || '', + vod_content: it.subTitle || '' + }); + } + } + + return { + list: videos, + page: page, + pagecount: 9999, + limit: 20, + total: 999999 + }; + + } catch (error) { + console.error(`categoryContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + safeFixYoukuInitialData(rawStr) { + if (!rawStr) return '{}'; + let s = rawStr + .replace(/^[\s\S]*?window\.__INITIAL_DATA__\s*[=:]\s*/, '') + .replace(/;[\s\S]*$/, '') + .replace(/\.{3,}[\s\S]*$/, '') + .replace(/,\s*$/, '') + .trim(); + + if (!s || s.length < 2 || !/^\{/.test(s)) { + return '{}'; + } + + let open = 0, close = 0; + for (let char of s) { + if (char === '{') open++; + if (char === '}') close++; + } + + if (open > close) { + s += '}'.repeat(open - close); + } + if (!s.startsWith('{')) { + s = '{' + s; + } + if (!s.endsWith('}')) { + s += '}'; + } + + return s; + } + + getSafe(obj, path, defaultValue = '') { + if (!obj || typeof obj !== 'object') return defaultValue; + try { + return path.split('.').reduce((o, key) => { + if (o == null) return defaultValue; + return o[key]; + }, obj) ?? defaultValue; + } catch { + return defaultValue; + } + } + + async detailContent(ids) { + try { + const id = ids[0]; + + // 获取剧集列表 + const apiUrl = `https://search.youku.com/api/search?appScene=show_episode&showIds=${id}`; + const apiResponse = await this.fetch(apiUrl, {}, this.headers); + const jsonData = apiResponse.data; + const videoLists = jsonData.serisesList || []; + + // 构建播放列表 + const playUrls = []; + if (videoLists.length > 0) { + for (const item of videoLists) { + const title = item.showVideoStage?.replace("期", "集") || + item.displayName || + item.title || + `第${item.index || '?'}集`; + const url = `https://v.youku.com/v_show/id_${item.videoId}.html`; + playUrls.push(`${title}$${url}`); + } + } + + // 获取详情信息 + let detailInfo = { + title: '', + cover: '', + category: '', + remarks: '', + desc: '' + }; + + try { + const detailUrl = `https://v.youku.com/v_show/id_${id}.html`; + const htmlResponse = await this.fetch(detailUrl, { + headers: { + ...this.headers, + 'Referer': 'https://v.youku.com/' + } + }); + const html = htmlResponse.data; + + // 检查是否触发人机验证 + if (html.includes("人机验证") || html.includes("captcha") || html.includes("verify")) { + detailInfo.desc = "触发优酷人机验证,建议在浏览器中访问优酷官网解除限制后再重试"; + } else if (html.includes("window.__INITIAL_DATA__ =")) { + let dataStr = html.split("window.__INITIAL_DATA__ =")[1]?.split(";")?.[0]?.trim() || '{}'; + dataStr = this.safeFixYoukuInitialData(dataStr); + + try { + const detailJson = JSON.parse(dataStr); + const item = this.getSafe(detailJson, 'moduleList.0.components.0.itemList.0', {}); + const extra = this.getSafe(detailJson, 'pageMap.extra', {}); + + detailInfo.title = item.introTitle || extra.showName || videoLists[0]?.title || ''; + detailInfo.cover = item.showImgV || extra.showImgV || extra.showImg || ''; + detailInfo.category = item.showGenre || extra.videoCategory || ''; + detailInfo.remarks = item.introSubTitle || extra.showSubtitle || item.mark?.text || ''; + } catch (parseErr) { + console.error(`JSON解析失败: ${parseErr.message}`); + } + } else { + detailInfo.title = videoLists[0]?.title?.split(" ")[0] || ''; + } + } catch (detailError) { + console.error(`获取详情失败: ${detailError.message}`); + } + + const vod = { + vod_id: id, + vod_name: detailInfo.title || videoLists[0]?.title || '未知标题', + type_name: detailInfo.category || '', + vod_year: '', + vod_remarks: detailInfo.remarks || '', + vod_content: detailInfo.desc || (detailInfo.remarks ? `简介: ${detailInfo.remarks}` : '暂无简介'), + vod_play_from: playUrls.length > 0 ? '优酷视频' : '', + vod_play_url: playUrls.length > 0 ? playUrls.join('#') : '' + }; + + return {list: [vod]}; + + } catch (error) { + console.error(`detailContent error: ${error.message}`); + return {list: []}; + } + } + + async searchContent(key, quick, pg = '1') { + try { + const page = parseInt(pg) || 1; + const url = `https://search.youku.com/api/search?pg=${page}&keyword=${encodeURIComponent(key)}`; + + const response = await this.fetch(url, {}, this.headers); + const data = response.data; + + const videos = []; + + if (data && Array.isArray(data.pageComponentList)) { + for (const item of data.pageComponentList) { + if (item.commonData) { + const common = item.commonData; + let vid = common.showId || ''; + + if (!vid && common.titleDTO && common.titleDTO.displayName) { + vid = `msearch:${common.titleDTO.displayName}`; + } + + videos.push({ + vod_id: vid, + vod_name: common.titleDTO?.displayName || '', + vod_pic: common.posterDTO?.vThumbUrl || '', + vod_remarks: common.stripeBottom || '', + vod_content: common.updateNotice || '' + }); + } + } + } + + return { + list: videos, + page: page, + pagecount: 9999, + limit: 20, + total: 999999 + }; + + } catch (error) { + console.error(`searchContent error: ${error.message}`); + return { + list: [], + page: pg, + pagecount: 0, + limit: 20, + total: 0 + }; + } + } + + async playerContent(flag, id, vipFlags) { + try { + // 关键:调用壳子超级解析 + // 壳子会自动读取json配置中的解析规则 + const playData = { + parse: 1, // 必须为1,表示需要解析 + jx: 1, // 必须为1,启用解析 + play_parse: true, // 启用播放解析 + parse_type: '壳子超级解析', + parse_source: '优酷视频', + url: id, // 原始优酷链接 + header: JSON.stringify({ + 'User-Agent': this.headers['User-Agent'], + 'Referer': 'https://www.youku.com', + 'Origin': 'https://www.youku.com' + }) + }; + + return playData; + + } catch (error) { + console.error(`playerContent error: ${error.message}`); + // 即使出错也返回超级解析参数,让壳子处理 + return { + parse: 1, + jx: 1, + play_parse: true, + parse_type: '壳子超级解析', + parse_source: '优酷视频', + url: id, + header: JSON.stringify(this.headers) + }; + } + } + + localProxy(param) { + return null; + } +} + +export default new Spider(); \ No newline at end of file diff --git "a/spider/js/3Q\345\275\261\350\247\206[\344\274\230].js" "b/spider/js/3Q\345\275\261\350\247\206[\344\274\230].js" new file mode 100644 index 00000000..7236e339 --- /dev/null +++ "b/spider/js/3Q\345\275\261\350\247\206[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '3Q影视', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICczUeW9seinhicsCiAgICBkZXNjOiAnM1HlvbHop4bmupAnLAogICAgaG9zdDogJ2h0dHBzOi8vcXFxeXMuY29tJywKICAgIGhvbWVVcmw6ICdodHRwczovL3FxcXlzLmNvbScsCiAgICB1cmw6ICcvYXBpLnBocC9maWx0ZXIvdm9kP3R5cGVfbmFtZT1meWNsYXNzJnBhZ2U9ZnlwYWdlJnNvcnQ9aGl0cycsCiAgICBzZWFyY2hVcmw6ICcvYXBpLnBocC9zZWFyY2gvaW5kZXg/d2Q9KiomcGFnZT1meXBhZ2UmbGltaXQ9MTUnLAogICAgc2VhcmNoYWJsZTogMSwKICAgIHF1aWNrU2VhcmNoOiAwLAogICAgZmlsdGVyYWJsZTogMSwKICAgIHRpbWVvdXQ6IDEwMDAwLAogICAgcGxheV9wYXJzZTogdHJ1ZSwKICAgIGhlYWRlcnM6IHsKICAgICAgICAnVXNlci1BZ2VudCc6ICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvMTQyLjAuMC4wIFNhZmFyaS81MzcuMzYnLAogICAgICAgICdhY2NlcHQtbGFuZ3VhZ2UnOiAnemgtQ04semg7cT0wLjknLAogICAgICAgICdjYWNoZS1jb250cm9sJzogJ25vLWNhY2hlJywKICAgICAgICAncHJhZ21hJzogJ25vLWNhY2hlJywKICAgICAgICAncHJpb3JpdHknOiAndT0xLCBpJywKICAgICAgICAnc2VjLWNoLXVhJzogJyJDaHJvbWl1bSI7dj0iMTQyIiwgIkdvb2dsZSBDaHJvbWUiO3Y9IjE0MiIsICJOb3RfQSBCcmFuZCI7dj0iOTkiJywKICAgICAgICAnc2VjLWNoLXVhLW1vYmlsZSc6ICI/MCIsCiAgICAgICAgJ3NlYy1jaC11YS1wbGF0Zm9ybSc6ICciV2luZG93cyInLAogICAgICAgICdzZWMtZmV0Y2gtZGVzdCc6ICJlbXB0eSIsCiAgICAgICAgJ3NlYy1mZXRjaC1tb2RlJzogImNvcnMiLAogICAgICAgICdzZWMtZmV0Y2gtc2l0ZSc6ICJzYW1lLW9yaWdpbiIKICAgIH0sCiAgICAKCgogICAganNvbjJ2b2RzOiBmdW5jdGlvbiAoYXJyKSB7CiAgICAgICAgbGV0IHZpZGVvcyA9IFtdOwogICAgICAgIGZvciAoY29uc3QgaSBvZiBhcnIpIHsKICAgICAgICAgICAgbGV0IHR5cGVfbmFtZSA9IGkudHlwZV9uYW1lIHx8ICcnOwogICAgICAgICAgICBpZiAoaS52b2RfY2xhc3MpIHsKICAgICAgICAgICAgICAgIHR5cGVfbmFtZSA9IHR5cGVfbmFtZSArICcsJyArIGkudm9kX2NsYXNzOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHZpZGVvcy5wdXNoKHsKICAgICAgICAgICAgICAgIHRpdGxlOiBpLnZvZF9uYW1lLAogICAgICAgICAgICAgICAgdXJsOiBgJHt0aGlzLmhvc3R9L2FwaS5waHAvdm9kL2dldF9kZXRhaWw/dm9kX2lkPSR7aS52b2RfaWR9YCwKICAgICAgICAgICAgICAgIGRlc2M6IGkudm9kX3JlbWFya3MsCiAgICAgICAgICAgICAgICBwaWNfdXJsOiBpLnZvZF9waWMsCiAgICAgICAgICAgICAgICB2b2RfeWVhcjogaS52b2RfeWVhciwKICAgICAgICAgICAgICAgIHR5cGVfbmFtZTogdHlwZV9uYW1lCiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdmlkZW9zOwogICAgfSwKICAgIAogICAg6aKE5aSE55CGOiBhc3luYyBmdW5jdGlvbiAoKSB7fSwKICAgIAogICAgY2xhc3NfcGFyc2U6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9hcGkucGhwL2luZGV4L2hvbWVgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgY2F0ZWdvcmllcyA9IGpzb24uZGF0YS5jYXRlZ29yaWVzOwogICAgICAgIGxldCBjbGFzc2VzID0gW107CiAgICAgICAgZm9yIChjb25zdCBpIG9mIGNhdGVnb3JpZXMpIHsKICAgICAgICAgICAgY2xhc3Nlcy5wdXNoKHsKICAgICAgICAgICAgICAgIHR5cGVfaWQ6IGkudHlwZV9uYW1lLAogICAgICAgICAgICAgICAgdHlwZV9uYW1lOiBpLnR5cGVfbmFtZQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHsgY2xhc3M6IGNsYXNzZXMsIGZpbHRlcnM6IHt9IH07CiAgICB9LAogICAgCiAgICDmjqjojZA6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9hcGkucGhwL2luZGV4L2hvbWVgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgY2F0ZWdvcmllcyA9IGpzb24uZGF0YS5jYXRlZ29yaWVzOwogICAgICAgIGxldCB2aWRlb3MgPSBbXTsKICAgICAgICBmb3IgKGNvbnN0IGkgb2YgY2F0ZWdvcmllcykgewogICAgICAgICAgICB2aWRlb3MucHVzaCguLi50aGlzLmpzb24ydm9kcyhpLnZpZGVvcykpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGxldCB1cmwgPSBgJHt0aGlzLmhvc3R9L2FwaS5waHAvZmlsdGVyL3ZvZD90eXBlX25hbWU9JHtlbmNvZGVVUklDb21wb25lbnQodGlkKX0mcGFnZT0ke3BnfSZzb3J0PWhpdHNgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgdmlkZW9zID0gdGhpcy5qc29uMnZvZHMoanNvbi5kYXRhKTsKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdm9kSWQgPSB0aGlzLmlucHV0Lm1hdGNoKC92b2RfaWQ9KFxkKykvKVsxXTsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9hcGkucGhwL3ZvZC9nZXRfZGV0YWlsP3ZvZF9pZD0ke3ZvZElkfWA7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godXJsLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpOwogICAgICAgIGxldCBkYXRhID0ganNvbi5kYXRhWzBdOwogICAgICAgIGxldCB2b2RwbGF5ZXIgPSBqc29uLnZvZHBsYXllcjsKICAgICAgICBsZXQgc2hvd3MgPSBbXTsKICAgICAgICBsZXQgcGxheV91cmxzID0gW107CiAgICAgICAgbGV0IHJhd19zaG93cyA9IGRhdGEudm9kX3BsYXlfZnJvbS5zcGxpdCgnJCQkJyk7CiAgICAgICAgbGV0IHJhd191cmxzX2xpc3QgPSBkYXRhLnZvZF9wbGF5X3VybC5zcGxpdCgnJCQkJyk7CiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCByYXdfc2hvd3MubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgbGV0IHNob3dfY29kZSA9IHJhd19zaG93c1tpXTsKICAgICAgICAgICAgbGV0IHVybHNfc3RyID0gcmF3X3VybHNfbGlzdFtpXTsKICAgICAgICAgICAgbGV0IG5lZWRfcGFyc2UgPSAwOwogICAgICAgICAgICBsZXQgaXNfc2hvdyA9IDA7CiAgICAgICAgICAgIGxldCBuYW1lID0gc2hvd19jb2RlOwogICAgICAgICAgICBmb3IgKGNvbnN0IHBsYXllciBvZiB2b2RwbGF5ZXIpIHsKICAgICAgICAgICAgICAgIGlmIChwbGF5ZXIuZnJvbSA9PT0gc2hvd19jb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaXNfc2hvdyA9IDE7CiAgICAgICAgICAgICAgICAgICAgbmVlZF9wYXJzZSA9IHBsYXllci5kZWNvZGVfc3RhdHVzOwogICAgICAgICAgICAgICAgICAgIGlmIChzaG93X2NvZGUudG9Mb3dlckNhc2UoKSAhPT0gcGxheWVyLnNob3cudG9Mb3dlckNhc2UoKSkgewogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gYCR7cGxheWVyLnNob3d9ICgke3Nob3dfY29kZX0pYDsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIGlmIChpc19zaG93ID09PSAxKSB7CiAgICAgICAgICAgICAgICBsZXQgdXJscyA9IFtdOwogICAgICAgICAgICAgICAgbGV0IGl0ZW1zID0gdXJsc19zdHIuc3BsaXQoJyMnKTsKICAgICAgICAgICAgICAgIGZvciAoY29uc3QgaXRlbSBvZiBpdGVtcykgewogICAgICAgICAgICAgICAgICAgIGlmIChpdGVtLmluY2x1ZGVzKCckJykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHBhcnRzID0gaXRlbS5zcGxpdCgnJCcpOwogICAgICAgICAgICAgICAgICAgICAgICBsZXQgZXBpc29kZSA9IHBhcnRzWzBdOwogICAgICAgICAgICAgICAgICAgICAgICBsZXQgbV91cmwgPSBwYXJ0c1sxXTsKICAgICAgICAgICAgICAgICAgICAgICAgdXJscy5wdXNoKGAke2VwaXNvZGV9JCR7c2hvd19jb2RlfUAke25lZWRfcGFyc2V9QCR7bV91cmx9YCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgKHVybHMubGVuZ3RoID4gMCkgewogICAgICAgICAgICAgICAgICAgIHBsYXlfdXJscy5wdXNoKHVybHMuam9pbignIycpKTsKICAgICAgICAgICAgICAgICAgICBzaG93cy5wdXNoKG5hbWUpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGxldCBWT0QgPSB7CiAgICAgICAgICAgIHZvZF9pZDogZGF0YS52b2RfaWQudG9TdHJpbmcoKSwKICAgICAgICAgICAgdm9kX25hbWU6IGRhdGEudm9kX25hbWUsCiAgICAgICAgICAgIHZvZF9waWM6IGRhdGEudm9kX3BpYywKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGRhdGEudm9kX3JlbWFya3MsCiAgICAgICAgICAgIHZvZF95ZWFyOiBkYXRhLnZvZF95ZWFyLAogICAgICAgICAgICB2b2RfYXJlYTogZGF0YS52b2RfYXJlYSwKICAgICAgICAgICAgdm9kX2FjdG9yOiBkYXRhLnZvZF9hY3RvciwKICAgICAgICAgICAgdm9kX2RpcmVjdG9yOiBkYXRhLnZvZF9kaXJlY3RvciwKICAgICAgICAgICAgdm9kX2NvbnRlbnQ6IGRhdGEudm9kX2NvbnRlbnQsCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206IHNob3dzLmpvaW4oJyQkJCcpLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHBsYXlfdXJscy5qb2luKCckJCQnKSwKICAgICAgICAgICAgdHlwZV9uYW1lOiBkYXRhLnZvZF9jbGFzcwogICAgICAgIH07CiAgICAgICAgcmV0dXJuIFZPRDsKICAgIH0sCiAgICAKICAgIOaQnOe0ojogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB1cmwgPSBgJHt0aGlzLmhvc3R9L2FwaS5waHAvc2VhcmNoL2luZGV4P3dkPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHRoaXMuS0VZKX0mcGFnZT0ke3RoaXMuTVlfUEFHRX0mbGltaXQ9MTVgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgdmlkZW9zID0gdGhpcy5qc29uMnZvZHMoanNvbi5kYXRhKTsKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICBsYXp5OiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IFtwbGF5X2Zyb20sIG5lZWRfcGFyc2UsIHJhd191cmxdID0gdGhpcy5pbnB1dC5zcGxpdCgnQCcpOwogICAgICAgIGxldCBqeCA9IDA7CiAgICAgICAgbGV0IGZpbmFsX3VybCA9ICcnOwogICAgICAgIGlmIChuZWVkX3BhcnNlID09PSAnMScpIHsKICAgICAgICAgICAgbGV0IGF1dGhfdG9rZW4gPSAnJzsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCAyOyBpKyspIHsKICAgICAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAgICAgbGV0IGFwaVVybCA9IGAke3RoaXMuaG9zdH0vYXBpLnBocC9kZWNvZGUvdXJsLz91cmw9JHtlbmNvZGVVUklDb21wb25lbnQocmF3X3VybCl9JnZvZEZyb209JHtwbGF5X2Zyb219JHthdXRoX3Rva2VufWA7CiAgICAgICAgICAgICAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2goYXBpVXJsLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICAgICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShhd2FpdCByZXNwLnRleHQoKSk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb24uY29kZSA9PT0gMiAmJiBqc29uLmNoYWxsZW5nZSkgewogICAgICAgICAgICAgICAgICAgICAgICBsZXQgdG9rZW4gPSBldmFsKGpzb24uY2hhbGxlbmdlKTsKICAgICAgICAgICAgICAgICAgICAgICAgYXV0aF90b2tlbiA9IGAmdG9rZW49JHt0b2tlbn1gOwogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgbGV0IHBsYXlfdXJsID0ganNvbi5kYXRhOwogICAgICAgICAgICAgICAgICAgIGlmIChwbGF5X3VybCAmJiBwbGF5X3VybC5zdGFydHNXaXRoKCdodHRwJykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgZmluYWxfdXJsID0gcGxheV91cmw7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGlmICghZmluYWxfdXJsKSB7CiAgICAgICAgICAgIGZpbmFsX3VybCA9IHJhd191cmw7CiAgICAgICAgICAgIGlmICgvKD86d3d3XC5pcWl5aXx2XC5xcXx2XC55b3VrdXx3d3dcLm1ndHZ8d3d3XC5iaWxpYmlsaSlcLmNvbS8udGVzdChyYXdfdXJsKSkgewogICAgICAgICAgICAgICAganggPSAxOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIHBhcnNlOiBqeCwKICAgICAgICAgICAgdXJsOiBmaW5hbF91cmwsCiAgICAgICAgICAgIGhlYWRlcjogeyAnVXNlci1BZ2VudCc6IHRoaXMuaGVhZGVyc1snVXNlci1BZ2VudCddIH0KICAgICAgICB9OwogICAgfQp9Ow== \ No newline at end of file diff --git "a/spider/js/DJ\351\237\263\344\271\220[\345\220\254].js" "b/spider/js/DJ\351\237\263\344\271\220[\345\220\254].js" index f5266a62..9cbca601 100644 --- "a/spider/js/DJ\351\237\263\344\271\220[\345\220\254].js" +++ "b/spider/js/DJ\351\237\263\344\271\220[\345\220\254].js" @@ -6,6 +6,10 @@ title: 'DJ音乐', author: 'EylinSir', '类型': '影视', + mergeList: true, + more: { + mergeList: 1 + }, logo: 'https://pic.289.com/up/2023-12/20231219154340126.png', lang: 'ds' }) diff --git a/spider/js/_debug.js b/spider/js/_debug.js new file mode 100644 index 00000000..d32b3df1 --- /dev/null +++ b/spider/js/_debug.js @@ -0,0 +1,85 @@ +// _debug.js +// 测试方法: http://localhost:5757/api/_debug?pwd=dzyyds +var rule = { + title: '_debug', + description: '这是描述', + 类型: '测试', + searchUrl: '', + class_parse: async () => { + log(`[${rule.title}] --class_parse--`); + return [ + {type_id: '1', type_name: '电影'}, + {type_id: '2', type_name: '电视剧'}, + {type_id: '3', type_name: '综艺'}, + {type_id: '4', type_name: '动漫'}, + ] + }, + 预处理: async () => { + log(`[${rule.title}] --预处理--`); + rule.title = '_debug'; + }, + 推荐: async () => { + // return '这是推荐:' + rule.title; + let d = []; + let html = '{}'; + html = await request('https://httpbin.org/headers', { + headers: { + 'Accept': '*/*', + 'User-Agent': '' + } + }); + // log(html); + d.push({ + title: 'request结果1-传空UA', + content: html.parseX.headers, + }); + + html = await request('https://httpbin.org/headers', { + headers: { + 'Accept': '*/*', + 'User-Agent': 'RemoveUserAgent', + } + }); + // log(html); + d.push({ + title: 'request结果2-不传UA', + content: html.parseX.headers, + }); + + html = (await req('https://httpbin.org/headers', { + headers: { + 'Accept': '*/*', + 'User-Agent': 'RemoveUserAgent', + } + })).content; + d.push({ + title: 'req结果-不传UA', + content: html.parseX.headers, + }); + + html = (await req('https://conn.origjoy.com/auth/init?appid=d4eeacc6cec3434fbc8c41608a3056a0&mac=0afa691314fd_a12d4a7c9n12&sn=a12d4a7c9n12&time=1768728113&ver=2.0&vn=4.1.3.03281430&sign=6a1ee16242b93a3ae6492bc55992b691', + { + headers: { + 'Accept': '*/*', + 'User-Agent': 'RemoveUserAgent', + } + })).content; + d.push({ + title: 'req结果-60wmv', + content: html, + }); + return d; + }, + 一级: async () => { + return '这是一级:' + rule.title + }, + 二级: async () => { + return '这是二级:' + rule.title + }, + 搜索: async () => { + return ['这是搜索:' + rule.title] + }, + lazy: async () => { + return '这是播放:' + rule.title + }, +}; \ No newline at end of file diff --git a/spider/js/_lib.request.js b/spider/js/_lib.request.js index 8e9f85be..2df7a794 100644 --- a/spider/js/_lib.request.js +++ b/spider/js/_lib.request.js @@ -1,5 +1,19 @@ const iconv = require('iconv-lite'); +function sanitizeUserAgent(headers) { + if (!headers) { + return headers; + } + const keys = Object.keys(headers); + for (const key of keys) { + if (key.toLowerCase() === 'user-agent' && headers[key] === 'RemoveUserAgent') { + delete headers[key]; + break; + } + } + return headers; +} + async function requestHtml(url, options) { try { let html = (await req(url, options)).content; @@ -30,15 +44,20 @@ async function getPublicIp() { async function getHtml(config) { try { - return await axios.request(typeof config === "string" ? config : { + if (typeof config === "string") { + return await axios.request(config) + } + const cfg = { url: config.url, method: config.method || 'GET', headers: config.headers || { 'User-Agent': PC_UA }, data: config.data || '', - responseType: config.responseType || '',//'arraybuffer' - }) + responseType: config.responseType || '' + }; + cfg.headers = sanitizeUserAgent(cfg.headers); + return await axios.request(cfg) } catch (e) { return e.response } @@ -54,6 +73,7 @@ async function req_(reqUrl, mt, headers, data) { }, data: data || '', }; + config.headers = sanitizeUserAgent(config.headers); let res = await axios.request(config); return res.data; } @@ -68,6 +88,7 @@ async function req_encoding(reqUrl, mt, headers, encoding, data) { data: data || '', responseType: 'arraybuffer' }; + config.headers = sanitizeUserAgent(config.headers); let res = await axios.request(config); if (encoding) { res.data = iconv.decode(res.data, encoding); @@ -88,6 +109,7 @@ async function req_proxy(reqUrl, mt, headers, data) { port: "7890" } }; + config.headers = sanitizeUserAgent(config.headers); if (data) { config.data = data; } diff --git "a/spider/js/\344\270\203\347\214\253\345\260\217\350\257\264[\344\271\246].js" "b/spider/js/\344\270\203\347\214\253\345\260\217\350\257\264[\344\271\246].js" index 6bc4e80b..5017eaf7 100644 --- "a/spider/js/\344\270\203\347\214\253\345\260\217\350\257\264[\344\271\246].js" +++ "b/spider/js/\344\270\203\347\214\253\345\260\217\350\257\264[\344\271\246].js" @@ -113,18 +113,20 @@ var rule = { }, 二级: async function () { let {input, pdfa, pdfh, pd} = this; + // console.log('Detail Input:', input); let html = await this.request(input); + // console.log('Detail HTML Length:', html.length); let VOD = {}; - VOD.vod_name = pdfh(html, 'span.txt&&Text'); + VOD.vod_name = pdfh(html, '.book-detail-info .title .txt&&Text'); VOD.type_name = pdfh(html, '.qm-tag:eq(-1)&&Text'); VOD.vod_pic = pd(html, '.wrap-pic&&img&&src'); - VOD.vod_content = pdfh(html, '.book-introduction-item&&.qm-with-title-tb&&Text'); + VOD.vod_content = pdfh(html, '.intro&&Text'); VOD.vod_remarks = pdfh(html, '.qm-tag&&Text'); VOD.vod_year = ''; VOD.vod_area = ''; VOD.vod_actor = pdfh(html, '.sub-title&&span:eq(1)&&Text'); VOD.vod_director = pdfh(html, '.sub-title&&span&&a&&Text'); - VOD.vod_play_from = pdfh(html, '.qm-sheader&&img&&alt'); + VOD.vod_play_from = '七猫小说'; let book_id = input.match(/shuku\/(\d+)/)[1]; let listUrl = buildUrl(rule.listUrl2, { book_id: book_id diff --git "a/spider/js/\344\272\272\344\272\272\345\275\261\350\247\206[\344\274\230].js" "b/spider/js/\344\272\272\344\272\272\345\275\261\350\247\206[\344\274\230].js" new file mode 100644 index 00000000..99611f90 --- /dev/null +++ "b/spider/js/\344\272\272\344\272\272\345\275\261\350\247\206[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '人人影视', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfkurrkurrlvbHop4YnLAogICAgaG9zdDogJ2h0dHBzOi8vcnJzcC1hcGkua2VqaXFpYW54aWFuLmNvbTo2MDQyNScsCiAgICB1cmw6ICcvYXBpLnBocC9tYWluX3Byb2dyYW0vbW92aWVzQWxsLycsCiAgICBzZWFyY2hVcmw6ICcvYXBpLnBocC9zZWFyY2gvc3ludGhldGljYWxTZWFyY2gvJywKICAgIHNlYXJjaGFibGU6IDEsCiAgICBxdWlja1NlYXJjaDogMCwKICAgIGZpbHRlcmFibGU6IDEsCiAgICB0aW1lb3V0OiAxMDAwMCwKICAgIHBsYXlfcGFyc2U6IHRydWUsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiAncnJzcC53YW5nJywKICAgICAgICAnb3JpZ2luJzogJyonLAogICAgICAgICdzZWMtZmV0Y2gtZGVzdCc6ICdlbXB0eScsCiAgICAgICAgJ3NlYy1mZXRjaC1tb2RlJzogJ2NvcnMnLAogICAgICAgICdzZWMtZmV0Y2gtc2l0ZSc6ICdjcm9zcy1zaXRlJywKICAgICAgICAnc2VjLWNoLXVhJzogJyJOb3RfQSBCcmFuZCI7dj0iOCIsICJDaHJvbWl1bSI7dj0iMTIwIicsCiAgICAgICAgJ3NlYy1jaC11YS1tb2JpbGUnOiAnPzAnLAogICAgICAgICdzZWMtY2gtdWEtcGxhdGZvcm0nOiAnIldpbmRvd3MiJywKICAgICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24sIHRleHQvcGxhaW4sICovKicsCiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywKICAgICAgICAnYWNjZXB0LWxhbmd1YWdlJzogJ3poLUNOJwogICAgfSwKCiAgICBwb3N0OiBhc3luYyBmdW5jdGlvbihwYXRoLCBkYXRhKSB7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godGhpcy5ob3N0ICsgcGF0aCwgewogICAgICAgICAgICBtZXRob2Q6ICdwb3N0JywKICAgICAgICAgICAgaGVhZGVyczogdGhpcy5oZWFkZXJzLAogICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShkYXRhKSwKICAgICAgICAgICAgdGltZW91dDogdGhpcy50aW1lb3V0CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpOwogICAgfSwKCiAgICBjbGFzc19wYXJzZTogYXN5bmMgZnVuY3Rpb24oKSB7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgY2xhc3M6IFsKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnMScsICd0eXBlX25hbWUnOiAn55S15b2xJyB9LAogICAgICAgICAgICAgICAgeyAndHlwZV9pZCc6ICcyJywgJ3R5cGVfbmFtZSc6ICfnlLXop4bliacnIH0sCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzMnLCAndHlwZV9uYW1lJzogJ+e7vOiJuicgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnNScsICd0eXBlX25hbWUnOiAn5Yqo5ryrJyB9LAogICAgICAgICAgICAgICAgeyAndHlwZV9pZCc6ICc0JywgJ3R5cGVfbmFtZSc6ICfnuqrlvZXniYcnIH0sCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzYnLCAndHlwZV9uYW1lJzogJ+efreWJpycgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnNycsICd0eXBlX25hbWUnOiAn54m55Yir6IqC55uuJyB9LAogICAgICAgICAgICAgICAgeyAndHlwZV9pZCc6ICc4JywgJ3R5cGVfbmFtZSc6ICflsJHlhL/lhoXlrrknIH0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgZmlsdGVyczoge30KICAgICAgICB9OwogICAgfSwKCiAgICDmjqjojZA6IGFzeW5jIGZ1bmN0aW9uKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMu5LiA57qnKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKTsKICAgIH0sCgogICAg5LiA57qnOiBhc3luYyBmdW5jdGlvbih0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGlmICghdGhpcy5ob3N0KSByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICBsZXQgZGF0YSA9IGF3YWl0IHRoaXMucG9zdCgnL2FwaS5waHAvbWFpbl9wcm9ncmFtL21vdmllc0FsbC8nLCB7CiAgICAgICAgICAgICd0eXBlJzogdGlkLCAnc29ydCc6ICd2b2RfdGltZScsICdwYWdlJzogcGcsICdsaW1pdCc6ICc2MCcsCiAgICAgICAgICAgICdhcmVhJzogJycsICdzdHlsZSc6ICcnLCAndGltZSc6ICcnLCAncGF5JzogJycKICAgICAgICB9KTsKICAgICAgICBsZXQgdmlkZW9zID0gdGhpcy5hcnIydm9kcyhkYXRhLmRhdGEubGlzdCkubWFwKGl0ZW0gPT4gKHsKICAgICAgICAgICAgdGl0bGU6IGl0ZW0udm9kX25hbWUsCiAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9hcGkucGhwL3BsYXllci9kZXRhaWxzLz9pZD0ke2l0ZW0udm9kX2lkfWAsCiAgICAgICAgICAgIGRlc2M6IGl0ZW0udm9kX3JlbWFya3MsCiAgICAgICAgICAgIHBpY191cmw6IGl0ZW0udm9kX3BpYywKICAgICAgICAgICAgdm9kX3llYXI6IGl0ZW0udm9kX3llYXIKICAgICAgICB9KSk7CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdCh2aWRlb3MpOwogICAgfSwKCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uKCkgewogICAgICAgIGxldCBpZCA9IHRoaXMuaW5wdXQubWF0Y2goL2lkPShcZCspLylbMV07CiAgICAgICAgbGV0IGRhdGEgPSBhd2FpdCB0aGlzLnBvc3QoJy9hcGkucGhwL3BsYXllci9kZXRhaWxzLycsIHsgJ2lkJzogaWQgfSk7CiAgICAgICAgbGV0IGQgPSBkYXRhLmRldGFpbERhdGE7CiAgICAgICAgcmV0dXJuIGQgPyB7CiAgICAgICAgICAgIHZvZF9pZDogZC52b2RfaWQudG9TdHJpbmcoKSwKICAgICAgICAgICAgdm9kX25hbWU6IGQudm9kX25hbWUsCiAgICAgICAgICAgIHZvZF9waWM6IGQudm9kX3BpYywKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGQudm9kX3JlbWFya3MsCiAgICAgICAgICAgIHZvZF95ZWFyOiBkLnZvZF95ZWFyLAogICAgICAgICAgICB2b2RfYXJlYTogZC52b2RfYXJlYSwKICAgICAgICAgICAgdm9kX2FjdG9yOiBkLnZvZF9hY3RvciwKICAgICAgICAgICAgdm9kX2NvbnRlbnQ6IGQudm9kX2NvbnRlbnQsCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206IGQudm9kX3BsYXlfZnJvbSwKICAgICAgICAgICAgdm9kX3BsYXlfdXJsOiBkLnZvZF9wbGF5X3VybCwKICAgICAgICAgICAgdHlwZV9uYW1lOiBkLnZvZF9jbGFzcwogICAgICAgIH0gOiB7fTsKICAgIH0sCgogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbigpIHsKICAgICAgICBsZXQgZGF0YSA9IGF3YWl0IHRoaXMucG9zdCgnL2FwaS5waHAvc2VhcmNoL3N5bnRoZXRpY2FsU2VhcmNoLycsIHsgJ2tleXdvcmQnOiB0aGlzLktFWSB9KTsKICAgICAgICBsZXQgbGlzdCA9IFsKICAgICAgICAgICAgLi4uKGRhdGEuZGF0YS5jaGFzaW5nRmFuQ29ycmVsYXRpb24gfHwgW10pLCAKICAgICAgICAgICAgLi4uKGRhdGEuZGF0YS5tb3ZpZXNDb3JyZWxhdGlvbiB8fCBbXSkKICAgICAgICBdOwogICAgICAgIGxldCByZXN1bHQgPSB0aGlzLmFycjJ2b2RzKGxpc3QpLm1hcChpdGVtID0+ICh7CiAgICAgICAgICAgIHRpdGxlOiBpdGVtLnZvZF9uYW1lLAogICAgICAgICAgICB1cmw6IGAke3RoaXMuaG9zdH0vYXBpLnBocC9wbGF5ZXIvZGV0YWlscy8/aWQ9JHtpdGVtLnZvZF9pZH1gLAogICAgICAgICAgICBkZXNjOiBpdGVtLnZvZF9yZW1hcmtzLAogICAgICAgICAgICBwaWNfdXJsOiBpdGVtLnZvZF9waWMsCiAgICAgICAgICAgIHZvZF95ZWFyOiBpdGVtLnZvZF95ZWFyCiAgICAgICAgfSkpOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQocmVzdWx0KTsKICAgIH0sCgogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24oKSB7CiAgICAgICAgbGV0IHVybCA9IHRoaXMuaW5wdXQ7CiAgICAgICAgbGV0IGp4ID0gMDsKICAgICAgICBjb25zdCB2aXBSZWdleCA9IC8oPzp3d3dcLmlxaXlpfHZcLnFxfHZcLnlvdWt1fHd3d1wubWd0dnx3d3dcLmJpbGliaWxpKVwuY29tLzsKICAgICAgICB0cnkgewogICAgICAgICAgICBsZXQgZGF0YSA9IGF3YWl0IHRoaXMucG9zdCgnL2FwaS5waHAvcGxheWVyL3BheVZpZGVvVXJsLycsIHsgJ3VybCc6IHVybCB9KTsKICAgICAgICAgICAgbGV0IHBsYXlfdXJsID0gZGF0YS5kYXRhLnVybDsKICAgICAgICAgICAgaWYgKHBsYXlfdXJsICYmIHBsYXlfdXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSkgewogICAgICAgICAgICAgICAgdXJsID0gcGxheV91cmw7CiAgICAgICAgICAgIH0KICAgICAgICB9IGNhdGNoIChlKSB7fQogICAgICAgIGlmICh2aXBSZWdleC50ZXN0KHVybCkpIGp4ID0gMTsKCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgcGFyc2U6IGp4LAogICAgICAgICAgICB1cmw6IHVybCwKICAgICAgICAgICAgaGVhZGVyOiB7CiAgICAgICAgICAgICAgICAnVXNlci1BZ2VudCc6ICdycnNwLndhbmcnLAogICAgICAgICAgICAgICAgJ29yaWdpbic6ICcqJywKICAgICAgICAgICAgICAgICdyZWZlcmVyJzogJ2h0dHBzOi8vZG9jcy5xcS5jb20vJwogICAgICAgICAgICB9CiAgICAgICAgfTsKICAgIH0sCgogICAgYXJyMnZvZHM6IGZ1bmN0aW9uKGFycikgewogICAgICAgIHJldHVybiAoYXJyIHx8IFtdKS5tYXAoaSA9PiAoewogICAgICAgICAgICAndm9kX2lkJzogaS52b2RfaWQudG9TdHJpbmcoKSwKICAgICAgICAgICAgJ3ZvZF9uYW1lJzogaS52b2RfbmFtZSwKICAgICAgICAgICAgJ3ZvZF9waWMnOiBpLnZvZF9waWMsCiAgICAgICAgICAgICd2b2RfcmVtYXJrcyc6IGkudm9kX3NlcmlhbCA9PT0gJzEnID8gJzHpm4YnIDogYOivhOWIhu+8miR7aS52b2Rfc2NvcmUgfHwgaS52b2RfZG91YmFuX3Njb3JlIHx8ICcnfWAsCiAgICAgICAgICAgICd2b2RfeWVhcic6IGkudm9kX3llYXIgfHwgbnVsbAogICAgICAgIH0pKTsKICAgIH0KfTs= \ No newline at end of file diff --git "a/spider/js/\345\205\211\347\244\276\346\274\253\347\224\273[\347\224\273].js" "b/spider/js/\345\205\211\347\244\276\346\274\253\347\224\273[\347\224\273].js" new file mode 100644 index 00000000..7e7a3948 --- /dev/null +++ "b/spider/js/\345\205\211\347\244\276\346\274\253\347\224\273[\347\224\273].js" @@ -0,0 +1,89 @@ +/* +@header({ + searchable: 2, + filterable: 0, + quickSearch: 0, + title: '光社漫画', + author: 'EylinSir', + '类型': '漫画', + logo: 'https://m.g-mh.org/favicon.ico', + lang: 'ds' +}) +*/ + +var rule = { + 类型: '漫画', + author: 'EylinSir', + title: '光社漫画', + host: 'https://m.g-mh.org', + url: '/manga-genre/fyclass/page/fypage', + searchUrl: '/s/**?page=fypage', + logo: 'https://m.g-mh.org/favicon.ico', + searchable: 2, + quickSearch: 0, + timeout: 5000, + play_parse: true, + class_name: '热门&国漫&韩漫&日漫&欧美&其他', + class_url: 'hots&cn&kr&jp&ou-mei&qita', + headers: { + 'User-Agent': 'PC_UA', + 'Referer': 'https://m.g-mh.org/' + }, + + _parse: function(html) { + return (html.match(//g) || []).map(it => { + let img = it.match(/src=["']([^"']+)["']/)[1]; + let descMatch = it.match(/

([\s\S]*?)<\/p>/); + let originalImgUrl = img.startsWith('http') ? img : this.host + img; + let jpgImgUrl = 'https://wsrv.nl/?url=' + encodeURIComponent(originalImgUrl) + '&output=jpg'; + return { + title: it.match(/]*>([\s\S]*?)<\/h3>/)[1].trim(), + img: jpgImgUrl, + url: it.match(/href=["']([^"']+)["']/)[1], + desc: descMatch ? descMatch[1].trim() : '' + }; + }); + }, + + 一级: async function(tid, pg, filter, extend) { + return setResult(this._parse(await request(this.input, { headers: this.headers }))); + }, + + 推荐: async function(tid, pg, filter, extend) { + return setResult(this._parse(await request(this.input, { headers: this.headers }))); + }, + + 二级: async function(ids) { + let html = await request(this.input, { headers: this.headers }); + let mid = (html.match(/data-mid=["'](\d+)["']/) || html.match(/mid\s*:\s*["']?(\d+)["']?/))[1]; + let json = JSON.parse(await request(`https://api-get-v3.mgsearcher.com/api/manga/get?mid=${mid}&mode=all`, { headers: this.headers })); + let chapters = json.data.chapters || json.data.data.chapters; + return { + vod_id: ids[0], + vod_name: pdfh(html, 'h1&&Text'), + vod_pic: pdfh(html, '.rounded-lg img&&src'), + vod_content: pdfh(html, '.text-medium&&Text'), + type_name: "漫画", + vod_play_from: "光社漫画", + vod_play_url: chapters.map(ch => { + return `${ch.attributes?.title || 'Chapter ' + ch.id}$https://api-get-v3.mgsearcher.com/api/chapter/getinfo?m=${mid}&c=${ch.id}`; + }).join("#") + }; + }, + + 搜索: async function(wd, quick, pg) { + return setResult(this._parse(await request(this.input, { headers: this.headers }))); + }, + + lazy: async function(flag, id, flags) { + let data = JSON.parse(await request(id, { headers: this.headers })); + let images = data.data.info.images.images.map(img => + img.url.startsWith('http') ? img.url : "https://f40-1-4.g-mh.online" + img.url + ); + return { + parse: 0, + url: "pics://" + images.join("&&"), + header: this.headers + }; + } +}; diff --git "a/spider/js/\345\205\224\345\260\217\350\264\235[\345\204\277].js" "b/spider/js/\345\205\224\345\260\217\350\264\235[\345\204\277].js" index 43927001..d97c9ad4 100644 --- "a/spider/js/\345\205\224\345\260\217\350\264\235[\345\204\277].js" +++ "b/spider/js/\345\205\224\345\260\217\350\264\235[\345\204\277].js" @@ -5,6 +5,10 @@ quickSearch: 0, title: '兔小贝[儿]', '类型': '影视', + mergeList: 1, + more: { + mergeList: 1 + }, lang: 'ds' }) */ @@ -12,6 +16,7 @@ var rule = { title: '兔小贝[儿]', host: 'https://www.tuxiaobei.com', + mergeList: 1, homeUrl: '', url: '/list/mip-data?typeId=fyclass&page=fypage&callback=', detailUrl: '/play/fyid', diff --git "a/spider/js/\345\211\247\346\265\267\345\275\261\350\247\206[\344\274\230].js" "b/spider/js/\345\211\247\346\265\267\345\275\261\350\247\206[\344\274\230].js" new file mode 100644 index 00000000..8cfc9a21 --- /dev/null +++ "b/spider/js/\345\211\247\346\265\267\345\275\261\350\247\206[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '剧海影视', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfliafmtbflvbHop4YnLAogICAgaG9zdDogJ2h0dHA6Ly9wcXlzZHEuZ3h0dGtlamkuY246MjAyNicsCiAgICBob21lVXJsOiAnaHR0cDovL3p4eXMuZ2FvemhvdWtqLmNuJywKICAgIHVybDogJy9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVMaXN0JnR5cGVfaWQ9ZnljbGFzcyZwYWdlPWZ5cGFnZSZsaW1pdD0xOCcsCiAgICBzZWFyY2hVcmw6ICcvcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuU2VhcmNoVm9kJmtleT0qKicsCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDAsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogMTAwMDAsCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAga2V5OiAnJywKICAgIGl2OiAnMTIzNDU2Nzg5MDEyMzQ1NicsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiAnb2todHRwLzMuMTIuMCcsCiAgICAgICAgJ0Nvbm5lY3Rpb24nOiAnS2VlcC1BbGl2ZScsCiAgICAgICAgJ0FjY2VwdC1FbmNvZGluZyc6ICdnemlwJwogICAgfSwKICAgIAogICAgY2xhc3NfcGFyc2U6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBpZiAoIXRoaXMuaG9zdCkgcmV0dXJuIHsgY2xhc3M6IFtdLCBmaWx0ZXJzOiB7fSB9OwogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIGNsYXNzOiBbCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzEnLCAndHlwZV9uYW1lJzogJ+eUteW9sScgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnMicsICd0eXBlX25hbWUnOiAn6L+e57ut5YmnJyB9LAogICAgICAgICAgICAgICAgeyAndHlwZV9pZCc6ICczJywgJ3R5cGVfbmFtZSc6ICfnu7zoibonIH0sCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzQnLCAndHlwZV9uYW1lJzogJ+WKqOa8qycgfQogICAgICAgICAgICBdLAogICAgICAgICAgICBmaWx0ZXJzOiB7fQogICAgICAgIH07CiAgICB9LAoKICAgIOmihOWkhOeQhjogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCByZXNwMSA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldFR5cGVMaXN0YCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgICAgIGxldCB0ZXh0MSA9IGF3YWl0IHJlc3AxLnRleHQoKTsKICAgICAgICAgICAgaWYgKHRleHQxLnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0MSA9IHRleHQxLnN1YnN0cmluZygxKTsKICAgICAgICAgICAgbGV0IGRhdGExID0gSlNPTi5wYXJzZSh0ZXh0MSk7CiAgICAgICAgICAgIGxldCBzaWduX3N0YXJ0ID0gKGRhdGExLkRhdGEgfHwgW10pLmZpbmQoaSA9PiBpLnR5cGVfaWQudG9TdHJpbmcoKSA9PT0gJzEnKT8udHlwZV91bmlvbiB8fCAnJzsKICAgICAgICAgICAgbGV0IHJlc3AyID0gYXdhaXQgX2ZldGNoKGAke3RoaXMuaG9zdH0vcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuR2V0QWRUeXBlYCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgICAgIGxldCB0ZXh0MiA9IGF3YWl0IHJlc3AyLnRleHQoKTsKICAgICAgICAgICAgaWYgKHRleHQyLnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0MiA9IHRleHQyLnN1YnN0cmluZygxKTsKICAgICAgICAgICAgbGV0IGRhdGEyID0gSlNPTi5wYXJzZSh0ZXh0Mik7CiAgICAgICAgICAgIGxldCBzaWduX2VuZCA9IGRhdGEyLkRhdGEudG1wIHx8ICcnOwogICAgICAgICAgICBsZXQgZnVsbEtleSA9IHNpZ25fc3RhcnQgKyBzaWduX2VuZDsKICAgICAgICAgICAgaWYgKGZ1bGxLZXkubGVuZ3RoID49IDE2KSB7CiAgICAgICAgICAgICAgICB0aGlzLmtleSA9IGZ1bGxLZXkuc3Vic3RyaW5nKDAsIDE2KTsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgY29uc29sZS5lcnJvcign5Yid5aeL5YyWIEtleSDlpLHotKU6JywgZSk7CiAgICAgICAgICAgIHRoaXMuaG9zdCA9ICcnOwogICAgICAgIH0KICAgIH0sCiAgICAKICAgIOaOqOiNkDogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGlmICghdGhpcy5ob3N0KSByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldEhvbWVMZXZlbGAsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgaWYgKHRleHQuc3RhcnRzV2l0aCgn77u/JykpIHRleHQgPSB0ZXh0LnN1YnN0cmluZygxKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UodGV4dCk7CiAgICAgICAgCiAgICAgICAgbGV0IHZpZGVvcyA9IHR5cGVvZiBkYXRhID09PSAnb2JqZWN0JyAmJiBkYXRhICE9PSBudWxsID8gCiAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZGF0YSkKICAgICAgICAgICAgICAgIC5maWx0ZXIoaXRlbSA9PiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiYgaXRlbSAhPT0gbnVsbCkKICAgICAgICAgICAgICAgIC5mbGF0TWFwKGl0ZW0gPT4gT2JqZWN0LnZhbHVlcyhpdGVtKSkKICAgICAgICAgICAgICAgIC5maWx0ZXIoQXJyYXkuaXNBcnJheSkKICAgICAgICAgICAgICAgIC5mbGF0TWFwKGxpc3QgPT4gbGlzdC5tYXAoayA9PiAoewogICAgICAgICAgICAgICAgICAgIHRpdGxlOiBrLnZvZF9uYW1lLAogICAgICAgICAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVNdkJ5SWQmdm9kaWQ9JHtrLnZvZF9pZH1gLAogICAgICAgICAgICAgICAgICAgIGRlc2M6IGsudm9kX3JlbWFya3MsCiAgICAgICAgICAgICAgICAgICAgcGljX3VybDogay52b2RfcGljLAogICAgICAgICAgICAgICAgICAgIHZvZF95ZWFyOiBrLnZvZF95ZWFyLAogICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGsudm9kX2NvbnRlbnQKICAgICAgICAgICAgICAgIH0pKSkKICAgICAgICAgICAgOiBbXTsKICAgICAgICAKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGlmICghdGhpcy5ob3N0KSByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVMaXN0JnR5cGVfaWQ9JHt0aWR9JnBhZ2U9JHtwZ30mbGltaXQ9MThgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IHRleHQgPSBhd2FpdCByZXNwLnRleHQoKTsKICAgICAgICBpZiAodGV4dC5zdGFydHNXaXRoKCfvu78nKSkgdGV4dCA9IHRleHQuc3Vic3RyaW5nKDEpOwogICAgICAgIGxldCBkYXRhID0gSlNPTi5wYXJzZSh0ZXh0KTsKICAgICAgICBsZXQgdmlkZW9zID0gKGRhdGEuRGF0YSB8fCBbXSkubWFwKGkgPT4gKHsKICAgICAgICAgICAgdGl0bGU6IGkudm9kX25hbWUsCiAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVNdkJ5SWQmdm9kaWQ9JHtpLnZvZF9pZH1gLAogICAgICAgICAgICBkZXNjOiBpLnZvZF9yZW1hcmtzLAogICAgICAgICAgICBwaWNfdXJsOiBpLnZvZF9waWMsCiAgICAgICAgICAgIHZvZF95ZWFyOiBpLnZvZF95ZWFyLAogICAgICAgICAgICBjb250ZW50OiBpLnZvZF9jb250ZW50CiAgICAgICAgfSkpOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQodmlkZW9zKTsKICAgIH0sCiAgICAKICAgIOS6jOe6pzogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB2b2RJZCA9IHRoaXMuaW5wdXQubWF0Y2goL3ZvZGlkPShcZCspLylbMV07CiAgICAgICAgbGV0IHVybCA9IGAke3RoaXMuaG9zdH0vcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuR2V0T25saW5lTXZCeUlkJnZvZGlkPSR7dm9kSWR9YDsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaCh1cmwsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgaWYgKHRleHQuc3RhcnRzV2l0aCgn77u/JykpIHRleHQgPSB0ZXh0LnN1YnN0cmluZygxKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UodGV4dCk7CiAgICAgICAgbGV0IGZpcnN0SXRlbSA9IChkYXRhLkRhdGEgfHwgW10pLmZpbmQoaSA9PiB0eXBlb2YgaSA9PT0gJ29iamVjdCcgJiYgaSAhPT0gbnVsbCk7CiAgICAgICAgCiAgICAgICAgaWYgKGZpcnN0SXRlbSkgewogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgdm9kX2lkOiBmaXJzdEl0ZW0udm9kX2lkLnRvU3RyaW5nKCksCiAgICAgICAgICAgICAgICB2b2RfbmFtZTogZmlyc3RJdGVtLnZvZF9uYW1lLAogICAgICAgICAgICAgICAgdm9kX3BpYzogZmlyc3RJdGVtLnZvZF9waWMsCiAgICAgICAgICAgICAgICB2b2RfcmVtYXJrczogZmlyc3RJdGVtLnZvZF9yZW1hcmtzLAogICAgICAgICAgICAgICAgdm9kX3llYXI6IGZpcnN0SXRlbS52b2RfeWVhciwKICAgICAgICAgICAgICAgIHZvZF9hcmVhOiBmaXJzdEl0ZW0udm9kX2FyZWEsCiAgICAgICAgICAgICAgICB2b2RfYWN0b3I6IGZpcnN0SXRlbS52b2RfYWN0b3IsCiAgICAgICAgICAgICAgICB2b2RfY29udGVudDogZmlyc3RJdGVtLnZvZF9jb250ZW50LAogICAgICAgICAgICAgICAgdm9kX3BsYXlfZnJvbTogZmlyc3RJdGVtLnZvZF9wbGF5X2Zyb20sCiAgICAgICAgICAgICAgICB2b2RfcGxheV91cmw6IGZpcnN0SXRlbS52b2RfcGxheV91cmwsCiAgICAgICAgICAgICAgICB0eXBlX25hbWU6IGZpcnN0SXRlbS52b2RfY2xhc3MKICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgcmV0dXJuIHt9OwogICAgfSwKICAgIAogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgaWYgKCF0aGlzLmhvc3QpIHJldHVybiBzZXRSZXN1bHQoW10pOwogICAgICAgIGxldCB1cmwgPSBgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LlNlYXJjaFZvZCZrZXk9JHtlbmNvZGVVUklDb21wb25lbnQodGhpcy5LRVkpfWA7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godXJsLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICBsZXQgdGV4dCA9IGF3YWl0IHJlc3AudGV4dCgpOwogICAgICAgIGlmICh0ZXh0LnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0ID0gdGV4dC5zdWJzdHJpbmcoMSk7CiAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKHRleHQpOwogICAgICAgIGxldCB2aWRlb3MgPSAoZGF0YS5EYXRhIHx8IFtdKS5tYXAoaSA9PiAoewogICAgICAgICAgICB0aXRsZTogaS52b2RfbmFtZSwKICAgICAgICAgICAgdXJsOiBgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldE9ubGluZU12QnlJZCZ2b2RpZD0ke2kudm9kX2lkfWAsCiAgICAgICAgICAgIGRlc2M6IGkudm9kX3JlbWFya3MsCiAgICAgICAgICAgIHBpY191cmw6IGkudm9kX3BpYywKICAgICAgICAgICAgdm9kX3llYXI6IGkudm9kX3llYXIsCiAgICAgICAgICAgIGNvbnRlbnQ6IGkudm9kX2NvbnRlbnQKICAgICAgICB9KSk7CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdCh2aWRlb3MpOwogICAgfSwKICAgIAogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBqeCA9IDA7CiAgICAgICAgbGV0IHVybCA9ICcnOwogICAgICAgIGxldCB1YSA9ICdjb20uZ2prai56eHlzZHEvMS4xLjAgKExpbnV4O0FuZHJvaWQgMTIpIEV4b1BsYXllckxpYi8yLjEyLjMnOwogICAgICAgIGxldCBpZCA9IHRoaXMuaW5wdXQ7CiAgICAgICAgaWYgKGlkLm1hdGNoKC9eaHR0cHM/OlwvXC8uKlwuKG0zdTh8bXA0fGZsdnxta3YpL2kpKSB7CiAgICAgICAgICAgIHVybCA9IGlkOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldE12SlhVcmxCeVVybCZ1cmw9JHtpZH1gLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICAgICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgICAgICAgICBpZiAodGV4dC5zdGFydHNXaXRoKCfvu78nKSkgdGV4dCA9IHRleHQuc3Vic3RyaW5nKDEpOwogICAgICAgICAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKHRleHQpOwogICAgICAgICAgICAgICAgbGV0IHJhd191cmwgPSBkYXRhLkRhdGEudXJsOwogICAgICAgICAgICAgICAgLy8g5bCd6K+V5L2/55SoQ3J5cHRvSlPov5vooYxBRVPop6Plr4YKICAgICAgICAgICAgICAgIGlmICh0aGlzLmtleSkgewogICAgICAgICAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBrZXkgPSBDcnlwdG9KUy5lbmMuVXRmOC5wYXJzZSh0aGlzLmtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBpdiA9IENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKHRoaXMuaXYpOwogICAgICAgICAgICAgICAgICAgICAgICBsZXQgZGVjcnlwdGVkVXJsID0gQ3J5cHRvSlMuQUVTLmRlY3J5cHQocmF3X3VybCwga2V5LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGU6IENyeXB0b0pTLm1vZGUuQ0JDLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkZGluZzogQ3J5cHRvSlMucGFkLlBrY3M1CiAgICAgICAgICAgICAgICAgICAgICAgIH0pLnRvU3RyaW5nKENyeXB0b0pTLmVuYy5VdGY4KTsKICAgICAgICAgICAgICAgICAgICAgICAgdXJsID0gZGVjcnlwdGVkVXJsICYmIGRlY3J5cHRlZFVybC5zdGFydHNXaXRoKCdodHRwJykgPyBkZWNyeXB0ZWRVcmwgOiB1cmw7CiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdBRVPop6Plr4blpLHotKU6JywgZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBpZiAoLyg/Ond3d1wuaXFpeWl8dlwucXF8dlwueW91a3V8d3d3XC5tZ3R2fHd3d1wuYmlsaWJpbGkpXC5jb20vLnRlc3QoaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gaWQ7CiAgICAgICAgICAgICAgICAgICAganggPSAxOwogICAgICAgICAgICAgICAgICAgIHVhID0gTU9CSUxFX1VBOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIHBhcnNlOiBqeCwKICAgICAgICAgICAgdXJsLAogICAgICAgICAgICBoZWFkZXI6IHsgJ1VzZXItQWdlbnQnOiB1YSB9CiAgICAgICAgfTsKICAgIH0KfTs= \ No newline at end of file diff --git "a/spider/js/\345\212\250\346\274\253\345\225\246[\347\224\273].js" "b/spider/js/\345\212\250\346\274\253\345\225\246[\347\224\273].js" new file mode 100644 index 00000000..f8afcdd1 --- /dev/null +++ "b/spider/js/\345\212\250\346\274\253\345\225\246[\347\224\273].js" @@ -0,0 +1,105 @@ +/* +@header({ + searchable: 2, + filterable: 0, + quickSearch: 0, + title: '动漫啦', + author: 'EylinSir', + '类型': '漫画', + logo: 'https://www.dongman.la/favicon.ico', + lang: 'ds' +}) +*/ + +var rule = { + 类型: '漫画', + author: 'EylinSir', + title: '动漫啦', + host: 'https://www.dongman.la', + url: '/manhua/fyclass/fypage.html', + searchUrl: '/manhua/so/**/fypage.html', + logo: 'https://www.dongman.la/favicon.ico', + searchable: 2, + quickSearch: 0, + timeout: 5000, + limit: 20, + play_parse: true, + class_name: '日本&国产&港台&欧美&韩漫&完结&连载中', + class_url: 'japan&guochan&hongkongtaiwan&oumei&hanguo&finish&serial', + headers: { + 'User-Agent': 'PC_UA', + 'Referer': 'https://www.dongman.la/', + }, + + _parseList: function(html) { + return pdfa(html, '.cy_list_mh ul').map(ul => { + let title = pdfh(ul, 'li a.pic img&&alt'); + let href = pdfh(ul, 'li a.pic&&href'); + if (!title || !href) return null; + let img = pdfh(ul, 'li a.pic img&&src'); + return { + title: title.replace(/(漫画|在线观看)/g, '').trim(), + img: img.startsWith('//') ? 'https:' + img : img, + desc: pdfh(ul, '.updata&&Text').replace('最新:', '').trim(), + url: href, + year: pdfh(ul, '.zuozhe&&Text').replace('状态:', '').trim() + }; + }).filter(Boolean); + }, + + 推荐: async function(tid, pg, filter, extend) { + return await this.一级(tid, pg, filter, extend); + }, + + 一级: async function(tid, pg, filter, extend) { + let url = this.input; + return setResult(this._parseList(await request(url))); + }, + + 二级: async function(ids) { + let url = this.input; + let html = await request(url); + let playUrls = pdfa(html, '#play_0 li').map(it => { + let u = pdfh(it, 'a&&href'); + let t = pdfh(it, 'a&&Text'); + return (u && !u.includes('javascript')) ? t + '$' + u : null; + }).filter(Boolean); + return { + vod_name: pdfh(html, '.detail-info-title&&Text'), + vod_pic: pdfh(html, 'img.pic&&src'), + vod_content: pdfh(html, '#comic-description&&Text').replace(/(详细简介↓|收起↑)/g, "").trim(), + vod_play_from: "动漫啦", + vod_play_url: playUrls.reverse().join('#'), + vod_area: (pdfh(html, '.cy_xinxi&&Text').match(/地区:(\S+)/) || [])[1] || '', + vod_director: pdfh(html, '.detail-info-author&&Text').replace('作者:', '').trim() + }; + }, + + 搜索: async function(wd, quick, pg) { + let url = this.input; + return setResult(this._parseList(await request(url))); + }, + + lazy: async function(flag, id, flags) { + let url = this.input; + let header = { 'User-Agent': this.headers['User-Agent'], 'Referer': url }; + let imgs = []; + let tryUrls = [url.replace(/\.html$/, '') + '/all.html', url]; + for (let u of tryUrls) { + let html = await request(u); + let regex = /(?:data-original|data-src|src)=["']([^"']+\.(?:jpg|png|jpeg|webp|bmp))/gi; + let match; + while ((match = regex.exec(html)) !== null) { + let src = match[1]; + if (/logo|icon|loading|hm\.baidu/.test(src)) continue; + if (src.startsWith('//')) src = 'https:' + src; + else if (src.startsWith('/')) src = this.host + src; + if (!imgs.includes(src)) imgs.push(src); + } + if (imgs.length > 0) break; + } + if (imgs.length > 0) { + return { parse: 0, url: 'pics://' + imgs.join('&&'), header: header }; + } + } +}; \ No newline at end of file diff --git "a/spider/js/\345\225\212\345\223\210DJ[\345\220\254].js" "b/spider/js/\345\225\212\345\223\210DJ[\345\220\254].js" index f77ee4ba..f3f4aabb 100644 --- "a/spider/js/\345\225\212\345\223\210DJ[\345\220\254].js" +++ "b/spider/js/\345\225\212\345\223\210DJ[\345\220\254].js" @@ -5,6 +5,10 @@ quickSearch: 0, title: '啊哈DJ[听]', '类型': '影视', + mergeList: true, + more: { + mergeList: 1 + }, lang: 'ds' }) */ diff --git "a/spider/js/\345\274\200\347\234\274.js" "b/spider/js/\345\274\200\347\234\274.js" index e834e0fa..f4c2a960 100644 --- "a/spider/js/\345\274\200\347\234\274.js" +++ "b/spider/js/\345\274\200\347\234\274.js" @@ -5,6 +5,10 @@ quickSearch: 0, title: '开眼视频', '类型': '影视', + mergeList: true, + more: { + mergeList: 1 + }, lang: 'ds' }) */ diff --git "a/spider/js/\346\230\237\350\276\260\345\275\261\351\231\242.js" "b/spider/js/\346\230\237\350\276\260\345\275\261\351\231\242[\344\274\230].js" similarity index 100% rename from "spider/js/\346\230\237\350\276\260\345\275\261\351\231\242.js" rename to "spider/js/\346\230\237\350\276\260\345\275\261\351\231\242[\344\274\230].js" diff --git "a/spider/js_bad/\346\234\250\345\205\256[\344\274\230].js" "b/spider/js/\346\234\250\345\205\256[\344\274\230].js" similarity index 88% rename from "spider/js_bad/\346\234\250\345\205\256[\344\274\230].js" rename to "spider/js/\346\234\250\345\205\256[\344\274\230].js" index 918c35da..59cac191 100644 --- "a/spider/js_bad/\346\234\250\345\205\256[\344\274\230].js" +++ "b/spider/js/\346\234\250\345\205\256[\344\274\230].js" @@ -20,7 +20,19 @@ var rule = { quickSearch: 1, filterable: 0, headers: { - 'User-Agent': MOBILE_UA + 'User-Agent': MOBILE_UA, + 'Accept': 'application/json, text/plain, */*', + 'accept-language': 'zh-CN,zh;q=0.9', + 'cache-control': 'no-cache', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'sec-ch-ua': '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'x-platform': 'web' }, play_parse: true, search_match: true, diff --git "a/spider/js/\346\242\250\345\233\255\350\241\214[\346\210\217].js" "b/spider/js/\346\242\250\345\233\255\350\241\214[\346\210\217].js" new file mode 100644 index 00000000..7204c5db --- /dev/null +++ "b/spider/js/\346\242\250\345\233\255\350\241\214[\346\210\217].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '梨园行戏曲', + author: 'EylinSir', + '类型': '影视', + logo: 'https://img.znds.com//uploads/new/221222/9-2212221050561N.png', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfmoqjlm63ooYzmiI/mm7InLAogICAgZGVzYzogJ+aiqOWbreihjOaIj+absua6kCcsCiAgICBob3N0OiAnaHR0cHM6Ly9mbHkuZGFvcmFuLnR2JywKICAgIGhvbWVVcmw6ICdodHRwczovL2ZseS5kYW9yYW4udHYnLAogICAgdXJsOiAnL0FQSV9ST1Avc2VhcmNoL2FsYnVtL3NjcmVlbicsCiAgICBzZWFyY2hVcmw6ICcvQVBJX1JPUC9zZWFyY2gvYWxidW0vbGlzdD9rZXl3b3JkPSoqJywKICAgIGxvZ286ICdodHRwczovL2ltZy56bmRzLmNvbS8vdXBsb2Fkcy9uZXcvMjIxMjIyLzktMjIxMjIyMTA1MDU2MU4ucG5nJywKICAgIHNlYXJjaGFibGU6IDEsCiAgICBxdWlja1NlYXJjaDogMCwKICAgIGZpbHRlcmFibGU6IDEsCiAgICB0aW1lb3V0OiAxMDAwMCwKICAgIHBsYXlfcGFyc2U6IHRydWUsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ21kNSc6ICdTa3Z5cldxSzlRSFRkQ1QxMlJoeHVuangrV3dNVGU5eTRLd2dlQVNGRGhiWWFiUlNQc2tSMFE9PScsCiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PVVURi04JywKICAgICAgICAnVXNlci1BZ2VudCc6ICdva2h0dHAvMy4xMi4xMCcsCiAgICAgICAgJ0hvc3QnOiAnZmx5LmRhb3Jhbi50dicsCiAgICAgICAgJ0Nvbm5lY3Rpb24nOiAnS2VlcC1BbGl2ZScKICAgIH0sCiAgICAKICAgIHJlcXVlc3Q6IGFzeW5jIGZ1bmN0aW9uICh1cmwsIG9iaikgewogICAgICAgIG9iaiA9IG9iaiB8fCB7fTsKICAgICAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCBfZmV0Y2godXJsLCB7CiAgICAgICAgICAgIG1ldGhvZDogb2JqLm1ldGhvZCB8fCAnUE9TVCcsCiAgICAgICAgICAgIGhlYWRlcnM6IG9iai5oZWFkZXJzIHx8IHRoaXMuaGVhZGVycywKICAgICAgICAgICAgYm9keTogb2JqLmRhdGEgPyBKU09OLnN0cmluZ2lmeShvYmouZGF0YSkgOiB1bmRlZmluZWQKICAgICAgICB9KTsKICAgICAgICByZXR1cm4gcmVzcG9uc2UudGV4dCgpOwogICAgfSwKCiAgICBfZm9ybWF0X2ltZzogZnVuY3Rpb24gKGltZykgewogICAgICAgIGlmICghaW1nKSB7CiAgICAgICAgICAgIHJldHVybiAnJzsKICAgICAgICB9CiAgICAgICAgaWYgKCFpbWcuc3RhcnRzV2l0aCgnaHR0cCcpKSB7CiAgICAgICAgICAgIHJldHVybiBgaHR0cHM6Ly9vdHRwaG90by5kYW9yYW4udHYvSEQvJHtpbWd9YDsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGltZzsKICAgIH0sCiAgICAKICAgIOmihOWkhOeQhjogYXN5bmMgZnVuY3Rpb24gKCkge30sCiAgICAKICAgIGNsYXNzX3BhcnNlOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IGNhdGVfbGlzdCA9IFsKICAgICAgICAgICAgeyJuIjogIuWFqOmDqCIsICJ2IjogIiJ9LAogICAgICAgICAgICB7Im4iOiAi6buE5qKF5oiPIiwgInYiOiAiaG14In0sIHsibiI6ICLkuqzliaciLCAidiI6ICJqaW5nanUifSwgeyJuIjogIuabsuWJpyIsICJ2IjogInF1anUifSwKICAgICAgICAgICAgeyJuIjogIuenpuiFlCIsICJ2IjogInFpbnEifSwgeyJuIjogIua9ruWJpyIsICJ2IjogImNoYW9qdSJ9LCB7Im4iOiAi5rKq5YmnIiwgInYiOiAiaHVqdSJ9LAogICAgICAgICAgICB7Im4iOiAi5piG5puyIiwgInYiOiAia3VucXUifSwgeyJuIjogIua3ruWJpyIsICJ2IjogImh1YWlqdSJ9LCB7Im4iOiAi5am65YmnIiwgInYiOiAid3VqdSJ9LAogICAgICAgICAgICB7Im4iOiAi5rKz5Y2X5aSn6byT5LmmIiwgInYiOiAiaG5kZ3MifSwgeyJuIjogIua7h+WJpyIsICJ2IjogImRpYW5qdSJ9LCB7Im4iOiAi6ICB5bm05aSn5a2mIiwgInYiOiAiV0sifSwKICAgICAgICAgICAgeyJuIjogIue7jeWJpyIsICJ2IjogInNoYW9qdiJ9LCB7Im4iOiAi5puy6Im65pma5LyaIiwgInYiOiAiZWxzZSJ9LCB7Im4iOiAi55qu5b2x5oiPIiwgInYiOiAicHl4In0sCiAgICAgICAgICAgIHsibiI6ICLlm5vlubPosIMiLCAidiI6ICJzcGQifSwgeyJuIjogIuWQleWJpyIsICJ2IjogImx2anYifSwgeyJuIjogIuafs+eQtOaIjyIsICJ2IjogImxpdXF4In0sCiAgICAgICAgICAgIHsibiI6ICLojobku5nmiI8iLCAidiI6ICJweHgifSwgeyJuIjogIuWum+aihiIsICJ2IjogIndiIn0sIHsibiI6ICLplKHliaciLCAidiI6ICJ4aWp1In0sCiAgICAgICAgICAgIHsibiI6ICLlpKflubPosIMiLCAidiI6ICJkcGQifSwgeyJuIjogIuivneWJpyIsICJ2IjogImh1YWp1In0sIHsibiI6ICLopb/np6bmiI8iLCAidiI6ICJ4cXgifSwKICAgICAgICAgICAgeyJuIjogIuW3neWJpyIsICJ2IjogImNodWFuanUifSwgeyJuIjogIui1o+WJpyIsICJ2IjogInRhZ0lkIn0sIHsibiI6ICLlpKrlurfpgZPmg4UiLCAidiI6ICJ0a2RxIn0sCiAgICAgICAgICAgIHsibiI6ICLpl73liaciLCAidiI6ICJtaW5qdSJ9LCB7Im4iOiAi5qKF6Iqx5aSn6byTIiwgInYiOiAibWhkZyJ9LCB7Im4iOiAi5ZCJ5YmnIiwgInYiOiAiamlqdSJ9LAogICAgICAgICAgICB7Im4iOiAi55m95a2X5oiPIiwgInYiOiAiYnp4In0sIHsibiI6ICLosavliaciLCAidiI6ICJ5dWp1In0sIHsibiI6ICLotorliaciLCAidiI6ICJ5dWVqdSJ9LAogICAgICAgICAgICB7Im4iOiAi6K+E5YmnIiwgInYiOiAicGluZ2p1In0sIHsibiI6ICLlnaDlrZAiLCAidiI6ICJobnp6In0sIHsibiI6ICLmsrPljJfmooblrZAiLCAidiI6ICJoYmJ6In0sCiAgICAgICAgICAgIHsibiI6ICLnsqTliaciLCAidiI6ICJnZGR4In0sIHsibiI6ICLkuozlpLnlvKYiLCAidiI6ICJlangifSwgeyJuIjogIuays+WNl+eQtOS5piIsICJ2IjogImhucXMifSwKICAgICAgICAgICAgeyJuIjogIuaIj+absiIsICJ2IjogInhxIn0sIHsibiI6ICLkuozkurrlj7AiLCAidiI6ICJFUlQifSwgeyJuIjogIui2iuiwgyIsICJ2IjogInl1ZWQifSwKICAgICAgICAgICAgeyJuIjogIuS5kOiFlCIsICJ2IjogImxxIn0sIHsibiI6ICLmiazliaciLCAidiI6ICJ5YW5nanUifSwgeyJuIjogIuS6rOmfteWkp+m8kyIsICJ2IjogImp5ZGcifSwKICAgICAgICAgICAgeyJuIjogIuW9qeiwgyIsICJ2IjogImNhaWRpYW8ifSwgeyJuIjogIueQvOWJpyIsICJ2IjogInFpb25nanUifSwgeyJuIjogIuiSsuWJpyIsICJ2IjogInB1anYifSwKICAgICAgICAgICAgeyJuIjogIuilv+ays+Wkp+m8kyIsICJ2IjogInhoZGcifSwgeyJuIjogIua5mOWJpyIsICJ2IjogInhqIn0sIHsibiI6ICLpuqbnlLDkuaHpn74iLCAidiI6ICJtdHh5In0sCiAgICAgICAgICAgIHsibiI6ICLor4TkuaYiLCAidiI6ICJwaW5nc2h1In0sIHsibiI6ICLlupDliaciLCAidiI6ICJsdWp1In0sIHsibiI6ICLljZXlvKYiLCAidiI6ICJkYW54aWFuIn0sCiAgICAgICAgICAgIHsibiI6ICLoirHpvJPmiI8iLCAidiI6ICJodWFneCJ9LCB7Im4iOiAi55u45aOwIiwgInYiOiAieGlhbmcifSwgeyJuIjogIuWbm+iCoeW8piIsICJ2IjogInNneCJ9LAogICAgICAgICAgICB7Im4iOiAi5L+d5a6a6ICB6LCDIiwgInYiOiAiYmRsZCJ9LCB7Im4iOiAi5pmL5YmnIiwgInYiOiAiamluanUifSwgeyJuIjogIuWFtuS7liIsICJ2IjogIm90aGVyIn0sCiAgICAgICAgICAgIHsibiI6ICLmraPlrZfmiI8iLCAidiI6ICJ6engifSwgeyJuIjogIualmuWJpyIsICJ2IjogImNodWp1In0KICAgICAgICBdOwogICAgICAgIAogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIGNsYXNzOiBbeyAndHlwZV9uYW1lJzogJ+aIj+absueJh+W6kycsICd0eXBlX2lkJzogJ2FsbCcgfV0sCiAgICAgICAgICAgIGZpbHRlcnM6IHsKICAgICAgICAgICAgICAgICJhbGwiOiBbCiAgICAgICAgICAgICAgICAgICAgeyJrZXkiOiAic2VjdCIsICJuYW1lIjogIuabsuenjSIsICJ2YWx1ZSI6IGNhdGVfbGlzdH0sCiAgICAgICAgICAgICAgICAgICAgeyJrZXkiOiAiYXJlYSIsICJuYW1lIjogIui1hOi0uSIsICJ2YWx1ZSI6IFt7Im4iOiAi5YWo6YOoIiwgInYiOiAiMCJ9LCB7Im4iOiAi5YWN6LS5IiwgInYiOiAiMSJ9LCB7Im4iOiAiVklQIiwgInYiOiAiMiJ9XX0sCiAgICAgICAgICAgICAgICAgICAgeyJrZXkiOiAic29ydCIsICJuYW1lIjogIuaOkuW6jyIsICJ2YWx1ZSI6IFt7Im4iOiAi5pyA54OtIiwgInYiOiAiaG90In0sIHsibiI6ICLmnIDmlrAiLCAidiI6ICJvbmxpbmUifV19CiAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgIH0KICAgICAgICB9OwogICAgfSwKICAgIAogICAg5o6o6I2QOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMu5LiA57qnKCdhbGwnLCAxLCB7fSwge30pOwogICAgfSwKICAgIAogICAg5LiA57qnOiBhc3luYyBmdW5jdGlvbiAodGlkLCBwZywgZmlsdGVyLCBleHRlbmQpIHsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9BUElfUk9QL3NlYXJjaC9hbGJ1bS9zY3JlZW5gOwogICAgICAgIGxldCBzZWN0ID0gZXh0ZW5kPy5zZWN0IHx8ICcnOwogICAgICAgIGlmICh0aWQgPT09ICdhbGwnICYmICFzZWN0KSB7CiAgICAgICAgICAgIHNlY3QgPSAnJzsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgbGV0IHBheWxvYWQgPSB7CiAgICAgICAgICAgICJjdXIiOiBwYXJzZUludChwZyksCiAgICAgICAgICAgICJwYWdlU2l6ZSI6IDMwLAogICAgICAgICAgICAicmVzVHlwZSI6IDEsCiAgICAgICAgICAgICJzZWN0Ijogc2VjdCwKICAgICAgICAgICAgIm9yZGVyYnkiOiBleHRlbmQ/LnNvcnQgfHwgJ2hvdCcsCiAgICAgICAgICAgICJ0YWdJZCI6IDAsCiAgICAgICAgICAgICJ1c2VySWQiOiAiOTIzMTVlYzZlNThhNDViYTdmNDdmZDE0M2IzZDc5NTYiLAogICAgICAgICAgICAiY2hhbm5lbCI6ICJ2aXZvIiwKICAgICAgICAgICAgIml0ZW0iOiAieTkiLAogICAgICAgICAgICAibm9kZUNvZGUiOiAiMDAxMDAwIiwKICAgICAgICAgICAgInByb2plY3QiOiAibHloeGN4IgogICAgICAgIH07CiAgICAgICAgCiAgICAgICAgbGV0IGFyZWEgPSBleHRlbmQ/LmFyZWEgfHwgJzAnOwogICAgICAgIGlmIChhcmVhID09PSAnMScgfHwgYXJlYSA9PT0gJzInKSB7CiAgICAgICAgICAgIHBheWxvYWRbJ2ZyZWUnXSA9IHBhcnNlSW50KGFyZWEpOwogICAgICAgIH0KICAgICAgICAKICAgICAgICB0cnkgewogICAgICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IHRoaXMucmVxdWVzdCh1cmwsIHsgZGF0YTogcGF5bG9hZCB9KTsKICAgICAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKHJlc3ApOwogICAgICAgICAgICBsZXQgZGF0YSA9IGpzb24ucGIgfHwganNvbi5kYXRhIHx8IHt9OwogICAgICAgICAgICBsZXQgdm9kX2xpc3QgPSBbXTsKICAgICAgICAgICAgZm9yIChsZXQgaXRlbSBvZiBkYXRhLmRhdGFMaXN0IHx8IFtdKSB7CiAgICAgICAgICAgICAgICB2b2RfbGlzdC5wdXNoKHsKICAgICAgICAgICAgICAgICAgICB0aXRsZTogaXRlbS5uYW1lLAogICAgICAgICAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9BUElfUk9QL2FsYnVtL3Jlcy9saXN0P2FsYnVtQ29kZT0ke2l0ZW0uY29kZX1gLAogICAgICAgICAgICAgICAgICAgIGRlc2M6IChpdGVtLnB1Ymxpc2hUaW1lIHx8ICcnKS5zcGxpdCgnICcpWzBdLAogICAgICAgICAgICAgICAgICAgIHBpY191cmw6IHRoaXMuX2Zvcm1hdF9pbWcoaXRlbS5pbWdzZWMpLAogICAgICAgICAgICAgICAgICAgIHZvZF95ZWFyOiAoaXRlbS5wdWJsaXNoVGltZSB8fCAnJykuc3Vic3RyaW5nKDAsIDQpCiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIHNldFJlc3VsdCh2b2RfbGlzdCk7CiAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpOwogICAgICAgICAgICByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICB9CiAgICB9LAogICAgCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgYWxidW1Db2RlID0gdGhpcy5pbnB1dC5tYXRjaCgvYWxidW1Db2RlPSguKj8pKD86JnwkKS8pWzFdOwogICAgICAgIGxldCB1cmwgPSBgJHt0aGlzLmhvc3R9L0FQSV9ST1AvYWxidW0vcmVzL2xpc3RgOwogICAgICAgIAogICAgICAgIGxldCBwYXlsb2FkID0gewogICAgICAgICAgICAiYWxidW1Db2RlIjogYWxidW1Db2RlLAogICAgICAgICAgICAiY3VyIjogMSwKICAgICAgICAgICAgInBhZ2VTaXplIjogNTAwLAogICAgICAgICAgICAidXNlcklkIjogIjkyMzE1ZWM2ZTU4YTQ1YmE3ZjQ3ZmQxNDNiM2Q3OTU2IiwKICAgICAgICAgICAgImNoYW5uZWwiOiAidml2byIsCiAgICAgICAgICAgICJpdGVtIjogInk5IiwKICAgICAgICAgICAgIm5vZGVDb2RlIjogIjAwMTAwMCIsCiAgICAgICAgICAgICJwcm9qZWN0IjogImx5aHhjeCIKICAgICAgICB9OwogICAgICAgIAogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCByZXNwID0gYXdhaXQgdGhpcy5yZXF1ZXN0KHVybCwgeyBkYXRhOiBwYXlsb2FkIH0pOwogICAgICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UocmVzcCk7CiAgICAgICAgICAgIGxldCBhbGJ1bSA9IGpzb24uYWxidW0gfHwge307CiAgICAgICAgICAgIGxldCB0cmFja3MgPSBqc29uLnBiPy5kYXRhTGlzdCB8fCBbXTsKICAgICAgICAgICAgdHJhY2tzLnNvcnQoKGEsIGIpID0+IHBhcnNlSW50KGEuc29ydCB8fCAwKSAtIHBhcnNlSW50KGIuc29ydCB8fCAwKSk7CiAgICAgICAgICAgIGxldCBwbGF5X3VybHMgPSBbXTsKICAgICAgICAgICAgZm9yIChsZXQgdCBvZiB0cmFja3MpIHsKICAgICAgICAgICAgICAgIGlmICh0LmNvZGUpIHsKICAgICAgICAgICAgICAgICAgICBwbGF5X3VybHMucHVzaChgJHt0Lm5hbWUucmVwbGFjZSgvXCQvZywgJ18nKX0kJHt0LmNvZGV9YCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIGxldCBWT0QgPSB7CiAgICAgICAgICAgICAgICB2b2RfaWQ6IGFsYnVtQ29kZSwKICAgICAgICAgICAgICAgIHZvZF9uYW1lOiBhbGJ1bS5uYW1lIHx8ICfmnKrnn6UnLAogICAgICAgICAgICAgICAgdm9kX3BpYzogdGhpcy5fZm9ybWF0X2ltZyhhbGJ1bS5pbWdzZWMpLAogICAgICAgICAgICAgICAgdHlwZV9uYW1lOiAi5oiP5puyIiwKICAgICAgICAgICAgICAgIHZvZF95ZWFyOiBhbGJ1bS5wdWJsaXNoVGltZSB8fCAnJywKICAgICAgICAgICAgICAgIHZvZF9hcmVhOiAi5Lit5Zu9IiwKICAgICAgICAgICAgICAgIHZvZF9jb250ZW50OiBhbGJ1bS5kZXMgfHwgJ+aaguaXoOeugOS7iycsCiAgICAgICAgICAgICAgICB2b2RfcGxheV9mcm9tOiAi5qKo5Zut6KGMIiwKICAgICAgICAgICAgICAgIHZvZF9wbGF5X3VybDogcGxheV91cmxzLmpvaW4oJyMnKQogICAgICAgICAgICB9OwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIFZPRDsKICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7CiAgICAgICAgICAgIHJldHVybiB7fTsKICAgICAgICB9CiAgICB9LAogICAgCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9BUElfUk9QL3NlYXJjaC9hbGJ1bS9saXN0YDsKICAgICAgICBsZXQgcGF5bG9hZCA9IHsKICAgICAgICAgICAgImN1ciI6IHBhcnNlSW50KHRoaXMuTVlfUEFHRSksCiAgICAgICAgICAgICJwYWdlU2l6ZSI6IDIwLAogICAgICAgICAgICAia2V5d29yZCI6IHRoaXMuS0VZLAogICAgICAgICAgICAiaXRlbSI6ICJ5OSIsCiAgICAgICAgICAgICJub2RlQ29kZSI6ICIwMDEwMDAiLAogICAgICAgICAgICAib3JkZXJieSI6ICJob3QiLAogICAgICAgICAgICAicHgiOiAyLAogICAgICAgICAgICAic2VjdCI6IFtdLAogICAgICAgICAgICAidXNlcklkIjogIjkyMzE1ZWM2ZTU4YTQ1YmE3ZjQ3ZmQxNDNiM2Q3OTU2IiwKICAgICAgICAgICAgInByb2plY3QiOiAibHloeGN4IgogICAgICAgIH07CiAgICAgICAgCiAgICAgICAgdHJ5IHsKICAgICAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCB0aGlzLnJlcXVlc3QodXJsLCB7IGRhdGE6IHBheWxvYWQgfSk7CiAgICAgICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShyZXNwKTsKICAgICAgICAgICAgbGV0IGRhdGEgPSBqc29uLnBiIHx8IGpzb24uZGF0YSB8fCB7fTsKICAgICAgICAgICAgbGV0IHZvZF9saXN0ID0gW107CiAgICAgICAgICAgIGZvciAobGV0IGl0ZW0gb2YgZGF0YS5kYXRhTGlzdCB8fCBbXSkgewogICAgICAgICAgICAgICAgdm9kX2xpc3QucHVzaCh7CiAgICAgICAgICAgICAgICAgICAgdGl0bGU6IGl0ZW0ubmFtZSwKICAgICAgICAgICAgICAgICAgICB1cmw6IGAke3RoaXMuaG9zdH0vQVBJX1JPUC9hbGJ1bS9yZXMvbGlzdD9hbGJ1bUNvZGU9JHtpdGVtLmNvZGV9YCwKICAgICAgICAgICAgICAgICAgICBkZXNjOiAoaXRlbS5wdWJsaXNoVGltZSB8fCAnJyksCiAgICAgICAgICAgICAgICAgICAgcGljX3VybDogdGhpcy5fZm9ybWF0X2ltZyhpdGVtLmltZ3NlYykKICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBzZXRSZXN1bHQodm9kX2xpc3QpOwogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgY29uc29sZS5lcnJvcihlKTsKICAgICAgICAgICAgcmV0dXJuIHNldFJlc3VsdChbXSk7CiAgICAgICAgfQogICAgfSwKICAgIAogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCByZXNDb2RlID0gdGhpcy5pbnB1dDsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9BUElfUk9QL3BsYXkvZ2V0L3BsYXl1cmxgOwogICAgICAgIGxldCBwYXlsb2FkID0gewogICAgICAgICAgICAicmVzQ29kZSI6IHJlc0NvZGUsCiAgICAgICAgICAgICJpdGVtIjogInk5IiwKICAgICAgICAgICAgIm1hc2siOiAwLAogICAgICAgICAgICAibm9kZUNvZGUiOiAiMDAxMDAwIiwKICAgICAgICAgICAgInByb2plY3QiOiAibHloeGN4IiwKICAgICAgICAgICAgInB4IjogMiwKICAgICAgICAgICAgInVzZXJJZCI6ICI5MjMxNWVjNmU1OGE0NWJhN2Y0N2ZkMTQzYjNkNzk1NiIKICAgICAgICB9OwogICAgICAgIAogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCByZXNwID0gYXdhaXQgdGhpcy5yZXF1ZXN0KHVybCwgeyBkYXRhOiBwYXlsb2FkIH0pOwogICAgICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UocmVzcCk7CiAgICAgICAgICAgIGxldCBwbGF5X3VybCA9IGpzb24ucGxheXJlcz8ucGxheXVybCB8fCAnJzsKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIHBhcnNlOiAwLAogICAgICAgICAgICAgICAgdXJsOiBwbGF5X3VybCwKICAgICAgICAgICAgICAgIGhlYWRlcjogeyAnVXNlci1BZ2VudCc6IHRoaXMuaGVhZGVyc1snVXNlci1BZ2VudCddIH0KICAgICAgICAgICAgfTsKICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7CiAgICAgICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICAgICBwYXJzZTogMCwKICAgICAgICAgICAgICAgIHVybDogJycKICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICB9Cn07 \ No newline at end of file diff --git "a/spider/js/\346\263\245\350\247\206\351\242\221.js" "b/spider/js/\346\263\245\350\247\206\351\242\221[\344\274\230].js" similarity index 100% rename from "spider/js/\346\263\245\350\247\206\351\242\221.js" rename to "spider/js/\346\263\245\350\247\206\351\242\221[\344\274\230].js" diff --git "a/spider/js/\347\203\255\346\222\255\345\275\261\350\247\206[\344\274\230].js" "b/spider/js/\347\203\255\346\222\255\345\275\261\350\247\206[\344\274\230].js" new file mode 100644 index 00000000..4ffd935a --- /dev/null +++ "b/spider/js/\347\203\255\346\222\255\345\275\261\350\247\206[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '热播APP', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfng63mkq1BUFAnLAogICAgZGVzYzogJ+eDreaSrUFQUOW9seinhua6kCcsCiAgICBob3N0OiAnaHR0cDovL3YucmJvdHYuY24nLAogICAgaG9tZVVybDogJ2h0dHA6Ly92LnJib3R2LmNuJywKICAgIHVybDogJy92My9ob21lL3R5cGVfc2VhcmNoJywKICAgIHNlYXJjaFVybDogJy92My9ob21lL3NlYXJjaCcsCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDEsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogMTAwMDAsCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAgaGVhZGVyczogewogICAgICAgICdVc2VyLUFnZW50JzogJ29raHR0cC1va2dvL2plYXNvbmx6eScsCiAgICAgICAgJ0FjY2VwdC1MYW5ndWFnZSc6ICd6aC1DTix6aDtxPTAuOCcKICAgIH0sCiAgICAKICAgIGdldGZpbGVzOiBmdW5jdGlvbiAocCA9IHt9KSB7CiAgICAgICAgbGV0IHQgPSBNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKS50b1N0cmluZygpOwogICAgICAgIGxldCBzID0gbWQ1KCc3Z3AwYm5kMnNyODV5ZGlpMmozMnBjeXBzY29jNHc2YzdnNXNwbCcgKyB0KTsKICAgICAgICBsZXQgZmlsZXMgPSB7CiAgICAgICAgICAgICdzaWduJzogcywKICAgICAgICAgICAgJ3RpbWVzdGFtcCc6IHQKICAgICAgICB9OwogICAgICAgIHJldHVybiB7IC4uLnAsIC4uLmZpbGVzIH07CiAgICB9LAoKICAgIGdldHY6IGZ1bmN0aW9uIChkYXRhKSB7CiAgICAgICAgcmV0dXJuIGRhdGEuZmlsdGVyKGkgPT4gaS52b2RfaWQgJiYgaS52b2RfaWQudG9TdHJpbmcoKSAhPT0gJzAnKS5tYXAoaSA9PiAoewogICAgICAgICAgICB0aXRsZTogaS52b2RfbmFtZSwKICAgICAgICAgICAgdXJsOiBgJHt0aGlzLmhvc3R9L3YzL2hvbWUvdm9kX2RldGFpbHM/dm9kX2lkPSR7aS52b2RfaWR9YCwKICAgICAgICAgICAgZGVzYzogaS52b2RfcmVtYXJrcywKICAgICAgICAgICAgcGljX3VybDogaS52b2RfcGljIHx8IGkudm9kX3BpY190aHVtYiwKICAgICAgICAgICAgdm9kX3llYXI6IGkudGFnCiAgICAgICAgfSkpOwogICAgfSwKICAgIAogICAg6aKE5aSE55CGOiBhc3luYyBmdW5jdGlvbiAoKSB7fSwKCiAgICBjbGFzc19wYXJzZTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBkYXRhID0gdGhpcy5nZXRmaWxlcyh7ICcnOiAnJyB9KTsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3YzL3R5cGUvdG9wX3R5cGVgLCB7CiAgICAgICAgICAgIG1ldGhvZDogJ3Bvc3QnLAogICAgICAgICAgICBoZWFkZXJzOiB7IC4uLnRoaXMuaGVhZGVycywgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnIH0sCiAgICAgICAgICAgIGJvZHk6IG5ldyBVUkxTZWFyY2hQYXJhbXMoZGF0YSkudG9TdHJpbmcoKQogICAgICAgIH0pOwogICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShhd2FpdCByZXNwLnRleHQoKSk7CiAgICAgICAgbGV0IGNsYXNzZXMgPSBqc29uLmRhdGEubGlzdC5tYXAoayA9PiAoewogICAgICAgICAgICB0eXBlX2lkOiBrLnR5cGVfaWQudG9TdHJpbmcoKSwKICAgICAgICAgICAgdHlwZV9uYW1lOiBrLnR5cGVfbmFtZQogICAgICAgIH0pKTsKICAgICAgICBsZXQgZmlsdGVycyA9IHt9OwogICAgICAgIGZvciAoY29uc3QgayBvZiBqc29uLmRhdGEubGlzdCkgewogICAgICAgICAgICBsZXQgZnRzID0gW107CiAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGspKSB7CiAgICAgICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID4gMikgewogICAgICAgICAgICAgICAgICAgIGZ0cy5wdXNoKHsKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZToga2V5LAogICAgICAgICAgICAgICAgICAgICAgICBrZXksCiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiB2YWx1ZS5maWx0ZXIoaiA9PiBqICYmIGogIT09ICflhajpg6gnKS5tYXAoaiA9PiAoeyBuOiBqLCB2OiBqIH0pKQogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChmdHMubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICBmaWx0ZXJzW2sudHlwZV9pZF0gPSBmdHM7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHsgY2xhc3M6IGNsYXNzZXMsIGZpbHRlcnMgfTsKICAgIH0sCiAgICAKICAgIOaOqOiNkDogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBkYXRhID0gdGhpcy5nZXRmaWxlcyh7ICcnOiAnJyB9KTsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3YzL3R5cGUvdGpfdm9kYCwgewogICAgICAgICAgICBtZXRob2Q6ICdwb3N0JywKICAgICAgICAgICAgaGVhZGVyczogeyAuLi50aGlzLmhlYWRlcnMsICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyB9LAogICAgICAgICAgICBib2R5OiBuZXcgVVJMU2VhcmNoUGFyYW1zKGRhdGEpLnRvU3RyaW5nKCkKICAgICAgICB9KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpOwogICAgICAgIGxldCB2aWRlb3MgPSB0aGlzLmdldHYoanNvbi5kYXRhLmNhaS5jb25jYXQoanNvbi5kYXRhLmxvb3ApKTsKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGxldCBmaWxlcyA9IHsKICAgICAgICAgICAgJ3R5cGVfaWQnOiB0aWQsCiAgICAgICAgICAgICdsaW1pdCc6ICcxMicsCiAgICAgICAgICAgICdwYWdlJzogcGcKICAgICAgICB9OwogICAgICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKGV4dGVuZCB8fCB7fSkpIHsKICAgICAgICAgICAgbGV0IGtleSA9IGsgPT09ICdleHRlbmQnID8gJ2NsYXNzJyA6IGs7CiAgICAgICAgICAgIGZpbGVzW2tleV0gPSB2OwogICAgICAgIH0KICAgICAgICBsZXQgZGF0YSA9IHRoaXMuZ2V0ZmlsZXMoZmlsZXMpOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKGAke3RoaXMuaG9zdH0vdjMvaG9tZS90eXBlX3NlYXJjaGAsIHsKICAgICAgICAgICAgbWV0aG9kOiAncG9zdCcsCiAgICAgICAgICAgIGhlYWRlcnM6IHsgLi4udGhpcy5oZWFkZXJzLCAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcgfSwKICAgICAgICAgICAgYm9keTogbmV3IFVSTFNlYXJjaFBhcmFtcyhkYXRhKS50b1N0cmluZygpCiAgICAgICAgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgdmlkZW9zID0gdGhpcy5nZXR2KGpzb24uZGF0YS5saXN0KTsKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdm9kSWQgPSB0aGlzLmlucHV0Lm1hdGNoKC92b2RfaWQ9KFxkKykvKVsxXTsKICAgICAgICBsZXQgZGF0YSA9IHRoaXMuZ2V0ZmlsZXMoeyAndm9kX2lkJzogdm9kSWQgfSk7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2goYCR7dGhpcy5ob3N0fS92My9ob21lL3ZvZF9kZXRhaWxzYCwgewogICAgICAgICAgICBtZXRob2Q6ICdwb3N0JywKICAgICAgICAgICAgaGVhZGVyczogeyAuLi50aGlzLmhlYWRlcnMsICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyB9LAogICAgICAgICAgICBib2R5OiBuZXcgVVJMU2VhcmNoUGFyYW1zKGRhdGEpLnRvU3RyaW5nKCkKICAgICAgICB9KTsKICAgICAgICBsZXQgdiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpLmRhdGE7CiAgICAgICAgbGV0IFZPRCA9IHsKICAgICAgICAgICAgdm9kX2lkOiB2LnZvZF9pZC50b1N0cmluZygpLAogICAgICAgICAgICB2b2RfbmFtZTogdi52b2RfbmFtZSwKICAgICAgICAgICAgdHlwZV9uYW1lOiB2LnR5cGVfbmFtZSwKICAgICAgICAgICAgdm9kX3llYXI6IHYudm9kX3llYXIsCiAgICAgICAgICAgIHZvZF9hcmVhOiB2LnZvZF9hcmVhLAogICAgICAgICAgICB2b2RfcmVtYXJrczogdi52b2RfcmVtYXJrcywKICAgICAgICAgICAgdm9kX2FjdG9yOiB2LnZvZF9hY3RvciwKICAgICAgICAgICAgdm9kX2RpcmVjdG9yOiB2LnZvZF9kaXJlY3RvciwKICAgICAgICAgICAgdm9kX2NvbnRlbnQ6ICh2LnZvZF9jb250ZW50IHx8ICfml6AnKS5yZXBsYWNlKC88W14+XSo+L2csICcnKS50cmltKCkKICAgICAgICB9OwogICAgICAgIGxldCBuID0gW107CiAgICAgICAgbGV0IHAgPSBbXTsKICAgICAgICBmb3IgKGNvbnN0IGkgb2Ygdi52b2RfcGxheV9saXN0KSB7CiAgICAgICAgICAgIGxldCBwYXJzZXMgPSBpLnBhcnNlX3VybHMuam9pbignLCcpOwogICAgICAgICAgICBsZXQgdXJscyA9IGkudXJscy5tYXAoaiA9PiBgJHtqLm5hbWV9JCR7ai51cmx9QCR7cGFyc2VzfUAke2kudWEgfHwgJyd9QCR7aS5yZWZlcmVyIHx8ICcnfWApOwogICAgICAgICAgICBwLnB1c2godXJscy5qb2luKCcjJykpOwogICAgICAgICAgICBsZXQgbmFtZSA9IChpLnRpdGxlIHx8IGkubmFtZSB8fCAnJykucmVwbGFjZSgvW1wo77yIXSg/OueCueWHu3zmjaIpW14pXSpbXCnvvIldfFta4pa24p2k44CQXS4qL2csICcnKTsKICAgICAgICAgICAgbi5wdXNoKG5hbWUgPT09IGkuZmxhZyA/IG5hbWUgOiBgJHtuYW1lfSgke2kuZmxhZ30pYCk7CiAgICAgICAgfQogICAgICAgIFZPRC52b2RfcGxheV9mcm9tID0gbi5qb2luKCckJCQnKTsKICAgICAgICBWT0Qudm9kX3BsYXlfdXJsID0gcC5qb2luKCckJCQnKTsKICAgICAgICByZXR1cm4gVk9EOwogICAgfSwKICAgIAogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IGZpbGVzID0gewogICAgICAgICAgICAnbGltaXQnOiAnMTInLAogICAgICAgICAgICAncGFnZSc6IHRoaXMuTVlfUEFHRSwKICAgICAgICAgICAgJ2tleXdvcmQnOiB0aGlzLktFWQogICAgICAgIH07CiAgICAgICAgbGV0IGRhdGEgPSB0aGlzLmdldGZpbGVzKGZpbGVzKTsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3YzL2hvbWUvc2VhcmNoYCwgewogICAgICAgICAgICBtZXRob2Q6ICdwb3N0JywKICAgICAgICAgICAgaGVhZGVyczogeyAuLi50aGlzLmhlYWRlcnMsICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyB9LAogICAgICAgICAgICBib2R5OiBuZXcgVVJMU2VhcmNoUGFyYW1zKGRhdGEpLnRvU3RyaW5nKCkKICAgICAgICB9KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpOwogICAgICAgIGxldCB2aWRlb3MgPSB0aGlzLmdldHYoanNvbi5kYXRhLmxpc3QpOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQodmlkZW9zKTsKICAgIH0sCiAgICAKICAgIGxhenk6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgW3Jhd191cmwsIHBhcnNlc1N0ciA9ICcnLCB1YSA9ICcnLCByZWZlcmVyID0gJyddID0gdGhpcy5pbnB1dC5zcGxpdCgnQCcpOwogICAgICAgIGxldCBwYXJzZXMgPSBwYXJzZXNTdHIgPyBwYXJzZXNTdHIuc3BsaXQoJywnKSA6IFtdOwogICAgICAgIGxldCBwbGF5X2hlYWRlcnMgPSB7fTsKICAgICAgICBpZiAodWEpIHBsYXlfaGVhZGVyc1snVXNlci1BZ2VudCddID0gdWE7CiAgICAgICAgaWYgKHJlZmVyZXIpIHBsYXlfaGVhZGVyc1snUmVmZXJlciddID0gcmVmZXJlcjsKICAgICAgICBsZXQgZmluYWxfdXJsID0gJyc7CiAgICAgICAgbGV0IGp4ID0gMDsKICAgICAgICBmb3IgKGNvbnN0IHBhcnNlIG9mIHBhcnNlcykgewogICAgICAgICAgICBpZiAocGFyc2Uuc3RhcnRzV2l0aCgnaHR0cCcpKSB7CiAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgIGxldCByZXMgPSBhd2FpdCBfZmV0Y2gocGFyc2UgKyByYXdfdXJsLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICAgICAgICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzLnRleHQoKSk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb24udXJsICYmIGpzb24udXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSkgewogICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF91cmwgPSBqc29uLnVybDsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge30KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgaWYgKCFmaW5hbF91cmwgJiYgcmF3X3VybC5zdGFydHNXaXRoKCdodHRwJykpIHsKICAgICAgICAgICAgZmluYWxfdXJsID0gcmF3X3VybDsKICAgICAgICAgICAgaWYgKC8oPzp3d3dcLmlxaXlpfHZcLnFxfHZcLnlvdWt1fHd3d1wubWd0dnx3d3dcLmJpbGliaWxpKVwuY29tLy50ZXN0KHJhd191cmwpKSB7CiAgICAgICAgICAgICAgICBqeCA9IDE7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgcGFyc2U6IGp4LAogICAgICAgICAgICB1cmw6IGZpbmFsX3VybCwKICAgICAgICAgICAgaGVhZGVyOiBwbGF5X2hlYWRlcnMKICAgICAgICB9OwogICAgfQp9Ow== \ No newline at end of file diff --git "a/spider/js/\347\210\261\347\216\251\351\237\263\344\271\220[\345\220\254].js" "b/spider/js/\347\210\261\347\216\251\351\237\263\344\271\220[\345\220\254].js" new file mode 100644 index 00000000..28c15f7c --- /dev/null +++ "b/spider/js/\347\210\261\347\216\251\351\237\263\344\271\220[\345\220\254].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '爱玩音乐', + author: 'EylinSir', + '类型': '音乐', + logo: 'https://www.22a5.com/favicon.ico', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICfpn7PkuZAnLAogICAgdGl0bGU6ICfniLHnjqnpn7PkuZAnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgaG9zdDogJ2h0dHBzOi8vd3d3LjIyYTUuY29tJywKICAgIHVybDogJy9meWNsYXNzL2Z5ZmlsdGVyLWZ5cGFnZS8nLAogICAgbG9nbzogJ2h0dHBzOi8vd3d3LjIyYTUuY29tL2Zhdmljb24uaWNvJywKICAgIGNsYXNzX25hbWU6ICfnlLXlj7Am5q2M5Y2VJuS4k+i+kSbmrYzmiYsm6auY5riFTVYm5paw5q2M5qacJlRPUOamnOWNlScsCiAgICBjbGFzc191cmw6ICcvcmFkaW9saXN0L2luZGV4Lmh0bWwmL3BsYXl0eXBlL2luZGV4Lmh0bWwmL2FsYnVtbGlzdC9pbmRleC5odG1sJi9zaW5nZXJsaXN0L2luZGV4L2luZGV4L2luZGV4L2luZGV4Lmh0bWwmL212bGlzdC9odWF5dS5odG1sJi9saXN0L25ldy5odG1sJi9saXN0L3RvcC5odG1sJywKICAgIHNlYXJjaFVybDogJy9zby8qKi9meXBhZ2UuaHRtbCcsCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDEsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogNTAwMCwKICAgIHBsYXlfcGFyc2U6IHRydWUsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiBNT0JJTEVfVUEsCiAgICAgICAgJ1JlZmVyZXInOiAnaHR0cHM6Ly93d3cuMjJhNS5jb20vJwogICAgfSwKICAgIGZpbHRlcjogewogICAgICAgICIvcmFkaW9saXN0L2luZGV4Lmh0bWwiOiBbeyBrZXk6ICJpZCIsIG5hbWU6ICLliIbnsbsiLCB2YWx1ZTogW3sgIm4iOiAi5pyA5pawIiwgInYiOiAiaW5kZXgiIH0sIHsgIm4iOiAi5pyA54OtIiwgInYiOiAiaG90IiB9LCB7ICJuIjogIuacieWjsOWwj+ivtCIsICJ2IjogIm5vdmVsIiB9LCB7ICJuIjogIuebuOWjsCIsICJ2IjogInhpYW5neWkiIH0sIHsgIm4iOiAi6Z+z5LmQIiwgInYiOiAibXVzaWMiIH0sIHsgIm4iOiAi5oOF5oSfIiwgInYiOiAiZW1vdGlvbiIgfSwgeyAibiI6ICLlm73mvKsiLCAidiI6ICJnYW1lIiB9LCB7ICJuIjogIuW9seinhiIsICJ2IjogInlpbmdzaGkiIH0sIHsgIm4iOiAi6ISx5Y+j56eAIiwgInYiOiAidGFsa3Nob3ciIH0sIHsgIm4iOiAi5Y6G5Y+yIiwgInYiOiAiaGlzdG9yeSIgfSwgeyAibiI6ICLlhL/nq6UiLCAidiI6ICJjaGlsZHJlbiIgfSwgeyAibiI6ICLmlZnogrIiLCAidiI6ICJlZHVjYXRpb24iIH0sIHsgIm4iOiAi5YWr5Y2mIiwgInYiOiAiZ29zc2lwIiB9LCB7ICJuIjogIuaOqOeQhiIsICJ2IjogInR1aWxpIiB9LCB7ICJuIjogIuWktOadoSIsICJ2IjogImhlYWRsaW5lIiB9XSB9XSwKICAgICAgICAiL3Npbmdlcmxpc3QvaW5kZXgvaW5kZXgvaW5kZXgvaW5kZXguaHRtbCI6IFsKICAgICAgICAgICAgeyAia2V5IjogImFyZWEiLCAibmFtZSI6ICLlnLDljLoiLCAidmFsdWUiOiBbeyAibiI6ICLlhajpg6giLCAidiI6ICJpbmRleCIgfSwgeyAibiI6ICLljY7or60iLCAidiI6ICJodWF5dSIgfSwgeyAibiI6ICLmrKfnvo4iLCAidiI6ICJvdW1laSIgfSwgeyAibiI6ICLpn6nlm70iLCAidiI6ICJoYW5ndW8iIH0sIHsgIm4iOiAi5pel5pysIiwgInYiOiAicmlicm4iIH1dIH0sCiAgICAgICAgICAgIHsgImtleSI6ICJzZXgiLCAibmFtZSI6ICLmgKfliKsiLCAidmFsdWUiOiBbeyAibiI6ICLlhajpg6giLCAidiI6ICJpbmRleCIgfSwgeyAibiI6ICLnlLciLCAidiI6ICJtYWxlIiB9LCB7ICJuIjogIuWlsyIsICJ2IjogImdpcmwiIH0sIHsgIm4iOiAi57uE5ZCIIiwgInYiOiAiYmFuZCIgfV0gfSwKICAgICAgICAgICAgeyAia2V5IjogImdlbnJlIiwgIm5hbWUiOiAi5rWB5rS+IiwgInZhbHVlIjogW3sgIm4iOiAi5YWo6YOoIiwgInYiOiAiaW5kZXgiIH0sIHsgIm4iOiAi5rWB6KGMIiwgInYiOiAibGl1eGluZyIgfSwgeyAibiI6ICLnlLXlrZAiLCAidiI6ICJkaWFuemkiIH0sIHsgIm4iOiAi5pGH5ruaIiwgInYiOiAieWFvZ3VuIiB9LCB7ICJuIjogIuWYu+WTiCIsICJ2IjogInhpaGEiIH0sIHsgIm4iOiAiUiZCIiwgInYiOiAicmIiIH0sIHsgIm4iOiAi5rCR6LCjIiwgInYiOiAibWlueWFvIiB9LCB7ICJuIjogIueIteWjqyIsICJ2IjogImp1ZXNoaSIgfSwgeyAibiI6ICLlj6TlhbgiLCAidiI6ICJndWRpYW4iIH1dIH0sCiAgICAgICAgICAgIHsgImtleSI6ICJjaGFyIiwgIm5hbWUiOiAi5a2X5q+NIiwgInZhbHVlIjogW3sgIm4iOiAi5YWo6YOoIiwgInYiOiAiaW5kZXgiIH0sIHsgIm4iOiAiQSIsICJ2IjogImEiIH0sIHsgIm4iOiAiQiIsICJ2IjogImIiIH0sIHsgIm4iOiAiQyIsICJ2IjogImMiIH0sIHsgIm4iOiAiRCIsICJ2IjogImQiIH0sIHsgIm4iOiAiRSIsICJ2IjogImUiIH0sIHsgIm4iOiAiRiIsICJ2IjogImYiIH0sIHsgIm4iOiAiRyIsICJ2IjogImciIH0sIHsgIm4iOiAiSCIsICJ2IjogImgiIH0sIHsgIm4iOiAiSSIsICJ2IjogImkiIH0sIHsgIm4iOiAiSiIsICJ2IjogImoiIH0sIHsgIm4iOiAiSyIsICJ2IjogImsiIH0sIHsgIm4iOiAiTCIsICJ2IjogImwiIH0sIHsgIm4iOiAiTSIsICJ2IjogIm0iIH0sIHsgIm4iOiAiTiIsICJ2IjogIm4iIH0sIHsgIm4iOiAiTyIsICJ2IjogIm8iIH0sIHsgIm4iOiAiUCIsICJ2IjogInAiIH0sIHsgIm4iOiAiUSIsICJ2IjogInEiIH0sIHsgIm4iOiAiUiIsICJ2IjogInIiIH0sIHsgIm4iOiAiUyIsICJ2IjogInMiIH0sIHsgIm4iOiAiVCIsICJ2IjogInQiIH0sIHsgIm4iOiAiVSIsICJ2IjogInUiIH0sIHsgIm4iOiAiViIsICJ2IjogInYiIH0sIHsgIm4iOiAiVyIsICJ2IjogInciIH0sIHsgIm4iOiAiWCIsICJ2IjogIngiIH0sIHsgIm4iOiAiWSIsICJ2IjogInkiIH0sIHsgIm4iOiAiWiIsICJ2IjogInoiIH1dIH0KICAgICAgICBdLAogICAgICAgICIvbXZsaXN0L2h1YXl1Lmh0bWwiOiBbeyAia2V5IjogImlkIiwgIm5hbWUiOiAi57G75Z6LIiwgInZhbHVlIjogW3sgIm4iOiAi5Y2O6K+t57K+6YCJIiwgInYiOiAiaHVheXUiIH0sIHsgIm4iOiAi5pel6Z+p57K+6YCJIiwgInYiOiAiIHJpaGFuIiB9LCB7ICJuIjogIuasp+e+jueyvumAiSIsICJ2IjogIm91bWVpIiB9XSB9XQogICAgfSwKCiAgICBfYWJzOiAodXJsKSA9PiB7CiAgICAgICAgaWYgKCF1cmwgfHwgdHlwZW9mIHVybCAhPT0gJ3N0cmluZycpIHJldHVybiAnJzsKICAgICAgICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHVybCA6IGBodHRwczovL3d3dy4yMmE1LmNvbSR7dXJsLnN0YXJ0c1dpdGgoJy8nKSA/IHVybCA6ICcvJyArIHVybH1gOwogICAgfSwKCiAgICBfY2xlYW46ICh0ZXh0KSA9PiAodGV4dCB8fCAnJykucmVwbGFjZSgvKOeIseeOqemfs+S5kOe9kXzop4bpopHkuIvovb3or7TmmI586KeG6aKR5LiL6L295Zyw5Z2AfHd3d1wuMnQ1OFwuY29tfE1QM+WFjei0ueS4i+i9vXxMUkPmrYzor43kuIvovb185YWo6YOo5q2M5puyfFxb56ysXGQr6aG1XF185Yi35pawfOavj+aXpeaOqOiNkHzmnIDmlrB854Ot6ZeofOaOqOiNkHxNVnzpq5jmuIV85peg5o2fKS9naSwgJycpLnRyaW0oKSwKCiAgICBfZ2V0X2l0ZW1zOiBmdW5jdGlvbihodG1sLCBwZGZhLCBwZCwgcGRmaCkgewogICAgICAgIGlmICh0eXBlb2YgaHRtbCAhPT0gJ3N0cmluZycpIHJldHVybiBbXTsKICAgICAgICBjb25zdCBzZWxlY3RvcnMgPSBbJy52aWRlb19saXN0IGxpJywgJy5wbGF5X2xpc3QgbGknLCAnLnBpY19saXN0IGxpJywgJy5zaW5nZXJfbGlzdCBsaScsICcuYWxpIGxpJywgJy5sYXl1aS1yb3cgbGknLCAnLmJhc2VfbCBsaScsICcubGlzdCBsaScsICcuaXRlbSBsaSddOwogICAgICAgIGxldCBpdGVtcyA9IFtdOwogICAgICAgIGZvciAobGV0IHNlbCBvZiBzZWxlY3RvcnMpIHsKICAgICAgICAgICAgbGV0IGxpc3QgPSBwZGZhKGh0bWwsIHNlbCkgfHwgW107CiAgICAgICAgICAgIGlmIChsaXN0Lmxlbmd0aCA9PT0gMCkgY29udGludWU7CiAgICAgICAgICAgIGZvciAobGV0IGl0IG9mIGxpc3QpIHsKICAgICAgICAgICAgICAgIGxldCBocmVmID0gcGQoaXQsICdhJiZocmVmJyk7CiAgICAgICAgICAgICAgICBpZiAoIWhyZWYgfHwgaHJlZiA9PT0gJy8nIHx8IC8oXC91c2VyXC98XC9sb2dpblwvfGphdmFzY3JpcHQpLy50ZXN0KGhyZWYpKSBjb250aW51ZTsKICAgICAgICAgICAgICAgIGxldCB0aXRsZSA9IHRoaXMuX2NsZWFuKHBkZmgoaXQsICcubmFtZSYmVGV4dCcpIHx8IHBkKGl0LCAnYSYmdGl0bGUnKSB8fCBwZChpdCwgJ2EmJlRleHQnKSk7CiAgICAgICAgICAgICAgICBpZiAoIXRpdGxlKSBjb250aW51ZTsKICAgICAgICAgICAgICAgIGxldCBpbWdfc3JjID0gcGQoaXQsICdpbWcmJmRhdGEtb3JpZ2luYWwnKSB8fCBwZChpdCwgJ2ltZyYmZGF0YS1zcmMnKSB8fCBwZChpdCwgJ2ltZyYmc3JjJyk7CiAgICAgICAgICAgICAgICBsZXQgcGljX3VybCA9IHRoaXMuX2FicygoaW1nX3NyYyB8fCAnJykucmVwbGFjZSgnMTIwJywgJzUwMCcpKTsKICAgICAgICAgICAgICAgIGl0ZW1zLnB1c2goewogICAgICAgICAgICAgICAgICAgIHRpdGxlOiB0aXRsZSwKICAgICAgICAgICAgICAgICAgICB1cmw6IHRoaXMuX2FicyhocmVmKSwKICAgICAgICAgICAgICAgICAgICBwaWNfdXJsOiBwaWNfdXJsLAogICAgICAgICAgICAgICAgICAgIGRlc2M6ICcnCiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gaXRlbXM7CiAgICB9LAoKICAgIOaOqOiNkDogYXN5bmMgZnVuY3Rpb24oKSB7CiAgICAgICAgcmV0dXJuIFtdOwogICAgfSwKCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgbGV0IHBhZ2UgPSBwZyB8fCAxOwogICAgICAgIGxldCB1cmwgPSB0aWQucmVwbGFjZSgvXC9cLy9nLCAnLycpLnJlcGxhY2UoJy9meWZpbHRlci0xLycsICcnKTsKICAgICAgICBpZiAoL1wvc2luZ2VybGlzdFwvLy50ZXN0KHRpZCkpIHsKICAgICAgICAgICAgbGV0IHAgPSB0aWQuc3BsaXQoJy8nKTsKICAgICAgICAgICAgaWYgKHAubGVuZ3RoID49IDIpIHsKICAgICAgICAgICAgICAgIGxldCBhcmVhID0gZXh0ZW5kLmFyZWEgfHwgcFsyXSB8fCAnaW5kZXgnOwogICAgICAgICAgICAgICAgbGV0IHNleCA9IGV4dGVuZC5zZXggfHwgcFszXSB8fCAnaW5kZXgnOwogICAgICAgICAgICAgICAgbGV0IGdlbnJlID0gZXh0ZW5kLmdlbnJlIHx8IHBbNF0gfHwgJ2luZGV4JzsKICAgICAgICAgICAgICAgIGxldCBjaGFyID0gZXh0ZW5kLmNoYXIgfHwgJ2luZGV4JzsKICAgICAgICAgICAgICAgIHVybCA9IGAvJHtwWzFdfS8ke2FyZWF9LyR7c2V4fS8ke2dlbnJlfS8ke2NoYXJ9Lmh0bWxgOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBpZiAoZXh0ZW5kLmlkICYmICFbJ2luZGV4JywgJ3RvcCddLmluY2x1ZGVzKGV4dGVuZC5pZCkpIHsKICAgICAgICAgICAgdXJsID0gdXJsLnJlcGxhY2UoLyhcdyspXC5odG1sJC8sIGV4dGVuZC5pZCArICcuaHRtbCcpOwogICAgICAgIH0KCiAgICAgICAgaWYgKHBhZ2UgPiAxKSB7CiAgICAgICAgICAgIGxldCBzZXAgPSAvKFwvc2luZ2VybGlzdFwvfFwvcmFkaW9saXN0XC98XC9tdmxpc3RcL3xcL3BsYXl0eXBlXC98XC9saXN0XC8pLy50ZXN0KHVybCkgPyAnLycgOiAnXyc7CiAgICAgICAgICAgIHVybCA9IHVybC5yZXBsYWNlKC8oX1xkK3xcL1xkKyk/XC5odG1sJC8sIGAke3NlcH0ke3BhZ2V9Lmh0bWxgKTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgbGV0IGZpbmFsVXJsID0gdGhpcy5fYWJzKHVybCk7CiAgICAgICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgcmVxKGZpbmFsVXJsKTsKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHRoaXMuX2dldF9pdGVtcyhyZXNwb25zZS5jb250ZW50IHx8IHJlc3BvbnNlLCB0aGlzLnBkZmEsIHRoaXMucGQsIHRoaXMucGRmaCkpOwogICAgfSwKCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uKHdkLCBxdWljaywgcGcpIHsKICAgICAgICBsZXQgcGFnZSA9IHBnIHx8IDE7CiAgICAgICAgbGV0IHVybCA9IHRoaXMuX2FicyhgL3NvLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHdkKX0vJHtwYWdlfS5odG1sYCk7CiAgICAgICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgcmVxKHVybCk7CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdCh0aGlzLl9nZXRfaXRlbXMocmVzcG9uc2UuY29udGVudCB8fCByZXNwb25zZSwgdGhpcy5wZGZhLCB0aGlzLnBkLCB0aGlzLnBkZmgpKTsKICAgIH0sCgogICAg5LqM57qnOiBhc3luYyBmdW5jdGlvbihpZHMpIHsKICAgICAgICBjb25zdCB7IHBkZmEsIHBkZmgsIHBkIH0gPSB0aGlzOwogICAgICAgIGxldCB1cmwgPSAoQXJyYXkuaXNBcnJheShpZHMpID8gaWRzWzBdIDogaWRzKS5yZXBsYWNlKC9bYCBdL2csICcnKTsKICAgICAgICB1cmwgPSB0aGlzLl9hYnModXJsKTsKICAgICAgICAKICAgICAgICBsZXQgaHRtbCA9IChhd2FpdCByZXEodXJsKSkuY29udGVudDsKICAgICAgICBsZXQgY292ZXIgPSB0aGlzLl9hYnMocGQoaHRtbCwgJy5zaW5nZXJfaW5mbyAucGljIGltZyYmc3JjJykpOwogICAgICAgIGxldCBjb250ZW50ID0gcGRmaChodG1sLCAnLnNpbmdlcl9pbmZvIC5saXN0X3IgLmluZm8gcCYmVGV4dCcpOwogICAgICAgIAogICAgICAgIGxldCB2b2QgPSB7CiAgICAgICAgICAgIHZvZF9uYW1lOiB0aGlzLl9jbGVhbihwZGZoKGh0bWwsICdoMSYmVGV4dCcpIHx8IHBkZmgoaHRtbCwgJ3RpdGxlJiZUZXh0JykpLAogICAgICAgICAgICB2b2RfcGljOiBjb3ZlciwKICAgICAgICAgICAgdm9kX3BsYXlfZnJvbTogJ+eIseeOqemfs+S5kCcsCiAgICAgICAgICAgIHZvZF9jb250ZW50OiBjb250ZW50IHx8ICcnLAogICAgICAgICAgICB2b2RfcmVtYXJrczogJycKICAgICAgICB9OwogICAgICAgIAogICAgICAgIGxldCBwbGF5X2xpc3QgPSBbXTsKICAgICAgICBsZXQgbWFrZUlkID0gKHUsIGxyYyA9ICcnKSA9PiBgJHt1fXx8fCR7Y292ZXJ9fHx8JHtscmN9YDsKICAgICAgICBsZXQgZ2V0THJjTGluayA9IChsaW5rKSA9PiB7CiAgICAgICAgICAgIGxldCBtYXRjaCA9IGxpbmsubWF0Y2goL1wvKHNvbmd8bXAzfHJhZGlvfHJhZGlvbGlzdHxyYWRpb3BsYXkpXC8oW14vXSspXC5odG1sLyk7CiAgICAgICAgICAgIHJldHVybiBtYXRjaCA/IHRoaXMuX2FicyhgL3BsdWcvZG93bi5waHA/YWM9bXVzaWMmbGs9bHJjJmlkPSR7bWF0Y2hbMl19YCkgOiAnJzsKICAgICAgICB9OwoKICAgICAgICBpZiAoL1wvKHZpZGVvfG1wNClcLyhbXi9dKylcLmh0bWwvLnRlc3QodXJsKSkgewogICAgICAgICAgICBsZXQgdmlkID0gdXJsLm1hdGNoKC9cLyh2aWRlb3xtcDQpXC8oW14vXSspXC5odG1sLylbMl07CiAgICAgICAgICAgIGxldCBxdWFsaXRpZXMgPSBbeyBuYW1lOiAn6JOd5YWJJywgcTogJzEwODAnIH0sIHsgbmFtZTogJ+i2hea4hScsIHE6ICc3MjAnIH0sIHsgbmFtZTogJ+mrmOa4hScsIHE6ICc0ODAnIH1dOwogICAgICAgICAgICBxdWFsaXRpZXMuZm9yRWFjaChxID0+IHsKICAgICAgICAgICAgICAgIHBsYXlfbGlzdC5wdXNoKGAke3EubmFtZX0kJHttYWtlSWQodGhpcy5fYWJzKGAvcGx1Zy9kb3duLnBocD9hYz12cGxheSZpZD0ke3ZpZH0mcT0ke3EucX1gKSl9YCk7CiAgICAgICAgICAgIH0pOwogICAgICAgIH0gCiAgICAgICAgZWxzZSB7CiAgICAgICAgICAgIGxldCBpdGVtcyA9IHBkZmEoaHRtbCwgJy5wbGF5X2xpc3QmJmxpJykgfHwgW107CiAgICAgICAgICAgIGlmICghaXRlbXMubGVuZ3RoKSBpdGVtcyA9IHBkZmEoaHRtbCwgJy5zb25nX2xpc3QmJmxpJykgfHwgW107CiAgICAgICAgICAgIGlmICghaXRlbXMubGVuZ3RoKSBpdGVtcyA9IHBkZmEoaHRtbCwgJy5tdXNpY19saXN0JiZsaScpIHx8IFtdOwogICAgICAgICAgICBpZiAoIWl0ZW1zLmxlbmd0aCkgaXRlbXMgPSBwZGZhKGh0bWwsICd1bCYmbGknKSB8fCBbXTsKICAgICAgICAgICAgaWYgKGl0ZW1zLmxlbmd0aCA9PT0gMCAmJiAvXC8oc29uZ3xtcDN8cmFkaW98cmFkaW9saXN0fHJhZGlvcGxheSlcLy8udGVzdCh1cmwpKSB7CiAgICAgICAgICAgICAgICBwbGF5X2xpc3QucHVzaChg5pKt5pS+JCR7bWFrZUlkKHVybCwgZ2V0THJjTGluayh1cmwpKX1gKTsKICAgICAgICAgICAgfSAKICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICBpdGVtcy5mb3JFYWNoKGl0ID0+IHsKICAgICAgICAgICAgICAgICAgICBsZXQgYSA9IHBkKGl0LCAnYSYmaHJlZicpOwogICAgICAgICAgICAgICAgICAgIGlmICghYSkgcmV0dXJuOwogICAgICAgICAgICAgICAgICAgIGlmICgvXC8oc29uZ3xtcDN8cmFkaW98cmFkaW9saXN0fHJhZGlvcGxheXx2aWRlb3xtcDQpXC8vLnRlc3QoYSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHRpdGxlID0gdGhpcy5fY2xlYW4ocGRmaChpdCwgJy5uYW1lJiZUZXh0JykgfHwgcGQoaXQsICdhJiZUZXh0JykpOwogICAgICAgICAgICAgICAgICAgICAgICBwbGF5X2xpc3QucHVzaChgJHt0aXRsZX0kJHttYWtlSWQodGhpcy5fYWJzKGEpLCBnZXRMcmNMaW5rKGEpKX1gKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB2b2Qudm9kX3BsYXlfdXJsID0gcGxheV9saXN0LmpvaW4oJyMnKTsKICAgICAgICByZXR1cm4gdm9kOwogICAgfSwKCiAgICBsYXp5OiBhc3luYyBmdW5jdGlvbihmbGFnLCBpZCwgZmxhZ3MpIHsKICAgICAgICBsZXQgW3VybCwgcGljLCBscmNVcmxdID0gaWQuc3BsaXQoJ3x8fCcpOwogICAgICAgIHVybCA9ICh1cmwgfHwgJycpLnJlcGxhY2UoL0AuKyQvLCAnJykudHJpbSgpOwogICAgICAgIGxldCBhcGlIZWFkZXJzID0gewogICAgICAgICAgICAnUmVmZXJlcic6IHRoaXMuaG9zdCArICcvJywKICAgICAgICAgICAgJ1VzZXItQWdlbnQnOiB0aGlzLmhlYWRlcnNbJ1VzZXItQWdlbnQnXQogICAgICAgIH07CgogICAgICAgIGxldCBwbGF5VXJsID0gJyc7CiAgICAgICAgbGV0IGlzVmlkZW8gPSB1cmwuaW5jbHVkZXMoJ2Rvd24ucGhwJykgJiYgdXJsLmluY2x1ZGVzKCdhYz12cGxheScpOwogICAgICAgIGlmIChpc1ZpZGVvKSB7CiAgICAgICAgICAgIGxldCByZXMgPSBhd2FpdCByZXEodXJsLCB7IG1ldGhvZDogJ0dFVCcsIHJlZGlyZWN0OiAwLCBoZWFkZXJzOiBhcGlIZWFkZXJzIH0pOwogICAgICAgICAgICBwbGF5VXJsID0gcmVzLmhlYWRlcnMuTG9jYXRpb24gfHwgcmVzLmhlYWRlcnMubG9jYXRpb24gfHwgdGhpcy5fcHJvY2Vzc19hcGlfcmVzcG9uc2UocmVzKTsKICAgICAgICB9CgogICAgICAgIGVsc2UgaWYgKC9cLyhzb25nfG1wM3xyYWRpb3xyYWRpb2xpc3R8cmFkaW9wbGF5KVwvKFteL10rKVwuaHRtbC8udGVzdCh1cmwpKSB7CiAgICAgICAgICAgIGxldCBtaWQgPSB1cmwubWF0Y2goL1wvKFteL10rKVwuaHRtbC8pWzFdOwogICAgICAgICAgICBsZXQgcmVzID0gYXdhaXQgcmVxKHRoaXMuX2FicygnL2pzL3BsYXkucGhwJyksIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLAogICAgICAgICAgICAgICAgaGVhZGVyczogewogICAgICAgICAgICAgICAgICAgIC4uLmFwaUhlYWRlcnMsCiAgICAgICAgICAgICAgICAgICAgJ1gtUmVxdWVzdGVkLVdpdGgnOiAnWE1MSHR0cFJlcXVlc3QnLAogICAgICAgICAgICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJwogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIGRhdGE6IHsgaWQ6IG1pZCwgdHlwZTogJ211c2ljJyB9CiAgICAgICAgICAgIH0pOwogICAgICAgICAgICBwbGF5VXJsID0gdGhpcy5fcHJvY2Vzc19hcGlfcmVzcG9uc2UocmVzKTsKICAgICAgICB9CgogICAgICAgIGxldCBmaW5hbFVybCA9IHRoaXMuX2FicyhwbGF5VXJsIHx8IHVybCk7CiAgICAgICAgbGV0IHBsYXllckhlYWRlcnMgPSB7CiAgICAgICAgICAgICdVc2VyLUFnZW50JzogYXBpSGVhZGVyc1snVXNlci1BZ2VudCddLAogICAgICAgICAgICAuLi4oZmluYWxVcmwuaW5jbHVkZXMoJzIyYTUuY29tJykgJiYgeyAnUmVmZXJlcic6IGFwaUhlYWRlcnNbJ1JlZmVyZXInXSB9KQogICAgICAgIH07CgogICAgICAgIGlmIChpc1ZpZGVvKSB7CiAgICAgICAgICAgIHJldHVybiB7IHBhcnNlOiAwLCB1cmw6IGZpbmFsVXJsLCBoZWFkZXI6IHBsYXllckhlYWRlcnMgfTsKICAgICAgICB9CgogICAgICAgIGxldCBjb3ZlciA9ICcnOwogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCBwYWdlUmVzID0gYXdhaXQgcmVxKHVybCwgeyBoZWFkZXJzOiBhcGlIZWFkZXJzIH0pOwogICAgICAgICAgICBsZXQgcGFnZUh0bWwgPSB0eXBlb2YgcGFnZVJlcyA9PT0gJ29iamVjdCcgPyBwYWdlUmVzLmNvbnRlbnQgOiBwYWdlUmVzOwogICAgICAgICAgICBsZXQgb2dJbWFnZU1hdGNoID0gcGFnZUh0bWwubWF0Y2goLzxtZXRhW14+XStwcm9wZXJ0eT0ib2c6aW1hZ2UiW14+XStjb250ZW50PSIoW14iXSspIltePl0qPi9pKTsKICAgICAgICAgICAgaWYgKG9nSW1hZ2VNYXRjaCAmJiBvZ0ltYWdlTWF0Y2hbMV0pIHsKICAgICAgICAgICAgICAgIGNvdmVyID0gb2dJbWFnZU1hdGNoWzFdOwogICAgICAgICAgICB9CiAgICAgICAgfSBjYXRjaCB7fQoKICAgICAgICBsZXQgbHJjRGF0YSA9ICcnOwogICAgICAgIGlmIChscmNVcmwpIHsKICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgIGxldCBscmNSZXMgPSBhd2FpdCByZXEobHJjVXJsLCB7IGhlYWRlcnM6IGFwaUhlYWRlcnMgfSk7CiAgICAgICAgICAgICAgICBsZXQgY29udGVudCA9IHR5cGVvZiBscmNSZXMgPT09ICdvYmplY3QnID8gbHJjUmVzLmNvbnRlbnQgOiBscmNSZXM7CiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnICYmIGNvbnRlbnQuaW5jbHVkZXMoJ1snKSAmJiBjb250ZW50LmluY2x1ZGVzKCddJykpIHsKICAgICAgICAgICAgICAgICAgICBscmNEYXRhID0gY29udGVudDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBjYXRjaCB7fQogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgcGFyc2U6IDAsCiAgICAgICAgICAgIHVybDogZmluYWxVcmwsCiAgICAgICAgICAgIGhlYWRlcjogcGxheWVySGVhZGVycywKICAgICAgICAgICAgbHJjOiBscmNEYXRhLAogICAgICAgICAgICBwaWM6IGNvdmVyLAogICAgICAgICAgICBjb3ZlcjogY292ZXIsCiAgICAgICAgICAgIGhlaWdodDogNzIwCiAgICAgICAgfTsKICAgIH0sCgogICAgX3Byb2Nlc3NfYXBpX3Jlc3BvbnNlOiAocmVzKSA9PiB7CiAgICAgICAgaWYgKCFyZXMpIHJldHVybiAnJzsKICAgICAgICBjb25zdCBjb250ZW50ID0gdHlwZW9mIHJlcyA9PT0gJ29iamVjdCcgPyByZXMuY29udGVudCA6IHJlczsKICAgICAgICBjb25zdCB0cmltbWVkID0gY29udGVudC50cmltKCk7CiAgICAgICAgbGV0IGpzb247CiAgICAgICAgdHJ5IHsganNvbiA9IEpTT04ucGFyc2UoY29udGVudCk7IH0gY2F0Y2gge30gCiAgICAgICAgcmV0dXJuIGpzb24/LnVybCA/IGpzb24udXJsLnJlcGxhY2UoL1xcXC8vZywgJy8nKSA6ICh0cmltbWVkLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRyaW1tZWQgOiAnJyk7CiAgICB9Cn07 \ No newline at end of file diff --git "a/spider/js/\347\213\254\346\222\255\345\272\223[\344\274\230].js" "b/spider/js/\347\213\254\346\222\255\345\272\223[\344\274\230].js" new file mode 100644 index 00000000..ba2d1f75 --- /dev/null +++ "b/spider/js/\347\213\254\346\222\255\345\272\223[\344\274\230].js" @@ -0,0 +1,133 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '独播库', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +var rule = { + 类型: '影视', + author: 'EylinSir', + title: '独播库', + host: 'https://api.dbokutv.com', + url: '/home', + searchUrl: '/vodsearch', + searchable: 1, + quickSearch: 0, + filterable: 1, + timeout: 10000, + play_parse: true, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer": "https://www.duboku.tv/" + }, + + get: async function(path) { + let url = this.getSignedUrl(path); + let resp = await _fetch(url, { headers: this.headers }); + return JSON.parse(await resp.text()); + }, + + format: function(list) { + return (list || []).map(j => ({ + title: j.Name, + url: `${this.host}${this.decodeData(j.DId || j.DuId)}`, + desc: j.Tag, + pic_url: this.decodeData(j.TnId), + vod_id: this.decodeData(j.DId || j.DuId) + })); + }, + + class_parse: async function() { + return { + class: [{ type_id: '2', type_name: '连续剧' }, { type_id: '1', type_name: '电影' }, { type_id: '3', type_name: '综艺' }, { type_id: '4', type_name: '动漫' }], + filters: {} + }; + }, + + 推荐: async function() { + let json = await this.get('/home'); + let videos = []; + (json || []).forEach(g => videos.push(...this.format(g.VodList))); + return setResult(videos); + }, + + 一级: async function(tid, pg) { + let page = (pg || 1).toString(); + let data = await this.get(`/vodshow/${tid}--------${page === '1' ? '' : page}---`); + return setResult(this.format(data.VodList)); + }, + + 二级: async function() { + let id = this.input.replace(this.host, ''); + let data = await this.get(id); + if (!data) return {}; + let playUrls = (data.Playlist || []).map(i => + `${i.EpisodeName}$${this.decodeData(i.VId)}` + ).join('#'); + return { + vod_id: id, + vod_name: data.Name, + vod_pic: this.decodeData(data.TnId), + vod_remarks: `评分:${data.Rating}`, + vod_year: data.ReleaseYear, + vod_area: data.Region, + vod_actor: Array.isArray(data.Actor) ? data.Actor.join(',') : data.Actor, + vod_director: data.Director, + vod_content: data.Description, + vod_play_from: '独播库', + vod_play_url: playUrls, + type_name: `${data.Genre || ''},${data.Scenario || ''}` + }; + }, + + 搜索: async function() { + let data = await this.get(`/vodsearch?wd=${this.KEY}`); + let url = this.getSignedUrl('/vodsearch') + `&wd=${this.KEY}`; + let resp = await _fetch(url, { headers: this.headers }); + return setResult(this.format(JSON.parse(await resp.text()))); + }, + + lazy: async function() { + let id = this.input.replace(this.host, ''); + let res = await this.get(id); + return { + parse: 0, + url: this.decodeData(res.HId), + header: { + 'User-Agent': this.headers['User-Agent'], + 'Origin': 'https://w.duboku.io', + 'Referer': 'https://w.duboku.io/' + } + }; + }, + + getSignedUrl: function(path) { + const ts = Math.floor(Date.now() / 1000).toString(); + const rand = Math.floor(Math.random() * 800000001); + const combined = (rand + 100000000).toString() + (900000000 - rand).toString(); + let interleaved = ''; + const len = Math.min(combined.length, ts.length); + for (let i = 0; i < len; i++) interleaved += combined[i] + ts[i]; + interleaved += combined.substring(len) + ts.substring(len); + const ssid = base64Encode(interleaved).replace(/=/g, '.'); + const rStr = (l) => Array(l).fill(0).map(() => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.charAt(Math.floor(Math.random() * 62))).join(''); + return `${this.host}${path}${path.includes('?') ? '&' : '?'}sign=${rStr(60)}&token=${rStr(38)}&ssid=${ssid}`; + }, + + decodeData: function(data) { + if (!data || typeof data !== 'string') return ''; + let str = data.replace(/['"]/g, '').trim(); + if (!str) return ''; + let res = ''; + for (let i = 0; i < str.length; i += 10) { + res += str.substring(i, i + 10).split('').reverse().join(''); + } + try { return base64Decode(res.replace(/\./g, '=')); } catch (e) { return ''; } + }, +}; \ No newline at end of file diff --git "a/spider/js/\347\223\234\345\255\220[\344\274\230].js" "b/spider/js/\347\223\234\345\255\220[\344\274\230].js" new file mode 100644 index 00000000..118a7540 --- /dev/null +++ "b/spider/js/\347\223\234\345\255\220[\344\274\230].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 2, + filterable: 1, + quickSearch: 0, + title: '瓜子', + author: 'EylinSir', + '类型': '影视', + logo: 'https://guaziyingshi.xxsnav.com/files/guaziyingshi.png', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfnk5zlrZAnLAogICAgaG9zdDogJ2h0dHBzOi8vYXBpLnczMno3dnRkLmNvbScsCiAgICB1cmw6ICcvQXBwL0luZGV4TGlzdC9pbmRleExpc3QnLAogICAgc2VhcmNoVXJsOiAnL0FwcC9JbmRleC9maW5kTW9yZVZvZCcsCiAgICBsb2dvOiAnaHR0cHM6Ly9ndWF6aXlpbmdzaGkueHhzbmF2LmNvbS9maWxlcy9ndWF6aXlpbmdzaGkucG5nJywKICAgIHNlYXJjaGFibGU6IDIsCiAgICBxdWlja1NlYXJjaDogMCwKICAgIGZpbHRlcmFibGU6IDEsCiAgICBwbGF5X3BhcnNlOiB0cnVlLCAKICAgIGNsYXNzX25hbWU6ICfnlLXlvbEm55S16KeG5YmnJuWKqOa8qybnu7zoibom55+t5YmnJywKICAgIGNsYXNzX3VybDogJzEmMiY0JjMmNjQnLAogICAgaGVhZGVyczogewogICAgICAgICdVc2VyLUFnZW50JzogJ29raHR0cC8zLjEyLjAnLAogICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJywKICAgICAgICAnQ2FjaGUtQ29udHJvbCc6ICduby1jYWNoZScsCiAgICAgICAgJ1ZlcnNpb24nOiAnMjQwNjAyNScsCiAgICAgICAgJ1BhY2thZ2VOYW1lJzogJ2NvbS51ZjA3NmJmMGMyNDYucWU0MzlmMGQ1ZS5tOGFhZjU2YjcyNWEuaWZlYjY0NzM0NmYnLAogICAgICAgICdWZXInOiAnMS45LjInLAogICAgICAgICdSZWZlcmVyJzogJ2h0dHBzOi8vYXBpLnczMno3dnRkLmNvbScKICAgIH0sCiAgICB0b2tlbjogJzFiZTg2ZThlMThhOWZhMThiMmI4ZDU0MzI2OTlkYWQwLmFjMDA4ZWQ2NTBmZDA4N2JmYmVjZjJmZGE5ZDgyZTk4MzUyNTNlZjI0ODQzZTZiMThmY2QxMjhiMTA3NjM0OTdiY2Y5ZDUzZTk1OWY1Mzc3Y2RlMDM4YzIwY2NmOWQxN2Y2MDRjOWI4YmI2ZTYxMDQxZGVmODY3MjliMmZjNzQwOGJkMjQxZTIzYzIxM2FjNTdmMDIyNmVlNjU2ZTJiYjBhNTgzYWUwZTRmM2JmNmM2YWI2YzQ5MGM5YTZmMGQ4Y2RmZDM2NmFhY2Y1ZDgzMTkzNjcxYThmNzdjZDFhZjFmZjJlOTE0NWRlOTJlYzQzZWM4N2NmNGJkYzU2M2Y2ZTkxOWZlMzI4NjFiMGU5M2IxMThlYzM3ZDgwMzVmYmIzYy41OWRkMDVjNWQ5YThhZTcyNjUyODc4MzEyODIxOGYxNWZlNmYyYzBjODE0NWVkZGFiMTEyYjM3NGZjZmUzZDc5JywKICAgIHJzYVByaXZhdGVLZXk6IGAtLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJQ2RnSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NBbUF3Z2dKY0FnRUFBb0dBZTZoS3JXTGkxelFtalRUMQpvemJFNFFkRmVKR054dWJ4bGQ2R3JGR3hpbXhmTXNNQjZCcEpocGNUb3VBcXl3QUZwcGlLZXRVQkJiWHdZc1lVCjF3TnI2NDhYVm1QbUNNQ3k0clk4dmRsaUZuYk1VajA4NkRVNlorL29YQmRXVTMvYjFHMEROM0U5d1VMUlN3Y0sKWlQzd2ovY0NJMXZzQ20zZ2oyUjVTcWtBOVkwQ0F3RUFBUUtCZ0FKSCs0Q3hWMC96QlZjTGlCQ0h2U0FObTBsNwpIZXR5YlRoL2oycDBZMXNUWHJvNEFMd0FhQ1RVZXFkQmpXaUxTbzlsTndESEZ5cTh6WDkwK2dOeGE3YzVFcWNXClY5Rm1sVlhyOFZoZkJ6Y1pvMW5YZU5kWEZUN3RRMnlhaC9vZHRkY3grdlJNU0dKZDF0LzVrNWJEZDl3QXZZZEkKRGJsTUFnK3dpS0taNUtjZEFrRUExY0Nha0VONE5leGtGNXRIUFJyUjZYT1kvWEhma3FYeEVoTXFtTmJCOVUzNApzYVRKbkxXSUhDOElYeXM2UW16ejMwVHR6Q2p1T3FLUlJ5K0ZNTTRUZHdKQkFKUVpGUGpzR0MrUnFjRzVVdlZNCmlNUGhud2UvYlhFZWhTaEs4NnlKSy9nL1VpS3JPODdoM2FFdTVnY0pxQnlnVHEzQkJCb0gybWQzcHIvVytoVU0KV0JzQ1FRQ2hmaFRJcmREaW5LaTZsUnhyZEJubjBPaGpnMmN3dXFLNXp6VTlwL04rUzl4N0NrOHdVSTUzREttOApqVUpFOFdBRzdXTGovb0NPV0VoK2ljNk5Jd1RkQWtFQWowWDhuaHg2QVhzZ0NZUnFsMWtsYnF0Vm1MOCs5NUtaCks3UG5MV0cvSWZqUVV5M3BQR29TYVo3ZmRxdUc4YnE4b3lmNStkempFL29UWGNCeVMrNlhSUUpBUC81Y2l5MWIKTDNOaFVoc2FPVnk1NU1IWG5QamRjVFgwRmFMaSt5YlhaSWZJUTJQNHJiMTltVnExZmVNYkNYaHorTDFyRzhvYQp0NWxZS2ZwZThrODNaQT09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS1gLAoKICAgIENyeXB0b1V0aWxzOiB7CiAgICAgICAgYWVzRW5jcnlwdDogKHQsIGssIGkpID0+IENyeXB0b0pTLkFFUy5lbmNyeXB0KHQsIENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKGspLCB7IGl2OiBDcnlwdG9KUy5lbmMuVXRmOC5wYXJzZShpKSwgbW9kZTogQ3J5cHRvSlMubW9kZS5DQkMsIHBhZGRpbmc6IENyeXB0b0pTLnBhZC5Qa2NzNyB9KS5jaXBoZXJ0ZXh0LnRvU3RyaW5nKCkudG9VcHBlckNhc2UoKSwKICAgICAgICBhZXNEZWNyeXB0OiAodCwgaywgaSkgPT4gQ3J5cHRvSlMuQUVTLmRlY3J5cHQoeyBjaXBoZXJ0ZXh0OiBDcnlwdG9KUy5lbmMuSGV4LnBhcnNlKHQpIH0sIENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKGspLCB7IGl2OiBDcnlwdG9KUy5lbmMuVXRmOC5wYXJzZShpKSwgbW9kZTogQ3J5cHRvSlMubW9kZS5DQkMsIHBhZGRpbmc6IENyeXB0b0pTLnBhZC5Qa2NzNyB9KS50b1N0cmluZyhDcnlwdG9KUy5lbmMuVXRmOCksCiAgICAgICAgcnNhRGVjcnlwdDogKHQsIGspID0+IHsgdHJ5IHsgcmV0dXJuIGZvcmdlLnBraS5wcml2YXRlS2V5RnJvbVBlbShrKS5kZWNyeXB0KGZvcmdlLnV0aWwuZGVjb2RlNjQodCkpOyB9IGNhdGNoIChlKSB7IHJldHVybiAiIjsgfSB9CiAgICB9LAoKICAgIG1ha2VQeXRob25Kc29uOiBmdW5jdGlvbihkYXRhTWFwLCBvcmRlcikgewogICAgICAgIGxldCBwYXJ0cyA9IFtdLCBrZXlzID0gb3JkZXIgfHwgT2JqZWN0LmtleXMoZGF0YU1hcCk7CiAgICAgICAgbGV0IGVzYyA9IChzKSA9PiBzLnJlcGxhY2UoL1xcL2csICdcXFxcJykucmVwbGFjZSgvIi9nLCAnXFwiJykucmVwbGFjZSgvW1x1MDAwMC1cdTAwMWZcdTAwN2YtXHVmZmZmXS9nLCBjID0+ICdcXHUnICsgKCcwMDAwJyArIGMuY2hhckNvZGVBdCgwKS50b1N0cmluZygxNikpLnNsaWNlKC00KSk7CiAgICAgICAgZm9yIChsZXQgayBvZiBrZXlzKSBwYXJ0cy5wdXNoKGAiJHtrfSI6ICR7dHlwZW9mIGRhdGFNYXBba10gPT09ICdzdHJpbmcnID8gYCIke2VzYyhkYXRhTWFwW2tdKX0iYCA6IFN0cmluZyhkYXRhTWFwW2tdKX1gKTsKICAgICAgICByZXR1cm4gYHske3BhcnRzLmpvaW4oJywgJyl9fWA7CiAgICB9LAoKICAgIHBvc3REYXRhOiBhc3luYyBmdW5jdGlvbihhcGlQYXRoLCBkYXRhTWFwLCBrZXlPcmRlcikgewogICAgICAgIGxldCB0ID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCkudG9TdHJpbmcoKTsKICAgICAgICBsZXQgcmVxdWVzdEpzb24gPSB0aGlzLm1ha2VQeXRob25Kc29uKGRhdGFNYXAsIGtleU9yZGVyKTsKICAgICAgICBsZXQgcmVxdWVzdF9rZXkgPSB0aGlzLkNyeXB0b1V0aWxzLmFlc0VuY3J5cHQocmVxdWVzdEpzb24sICdtdlhCU1c3ZWtyZUl0TnNUJywgJzJVM0lySkw4c3pBS3AwRmonKTsKICAgICAgICBsZXQga2V5c19yYXcgPSAiUW14aTVjaVdYYlF6a3I3bytTVU5pVXVReFFFZjgvQVZ5VVdZNFQvQkdoY1hCSVV6NG5PeUhCR2Y5QTRLYk0waUtGM3lwOU03V0FZMHJyczVQemRUQU9CNDVwbGNTMnpaMHdVaWJjWHVHSjI5VlZHUldLR3dFOXp1MnZMd2hmZ2pUYWFEcFhvNHJieSs3R3hYVGt0ekpteHZuZU9VZFllSGkrUFpzVGhsdlBJPSI7CiAgICAgICAgbGV0IHNpZ25hdHVyZSA9IG1kNShgdG9rZW5faWQ9LHRva2VuPSR7dGhpcy50b2tlbn0scGhvbmVfdHlwZT0xLHJlcXVlc3Rfa2V5PSR7cmVxdWVzdF9rZXl9LGFwcF9pZD0xLHRpbWU9JHt0fSxrZXlzPSR7a2V5c19yYXd9KiZ6dmR2ZHZkZGJmaWtra3VtdG1kd3FwcHA/fDRZIXMhMmJyYCk7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCByZXEodGhpcy5ob3N0ICsgYXBpUGF0aCwgewogICAgICAgICAgICBtZXRob2Q6ICdwb3N0JywgaGVhZGVyczogdGhpcy5oZWFkZXJzLAogICAgICAgICAgICBkYXRhOiBbYHRva2VuPSR7dGhpcy50b2tlbn1gLCBgdG9rZW5faWQ9YCwgYHBob25lX3R5cGU9MWAsIGB0aW1lPSR7dH1gLCBgcGhvbmVfbW9kZWw9eGlhb21pLTIyMDIxMjExcmNgLCBga2V5cz0ke2VuY29kZVVSSUNvbXBvbmVudChrZXlzX3Jhdyl9YCwgYHJlcXVlc3Rfa2V5PSR7cmVxdWVzdF9rZXl9YCwgYHNpZ25hdHVyZT0ke3NpZ25hdHVyZX1gLCBgYXBwX2lkPTFgLCBgYWRfdmVyc2lvbj0xYF0uam9pbignJicpCiAgICAgICAgfSk7CiAgICAgICAgaWYgKCFyZXNwIHx8ICFyZXNwLmNvbnRlbnQpIHJldHVybiBudWxsOwogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShyZXNwLmNvbnRlbnQpOwogICAgICAgICAgICBpZiAoIWpzb24uZGF0YSkgcmV0dXJuIG51bGw7CiAgICAgICAgICAgIGxldCBib2R5a2kgPSBKU09OLnBhcnNlKHRoaXMuQ3J5cHRvVXRpbHMucnNhRGVjcnlwdChqc29uLmRhdGEua2V5cywgdGhpcy5yc2FQcml2YXRlS2V5KSk7CiAgICAgICAgICAgIGxldCBkZWNyeXB0ZWQgPSB0aGlzLkNyeXB0b1V0aWxzLmFlc0RlY3J5cHQoanNvbi5kYXRhLnJlc3BvbnNlX2tleSwgYm9keWtpLmtleSwgYm9keWtpLml2KTsKICAgICAgICAgICAgcmV0dXJuIChkZWNyeXB0ZWQgJiYgZGVjcnlwdGVkICE9PSAne30nKSA/IEpTT04ucGFyc2UoZGVjcnlwdGVkKSA6IG51bGw7CiAgICAgICAgfSBjYXRjaCAoZSkgeyByZXR1cm4gbnVsbDsgfQogICAgfSwKCiAgICDpooTlpITnkIY6IGFzeW5jIGZ1bmN0aW9uKCkgewogICAgICAgIGxldCBhcmVhID0gW3sibiI6ICLlhajpg6giLCAidiI6ICIwIn0sIHsibiI6ICLlpKfpmYYiLCAidiI6ICLlpKfpmYYifSwgeyJuIjogIummmea4ryIsICJ2IjogIummmea4ryJ9LCB7Im4iOiAi5Y+w5rm+IiwgInYiOiAi5Y+w5rm+In0sIHsibiI6ICLnvo7lm70iLCAidiI6ICLnvo7lm70ifSwgeyJuIjogIumfqeWbvSIsICJ2IjogIumfqeWbvSJ9LCB7Im4iOiAi5pel5pysIiwgInYiOiAi5pel5pysIn0sIHsibiI6ICLlhbbku5YiLCAidiI6ICLlhbbku5YifV07CiAgICAgICAgbGV0IHllYXIgPSBbeyJuIjogIuWFqOmDqCIsICJ2IjogIjAifV07CiAgICAgICAgZm9yIChsZXQgaSA9IDIwMjU7IGkgPj0gMjAwNTsgaS0tKSB5ZWFyLnB1c2goeyJuIjogU3RyaW5nKGkpLCAidiI6IFN0cmluZyhpKX0pOwogICAgICAgIGxldCBzb3J0ID0gW3sibiI6ICLmnIDmlrAiLCAidiI6ICJkX2lkIn0sIHsibiI6ICLmnIDng60iLCAidiI6ICJkX2hpdHMifSwgeyJuIjogIuaOqOiNkCIsICJ2IjogImRfc2NvcmUifV07CiAgICAgICAgbGV0IGZpbHRlcnMgPSB7fTsKICAgICAgICB0aGlzLmNsYXNzX3VybC5zcGxpdCgnJicpLmZvckVhY2godGlkID0+IHsKICAgICAgICAgICAgZmlsdGVyc1t0aWRdID0gW3sia2V5IjogImFyZWEiLCAibmFtZSI6ICLlnLDljLoiLCAidmFsdWUiOiBhcmVhfSwgeyJrZXkiOiAieWVhciIsICJuYW1lIjogIuW5tOS7vSIsICJ2YWx1ZSI6IHllYXJ9LCB7ImtleSI6ICJzb3J0IiwgIm5hbWUiOiAi5o6S5bqPIiwgInZhbHVlIjogc29ydH1dOwogICAgICAgIH0pOwogICAgICAgIHJ1bGUuZmlsdGVyID0gZmlsdGVyczsKICAgIH0sCgogICAg5o6o6I2QOiBhc3luYyBmdW5jdGlvbigpIHsgcmV0dXJuIFtdOyB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgbGV0IGJvZHkgPSB7ICJhcmVhIjogU3RyaW5nKGV4dGVuZC5hcmVhIHx8ICcwJyksICJ5ZWFyIjogU3RyaW5nKGV4dGVuZC55ZWFyIHx8ICcwJyksICJwYWdlU2l6ZSI6ICIzMCIsICJzb3J0IjogU3RyaW5nKGV4dGVuZC5zb3J0IHx8ICdkX2lkJyksICJwYWdlIjogU3RyaW5nKHBnKSwgInRpZCI6IFN0cmluZyh0aWQpIH07CiAgICAgICAgbGV0IGRhdGEgPSBhd2FpdCB0aGlzLnBvc3REYXRhKCcvQXBwL0luZGV4TGlzdC9pbmRleExpc3QnLCBib2R5LCBbJ2FyZWEnLCAneWVhcicsICdwYWdlU2l6ZScsICdzb3J0JywgJ3BhZ2UnLCAndGlkJ10pOwogICAgICAgIHJldHVybiAoZGF0YSAmJiBkYXRhLmxpc3QpID8gZGF0YS5saXN0Lm1hcChpdGVtID0+ICh7CiAgICAgICAgICAgIHZvZF9pZDogaXRlbS52b2RfaWQgKyAnLycgKyAoaXRlbS52b2RfY29udGludSB8fCAwKSwKICAgICAgICAgICAgdm9kX25hbWU6IGl0ZW0udm9kX25hbWUsCiAgICAgICAgICAgIHZvZF9waWM6IGl0ZW0udm9kX3BpYywKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGl0ZW0udm9kX2NvbnRpbnUgPT09IDAgPyAn55S15b2xJyA6IGDmm7TmlrDoh7Mke2l0ZW0udm9kX2NvbnRpbnV96ZuGYAogICAgICAgIH0pKSA6IFtdOwogICAgfSwKCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uKGlkcykgewogICAgICAgIGxldCB2b2RfaWQgPSBpZHNbMF0uc3BsaXQoJy8nKVswXSwgdCA9IE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApLnRvU3RyaW5nKCk7CiAgICAgICAgbGV0IHFkYXRhID0gYXdhaXQgdGhpcy5wb3N0RGF0YSgnL0FwcC9JbmRleFBsYXkvcGxheUluZm8nLCB7ICJ0b2tlbl9pZCI6ICIxNjQ5NDEyIiwgInZvZF9pZCI6IFN0cmluZyh2b2RfaWQpLCAibW9iaWxlX3RpbWUiOiB0LCAidG9rZW4iOiB0aGlzLnRva2VuIH0sIFsndG9rZW5faWQnLCAndm9kX2lkJywgJ21vYmlsZV90aW1lJywgJ3Rva2VuJ10pOwogICAgICAgIGxldCBqZGF0YSA9IGF3YWl0IHRoaXMucG9zdERhdGEoJy9BcHAvUmVzb3VyY2UvVnVybC9zaG93JywgeyAidnVybF9jbG91ZF9pZCI6ICIyIiwgInZvZF9kX2lkIjogU3RyaW5nKHZvZF9pZCkgfSwgWyd2dXJsX2Nsb3VkX2lkJywgJ3ZvZF9kX2lkJ10pOwogICAgICAgIGlmICghcWRhdGEgfHwgIXFkYXRhLnZvZEluZm8pIHJldHVybiB7fTsKICAgICAgICBsZXQgdm9kID0gcWRhdGEudm9kSW5mbzsKICAgICAgICBsZXQgcGxheUxpc3QgPSAoamRhdGEgJiYgamRhdGEubGlzdCkgPyBqZGF0YS5saXN0LmZpbHRlcihpID0+IGkucGxheSkubWFwKChpdGVtLCBpZHgpID0+IHsKICAgICAgICAgICAgbGV0IG4gPSBbXSwgcCA9IFtdOwogICAgICAgICAgICBmb3IgKGxldCBrIGluIGl0ZW0ucGxheSkgeyBpZiAoaXRlbS5wbGF5W2tdPy5wYXJhbSkgeyBuLnB1c2goayk7IHAucHVzaChpdGVtLnBsYXlba10ucGFyYW0pOyB9IH0KICAgICAgICAgICAgcmV0dXJuIHAubGVuZ3RoID4gMCA/IGAke2pkYXRhLmxpc3QubGVuZ3RoID09PSAxID8gdm9kLnZvZF9uYW1lIDogaWR4ICsgMX0kJHtwW3AubGVuZ3RoLTFdfXx8JHtuLmpvaW4oJ0AnKX1gIDogbnVsbDsKICAgICAgICB9KS5maWx0ZXIoQm9vbGVhbikgOiBbXTsKCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgdm9kX2lkOiB2b2RfaWQsIAogICAgICAgICAgICB2b2RfbmFtZTogdm9kLnZvZF9uYW1lLCAKICAgICAgICAgICAgdm9kX3BpYzogdm9kLnZvZF9waWMsIAogICAgICAgICAgICB0eXBlX25hbWU6IHZvZC52b2RfY2xhc3MgfHwgJycsCiAgICAgICAgICAgIHZvZF95ZWFyOiB2b2Qudm9kX3llYXIsIAogICAgICAgICAgICB2b2RfYXJlYTogdm9kLnZvZF9hcmVhLCAKICAgICAgICAgICAgdm9kX2FjdG9yOiB2b2Qudm9kX2FjdG9yLCAKICAgICAgICAgICAgdm9kX2RpcmVjdG9yOiB2b2Qudm9kX2RpcmVjdG9yLAogICAgICAgICAgICB2b2RfY29udGVudDogKHZvZC52b2RfdXNlX2NvbnRlbnQgfHwgJycpLnRyaW0oKSwgCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206ICfll5Hnk5zlrZAnLCAKICAgICAgICAgICAgdm9kX3BsYXlfdXJsOiBwbGF5TGlzdC5qb2luKCcjJykKICAgICAgICB9OwogICAgfSwKCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uKGtleSwgcXVpY2ssIHBnKSB7CiAgICAgICAgbGV0IGRhdGEgPSBhd2FpdCB0aGlzLnBvc3REYXRhKCcvQXBwL0luZGV4L2ZpbmRNb3JlVm9kJywgeyAia2V5d29yZHMiOiBrZXksICJvcmRlcl92YWwiOiAiMSIsICJwYWdlIjogU3RyaW5nKHBnIHx8IDEpIH0sIFsna2V5d29yZHMnLCAnb3JkZXJfdmFsJywgJ3BhZ2UnXSk7CiAgICAgICAgcmV0dXJuIChkYXRhICYmIGRhdGEubGlzdCkgPyBkYXRhLmxpc3QubWFwKGl0ZW0gPT4gKHsKICAgICAgICAgICAgdm9kX2lkOiBpdGVtLnZvZF9pZCArICcvJyArIChpdGVtLnZvZF9jb250aW51IHx8IDApLAogICAgICAgICAgICB2b2RfbmFtZTogaXRlbS52b2RfbmFtZSwKICAgICAgICAgICAgdm9kX3BpYzogaXRlbS52b2RfcGljLAogICAgICAgICAgICB2b2RfcmVtYXJrczogaXRlbS52b2RfY29udGludSA9PT0gMCA/ICfnlLXlvbEnIDogYOabtOaWsOiHsyR7aXRlbS52b2RfY29udGludX3pm4ZgCiAgICAgICAgfSkpIDogW107CiAgICB9LAoKICAgIGxhenk6IGFzeW5jIGZ1bmN0aW9uKGZsYWcsIGlkLCBmbGFncykgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCBwYXJ0cyA9IGlkLnNwbGl0KCd8fCcpOwogICAgICAgICAgICBpZiAocGFydHMubGVuZ3RoIDwgMikgcmV0dXJuIHsgcGFyc2U6IDAsIHVybDogJycgfTsKICAgICAgICAgICAgbGV0IHBhcmFtcyA9IHt9LCBrZXlzID0gW107CiAgICAgICAgICAgIHBhcnRzWzBdLnNwbGl0KCcmJykuZm9yRWFjaChwYWlyID0+IHsKICAgICAgICAgICAgICAgIGxldCBbaywgdl0gPSBwYWlyLnNwbGl0KCc9Jyk7CiAgICAgICAgICAgICAgICBpZiAoaykgeyBwYXJhbXNba10gPSB2OyBpZiAoIWtleXMuaW5jbHVkZXMoaykpIGtleXMucHVzaChrKTsgfQogICAgICAgICAgICB9KTsKICAgICAgICAgICAgbGV0IHJlcyA9IChwYXJ0c1sxXSB8fCAnJykuc3BsaXQoJ0AnKS5zb3J0KChhLCBiKSA9PiAocGFyc2VJbnQoYi5yZXBsYWNlKC9cRC9nLCAnJykpIHx8IDApIC0gKHBhcnNlSW50KGEucmVwbGFjZSgvXEQvZywgJycpKSB8fCAwKSk7CiAgICAgICAgICAgIGlmIChyZXMubGVuZ3RoID4gMCkgewogICAgICAgICAgICAgICAgcGFyYW1zWydyZXNvbHV0aW9uJ10gPSByZXNbMF07CiAgICAgICAgICAgICAgICBpZiAoIWtleXMuaW5jbHVkZXMoJ3Jlc29sdXRpb24nKSkga2V5cy5wdXNoKCdyZXNvbHV0aW9uJyk7CiAgICAgICAgICAgICAgICBsZXQgZGF0YSA9IGF3YWl0IHRoaXMucG9zdERhdGEoJy9BcHAvUmVzb3VyY2UvVnVybERldGFpbC9zaG93T25lJywgcGFyYW1zLCBrZXlzKTsKICAgICAgICAgICAgICAgIGlmIChkYXRhICYmIChkYXRhLnVybCB8fCBkYXRhLm0zdTggfHwgZGF0YS52dXJsKSkgcmV0dXJuIHsgcGFyc2U6IDAsIHVybDogZGF0YS51cmwgfHwgZGF0YS5tM3U4IHx8IGRhdGEudnVybCwgaGVhZGVyOiB0aGlzLmhlYWRlcnMgfTsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGUpIHt9CiAgICAgICAgcmV0dXJuIHsgcGFyc2U6IDAsIHVybDogJycgfTsKICAgIH0KfTs= diff --git "a/spider/js/\347\224\265\345\275\261\346\270\257[\347\243\201].js" "b/spider/js/\347\224\265\345\275\261\346\270\257[\347\243\201].js" new file mode 100644 index 00000000..59af8043 --- /dev/null +++ "b/spider/js/\347\224\265\345\275\261\346\270\257[\347\243\201].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 0, + title: '电影港', + author: 'EylinSir', + '类型': '影视', + logo: 'https://www.dyg123.net/favicon.ico', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfnlLXlvbHmuK8nLAogICAgaG9zdDogJ2h0dHBzOi8vd3d3LmR5ZzEyMy5uZXQnLAogICAgaG9tZVVybDogJy8nLAogICAgdXJsOiAnL2UvYWN0aW9uL0xpc3RJbmZvLnBocD9meWZpbHRlcicsCiAgICBsb2dvOiAnaHR0cHM6Ly93d3cuZHlnMTIzLm5ldC9mYXZpY29uLmljbycsCiAgICBmaWx0ZXJfdXJsOiAnY2xhc3NpZD17e2ZsLmNhdGVJZCBvciAiZnljbGFzcyJ9fSZwYWdlPShmeXBhZ2UtMSkmbGluZT0zMCZ0ZW1waWQ9MSZvcmRlcmJ5PXt7ZmwuYnkgb3IgIm5ld3N0aW1lIn19JywKICAgIHNlYXJjaFVybDogJy9lL3NlYXJjaC9pbmRleC5waHAnLAogICAgZGV0YWlsVXJsOiAnJywKICAgIHNlYXJjaGFibGU6IDEsIAogICAgcXVpY2tTZWFyY2g6IDAsIAogICAgZmlsdGVyYWJsZTogMCwgCiAgICB0aW1lb3V0OiA1MDAwLAogICAgbGltaXQ6IDIwLAogICAgaGVhZGVyczogeydVc2VyLUFnZW50JzogTU9CSUxFX1VBfSwKICAgIGNsYXNzX25hbWU6ICfnlLXlvbEm5Ymn6ZuGJue7vOiJuibliqjnlLsm55+t5YmnJywKICAgIGNsYXNzX3VybDogJzEmMjAmMzEmMzAmMzInLAogICAgZmlsdGVyOiAnSDRzSUFBQUFBQUFBQTZ2bVVnQUNKVU1scTJnd0N3U3FsYkpUSzVXc2xKSVRTMUk5VTVSMGxQSVNjMU9CL09jYmR6K2QxdzNrbHlYbWxBSUZvcXVWOG9EQ1QxdFh2R3hlQVJJR2NneVZhbldnd2wwcm51eWQ4N3l6SFNwamhKQ1pOdWRwNTNLRWpERmM1bm5IeG1mTnJRZ1pFNFRNOG9sUGQrNUd5SmdpVE90Y2pxTEhEQzd6ckhIQ3M0WnBDQmx6aEV6SGpDZTdPaEV5aGdpcDU3dFdQZDA3RlVuS1FxazJ0bFlISTNDU0toRUI4Nnh2MHROZC9SZ0I4MnhPdzdOcEc2RG01S1dXRjVka0FwWERMSHF5YTllekRWT2dzdmw1eVRtWnlka2dxOEEyeFVJc1ZESXlvRmJFQUUyQ2g5anN2Y0JBZzRrall1elo5S1V2NTY5RWtrSkV6TE0xeTUvdjYwT1NNaG5JVURIR21senBaRGZXR0tHVDNVWURZRGRYTFFBTWhxdkhKQVFBQUE9PScsCiAgICDmjqjojZA6ICcqJywKICAgIAogICAg5LiA57qnOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgcmV0dXJuIHJ1bGUuZ2V0Vm9kTGlzdChhd2FpdCBmZXRjaCh0aGlzLmlucHV0KSk7CiAgICB9LAoKICAgIOaQnOe0ojogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB7aW5wdXQsIEtFWSwgSE9TVH0gPSB0aGlzOwogICAgICAgIGxldCBWT0RTID0gW107CiAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCBmZXRjaChpbnB1dCwgewogICAgICAgICAgICBoZWFkZXJzOiB7Li4ucnVsZS5oZWFkZXJzLCAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD11dGYtOCJ9LAogICAgICAgICAgICBtZXRob2Q6ICdQT1NUJywKICAgICAgICAgICAgYm9keTogYGtleWJvYXJkPSR7S0VZfSZzdWJtaXQ95pCc57SiJnNob3c9dGl0bGUmdGVtcGlkPTFgCiAgICAgICAgfSk7CiAgICAgICAgVk9EUy5wdXNoKC4uLnJ1bGUuZ2V0Vm9kTGlzdChodG1sKSk7CiAgICAgICAgbGV0IHNlYXJjaElkID0gcnVsZS5jdXRTdHIoaHRtbCwgJ3NlYXJjaGlkPScsICciJywgJycpOwogICAgICAgIGlmIChzZWFyY2hJZCkgewogICAgICAgICAgICBsZXQgbmV4dEh0bWwgPSBhd2FpdCBmZXRjaChgJHtIT1NUIHx8IHJ1bGUuaG9zdH0vZS9zZWFyY2gvcmVzdWx0L2luZGV4LnBocD9wYWdlPTEmc2VhcmNoaWQ9JHtzZWFyY2hJZH1gKTsKICAgICAgICAgICAgVk9EUy5wdXNoKC4uLnJ1bGUuZ2V0Vm9kTGlzdChuZXh0SHRtbCkpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gVk9EUzsKICAgIH0sCgogICAg5LqM57qnOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IHtpbnB1dCwgSE9TVH0gPSB0aGlzOwogICAgICAgIGxldCBbaWQsIGtuYW1lLCBrcGljLCBrcmVtYXJrc10gPSBpbnB1dC5zcGxpdCgnQCcpOwogICAgICAgIGxldCBodG1sID0gYXdhaXQgZmV0Y2goaWQpOwogICAgICAgIGNvbnN0IGNsZWFuID0gKHMpID0+IHsKICAgICAgICAgICAgaWYgKCFzKSByZXR1cm4gJyc7CiAgICAgICAgICAgIHRyeSB7IHMgPSBkZWNvZGVVUklDb21wb25lbnQocyk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgICAgICAgIHJldHVybiBzLnJlcGxhY2UoLzxbXj5dKz58Jm5ic3A7fFxzK3zmiYvmnLrniYh8LeWcqOe6v+WFjei0ueingueciy1855S15b2x5rivKD86XChEU1wpKT8vZywgJycpLnRyaW0oKTsKICAgICAgICB9OwogICAgICAgIGxldCBrZGV0YWlsID0gcGRmaChodG1sLCAnLmN0LWwnKS5zcGxpdCgnPHN0cm9uZz4nKVswXTsKICAgICAgICBsZXQgZmluYWxOYW1lID0gY2xlYW4ocGRmaChodG1sLCAndGl0bGUnKSkgfHwgY2xlYW4oa25hbWUpOwogICAgICAgIGxldCB0YWJzID0gWwogICAgICAgICAgICAuLi4ocGRmYShodG1sLCAnc3Ryb25nOmhhcyhzcGFuKScpLm1hcCgoaXQsIGkpID0+IHJ1bGUuY3V0U3RyKGl0LCAn44CQJywgJ+OAkScsIGDno4Hlipvnur8ke2kgKyAxfWApKSksCiAgICAgICAgICAgIC4uLihwZGZhKGh0bWwsICcjdGFiODEnKS5tYXAoaXQgPT4gcGRmaChpdCwgJ2JvZHkmJlRleHQnKSkpCiAgICAgICAgXTsKICAgICAgICBsZXQgdXJscyA9IFsKICAgICAgICAgICAgLi4uKHBkZmEoaHRtbCwgJ3Rib2R5JykubWFwKGl0ZW0gPT4gCiAgICAgICAgICAgICAgICBwZGZhKGl0ZW0sICdhJykubWFwKGl0ID0+IHBkZmgoaXQsICdib2R5JiZUZXh0JykgKyAnJCcgKyBwZGZoKGl0LCAnYSYmaHJlZicpKS5qb2luKCcjJykKICAgICAgICAgICAgKSksCiAgICAgICAgICAgIC4uLihwZGZhKGh0bWwsICcudmlkZW91cmwnKS5tYXAoaXRlbSA9PiAKICAgICAgICAgICAgICAgIHBkZmEoaXRlbSwgJ2EnKS5tYXAoaXQgPT4gcGRmaChpdCwgJ2JvZHkmJlRleHQnKSArICckJyArIHBkKGl0LCAnYSYmaHJlZicsIEhPU1QgfHwgcnVsZS5ob3N0KSkuam9pbignIycpCiAgICAgICAgICAgICkpCiAgICAgICAgXTsKCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgdm9kX2lkOiBpZCwKICAgICAgICAgICAgdm9kX25hbWU6IGZpbmFsTmFtZSwKICAgICAgICAgICAgdm9kX3BpYzoga3BpYywKICAgICAgICAgICAgdHlwZV9uYW1lOiBydWxlLmN1dFN0cihrZGV0YWlsLCAn4peO57G75YirJywgJ+KXjicsICfnsbvlnosnKSwKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGNsZWFuKGtyZW1hcmtzKSwKICAgICAgICAgICAgdm9kX3llYXI6IHJ1bGUuY3V0U3RyKGtkZXRhaWwsICfil47lubTku6MnLCAn4peOJywgJzEwMDAnKSwKICAgICAgICAgICAgdm9kX2FyZWE6IHJ1bGUuY3V0U3RyKGtkZXRhaWwsICfil47kuqflnLAnLCAn4peOJywgJ+WcsOWMuicpLAogICAgICAgICAgICB2b2RfbGFuZzogcnVsZS5jdXRTdHIoa2RldGFpbCwgJ+KXjuivreiogCcsICfil44nLCAn6K+t6KiAJyksCiAgICAgICAgICAgIHZvZF9kaXJlY3RvcjogcnVsZS5jdXRTdHIoa2RldGFpbCwgJ+KXjuWvvOa8lCcsICfil44nLCAn5a+85ryUJyksCiAgICAgICAgICAgIHZvZF9hY3RvcjogcnVsZS5jdXRTdHIoa2RldGFpbCwgJ+KXjua8lOWRmCcsICc8L3A+JywgJycpIHx8IHJ1bGUuY3V0U3RyKGtkZXRhaWwsICfil47kuLvmvJQnLCAnPC9wPicsICfkuLvmvJQnKSwKICAgICAgICAgICAgdm9kX2NvbnRlbnQ6IGNsZWFuKHJ1bGUuY3V0U3RyKGtkZXRhaWwsICfil47nroDku4vCoz4nLCAnPC9wPicsICcnKSkgfHwgZmluYWxOYW1lLAogICAgICAgICAgICB2b2RfcGxheV9mcm9tOiB0YWJzLmpvaW4oJyQkJCcpLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHVybHMuam9pbignJCQkJykKICAgICAgICB9OwogICAgfSwKCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB7aW5wdXR9ID0gdGhpczsKICAgICAgICBpZiAoL15tYWduZXQvLnRlc3QoaW5wdXQpKSByZXR1cm4geyBqeDogMCwgcGFyc2U6IDAsIHVybDogaW5wdXQgfTsKICAgICAgICBsZXQgdXJsID0gaW5wdXQ7CiAgICAgICAgdHJ5IHsKICAgICAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCBmZXRjaChpbnB1dCk7CiAgICAgICAgICAgIGxldCByZWFsVXJsID0gcnVsZS5jdXRTdHIoaHRtbCwgImE6JyIsICInIiwgJycpOwogICAgICAgICAgICBpZiAoIS9tM3U4fG1wNHxta3YvLnRlc3QocmVhbFVybCkpIHsKICAgICAgICAgICAgICAgIGxldCBpZnJhbWVTcmMgPSBydWxlLmN1dFN0cihodG1sLCAnPGlmcmFtZcKjc3JjPSInLCAnIicsICcnKTsKICAgICAgICAgICAgICAgIGlmIChpZnJhbWVTcmMpIHsKICAgICAgICAgICAgICAgICAgICBsZXQgaWZyYW1lSHRtbCA9IGF3YWl0IGZldGNoKGlmcmFtZVNyYyk7CiAgICAgICAgICAgICAgICAgICAgcmVhbFVybCA9IGdldEhvbWUoaWZyYW1lU3JjKSArIHJ1bGUuY3V0U3RyKGlmcmFtZUh0bWwsICd1cmwgPSAiJywgJyInLCAnJyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKC9tM3U4fG1wNHxta3YvLnRlc3QocmVhbFVybCkpIHVybCA9IHJlYWxVcmw7CiAgICAgICAgfSBjYXRjaCAoZSkge30KICAgICAgICAKICAgICAgICByZXR1cm4geyBqeDogMCwgcGFyc2U6IDEsIHVybDogdXJsLCBoZWFkZXI6IHJ1bGUuaGVhZGVycyB9OwogICAgfSwKCiAgICBnZXRWb2RMaXN0OiBmdW5jdGlvbihodG1sKSB7CiAgICAgICAgbGV0IGxpc3QgPSBwZGZhKGh0bWwsICcubTEnKSB8fCBbXTsKICAgICAgICByZXR1cm4gbGlzdC5tYXAoaXQgPT4gewogICAgICAgICAgICBsZXQgbmFtZSA9IHJ1bGUuY3V0U3RyKGl0LCAnYWx0PSInLCAnIicsICflkI3np7AnKTsKICAgICAgICAgICAgbGV0IHBpYyA9IHJ1bGUuY3V0U3RyKGl0LCAnZGF0YS1vcmlnaW5hbD0iJywgJyInLCAn5Zu+54mHJyk7CiAgICAgICAgICAgIGxldCByZW1hcmsgPSBydWxlLmN1dFN0cihpdCwgJ290aGVyIj4nLCAnPC9wPicsICfnirbmgIEnKTsKICAgICAgICAgICAgdHJ5IHsgcmVtYXJrID0gZGVjb2RlVVJJQ29tcG9uZW50KHJlbWFyaykucmVwbGFjZSgvPFtePl0rPnwmbmJzcDsvZywgJycpLnRyaW0oKTsgfSBjYXRjaChlKSB7fQogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgdm9kX25hbWU6IG5hbWUsCiAgICAgICAgICAgICAgICB2b2RfcGljOiBwaWMsCiAgICAgICAgICAgICAgICB2b2RfcmVtYXJrczogcmVtYXJrLAogICAgICAgICAgICAgICAgdm9kX2lkOiBgJHtydWxlLmN1dFN0cihpdCwgJ2hyZWY9IicsICciJywgJ0lkJyl9QCR7bmFtZX1AJHtwaWN9QCR7cmVtYXJrfWAKICAgICAgICAgICAgfTsKICAgICAgICB9KTsKICAgIH0sCgogICAgY3V0U3RyOiBmdW5jdGlvbihzdHIsIHByZSwgc3VmLCBkZWYgPSAnJykgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGlmICghc3RyKSByZXR1cm4gZGVmOwogICAgICAgICAgICBsZXQgZXNjID0gcyA9PiBzLnJlcGxhY2UoL1suKis/JHt9KCl8W1xdXFwvXl0vZywgJ1xcJCYnKS5yZXBsYWNlKC/Coy9nLCAnW15dKj8nKTsKICAgICAgICAgICAgbGV0IHJlZyA9IG5ldyBSZWdFeHAoYCR7ZXNjKHByZSl9KFteXSo/KSR7ZXNjKHN1Zil9YCk7CiAgICAgICAgICAgIGxldCByZXMgPSBzdHIubWF0Y2gocmVnKT8uWzFdID8/IGRlZjsKICAgICAgICAgICAgcmV0dXJuIHJlcy5yZXBsYWNlKC88W14+XSs+fCZuYnNwO3xccysvZywgJyAnKS50cmltKCk7CiAgICAgICAgfSBjYXRjaCB7IHJldHVybiBkZWY7IH0KICAgIH0KfTs= \ No newline at end of file diff --git "a/spider/js/\347\225\252\350\214\204\345\260\217\350\257\264[\344\271\246].js" "b/spider/js/\347\225\252\350\214\204\345\260\217\350\257\264[\344\271\246].js" index 7a5a2929..2450618a 100644 --- "a/spider/js/\347\225\252\350\214\204\345\260\217\350\257\264[\344\271\246].js" +++ "b/spider/js/\347\225\252\350\214\204\345\260\217\350\257\264[\344\271\246].js" @@ -6,6 +6,7 @@ title: '番茄小说[书]', author: '道长', '类型': '小说', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', lang: 'ds' }) */ @@ -23,7 +24,8 @@ const {getRandomFromList} = $.require('./_lib.random.js'); const {requestHtml} = $.require('./_lib.request.js'); // const fqweb_host = 'http://fqweb.jsj66.com'; // const fqweb_host = 'http://fanqie.mduge.com'; -const fqweb_host = 'https://qkfqapi.vv9v.cn'; +// const fqweb_host = 'https://qkfqapi.vv9v.cn'; +const fqweb_host = 'http://101.35.133.34:5000'; // const fqweb_host = 'http://101.35.133.34:5000/docs'; //备选 // const fqweb_host = 'http://103.236.91.147:9999/docs'; //备选 // const fqweb_host = 'http://47.108.80.161:5005/docs'; //备选 @@ -36,6 +38,7 @@ var rule = { title: '番茄小说[书]', desc: '番茄小说纯js版本', host: 'https://fanqienovel.com/', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', homeUrl: 'https://fanqienovel.com/api/author/book/category_list/v0/', url: '/api/author/library/book_list/v0/?page_count=18&page_index=(fypage-1)&gender=1&category_id=fyclass&creation_status=-1&word_count=-1&book_type=-1&sort=0#fyfilter', // searchUrl: fqweb_host + '/search?query=**&page=fypage', @@ -60,7 +63,7 @@ var rule = { api: 'https://novel.snssdk.com/api', 封面域名: 'http://p6-novel.byteimg.com/large/', }, - timeout: 5000, + timeout: 20000, play_parse: true, class_parse: async () => { // let html = (await req(rule.homeUrl)).content; @@ -206,7 +209,8 @@ var rule = { content = content.replace(/<\/p>/g, '\n').replace(/<\w+>/g, '').replace(/<[^>]*>/g, ''); */ - let html = (await req(content_url, {headers: {Cookie: getFqCookie()}})).content; + // let html = (await req(content_url, {headers: {Cookie: getFqCookie()},timeout:this.timeout})).content; + let html = await request(content_url); /* let json = JSON.parse(html).data.data; title = json.novel_data.title; diff --git "a/spider/js/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" "b/spider/js/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" index 04519808..98e71dda 100644 --- "a/spider/js/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" +++ "b/spider/js/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" @@ -5,18 +5,22 @@ quickSearch: 0, title: '番茄漫画', '类型': '漫画', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', lang: 'ds' }) */ + var rule = { 类型: '漫画', title: '番茄漫画', - host: 'https://qkfqapi.vv9v.cn', + // host: 'https://qkfqapi.vv9v.cn', + host: 'http://47.108.80.161:5005', homeUrl: '/api/discover/style?tab=漫画', url: 'fyclass', searchUrl: '/api/search?key=**&tab_type=8&offset=((fypage-1)*10)', detailUrl: '/api/detail?book_id=fyid', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', headers: {'User-Agent': 'UC_UA'}, searchable: 1, quickSearch: 0, @@ -26,21 +30,22 @@ var rule = { limit: 10, class_parse: async function () { let {input} = this; + // log('[class_parse] input:', input); let html = await request(input); let data = html.parseX.data; let d = data.filter(item => item.url.trim()).map((it) => { return { type_name: it.title, - type_id: it.url, + type_id: gzip(it.url), } }); return {class: d} }, lazy: async function () { - let {input, pdfa, pdfh} = this; + let {input, pdfa, pdfh, HOST} = this; let title = input.split('@')[1]; input = input.split('@')[0]; - let content_url = `https://qkfqapi.vv9v.cn/api/content?tab=漫画&item_id=${input}&show_html=0`; // 正文获取接口 + let content_url = `${HOST}/api/content?tab=漫画&item_id=${input}&show_html=0`; // 正文获取接口 let jsonStr = await request(content_url); let images = jsonStr.parseX.data.images; images = pdfa(images, 'img'); @@ -71,16 +76,20 @@ var rule = { 推荐: async function () { let {HOST} = this; let url = HOST + '/api/discover?tab=漫画&type=7&gender=2&genre_type=110&page=1'; + // log('[推荐]: url: ' + url); let html = await request(url); return this.parseList(html); }, 一级: async function (tid, pg, filter, extend) { + // log('[一级]: tid:', tid); + tid = ungzip(tid); input = jinja.render(tid, {page: pg}); + // log('[一级]: input: ' + input); let html = await request(input); return this.parseList(html); }, 二级: async function () { - let {input, orId} = this; + let {input, orId, HOST} = this; let html = await request(input); let data = html.parseX.data.data; let VOD = {}; @@ -94,7 +103,7 @@ var rule = { VOD.vod_actor = ''; VOD.vod_director = data.author; VOD.vod_play_from = '番茄漫画'; - let jsonStr = await request(`https://qkfqapi.vv9v.cn/api/book?book_id=${orId}`); + let jsonStr = await request(`${HOST}/api/book?book_id=${orId}`); let book_info = jsonStr.parseX.data.data; let list = book_info.chapterListWithVolume.flat(); let urls = []; diff --git "a/spider/js/\347\225\252\350\214\204\347\225\205\345\220\254[\345\220\254].js" "b/spider/js/\347\225\252\350\214\204\347\225\205\345\220\254[\345\220\254].js" index d55334d0..9411cfbc 100644 --- "a/spider/js/\347\225\252\350\214\204\347\225\205\345\220\254[\345\220\254].js" +++ "b/spider/js/\347\225\252\350\214\204\347\225\205\345\220\254[\345\220\254].js" @@ -5,9 +5,10 @@ quickSearch: 0, title: '番茄畅听', author: 'EylinSir', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', '类型': '听书', lang: 'ds' }) */ -dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflkKzkuaYnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfnlarojITnlYXlkKwnLAogICAgaG9zdDogJ2h0dHBzOi8vcWtmcWFwaS52djl2LmNuJywKICAgIHVybDogJy9hcGkvZGlzY292ZXI/dGFiPeWQrOS5piZ0eXBlPWZ5Y2xhc3MmZ2VuZGVyPTImZ2VucmVfdHlwZT0xJnBhZ2U9e3twYWdlfX0nLAogICAgc2VhcmNoVXJsOiAnL2FwaS9zZWFyY2g/a2V5PSoqJnRhYl90eXBlPTImb2Zmc2V0PSgoZnlwYWdlLTEpKjEwKScsCiAgICBkZXRhaWxVcmw6ICcvYXBpL2RldGFpbD9ib29rX2lkPWZ5aWQnLAogICAgaGVhZGVyczogeydVc2VyLUFnZW50JzogJ1VDX1VBJ30sCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDAsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgZG91YmxlOiB0cnVlLAogICAgcGxheV9wYXJzZTogdHJ1ZSwKICAgIGxpbWl0OiAxMiwKICAgIGNsYXNzX25hbWU6ICfnsr7lk4HlsI/or7Qm55u45aOw6K+E5LmmJuS4lueVjOWOhuWPsiblkI3okZfop6Por7sm5a2m5Lmg5oiQ6ZW/JuaIj+absuiJuuacrybnlJ/mtLvnmb7np5Em5a625bqt5pWZ6IKyJuS6uuaWh+enkeWtpiblhbbku5YnLAogICAgY2xhc3NfdXJsOiAnODk5JjQ0NSYxMiYxMzImNDQ5JjExMyY5NjAmNDUwJjQ0NyYzOScsCiAgICBmaWx0ZXI6IHsKICAgICAgICAiODk5IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6Ijg5OSJ9LHsibiI6IumDveW4giIsInYiOiIxIn0seyJuIjoi56m/6LaKIiwidiI6IjM3In0seyJuIjoi546w5Luj6KiA5oOFIiwidiI6IjMifSx7Im4iOiLlj6Tku6PoqIDmg4UiLCJ2IjoiNSJ9LHsibiI6IuaAu+ijgSIsInYiOiIyOSJ9LHsibiI6IueOhOW5uyIsInYiOiI3In0seyJuIjoi6YeN55SfIiwidiI6IjM2In0seyJuIjoi5oKs55aRIiwidiI6IjEwIn0seyJuIjoi54G15byCIiwidiI6IjEwMCJ9LHsibiI6Iuezu+e7nyIsInYiOiIxOSJ9LHsibiI6IuenjeeUsCIsInYiOiIyMyJ9LHsibiI6IueUnOWuoCIsInYiOiI5NiJ9LHsibiI6IuWuoOWmuyIsInYiOiIzMCJ9LHsibiI6IumDveW4gueUn+a0uyIsInYiOiIyIn0seyJuIjoi6LWY5am/IiwidiI6IjI1In0seyJuIjoi5YWI5ama5ZCO54ixIiwidiI6IjI2NSJ9LHsibiI6IuWuq+aWl+WuheaWlyIsInYiOiIyNDYifSx7Im4iOiLpg73luILml6XluLgiLCJ2IjoiMjYxIn0seyJuIjoi5oiY56We6LWY5am/IiwidiI6IjI3In0seyJuIjoi56We5Yy7IiwidiI6IjI2In0seyJuIjoi5b2x6KeG5bCP6K+0IiwidiI6IjQ1In0seyJuIjoi5Zu95YaF5b2x6KeGIiwidiI6Ijk5MSJ9LHsibiI6IuWbveWkluW9seinhiIsInYiOiI5OTIifSx7Im4iOiLnsr7lk4HlsI/or7QiLCJ2IjoiOTcwIn0seyJuIjoi5oKs55aR5o6o55CGIiwidiI6IjE2NSJ9LHsibiI6IuenkeW5u+Wwj+ivtCIsInYiOiIxNjYifSx7Im4iOiLmrabkvqDlsI/or7QiLCJ2IjoiOTkzIn0seyJuIjoi546E5bm75bCP6K+0IiwidiI6Ijk3MSJ9LHsibiI6IueOsOWunuWwj+ivtCIsInYiOiI0MDAifSx7Im4iOiLmg4XmhJ/lsI/or7QiLCJ2IjoiOTcyIn0seyJuIjoi5Lyg57uf546E5bm7IiwidiI6IjI1OCJ9LHsibiI6IueOi+WmgyIsInYiOiI4NSJ9LHsibiI6IuWlh+W5u+S7meS+oCIsInYiOiIyNTkifSx7Im4iOiLokIzlrp0iLCJ2IjoiMjgifSx7Im4iOiLpg73luILohJHmtJ4iLCJ2IjoiMjYyIn0seyJuIjoi6IGM5Zy6IiwidiI6IjEyNyJ9LHsibiI6IuWroeWlsyIsInYiOiI4OCJ9LHsibiI6IumDveW4guS/ruecnyIsInYiOiIxMjQifSx7Im4iOiLlubvmg7PoqIDmg4UiLCJ2IjoiMzIifSx7Im4iOiLnpZ7osaoiLCJ2IjoiMjAifSx7Im4iOiLnqbrpl7QiLCJ2IjoiNDQifSx7Im4iOiLlhbbku5YiLCJ2IjoiMzEifSx7Im4iOiLnjoTlubvoqIDmg4UiLCJ2IjoiMjQ4In0seyJuIjoi546E5bm76ISR5rSeIiwidiI6IjI1NyJ9LHsibiI6IuWOhuWPsuWPpOS7oyIsInYiOiIyNzMifSx7Im4iOiLnp5HlubvmnKvkuJYiLCJ2IjoiOCJ9LHsibiI6IuW5tOS7oyIsInYiOiI3OSJ9LHsibiI6IuWkqeaJjSIsInYiOiI5MCJ9LHsibiI6IuWls+W8uiIsInYiOiI4NiJ9LHsibiI6IuaOqOeQhiIsInYiOiI2MSJ9LHsibiI6IuiFuem7kSIsInYiOiI5MiJ9LHsibiI6IuivuOWkqeS4h+eVjCIsInYiOiI3MSJ9LHsibiI6IuWMu+acryIsInYiOiIyNDcifSx7Im4iOiLmmJ/pmYUiLCJ2IjoiNzcifSx7Im4iOiLpibTlrp0iLCJ2IjoiMTcifSx7Im4iOiLlm6LlrqAiLCJ2IjoiOTQifSx7Im4iOiLmia7njKrlkIPomY4iLCJ2IjoiOTMifSx7Im4iOiLmrabkvqAiLCJ2IjoiMTYifSx7Im4iOiLnjrDoqIDohJHmtJ4iLCJ2IjoiMjY3In0seyJuIjoi6YO95biC56eN55SwIiwidiI6IjI2MyJ9LHsibiI6IuaXoOaVjCIsInYiOiIzODQifSx7Im4iOiLnm5flopMiLCJ2IjoiODEifSx7Im4iOiLpqaznlLIiLCJ2IjoiMjY2In0seyJuIjoi55qH5ZCOIiwidiI6Ijg0In0seyJuIjoi54m556eN5YW1IiwidiI6IjM3NSJ9LHsibiI6IuWkp+WUkCIsInYiOiI3MyJ9LHsibiI6IuWFrOS4uyIsInYiOiI4MyJ9LHsibiI6IuWoseS5kOWciCIsInYiOiI0MyJ9LHsibiI6IumdkuaiheeruemprCIsInYiOiIzODcifSx7Im4iOiLlj6ToqIDohJHmtJ4iLCJ2IjoiMjUzIn0seyJuIjoi5Y6G5Y+y6ISR5rSeIiwidiI6IjI3MiJ9LHsibiI6Iuacq+S4liIsInYiOiI2OCJ9LHsibiI6IuWJkemBkyIsInYiOiI4MCJ9LHsibiI6IueOsOiogOeUnOWuoCIsInYiOiIzOTUifSx7Im4iOiLmuLjmiI/liqjmvKsiLCJ2IjoiNTcifSx7Im4iOiLmtKrojZIiLCJ2IjoiNjYifSx7Im4iOiLlv6vnqb8iLCJ2IjoiMjQifSx7Im4iOiLmmI7mnJ0iLCJ2IjoiMTI2In0seyJuIjoi5aSW5Y2WIiwidiI6Ijc1In0seyJuIjoi5qCh6IqxIiwidiI6IjM4NSJ9LHsibiI6IuWltueIuCIsInYiOiI0MiJ9LHsibiI6IuagoeWbrSIsInYiOiI0In0seyJuIjoi5LiJ5Zu9IiwidiI6IjY3In0seyJuIjoi55u05pKtIiwidiI6IjY5In0seyJuIjoi56m/5LmmIiwidiI6IjM4MiJ9LHsibiI6Iua1t+WymyIsInYiOiI0MCJ9LHsibiI6Iue+jumjnyIsInYiOiI3OCJ9LHsibiI6IuWPjea0viIsInYiOiIzNjkifSx7Im4iOiLnjrDoqIDlpI3ku4ciLCJ2IjoiMjY4In0seyJuIjoi6KW/5ri46KGN55SfIiwidiI6IjM3MyJ9LHsibiI6IuaxgueUnyIsInYiOiIzNzkifSx7Im4iOiLmsJHlm70iLCJ2IjoiMzkwIn0seyJuIjoi5a625bqtIiwidiI6IjEyNSJ9LHsibiI6IuWtpumcuCIsInYiOiI4MiJ9LHsibiI6Iueah+WPlCIsInYiOiI4NyJ9LHsibiI6IuWuoOeJqSIsInYiOiI3NCJ9LHsibiI6IuaXoENQIiwidiI6IjM5MiJ9LHsibiI6IuWls+aJrueUt+ijhSIsInYiOiIzODgifSx7Im4iOiLnvZHmuLgiLCJ2IjoiMzcyIn0seyJuIjoi55eF5aiHIiwidiI6IjM4MCJ9LHsibiI6IueyvueBtSIsInYiOiI4OSJ9LHsibiI6IuiZkOaWhyIsInYiOiI5NSJ9LHsibiI6IumDveW4gumdkuaYpSIsInYiOiIzOTYifSx7Im4iOiLmuIXnqb8iLCJ2IjoiNzYifV19XSwKICAgICAgICAiNDQ1IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjQ0NSJ9LHsibiI6IumDreW+t+e6siIsInYiOiIxMjgifSx7Im4iOiLnlLDov57lhYMiLCJ2IjoiMjAzIn0seyJuIjoi5YiY5YWw6IqzIiwidiI6IjIwMiJ9LHsibiI6IuWwj+WTgSIsInYiOiIxMTQifSx7Im4iOiLoooHpmJTmiJAiLCJ2IjoiMjA0In0seyJuIjoi5Y2V55Sw6IqzIiwidiI6IjIwMSJ9LHsibiI6IuivhOS5piIsInYiOiIxMTAifSx7Im4iOiLnm7jlo7AiLCJ2IjoiMTExIn1dfV0sCiAgICAgICAgIjEyIjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjEyIn0seyJuIjoi6a2P5pmL5Y2X5YyX5pydIiwidiI6IjIwOSJ9LHsibiI6IuenpuaxiSIsInYiOiIyMDgifSx7Im4iOiLkuJbnlYzlj7IiLCJ2IjoiMjE0In0seyJuIjoi5ZSQ5a6LIiwidiI6IjIxMCJ9LHsibiI6IuaYjua4hSIsInYiOiIyMTEifSx7Im4iOiLov5HnjrDku6MiLCJ2IjoiMjEzIn0seyJuIjoi5Lit5Zu95Y+yIiwidiI6IjIxMiJ9LHsibiI6IuaImOS6ieWPsiIsInYiOiIyMDYifSx7Im4iOiLlkI3kurrkvKAiLCJ2IjoiMjA3In0seyJuIjoi5Lit5Zu95Y6G5Y+yIiwidiI6IjQwMiJ9LHsibiI6IuS4lueVjOWOhuWPsiIsInYiOiI0MDMifSx7Im4iOiLljoblj7LlsI/or7QiLCJ2IjoiOTg4In0seyJuIjoi5Y6G5Y+y5paH5YyWIiwidiI6IjI0MSJ9XX1dLAogICAgICAgICIxMzIiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiMTMyIn0seyJuIjoi5oiY5LqJIiwidiI6Ijk3In0seyJuIjoi5Lit5Zu95ZCN6JGXIiwidiI6Ijk4In0seyJuIjoi5aSW5Zu95ZCN6JGXIiwidiI6Ijk5In0seyJuIjoi57uP566h5Yqx5b+XIiwidiI6IjI0MiJ9LHsibiI6IuS6uueJqeS8oOiusCIsInYiOiI0MDkifSx7Im4iOiLnu4/lhbjmloflraYiLCJ2IjoiMjQzIn0seyJuIjoi6Z2S5pil5paH5a2mIiwidiI6IjE2OCJ9LHsibiI6IuaImOS6ieWGm+aXhSIsInYiOiI5NzMifV19XSwKICAgICAgICAiNDQ5IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjQ0OSJ9LHsibiI6IuazleW+iyIsInYiOiIxNDIifSx7Im4iOiLlv4PnkIYiLCJ2IjoiMTQ2In0seyJuIjoi5b+D55CG5a2mIiwidiI6IjQwNyJ9LHsibiI6IuWwkeWEv+W/g+eQhiIsInYiOiI5ODYifSx7Im4iOiLlv4PnkIbnlpfmhIgiLCJ2IjoiOTk0In0seyJuIjoi5oqV6LWE55CG6LSiIiwidiI6IjE0MyJ9LHsibiI6IuiBjOS4muiBjOWcuiIsInYiOiIxNDUifSx7Im4iOiLnu4/nrqHllYbkuJoiLCJ2IjoiMjM4In0seyJuIjoi5rKf6YCa6KGo6L6+IiwidiI6IjE0NCJ9LHsibiI6IuWKseW/l+aIkOWKnyIsInYiOiIyMzkifV19XSwKICAgICAgICAiMTEzIjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjExMyJ9LHsibiI6IuS6rOWJpyIsInYiOiIxNzEifSx7Im4iOiLotorliaciLCJ2IjoiMTcyIn0seyJuIjoi6buE5qKF5oiPIiwidiI6IjE3MyJ9LHsibiI6IuivhOWJpyIsInYiOiIxNzQifSx7Im4iOiLosavliaciLCJ2IjoiMTc1In0seyJuIjoi5piG5puyIiwidiI6IjE3NiJ9LHsibiI6IuiJuuacryIsInYiOiIxMzQifSx7Im4iOiLoibrmnK/mlofljJYiLCJ2IjoiOTQ1In0seyJuIjoi6Z+z5LmQ6Iie6LmIIiwidiI6Ijk0NiJ9LHsibiI6Iue7mOeUuyIsInYiOiI5NTkifSx7Im4iOiLmkYTlvbEiLCJ2IjoiOTYxIn0seyJuIjoi6Ym06LWP55CG6K66IiwidiI6Ijk2MiJ9XX1dLAogICAgICAgICI5NjAiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiOTYwIn0seyJuIjoi576O6aOf54O56aWqIiwidiI6IjI0MCJ9LHsibiI6IuaXhea4uCIsInYiOiIxNTgifSx7Im4iOiLlgaXlurflhbvnlJ8iLCJ2IjoiMTU5In0seyJuIjoi6L+Q5Yqo5YGl6LqrIiwidiI6IjQxNyJ9LHsibiI6IuaXtuWwmue+juWmhiIsInYiOiI0MTgifSx7Im4iOiLnvo7po5/kvJHpl7IiLCJ2IjoiNDE5In0seyJuIjoi5a625bGF5peF5ri4IiwidiI6IjQyMCJ9LHsibiI6IumjjuawtOWNoOWNnCIsInYiOiI0MjEifSx7Im4iOiLnmb7np5HluLjor4YiLCJ2IjoiOTYzIn0seyJuIjoi55Sf5rS75oOF5oSfIiwidiI6Ijk2NCJ9XX1dLAogICAgICAgICI0NTAiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiNDUwIn0seyJuIjoi5Lqy5a2QIiwidiI6IjQ0NyJ9LHsibiI6IuWpmuWnuyIsInYiOiIyMzQifSx7Im4iOiLnvo7mlociLCJ2IjoiMjMwIn0seyJuIjoi5aSc6K+dIiwidiI6IjIzMiJ9LHsibiI6IuWls+aApyIsInYiOiIyMzEifSx7Im4iOiLlv4PnkIblgaXlurciLCJ2IjoiMjMzIn0seyJuIjoi5oOF5oSf5pWF5LqLIiwidiI6IjIyOSJ9LHsibiI6IuS6p+WQjuaKpOeQhiIsInYiOiIyMjYifSx7Im4iOiLlhL/nq6Xoi7Hor60iLCJ2IjoiMjIyIn0seyJuIjoi5a2V5pyf5L+d5YGlIiwidiI6IjIyNyJ9LHsibiI6IuS6suWtkOWBpeW6tyIsInYiOiIyMjgifSx7Im4iOiLnp5Hmma4iLCJ2IjoiMjIzIn0seyJuIjoi5Zu95a2m5Y6G5Y+yIiwidiI6IjIyNCJ9LHsibiI6IuiDjuaVmeaXqeaVmSIsInYiOiIyMjUifSx7Im4iOiLlhL/nq6XmloflraYiLCJ2IjoiMjIwIn1dfV0sCiAgICAgICAgIjQ0NyI6IFt7a2V5OiAidHlwZSIsIG5hbWU6ICLnsbvlnosiLCB2YWx1ZTogW3sibiI6IuWFqOmDqCIsInYiOiI0NDcifSx7Im4iOiLnp5HlrabmioDmnK8iLCJ2IjoiMTAwMCJ9LHsibiI6IuiHqueEtuenkeWtpiIsInYiOiIxMzYifSx7Im4iOiLkvZvlraYiLCJ2IjoiMTE1In0seyJuIjoi56S+56eRIiwidiI6IjIzNyJ9LHsibiI6IuWbveWtpiIsInYiOiIxMTYifSx7Im4iOiLmlaPmlofmiI/liaciLCJ2IjoiMjM2In0seyJuIjoi5paH5YyWIiwidiI6IjExOCJ9LHsibiI6IuaKgOacryIsInYiOiI5NjkifSx7Im4iOiLnp5HlraYiLCJ2IjoiOTc5In0seyJuIjoi5bel5LiaIiwidiI6Ijk4MCJ9LHsibiI6IuWGnOael+eJp+a4lCIsInYiOiI5ODEifSx7Im4iOiLorqHnrpfmnLoiLCJ2IjoiOTgyIn0seyJuIjoi5bu6562R5Zut5p6XIiwidiI6Ijk4MyJ9XX1dLAogICAgICAgICIzOSI6IFt7a2V5OiAidHlwZSIsIG5hbWU6ICLnsbvlnosiLCB2YWx1ZTogW3sibiI6IuS6jOasoeWFgyIsInYiOiIzOSJ9LHsibiI6IuWoseS5kCIsInYiOiIxMjEifSx7Im4iOiLnuqrlvZUiLCJ2IjoiMTMzIn0seyJuIjoi5paw6Ze7IiwidiI6IjQ0NiJ9XX1dCiAgICB9LAoKICAgIOS4gOe6pzogYXN5bmMgZnVuY3Rpb24gKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgbGV0IHBhZ2UgPSBwZyB8fCAxOwogICAgICAgIGxldCB0eXBlSWQgPSAoZXh0ZW5kICYmIGV4dGVuZC50eXBlKSA/IGV4dGVuZC50eXBlIDogdGlkOwogICAgICAgIGxldCBmaW5hbFVybCA9IGAke3RoaXMuaG9zdH0vYXBpL2Rpc2NvdmVyP3RhYj3lkKzkuaYmdHlwZT0ke3R5cGVJZH0mZ2VuZGVyPTImZ2VucmVfdHlwZT0xJnBhZ2U9JHtwYWdlfWA7CiAgICAgICAgbGV0IGpzb24gPSBhd2FpdCByZXF1ZXN0KGZpbmFsVXJsKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UoanNvbik7CiAgICAgICAgbGV0IGJvb2tfbGlzdCA9IGRhdGEuY29kZSA9PT0gMjAwID8gZGF0YS5kYXRhIDogW107CiAgICAgICAgbGV0IGQgPSBbXTsKICAgICAgICBib29rX2xpc3QuZm9yRWFjaCgoaXQpID0+IHsKICAgICAgICAgICAgZC5wdXNoKHsKICAgICAgICAgICAgICAgIHRpdGxlOiBpdC5ib29rX25hbWUgfHwgaXQuQm9va05hbWUsCiAgICAgICAgICAgICAgICB1cmw6IGl0LmJvb2tfaWQgfHwgaXQuQm9va0lkLAogICAgICAgICAgICAgICAgZGVzYzogaXQuYXV0aG9yIHx8IGl0LkF1dGhvciwKICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGl0LmFic3RyYWN0IHx8IGl0LkFic3RyYWN0IHx8IGl0LmJvb2tfYWJzdHJhY3RfdjIsCiAgICAgICAgICAgICAgICBwaWNfdXJsOiBpdC50aHVtYl91cmwgfHwgaXQuVGh1bWJVUkwgfHwgaXQuYXVkaW9fdGh1bWJfdXJpCiAgICAgICAgICAgIH0pOwogICAgICAgIH0pOwoKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KGQpOwogICAgfSwKCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgZGV0YWlsQXBpID0gYCR7dGhpcy5ob3N0fS9hcGkvZGV0YWlsP2Jvb2tfaWQ9JHt0aGlzLm9ySWR9YDsKICAgICAgICBsZXQgZGV0YWlsSnNvbiA9IGF3YWl0IHJlcXVlc3QoZGV0YWlsQXBpKTsKICAgICAgICBsZXQgZGV0YWlsRGF0YSA9IEpTT04ucGFyc2UoZGV0YWlsSnNvbik7CiAgICAgICAgbGV0IGRhdGEgPSBkZXRhaWxEYXRhLmRhdGEuZGF0YTsKICAgICAgICBsZXQgY2hhcHRlcnNBcGkgPSBgJHt0aGlzLmhvc3R9L2FwaS9ib29rP2Jvb2tfaWQ9JHt0aGlzLm9ySWR9YDsKICAgICAgICBsZXQgY2hhcHRlcnNKc29uID0gYXdhaXQgcmVxdWVzdChjaGFwdGVyc0FwaSk7CiAgICAgICAgbGV0IGNoYXB0ZXJzRGF0YSA9IEpTT04ucGFyc2UoY2hhcHRlcnNKc29uKTsKICAgICAgICBsZXQgYm9va0RhdGEgPSBjaGFwdGVyc0RhdGEuZGF0YS5kYXRhOwogICAgICAgIGxldCBsaXN0ID0gYm9va0RhdGEuY2hhcHRlckxpc3RXaXRoVm9sdW1lPy5mbGF0KCkgfHwgYm9va0RhdGEuY2hhcHRlckxpc3QgfHwgW107CiAgICAgICAgbGV0IHVybHMgPSBsaXN0Lm1hcChpdCA9PiBpdC50aXRsZSArICckJyArIGl0Lml0ZW1JZCArICdAJyArIGl0LnRpdGxlKS5qb2luKCcjJyk7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgdm9kX2lkOiB0aGlzLm9ySWQsCiAgICAgICAgICAgIHZvZF9uYW1lOiBkYXRhLmJvb2tfbmFtZSwKICAgICAgICAgICAgdHlwZV9uYW1lOiBkYXRhLmNhdGVnb3J5LAogICAgICAgICAgICB2b2RfcGljOiBkYXRhLnRodW1iX3VybCB8fCBkYXRhLmV4cGFuZF90aHVtYl91cmwsCiAgICAgICAgICAgIHZvZF9jb250ZW50OiBkYXRhLmFic3RyYWN0IHx8IGRhdGEuYm9va19hYnN0cmFjdF92MiwKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGRhdGEuc3ViX2luZm8sCiAgICAgICAgICAgIHZvZF9kaXJlY3RvcjogZGF0YS5hdXRob3IsCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206ICfnlarojITnlYXlkKwnLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHVybHMKICAgICAgICB9OwogICAgfSwKCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQge2lucHV0fSA9IHRoaXM7CiAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCByZXF1ZXN0KGlucHV0KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoaHRtbCk7CiAgICAgICAgbGV0IGRhdGEgPSBqc29uLmRhdGEuc2VhcmNoX3RhYnNbNF0uZGF0YTsKICAgICAgICBsZXQgZCA9IFtdOwogICAgICAgIGZvciAobGV0IGl0IG9mIGRhdGEuZmlsdGVyKGkgPT4gaS5ib29rX2RhdGEpKSB7CiAgICAgICAgICAgIGxldCBib29rID0gaXQuYm9va19kYXRhWzBdOwogICAgICAgICAgICBkLnB1c2goewogICAgICAgICAgICAgICAgdGl0bGU6IGJvb2suYm9va19uYW1lLAogICAgICAgICAgICAgICAgdXJsOiBib29rLmJvb2tfaWQsCiAgICAgICAgICAgICAgICBkZXNjOiBib29rLmF1dGhvciwKICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGJvb2suYm9va19hYnN0cmFjdCB8fCBib29rLmFic3RyYWN0LAogICAgICAgICAgICAgICAgcGljX3VybDogYm9vay50aHVtYl91cmwKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBzZXRSZXN1bHQoZCk7CiAgICB9LAoKICAgIGxhenk6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQge2lucHV0fSA9IHRoaXM7CiAgICAgICAgbGV0IHBhcnRzID0gaW5wdXQuc3BsaXQoJ0AnKTsKICAgICAgICBsZXQgaXRlbUlkID0gcGFydHNbMF07CiAgICAgICAgbGV0IHRvbmVJZCA9ICcxJzsKICAgICAgICBsZXQgY29udGVudF91cmwgPSBgJHt0aGlzLmhvc3R9L2FwaS9jb250ZW50P2l0ZW1faWQ9JHtpdGVtSWR9JnRhYj3lkKzkuaYmdG9uZV9pZD0ke3RvbmVJZH1gOwogICAgICAgIGxldCBqc29uU3RyID0gYXdhaXQgcmVxdWVzdChjb250ZW50X3VybCk7CiAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKGpzb25TdHIpOwogICAgICAgIHJldHVybiB7cGFyc2U6IDAsIHVybDogZGF0YS5kYXRhLmNvbnRlbnR9OwogICAgfQp9Ow== \ No newline at end of file +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflkKzkuaYnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfnlarojITnlYXlkKwnLAogICAgaG9zdDogJ2h0dHBzOi8vcWtmcWFwaS52djl2LmNuJywKICAgIHVybDogJy9hcGkvZGlzY292ZXI/dGFiPeWQrOS5piZ0eXBlPWZ5Y2xhc3MmZ2VuZGVyPTImZ2VucmVfdHlwZT0xJnBhZ2U9e3twYWdlfX0nLAogICAgc2VhcmNoVXJsOiAnL2FwaS9zZWFyY2g/a2V5PSoqJnRhYl90eXBlPTImb2Zmc2V0PSgoZnlwYWdlLTEpKjEwKScsCiAgICBkZXRhaWxVcmw6ICcvYXBpL2RldGFpbD9ib29rX2lkPWZ5aWQnLAogICAgaGVhZGVyczogeydVc2VyLUFnZW50JzogJ1VDX1VBJ30sCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDAsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgZG91YmxlOiB0cnVlLAogICAgcGxheV9wYXJzZTogdHJ1ZSwKICAgIGxpbWl0OiAxMiwKICAgIGNsYXNzX25hbWU6ICfnsr7lk4HlsI/or7Qm55u45aOw6K+E5LmmJuS4lueVjOWOhuWPsiblkI3okZfop6Por7sm5a2m5Lmg5oiQ6ZW/JuaIj+absuiJuuacrybnlJ/mtLvnmb7np5Em5a625bqt5pWZ6IKyJuS6uuaWh+enkeWtpiblhbbku5YnLAogICAgY2xhc3NfdXJsOiAnODk5JjQ0NSYxMiYxMzImNDQ5JjExMyY5NjAmNDUwJjQ0NyYzOScsCiAgICBmaWx0ZXI6IHsKICAgICAgICAiODk5IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6Ijg5OSJ9LHsibiI6IumDveW4giIsInYiOiIxIn0seyJuIjoi56m/6LaKIiwidiI6IjM3In0seyJuIjoi546w5Luj6KiA5oOFIiwidiI6IjMifSx7Im4iOiLlj6Tku6PoqIDmg4UiLCJ2IjoiNSJ9LHsibiI6IuaAu+ijgSIsInYiOiIyOSJ9LHsibiI6IueOhOW5uyIsInYiOiI3In0seyJuIjoi6YeN55SfIiwidiI6IjM2In0seyJuIjoi5oKs55aRIiwidiI6IjEwIn0seyJuIjoi54G15byCIiwidiI6IjEwMCJ9LHsibiI6Iuezu+e7nyIsInYiOiIxOSJ9LHsibiI6IuenjeeUsCIsInYiOiIyMyJ9LHsibiI6IueUnOWuoCIsInYiOiI5NiJ9LHsibiI6IuWuoOWmuyIsInYiOiIzMCJ9LHsibiI6IumDveW4gueUn+a0uyIsInYiOiIyIn0seyJuIjoi6LWY5am/IiwidiI6IjI1In0seyJuIjoi5YWI5ama5ZCO54ixIiwidiI6IjI2NSJ9LHsibiI6IuWuq+aWl+WuheaWlyIsInYiOiIyNDYifSx7Im4iOiLpg73luILml6XluLgiLCJ2IjoiMjYxIn0seyJuIjoi5oiY56We6LWY5am/IiwidiI6IjI3In0seyJuIjoi56We5Yy7IiwidiI6IjI2In0seyJuIjoi5b2x6KeG5bCP6K+0IiwidiI6IjQ1In0seyJuIjoi5Zu95YaF5b2x6KeGIiwidiI6Ijk5MSJ9LHsibiI6IuWbveWkluW9seinhiIsInYiOiI5OTIifSx7Im4iOiLnsr7lk4HlsI/or7QiLCJ2IjoiOTcwIn0seyJuIjoi5oKs55aR5o6o55CGIiwidiI6IjE2NSJ9LHsibiI6IuenkeW5u+Wwj+ivtCIsInYiOiIxNjYifSx7Im4iOiLmrabkvqDlsI/or7QiLCJ2IjoiOTkzIn0seyJuIjoi546E5bm75bCP6K+0IiwidiI6Ijk3MSJ9LHsibiI6IueOsOWunuWwj+ivtCIsInYiOiI0MDAifSx7Im4iOiLmg4XmhJ/lsI/or7QiLCJ2IjoiOTcyIn0seyJuIjoi5Lyg57uf546E5bm7IiwidiI6IjI1OCJ9LHsibiI6IueOi+WmgyIsInYiOiI4NSJ9LHsibiI6IuWlh+W5u+S7meS+oCIsInYiOiIyNTkifSx7Im4iOiLokIzlrp0iLCJ2IjoiMjgifSx7Im4iOiLpg73luILohJHmtJ4iLCJ2IjoiMjYyIn0seyJuIjoi6IGM5Zy6IiwidiI6IjEyNyJ9LHsibiI6IuWroeWlsyIsInYiOiI4OCJ9LHsibiI6IumDveW4guS/ruecnyIsInYiOiIxMjQifSx7Im4iOiLlubvmg7PoqIDmg4UiLCJ2IjoiMzIifSx7Im4iOiLnpZ7osaoiLCJ2IjoiMjAifSx7Im4iOiLnqbrpl7QiLCJ2IjoiNDQifSx7Im4iOiLlhbbku5YiLCJ2IjoiMzEifSx7Im4iOiLnjoTlubvoqIDmg4UiLCJ2IjoiMjQ4In0seyJuIjoi546E5bm76ISR5rSeIiwidiI6IjI1NyJ9LHsibiI6IuWOhuWPsuWPpOS7oyIsInYiOiIyNzMifSx7Im4iOiLnp5HlubvmnKvkuJYiLCJ2IjoiOCJ9LHsibiI6IuW5tOS7oyIsInYiOiI3OSJ9LHsibiI6IuWkqeaJjSIsInYiOiI5MCJ9LHsibiI6IuWls+W8uiIsInYiOiI4NiJ9LHsibiI6IuaOqOeQhiIsInYiOiI2MSJ9LHsibiI6IuiFuem7kSIsInYiOiI5MiJ9LHsibiI6IuivuOWkqeS4h+eVjCIsInYiOiI3MSJ9LHsibiI6IuWMu+acryIsInYiOiIyNDcifSx7Im4iOiLmmJ/pmYUiLCJ2IjoiNzcifSx7Im4iOiLpibTlrp0iLCJ2IjoiMTcifSx7Im4iOiLlm6LlrqAiLCJ2IjoiOTQifSx7Im4iOiLmia7njKrlkIPomY4iLCJ2IjoiOTMifSx7Im4iOiLmrabkvqAiLCJ2IjoiMTYifSx7Im4iOiLnjrDoqIDohJHmtJ4iLCJ2IjoiMjY3In0seyJuIjoi6YO95biC56eN55SwIiwidiI6IjI2MyJ9LHsibiI6IuaXoOaVjCIsInYiOiIzODQifSx7Im4iOiLnm5flopMiLCJ2IjoiODEifSx7Im4iOiLpqaznlLIiLCJ2IjoiMjY2In0seyJuIjoi55qH5ZCOIiwidiI6Ijg0In0seyJuIjoi54m556eN5YW1IiwidiI6IjM3NSJ9LHsibiI6IuWkp+WUkCIsInYiOiI3MyJ9LHsibiI6IuWFrOS4uyIsInYiOiI4MyJ9LHsibiI6IuWoseS5kOWciCIsInYiOiI0MyJ9LHsibiI6IumdkuaiheeruemprCIsInYiOiIzODcifSx7Im4iOiLlj6ToqIDohJHmtJ4iLCJ2IjoiMjUzIn0seyJuIjoi5Y6G5Y+y6ISR5rSeIiwidiI6IjI3MiJ9LHsibiI6Iuacq+S4liIsInYiOiI2OCJ9LHsibiI6IuWJkemBkyIsInYiOiI4MCJ9LHsibiI6IueOsOiogOeUnOWuoCIsInYiOiIzOTUifSx7Im4iOiLmuLjmiI/liqjmvKsiLCJ2IjoiNTcifSx7Im4iOiLmtKrojZIiLCJ2IjoiNjYifSx7Im4iOiLlv6vnqb8iLCJ2IjoiMjQifSx7Im4iOiLmmI7mnJ0iLCJ2IjoiMTI2In0seyJuIjoi5aSW5Y2WIiwidiI6Ijc1In0seyJuIjoi5qCh6IqxIiwidiI6IjM4NSJ9LHsibiI6IuWltueIuCIsInYiOiI0MiJ9LHsibiI6IuagoeWbrSIsInYiOiI0In0seyJuIjoi5LiJ5Zu9IiwidiI6IjY3In0seyJuIjoi55u05pKtIiwidiI6IjY5In0seyJuIjoi56m/5LmmIiwidiI6IjM4MiJ9LHsibiI6Iua1t+WymyIsInYiOiI0MCJ9LHsibiI6Iue+jumjnyIsInYiOiI3OCJ9LHsibiI6IuWPjea0viIsInYiOiIzNjkifSx7Im4iOiLnjrDoqIDlpI3ku4ciLCJ2IjoiMjY4In0seyJuIjoi6KW/5ri46KGN55SfIiwidiI6IjM3MyJ9LHsibiI6IuaxgueUnyIsInYiOiIzNzkifSx7Im4iOiLmsJHlm70iLCJ2IjoiMzkwIn0seyJuIjoi5a625bqtIiwidiI6IjEyNSJ9LHsibiI6IuWtpumcuCIsInYiOiI4MiJ9LHsibiI6Iueah+WPlCIsInYiOiI4NyJ9LHsibiI6IuWuoOeJqSIsInYiOiI3NCJ9LHsibiI6IuaXoENQIiwidiI6IjM5MiJ9LHsibiI6IuWls+aJrueUt+ijhSIsInYiOiIzODgifSx7Im4iOiLnvZHmuLgiLCJ2IjoiMzcyIn0seyJuIjoi55eF5aiHIiwidiI6IjM4MCJ9LHsibiI6IueyvueBtSIsInYiOiI4OSJ9LHsibiI6IuiZkOaWhyIsInYiOiI5NSJ9LHsibiI6IumDveW4gumdkuaYpSIsInYiOiIzOTYifSx7Im4iOiLmuIXnqb8iLCJ2IjoiNzYifV19XSwKICAgICAgICAiNDQ1IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjQ0NSJ9LHsibiI6IumDreW+t+e6siIsInYiOiIxMjgifSx7Im4iOiLnlLDov57lhYMiLCJ2IjoiMjAzIn0seyJuIjoi5YiY5YWw6IqzIiwidiI6IjIwMiJ9LHsibiI6IuWwj+WTgSIsInYiOiIxMTQifSx7Im4iOiLoooHpmJTmiJAiLCJ2IjoiMjA0In0seyJuIjoi5Y2V55Sw6IqzIiwidiI6IjIwMSJ9LHsibiI6IuivhOS5piIsInYiOiIxMTAifSx7Im4iOiLnm7jlo7AiLCJ2IjoiMTExIn1dfV0sCiAgICAgICAgIjEyIjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjEyIn0seyJuIjoi6a2P5pmL5Y2X5YyX5pydIiwidiI6IjIwOSJ9LHsibiI6IuenpuaxiSIsInYiOiIyMDgifSx7Im4iOiLkuJbnlYzlj7IiLCJ2IjoiMjE0In0seyJuIjoi5ZSQ5a6LIiwidiI6IjIxMCJ9LHsibiI6IuaYjua4hSIsInYiOiIyMTEifSx7Im4iOiLov5HnjrDku6MiLCJ2IjoiMjEzIn0seyJuIjoi5Lit5Zu95Y+yIiwidiI6IjIxMiJ9LHsibiI6IuaImOS6ieWPsiIsInYiOiIyMDYifSx7Im4iOiLlkI3kurrkvKAiLCJ2IjoiMjA3In0seyJuIjoi5Lit5Zu95Y6G5Y+yIiwidiI6IjQwMiJ9LHsibiI6IuS4lueVjOWOhuWPsiIsInYiOiI0MDMifSx7Im4iOiLljoblj7LlsI/or7QiLCJ2IjoiOTg4In0seyJuIjoi5Y6G5Y+y5paH5YyWIiwidiI6IjI0MSJ9XX1dLAogICAgICAgICIxMzIiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiMTMyIn0seyJuIjoi5oiY5LqJIiwidiI6Ijk3In0seyJuIjoi5Lit5Zu95ZCN6JGXIiwidiI6Ijk4In0seyJuIjoi5aSW5Zu95ZCN6JGXIiwidiI6Ijk5In0seyJuIjoi57uP566h5Yqx5b+XIiwidiI6IjI0MiJ9LHsibiI6IuS6uueJqeS8oOiusCIsInYiOiI0MDkifSx7Im4iOiLnu4/lhbjmloflraYiLCJ2IjoiMjQzIn0seyJuIjoi6Z2S5pil5paH5a2mIiwidiI6IjE2OCJ9LHsibiI6IuaImOS6ieWGm+aXhSIsInYiOiI5NzMifV19XSwKICAgICAgICAiNDQ5IjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjQ0OSJ9LHsibiI6IuazleW+iyIsInYiOiIxNDIifSx7Im4iOiLlv4PnkIYiLCJ2IjoiMTQ2In0seyJuIjoi5b+D55CG5a2mIiwidiI6IjQwNyJ9LHsibiI6IuWwkeWEv+W/g+eQhiIsInYiOiI5ODYifSx7Im4iOiLlv4PnkIbnlpfmhIgiLCJ2IjoiOTk0In0seyJuIjoi5oqV6LWE55CG6LSiIiwidiI6IjE0MyJ9LHsibiI6IuiBjOS4muiBjOWcuiIsInYiOiIxNDUifSx7Im4iOiLnu4/nrqHllYbkuJoiLCJ2IjoiMjM4In0seyJuIjoi5rKf6YCa6KGo6L6+IiwidiI6IjE0NCJ9LHsibiI6IuWKseW/l+aIkOWKnyIsInYiOiIyMzkifV19XSwKICAgICAgICAiMTEzIjogW3trZXk6ICJ0eXBlIiwgbmFtZTogIuexu+WeiyIsIHZhbHVlOiBbeyJuIjoi5YWo6YOoIiwidiI6IjExMyJ9LHsibiI6IuS6rOWJpyIsInYiOiIxNzEifSx7Im4iOiLotorliaciLCJ2IjoiMTcyIn0seyJuIjoi6buE5qKF5oiPIiwidiI6IjE3MyJ9LHsibiI6IuivhOWJpyIsInYiOiIxNzQifSx7Im4iOiLosavliaciLCJ2IjoiMTc1In0seyJuIjoi5piG5puyIiwidiI6IjE3NiJ9LHsibiI6IuiJuuacryIsInYiOiIxMzQifSx7Im4iOiLoibrmnK/mlofljJYiLCJ2IjoiOTQ1In0seyJuIjoi6Z+z5LmQ6Iie6LmIIiwidiI6Ijk0NiJ9LHsibiI6Iue7mOeUuyIsInYiOiI5NTkifSx7Im4iOiLmkYTlvbEiLCJ2IjoiOTYxIn0seyJuIjoi6Ym06LWP55CG6K66IiwidiI6Ijk2MiJ9XX1dLAogICAgICAgICI5NjAiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiOTYwIn0seyJuIjoi576O6aOf54O56aWqIiwidiI6IjI0MCJ9LHsibiI6IuaXhea4uCIsInYiOiIxNTgifSx7Im4iOiLlgaXlurflhbvnlJ8iLCJ2IjoiMTU5In0seyJuIjoi6L+Q5Yqo5YGl6LqrIiwidiI6IjQxNyJ9LHsibiI6IuaXtuWwmue+juWmhiIsInYiOiI0MTgifSx7Im4iOiLnvo7po5/kvJHpl7IiLCJ2IjoiNDE5In0seyJuIjoi5a625bGF5peF5ri4IiwidiI6IjQyMCJ9LHsibiI6IumjjuawtOWNoOWNnCIsInYiOiI0MjEifSx7Im4iOiLnmb7np5HluLjor4YiLCJ2IjoiOTYzIn0seyJuIjoi55Sf5rS75oOF5oSfIiwidiI6Ijk2NCJ9XX1dLAogICAgICAgICI0NTAiOiBbe2tleTogInR5cGUiLCBuYW1lOiAi57G75Z6LIiwgdmFsdWU6IFt7Im4iOiLlhajpg6giLCJ2IjoiNDUwIn0seyJuIjoi5Lqy5a2QIiwidiI6IjQ0NyJ9LHsibiI6IuWpmuWnuyIsInYiOiIyMzQifSx7Im4iOiLnvo7mlociLCJ2IjoiMjMwIn0seyJuIjoi5aSc6K+dIiwidiI6IjIzMiJ9LHsibiI6IuWls+aApyIsInYiOiIyMzEifSx7Im4iOiLlv4PnkIblgaXlurciLCJ2IjoiMjMzIn0seyJuIjoi5oOF5oSf5pWF5LqLIiwidiI6IjIyOSJ9LHsibiI6IuS6p+WQjuaKpOeQhiIsInYiOiIyMjYifSx7Im4iOiLlhL/nq6Xoi7Hor60iLCJ2IjoiMjIyIn0seyJuIjoi5a2V5pyf5L+d5YGlIiwidiI6IjIyNyJ9LHsibiI6IuS6suWtkOWBpeW6tyIsInYiOiIyMjgifSx7Im4iOiLnp5Hmma4iLCJ2IjoiMjIzIn0seyJuIjoi5Zu95a2m5Y6G5Y+yIiwidiI6IjIyNCJ9LHsibiI6IuiDjuaVmeaXqeaVmSIsInYiOiIyMjUifSx7Im4iOiLlhL/nq6XmloflraYiLCJ2IjoiMjIwIn1dfV0sCiAgICAgICAgIjQ0NyI6IFt7a2V5OiAidHlwZSIsIG5hbWU6ICLnsbvlnosiLCB2YWx1ZTogW3sibiI6IuWFqOmDqCIsInYiOiI0NDcifSx7Im4iOiLnp5HlrabmioDmnK8iLCJ2IjoiMTAwMCJ9LHsibiI6IuiHqueEtuenkeWtpiIsInYiOiIxMzYifSx7Im4iOiLkvZvlraYiLCJ2IjoiMTE1In0seyJuIjoi56S+56eRIiwidiI6IjIzNyJ9LHsibiI6IuWbveWtpiIsInYiOiIxMTYifSx7Im4iOiLmlaPmlofmiI/liaciLCJ2IjoiMjM2In0seyJuIjoi5paH5YyWIiwidiI6IjExOCJ9LHsibiI6IuaKgOacryIsInYiOiI5NjkifSx7Im4iOiLnp5HlraYiLCJ2IjoiOTc5In0seyJuIjoi5bel5LiaIiwidiI6Ijk4MCJ9LHsibiI6IuWGnOael+eJp+a4lCIsInYiOiI5ODEifSx7Im4iOiLorqHnrpfmnLoiLCJ2IjoiOTgyIn0seyJuIjoi5bu6562R5Zut5p6XIiwidiI6Ijk4MyJ9XX1dLAogICAgICAgICIzOSI6IFt7a2V5OiAidHlwZSIsIG5hbWU6ICLnsbvlnosiLCB2YWx1ZTogW3sibiI6IuS6jOasoeWFgyIsInYiOiIzOSJ9LHsibiI6IuWoseS5kCIsInYiOiIxMjEifSx7Im4iOiLnuqrlvZUiLCJ2IjoiMTMzIn0seyJuIjoi5paw6Ze7IiwidiI6IjQ0NiJ9XX1dCiAgICB9LAoKICAgIOS4gOe6pzogYXN5bmMgZnVuY3Rpb24gKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgbGV0IHBhZ2UgPSBwZyB8fCAxOwogICAgICAgIGxldCB0eXBlSWQgPSAoZXh0ZW5kICYmIGV4dGVuZC50eXBlKSA/IGV4dGVuZC50eXBlIDogdGlkOwogICAgICAgIGxldCBmaW5hbFVybCA9IGAke3RoaXMuaG9zdH0vYXBpL2Rpc2NvdmVyP3RhYj3lkKzkuaYmdHlwZT0ke3R5cGVJZH0mZ2VuZGVyPTImZ2VucmVfdHlwZT0xJnBhZ2U9JHtwYWdlfWA7CiAgICAgICAgbGV0IGpzb24gPSBhd2FpdCByZXF1ZXN0KGZpbmFsVXJsKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UoanNvbik7CiAgICAgICAgbGV0IGJvb2tfbGlzdCA9IGRhdGEuY29kZSA9PT0gMjAwID8gZGF0YS5kYXRhIDogW107CiAgICAgICAgbGV0IGQgPSBbXTsKICAgICAgICBib29rX2xpc3QuZm9yRWFjaCgoaXQpID0+IHsKICAgICAgICAgICAgZC5wdXNoKHsKICAgICAgICAgICAgICAgIHRpdGxlOiBpdC5ib29rX25hbWUgfHwgaXQuQm9va05hbWUsCiAgICAgICAgICAgICAgICB1cmw6IGl0LmJvb2tfaWQgfHwgaXQuQm9va0lkLAogICAgICAgICAgICAgICAgZGVzYzogaXQuYXV0aG9yIHx8IGl0LkF1dGhvciwKICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGl0LmFic3RyYWN0IHx8IGl0LkFic3RyYWN0IHx8IGl0LmJvb2tfYWJzdHJhY3RfdjIsCiAgICAgICAgICAgICAgICBwaWNfdXJsOiBpdC50aHVtYl91cmwgfHwgaXQuVGh1bWJVUkwgfHwgaXQuYXVkaW9fdGh1bWJfdXJpCiAgICAgICAgICAgIH0pOwogICAgICAgIH0pOwoKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KGQpOwogICAgfSwKCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgZGV0YWlsQXBpID0gYCR7dGhpcy5ob3N0fS9hcGkvZGV0YWlsP2Jvb2tfaWQ9JHt0aGlzLm9ySWR9YDsKICAgICAgICBsZXQgZGV0YWlsSnNvbiA9IGF3YWl0IHJlcXVlc3QoZGV0YWlsQXBpKTsKICAgICAgICBsZXQgZGV0YWlsRGF0YSA9IEpTT04ucGFyc2UoZGV0YWlsSnNvbik7CiAgICAgICAgbGV0IGRhdGEgPSBkZXRhaWxEYXRhLmRhdGEuZGF0YTsKICAgICAgICBsZXQgY2hhcHRlcnNBcGkgPSBgJHt0aGlzLmhvc3R9L2FwaS9ib29rP2Jvb2tfaWQ9JHt0aGlzLm9ySWR9YDsKICAgICAgICBsZXQgY2hhcHRlcnNKc29uID0gYXdhaXQgcmVxdWVzdChjaGFwdGVyc0FwaSk7CiAgICAgICAgbGV0IGNoYXB0ZXJzRGF0YSA9IEpTT04ucGFyc2UoY2hhcHRlcnNKc29uKTsKICAgICAgICBsZXQgYm9va0RhdGEgPSBjaGFwdGVyc0RhdGEuZGF0YS5kYXRhOwogICAgICAgIGxldCBsaXN0ID0gYm9va0RhdGEuY2hhcHRlckxpc3RXaXRoVm9sdW1lPy5mbGF0KCkgfHwgYm9va0RhdGEuY2hhcHRlckxpc3QgfHwgW107CiAgICAgICAgbGV0IHVybHMgPSBsaXN0Lm1hcChpdCA9PiBpdC50aXRsZSArICckJyArIGl0Lml0ZW1JZCArICdAJyArIGl0LnRpdGxlKS5qb2luKCcjJyk7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgdm9kX2lkOiB0aGlzLm9ySWQsCiAgICAgICAgICAgIHZvZF9uYW1lOiBkYXRhLmJvb2tfbmFtZSwKICAgICAgICAgICAgdHlwZV9uYW1lOiBkYXRhLmNhdGVnb3J5LAogICAgICAgICAgICB2b2RfcGljOiBkYXRhLnRodW1iX3VybCB8fCBkYXRhLmV4cGFuZF90aHVtYl91cmwsCiAgICAgICAgICAgIHZvZF9jb250ZW50OiBkYXRhLmFic3RyYWN0IHx8IGRhdGEuYm9va19hYnN0cmFjdF92MiwKICAgICAgICAgICAgdm9kX3JlbWFya3M6IGRhdGEuc3ViX2luZm8sCiAgICAgICAgICAgIHZvZF9kaXJlY3RvcjogZGF0YS5hdXRob3IsCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206ICfnlarojITnlYXlkKwnLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHVybHMKICAgICAgICB9OwogICAgfSwKCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQge2lucHV0fSA9IHRoaXM7CiAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCByZXF1ZXN0KGlucHV0KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoaHRtbCk7CiAgICAgICAgbGV0IGRhdGEgPSBqc29uLmRhdGEuc2VhcmNoX3RhYnNbNF0uZGF0YTsKICAgICAgICBsZXQgZCA9IFtdOwogICAgICAgIGZvciAobGV0IGl0IG9mIGRhdGEuZmlsdGVyKGkgPT4gaS5ib29rX2RhdGEpKSB7CiAgICAgICAgICAgIGxldCBib29rID0gaXQuYm9va19kYXRhWzBdOwogICAgICAgICAgICBkLnB1c2goewogICAgICAgICAgICAgICAgdGl0bGU6IGJvb2suYm9va19uYW1lLAogICAgICAgICAgICAgICAgdXJsOiBib29rLmJvb2tfaWQsCiAgICAgICAgICAgICAgICBkZXNjOiBib29rLmF1dGhvciwKICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGJvb2suYm9va19hYnN0cmFjdCB8fCBib29rLmFic3RyYWN0LAogICAgICAgICAgICAgICAgcGljX3VybDogYm9vay50aHVtYl91cmwKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBzZXRSZXN1bHQoZCk7CiAgICB9LAoKICAgIGxhenk6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQge2lucHV0fSA9IHRoaXM7CiAgICAgICAgbGV0IHBhcnRzID0gaW5wdXQuc3BsaXQoJ0AnKTsKICAgICAgICBsZXQgaXRlbUlkID0gcGFydHNbMF07CiAgICAgICAgbGV0IHRvbmVJZCA9ICcxJzsKICAgICAgICBsZXQgY29udGVudF91cmwgPSBgJHt0aGlzLmhvc3R9L2FwaS9jb250ZW50P2l0ZW1faWQ9JHtpdGVtSWR9JnRhYj3lkKzkuaYmdG9uZV9pZD0ke3RvbmVJZH1gOwogICAgICAgIGxldCBqc29uU3RyID0gYXdhaXQgcmVxdWVzdChjb250ZW50X3VybCk7CiAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKGpzb25TdHIpOwogICAgICAgIHJldHVybiB7cGFyc2U6IDAsIHVybDogZGF0YS5kYXRhLmNvbnRlbnR9OwogICAgfQp9Ow== diff --git "a/spider/js/\347\225\252\350\214\204\347\237\255\345\211\247[\347\237\255].js" "b/spider/js/\347\225\252\350\214\204\347\237\255\345\211\247[\347\237\255].js" index ac10661c..001dd9a4 100644 --- "a/spider/js/\347\225\252\350\214\204\347\237\255\345\211\247[\347\237\255].js" +++ "b/spider/js/\347\225\252\350\214\204\347\237\255\345\211\247[\347\237\255].js" @@ -4,6 +4,7 @@ filterable: 0, quickSearch: 0, title: '番茄短剧', + logo: 'https://www.18zf.net/d/file/p/2023/1107/3ty5orktxrc.jpg', '类型': '影视', lang: 'ds' }) @@ -177,4 +178,4 @@ var rule = { }); return VODS }, -} \ No newline at end of file +} diff --git "a/spider/js/\347\237\255\345\211\247\347\275\221[\347\233\230].js" "b/spider/js/\347\237\255\345\211\247\347\275\221[\347\233\230].js" new file mode 100644 index 00000000..2ac60d23 --- /dev/null +++ "b/spider/js/\347\237\255\345\211\247\347\275\221[\347\233\230].js" @@ -0,0 +1,68 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 0, + title: '短剧网', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +var rule = { + 类型: '影视', + author: 'EylinSir', + title: '短剧网', + host: 'https://sm3.cc', + url: '/?cate=fyclass&page=fypage', + searchUrl: '/search.php?q={wd}&page={pg}', + searchable: 1, + quickSearch: 0, + timeout: 5000, + play_parse: true, + class_name: '短剧大全&更新短剧', + class_url: '1&2', + headers: { 'User-Agent': 'MOBILE_UA' }, + + lazy: async function () { + return { url: this.input, parse: 0 }; + }, + + 推荐: async function() { + return await this.一级(); + }, + + 一级: async function(tid, pg, filter, extend) { + let url = this.input; + let html = await request(url); + let list = pdfa(html, 'li.col-6').map(it => ({ + title: pdfh(it, 'h3.f-14 a&&Text'), + pic_url: pdfh(it, 'img.lazy&&data-original'), + desc: pdfh(it, 'h3.f-14 a&&title').replace(/^[^(]*(/, '').replace(/)$/, ''), + url: pdfh(it, 'h3.f-14 a&&href'), + content: '' + })); + return setResult(list); + }, + + 二级: async function(ids) { + let url = this.input; + let html = await request(url); + let list = pdfa(html, '.content').map(content => { + let playList = pdfh(content, 'p'); + return '点我播放$push://' + pdfh(playList, 'a&&href'); + }); + return { + vod_name: pdfh(html, '[title]&&('), + vod_pic: pdfh(html, '[data-original]&&"'), + vod_content: '此为推送网盘规则', + vod_play_from: '短剧网', + vod_play_url: list.join('$$$') + }; + }, + + 搜索: async function () { + return await this.一级(); + } +}; \ No newline at end of file diff --git "a/spider/js/\347\253\213\346\222\255[\347\233\230].js" "b/spider/js/\347\253\213\346\222\255[\347\233\230].js" index a4177c14..6ce11df2 100644 --- "a/spider/js/\347\253\213\346\222\255[\347\233\230].js" +++ "b/spider/js/\347\253\213\346\222\255[\347\233\230].js" @@ -34,7 +34,7 @@ var rule = { limit: 90, double: false, play_parse: true, - class_parse: '.stui-header__menu li:gt(0):lt(7);a&&Text;a&&href;/(\\d+).html', + // class_parse: '.stui-header__menu li:gt(0):lt(7);a&&Text;a&&href;/(\\d+).html', 推荐: async function(tid, pg, filter, extend) { return this.一级(); diff --git "a/spider/js/\347\261\263\345\205\224\351\237\263\344\271\220[\345\220\254].js" "b/spider/js/\347\261\263\345\205\224\351\237\263\344\271\220[\345\220\254].js" index 6a2a6720..92050aca 100644 --- "a/spider/js/\347\261\263\345\205\224\351\237\263\344\271\220[\345\220\254].js" +++ "b/spider/js/\347\261\263\345\205\224\351\237\263\344\271\220[\345\220\254].js" @@ -9,51 +9,4 @@ }) */ -var rule = { - title: '米兔音乐', - host: 'https://api.qqmp3.vip', - url: '/api/fyclass', - searchUrl: '/api/songs.php?type=search&keyword=**', - class_name: '热门歌曲&新歌曲&随机歌曲', - class_url: 'songs.php&songs.php?type=new&songs.php?type=rand', - searchable: 2, - quickSearch: 0, - filterable: 0, - play_parse: true, - limit: 6, - double: true, - headers: { - 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; V2284A Build/V417IR; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Safari/537.36', - 'Accept': '*/*', - 'Origin': 'https://www.qqmp3.vip', - 'referer': 'https://www.qqmp3.vip', - 'x-requested-with': 'com.mmbox.xbrowser', - 'Sec-Fetch-Site': 'same-site', - 'Sec-Fetch-Mode': 'cors', - 'Sec-Fetch-Dest': 'empty', - 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7' - }, - 推荐: '*', - 一级: 'json:data;name;pic;artist;rid', - 二级: '*', - 搜索: 'json:data;name;pic;artist;rid', - lazy: async function() { - let ridMatch = this.input.match(/api\/([^/?]+)/); - if (!ridMatch) return this.input; - let rid = ridMatch[1]; - let api = 'https://api.qqmp3.vip/api/kw.php?rid=' + rid; - // console.log('解析接口:', api); - let json = await request(api); - let data = JSON.parse(json); - if (data.code === 200 && data.data?.url) { - return { - parse: 0, - url: data.data.url, - header: this.headers, - lrc: data.data.lrc || '', - playMode: 'repeat' - }; - } - return this.input; - }, -}; \ No newline at end of file +dmFyIHJ1bGUgPSB7CiAgdGl0bGU6ICfnsbPlhZTpn7PkuZAnLAogIGhvc3Q6ICdodHRwczovL2FwaS5xcW1wMy52aXAnLAogIHVybDogJy9hcGkvZnljbGFzcycsCiAgc2VhcmNoVXJsOiAnL2FwaS9zb25ncy5waHA/dHlwZT1zZWFyY2gma2V5d29yZD0qKicsCiAgY2xhc3NfbmFtZTogJ+eDremXqOatjOabsibmlrDmrYzmm7Im6ZqP5py65q2M5puyJywKICBjbGFzc191cmw6ICdzb25ncy5waHAmc29uZ3MucGhwP3R5cGU9bmV3JnNvbmdzLnBocD90eXBlPXJhbmQnLAogIHNlYXJjaGFibGU6IDIsCiAgcXVpY2tTZWFyY2g6IDAsCiAgZmlsdGVyYWJsZTogMCwKICBwbGF5X3BhcnNlOiB0cnVlLAogIGxpbWl0OiA2LAogIGRvdWJsZTogdHJ1ZSwKICBoZWFkZXJzOiB7CiAgICAnVXNlci1BZ2VudCc6ICdNb3ppbGxhLzUuMCAoTGludXg7IEFuZHJvaWQgMTI7IFYyMjg0QSBCdWlsZC9WNDE3SVI7IHd2KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBWZXJzaW9uLzQuMCBDaHJvbWUvMTAxLjAuNDk1MS42MSBTYWZhcmkvNTM3LjM2JywKICAgICdBY2NlcHQnOiAnKi8qJywKICAgICdPcmlnaW4nOiAnaHR0cHM6Ly93d3cucXFtcDMudmlwJywKICAgICdyZWZlcmVyJzogJ2h0dHBzOi8vd3d3LnFxbXAzLnZpcCcsCiAgICAneC1yZXF1ZXN0ZWQtd2l0aCc6ICdjb20ubW1ib3gueGJyb3dzZXInLAogICAgJ1NlYy1GZXRjaC1TaXRlJzogJ3NhbWUtc2l0ZScsCiAgICAnU2VjLUZldGNoLU1vZGUnOiAnY29ycycsCiAgICAnU2VjLUZldGNoLURlc3QnOiAnZW1wdHknLAogICAgJ0FjY2VwdC1MYW5ndWFnZSc6ICd6aC1DTix6aDtxPTAuOSxlbi1VUztxPTAuOCxlbjtxPTAuNycKICB9LAogIOaOqOiNkDogJyonLAogIOS4gOe6pzogJ2pzb246ZGF0YTtuYW1lO3BpYzthcnRpc3Q7cmlkJywKICDkuoznuqc6ICcqJywKICDmkJzntKI6ICdqc29uOmRhdGE7bmFtZTtwaWM7YXJ0aXN0O3JpZCcsCiAgbGF6eTogYXN5bmMgZnVuY3Rpb24oKSB7CiAgICBsZXQgcmlkTWF0Y2ggPSB0aGlzLmlucHV0Lm1hdGNoKC9hcGlcLyhbXi8/XSspLyk7CiAgICBpZiAoIXJpZE1hdGNoKSByZXR1cm4gdGhpcy5pbnB1dDsKICAgIGxldCByaWQgPSByaWRNYXRjaFsxXTsKICAgIGxldCBhcGkgPSAnaHR0cHM6Ly9hcGkucXFtcDMudmlwL2FwaS9rdy5waHA/cmlkPScgKyByaWQ7CiAgLy8gIGNvbnNvbGUubG9nKCfop6PmnpDmjqXlj6M6JywgYXBpKTsKICAgIGxldCBqc29uID0gYXdhaXQgcmVxdWVzdChhcGkpOwogICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKGpzb24pOwogICAgaWYgKGRhdGEuY29kZSA9PT0gMjAwICYmIGRhdGEuZGF0YT8udXJsKSB7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgcGFyc2U6IDAsCiAgICAgICAgICAgIHVybDogZGF0YS5kYXRhLnVybCwKICAgICAgICAgICAgaGVhZGVyOiB0aGlzLmhlYWRlcnMsCiAgICAgICAgICAgIGxyYzogZGF0YS5kYXRhLmxyYyB8fCAnJywKICAgICAgICAgICAgcGxheU1vZGU6ICdyZXBlYXQnCiAgICAgICAgfTsKICAgIH0KICAgIHJldHVybiB0aGlzLmlucHV0OwogIH0sCn07 \ No newline at end of file diff --git "a/spider/js/\347\276\216\351\242\234\346\200\252[\346\223\246].js" "b/spider/js/\347\276\216\351\242\234\346\200\252[\346\223\246].js" index 27f3a53d..8f283947 100644 --- "a/spider/js/\347\276\216\351\242\234\346\200\252[\346\223\246].js" +++ "b/spider/js/\347\276\216\351\242\234\346\200\252[\346\223\246].js" @@ -4,10 +4,17 @@ filterable: 0, quickSearch: 0, title: '美颜怪', + author: 'LoyDgIk', + '类型': '影视', + mergeList: 1, + more: { + mergeList: 1 + }, lang: 'ds' }) */ + class Rule { 类型 = "影视"; author = "LoyDgIk"; diff --git "a/spider/js/\350\200\220\347\234\213\347\202\271\346\222\255[\344\274\230].js" "b/spider/js/\350\200\220\347\234\213\347\202\271\346\222\255[\344\274\230].js" new file mode 100644 index 00000000..df7478a5 --- /dev/null +++ "b/spider/js/\350\200\220\347\234\213\347\202\271\346\222\255[\344\274\230].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 2, + filterable: 1, + quickSearch: 0, + title: '耐看影视', + author: 'EylinSir', + '类型': '影视', + logo: 'https://nkvod.org/upload/site/20241223-1/7c00a9d60fffa62f46be199e52d6cc85.png', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfogJDnnIvlvbHop4YnLAogICAgaG9zdDogJ2h0dHBzOi8vbmt2b2Qub3JnJywKICAgIHVybDogJy9zaG93L2Z5Y2xhc3MtZnlmaWx0ZXItZnlwYWdlLmh0bWwnLAogICAgc2VhcmNoVXJsOiAnL25rLy0tLS0tLS0tLS0tLS0uaHRtbD93ZD0qKicsCiAgICBsb2dvOiAnaHR0cHM6Ly9ua3ZvZC5vcmcvdXBsb2FkL3NpdGUvMjAyNDEyMjMtMS83YzAwYTlkNjBmZmZhNjJmNDZiZTE5OWU1MmQ2Y2M4NS5wbmcnLAogICAgc2VhcmNoYWJsZTogMiwKICAgIHF1aWNrU2VhcmNoOiAwLAogICAgZmlsdGVyYWJsZTogMSwKICAgIHBsYXlfcGFyc2U6IHRydWUsCiAgICBzZWFyY2hDb29raWU6ICcnLAogICAgY2xhc3NfbmFtZTogJ+eUteW9sSbliafpm4Ym5Yqo5ryrJue7vOiJuicsCiAgICBjbGFzc191cmw6ICcxJjImNCYzJywKICAgIGhlYWRlcnM6IHsKICAgICAgICAnVXNlci1BZ2VudCc6ICdNb3ppbGxhLzUuMCAoTGludXg7IEFuZHJvaWQgMTI7IEFMTi1BTDAwIEJ1aWxkL0hVQVdFSUFMTi1BTDAwOyB3dikgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgVmVyc2lvbi80LjAgQ2hyb21lLzExNC4wLjU3MzUuMTk2IE1vYmlsZSBTYWZhcmkvNTM3LjM2JywKICAgICAgICAnUmVmZXJlcic6ICdodHRwczovL25rdm9kLm9yZy8nCiAgICB9LAoKICAgIHVwZGF0ZUNvb2tpZTogZnVuY3Rpb24oYykgewogICAgICAgIGlmICghYykgcmV0dXJuOwogICAgICAgIGxldCBjQXJyID0gQXJyYXkuaXNBcnJheShjKSA/IGMgOiBbY107CiAgICAgICAgbGV0IGRpY3QgPSB7fTsKICAgICAgICBpZiAodGhpcy5zZWFyY2hDb29raWUpIHsKICAgICAgICAgICAgdGhpcy5zZWFyY2hDb29raWUuc3BsaXQoJzsnKS5mb3JFYWNoKGkgPT4gewogICAgICAgICAgICAgICAgbGV0IHAgPSBpLnNwbGl0KCc9Jyk7CiAgICAgICAgICAgICAgICBpZiAocC5sZW5ndGggPj0gMikgZGljdFtwWzBdLnRyaW0oKV0gPSBwLnNsaWNlKDEpLmpvaW4oJz0nKTsKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIGNBcnIuZm9yRWFjaChpID0+IHsKICAgICAgICAgICAgbGV0IGt2ID0gaS5zcGxpdCgnOycpWzBdLnRyaW0oKTsKICAgICAgICAgICAgbGV0IHAgPSBrdi5zcGxpdCgnPScpOwogICAgICAgICAgICBpZiAocC5sZW5ndGggPj0gMikgZGljdFtwWzBdLnRyaW0oKV0gPSBwLnNsaWNlKDEpLmpvaW4oJz0nKTsKICAgICAgICB9KTsKICAgICAgICBsZXQgcmVzID0gW107CiAgICAgICAgZm9yIChsZXQgayBpbiBkaWN0KSByZXMucHVzaChrICsgJz0nICsgZGljdFtrXSk7CiAgICAgICAgdGhpcy5zZWFyY2hDb29raWUgPSByZXMuam9pbignOycpOwogICAgfSwKCiAgICBDcnlwdG9Ub29sOiB7CiAgICAgICAgZ2V0cmFuZG9tOiBmdW5jdGlvbihiKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBsZXQgc3RyaW5nID0gYi5zdWJzdHJpbmcoMTApOwogICAgICAgICAgICAgICAgbGV0IHdvcmRzID0gQ3J5cHRvSlMuZW5jLkJhc2U2NC5wYXJzZShzdHJpbmcpOwogICAgICAgICAgICAgICAgbGV0IHN1YnN0ciA9IENyeXB0b0pTLmVuYy5MYXRpbjEuc3RyaW5naWZ5KHdvcmRzKTsKICAgICAgICAgICAgICAgIGlmICghc3Vic3RyKSByZXR1cm4gIiI7CiAgICAgICAgICAgICAgICBsZXQgZGF0YTIgPSBzdWJzdHIuc3Vic3RyaW5nKDEwKS5yZXBsYWNlKCdfbmFua2UnLCAnJyk7CiAgICAgICAgICAgICAgICBsZXQgZGF0YTMgPSBkYXRhMi5zbGljZSgwLCAyMCkgKyBkYXRhMi5zbGljZSgyMSk7CiAgICAgICAgICAgICAgICBsZXQgaGV4U3RyID0gZGF0YTMucmVwbGFjZSgvW14wLTlhLWZBLUZdL2csICcnKTsKICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmhleERlY29kZUFuZEZpbHRlcihoZXhTdHIpOwogICAgICAgICAgICB9IGNhdGNoIChlcnJvcikgeyByZXR1cm4gIiI7IH0KICAgICAgICB9LAogICAgICAgIGhleERlY29kZUFuZEZpbHRlcjogZnVuY3Rpb24oaGV4U3RyKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBsZXQgcHVyZUhleCA9IGhleFN0ci5yZXBsYWNlKC9bXjAtOWEtZkEtRl0vZywgJycpOwogICAgICAgICAgICAgICAgaWYgKHB1cmVIZXgubGVuZ3RoICUgMiAhPT0gMCkgcHVyZUhleCArPSAnMCc7CiAgICAgICAgICAgICAgICBsZXQgZGVjb2RlZCA9ICcnOwogICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwdXJlSGV4Lmxlbmd0aDsgaSArPSAyKSB7CiAgICAgICAgICAgICAgICAgICAgbGV0IGNoYXIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlKHBhcnNlSW50KHB1cmVIZXguc3Vic3RyKGksIDIpLCAxNikpOwogICAgICAgICAgICAgICAgICAgIGlmICgvW2EtekEtWjAtOTpcL1wuXC1cP1wmPVwlX35dLy50ZXN0KGNoYXIpKSBkZWNvZGVkICs9IGNoYXI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBsZXQgdXJsTWF0Y2ggPSBkZWNvZGVkLm1hdGNoKC9odHRwcz86XC9cL1teXHNdKy8pOwogICAgICAgICAgICAgICAgcmV0dXJuIHVybE1hdGNoID8gdXJsTWF0Y2hbMF0gOiBkZWNvZGVkLnRyaW0oKTsKICAgICAgICAgICAgfSBjYXRjaCAoZSkgeyByZXR1cm4gIiI7IH0KICAgICAgICB9LAogICAgICAgIGFycjJoZXg6IGZ1bmN0aW9uKGFycikgewogICAgICAgICAgICByZXR1cm4gYXJyLm1hcChmdW5jdGlvbihiKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gKCcwJyArIChiICYgMHhGRikudG9TdHJpbmcoMTYpKS5zbGljZSgtMik7CiAgICAgICAgICAgIH0pLmpvaW4oJycpOwogICAgICAgIH0sCiAgICAgICAgZGVjcnlwdERhdGE6IGZ1bmN0aW9uKGhleCwga2V5QXJyLCBpdkFycikgewogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgbGV0IGtleSA9IENyeXB0b0pTLmVuYy5IZXgucGFyc2UodGhpcy5hcnIyaGV4KGtleUFycikpOwogICAgICAgICAgICAgICAgbGV0IGl2ID0gQ3J5cHRvSlMuZW5jLkhleC5wYXJzZSh0aGlzLmFycjJoZXgoaXZBcnIpKTsKICAgICAgICAgICAgICAgIGxldCBzcmMgPSBDcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGhleCk7CiAgICAgICAgICAgICAgICBsZXQgbW9kZXMgPSBbQ3J5cHRvSlMubW9kZS5DQkMsIENyeXB0b0pTLm1vZGUuRUNCLCBDcnlwdG9KUy5tb2RlLk9GQiwgQ3J5cHRvSlMubW9kZS5DRkIsIENyeXB0b0pTLm1vZGUuQ1RSXTsKICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbW9kZXMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgICAgICBsZXQgcGFyYW0gPSB7IG1vZGU6IG1vZGVzW2ldLCBwYWRkaW5nOiBDcnlwdG9KUy5wYWQuUGtjczcgfTsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZGVzW2ldICE9PSBDcnlwdG9KUy5tb2RlLkVDQikgcGFyYW0uaXYgPSBpdjsKICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGRlYyA9IENyeXB0b0pTLkFFUy5kZWNyeXB0KHtjaXBoZXJ0ZXh0OiBzcmN9LCBrZXksIHBhcmFtKTsKICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHJlcyA9IGRlYy50b1N0cmluZyhDcnlwdG9KUy5lbmMuVXRmOCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXMpIHJldHVybiByZXM7CiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge30KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7IHJldHVybiBudWxsOyB9CiAgICAgICAgfQogICAgfSwKCiAgICByZXF1ZXN0OiBhc3luYyBmdW5jdGlvbih1cmwsIG9wdCkgewogICAgICAgIGxldCBvcHRzID0geyBtZXRob2Q6IG9wdD8ubWV0aG9kIHx8ICdHRVQnLCBoZWFkZXJzOiBPYmplY3QuYXNzaWduKHt9LCB0aGlzLmhlYWRlcnMsIG9wdD8uaGVhZGVycyB8fCB7fSksIGRhdGE6IG9wdD8uYm9keSB9OwogICAgICAgIGlmICh0aGlzLnNlYXJjaENvb2tpZSkgb3B0cy5oZWFkZXJzLkNvb2tpZSA9IHRoaXMuc2VhcmNoQ29va2llOwogICAgICAgIGxldCByZXMgPSBhd2FpdCByZXEodXJsLCBvcHRzKTsKICAgICAgICBpZiAocmVzLmhlYWRlcnNbJ3NldC1jb29raWUnXSkgewogICAgICAgICAgICB0aGlzLnVwZGF0ZUNvb2tpZShyZXMuaGVhZGVyc1snc2V0LWNvb2tpZSddKTsKICAgICAgICB9CiAgICAgICAgaWYgKHJlcy5jb250ZW50LmluY2x1ZGVzKCfns7vnu5/lronlhajpqozor4EnKSB8fCByZXMuY29udGVudC5pbmNsdWRlcygn6K+36L6T5YWl6aqM6K+B56CBJykgfHwgcmVzLmNvbnRlbnQuaW5jbHVkZXMoJ3ZlcmlmeV9jaGVjaycpKSB7CiAgICAgICAgICAgIGlmIChhd2FpdCB0aGlzLmZldGNoQ2sodXJsKSkgewogICAgICAgICAgICAgICAgb3B0cy5oZWFkZXJzLkNvb2tpZSA9IHRoaXMuc2VhcmNoQ29va2llOwogICAgICAgICAgICAgICAgaWYgKCFvcHRzLmhlYWRlcnMuUmVmZXJlcikgb3B0cy5oZWFkZXJzLlJlZmVyZXIgPSB1cmw7CiAgICAgICAgICAgICAgICByZXMgPSBhd2FpdCByZXEodXJsLCBvcHRzKTsKICAgICAgICAgICAgICAgIGlmIChyZXMuaGVhZGVyc1snc2V0LWNvb2tpZSddKSB0aGlzLnVwZGF0ZUNvb2tpZShyZXMuaGVhZGVyc1snc2V0LWNvb2tpZSddKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gcmVzLmNvbnRlbnQ7CiAgICB9LAoKICAgIGZldGNoQ2s6IGFzeW5jIGZ1bmN0aW9uKHJlZikgewogICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgMzsgaSsrKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBsZXQgeXptID0gdGhpcy5ob3N0ICsgIi9pbmRleC5waHAvdmVyaWZ5L2luZGV4Lmh0bWw/IiArIE1hdGgucmFuZG9tKCk7CiAgICAgICAgICAgICAgICBsZXQgaCA9IHsgJ1VzZXItQWdlbnQnOiB0aGlzLmhlYWRlcnNbJ1VzZXItQWdlbnQnXSwgJ1JlZmVyZXInOiByZWYgfTsKICAgICAgICAgICAgICAgIGlmICh0aGlzLnNlYXJjaENvb2tpZSkgaC5Db29raWUgPSB0aGlzLnNlYXJjaENvb2tpZTsKICAgICAgICAgICAgICAgIGxldCByZXMgPSBhd2FpdCByZXEoeXptLCB7IGhlYWRlcnM6IGgsIGJ1ZmZlcjogMiB9KTsKICAgICAgICAgICAgICAgIGlmIChyZXMuaGVhZGVyc1snc2V0LWNvb2tpZSddKSB0aGlzLnVwZGF0ZUNvb2tpZShyZXMuaGVhZGVyc1snc2V0LWNvb2tpZSddKTsKICAgICAgICAgICAgICAgIGxldCBjb2RlID0gJyc7CiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIG9jciA9PT0gJ2Z1bmN0aW9uJykgewogICAgICAgICAgICAgICAgICAgIHRyeSB7IGNvZGUgPSBhd2FpdCBvY3IoeXptLCByZXMuY29udGVudCk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAoIWNvZGUpIHsKICAgICAgICAgICAgICAgICAgICBsZXQgciA9IGF3YWl0IHJlcSgiaHR0cHM6Ly9hcGkubm4uY2kvb2NyL2I2NC90ZXh0IiwgeyBtZXRob2Q6ICdQT1NUJywgaGVhZGVyczogeydDb250ZW50LVR5cGUnOid0ZXh0L3BsYWluJ30sIGJvZHk6IHJlcy5jb250ZW50IH0pOwogICAgICAgICAgICAgICAgICAgIGNvZGUgPSByLmNvbnRlbnQ7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBjb2RlID0gY29kZS50cmltKCkucmVwbGFjZSgvXHMrL2csICcnKTsKICAgICAgICAgICAgICAgIGlmICghY29kZSkgY29udGludWU7CiAgICAgICAgICAgICAgICBsZXQgdlJlcyA9IGF3YWl0IHJlcSh0aGlzLmhvc3QgKyAiL2luZGV4LnBocC9hamF4L3ZlcmlmeV9jaGVjaz90eXBlPXNlYXJjaCZ2ZXJpZnk9IiArIGNvZGUsIHsKICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdHRVQnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgJ1VzZXItQWdlbnQnOiB0aGlzLmhlYWRlcnNbJ1VzZXItQWdlbnQnXSwgJ1JlZmVyZXInOiByZWYsICdYLVJlcXVlc3RlZC1XaXRoJzogJ1hNTEh0dHBSZXF1ZXN0JywgJ0Nvb2tpZSc6IHRoaXMuc2VhcmNoQ29va2llIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgaWYgKHZSZXMuY29udGVudC5pbmNsdWRlcygnImNvZGUiOjEnKSkgewogICAgICAgICAgICAgICAgICAgIGlmICh2UmVzLmhlYWRlcnNbJ3NldC1jb29raWUnXSkgdGhpcy51cGRhdGVDb29raWUodlJlcy5oZWFkZXJzWydzZXQtY29va2llJ10pOwogICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGNhdGNoIChlKSB7fQogICAgICAgIH0KICAgICAgICByZXR1cm4gZmFsc2U7CiAgICB9LAoKICAgIOmihOWkhOeQhjogYXN5bmMgZnVuY3Rpb24oKSB7CiAgICAgICAgbGV0IGZpbHRlcnMgPSB7CiAgICAgICAgICAgICIxIjpbeyJrZXkiOiJjbGFzcyIsIm5hbWUiOiLliafmg4UiLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiLllpzliaciLCJ2Ijoi5Zac5YmnIn0seyJuIjoi54ix5oOFIiwidiI6IueIseaDhSJ9LHsibiI6IuaBkOaAliIsInYiOiLmgZDmgJYifSx7Im4iOiLliqjkvZwiLCJ2Ijoi5Yqo5L2cIn0seyJuIjoi56eR5bm7IiwidiI6IuenkeW5uyJ9LHsibiI6IuWJp+aDhSIsInYiOiLliafmg4UifSx7Im4iOiLmiJjkuokiLCJ2Ijoi5oiY5LqJIn0seyJuIjoi6K2m5YyqIiwidiI6IuitpuWMqiJ9LHsibiI6IueKr+e9qiIsInYiOiLniq/nvaoifSx7Im4iOiLliqjnlLsiLCJ2Ijoi5Yqo55S7In0seyJuIjoi5aWH5bm7IiwidiI6IuWlh+W5uyJ9LHsibiI6IuatpuS+oCIsInYiOiLmrabkvqAifSx7Im4iOiLlhpLpmakiLCJ2Ijoi5YaS6ZmpIn0seyJuIjoi5p6q5oiYIiwidiI6IuaequaImCJ9LHsibiI6IuaCrOeWkSIsInYiOiLmgqznlpEifSx7Im4iOiLmg4rmgpoiLCJ2Ijoi5oOK5oKaIn0seyJuIjoi57uP5YW4IiwidiI6Iue7j+WFuCJ9LHsibiI6IumdkuaYpSIsInYiOiLpnZLmmKUifSx7Im4iOiLmlofoiboiLCJ2Ijoi5paH6Im6In0seyJuIjoi5b6u55S15b2xIiwidiI6IuW+rueUteW9sSJ9LHsibiI6IuWPpOijhSIsInYiOiLlj6Too4UifSx7Im4iOiLljoblj7IiLCJ2Ijoi5Y6G5Y+yIn0seyJuIjoi6L+Q5YqoIiwidiI6Iui/kOWKqCJ9LHsibiI6IuWGnOadkSIsInYiOiLlhpzmnZEifSx7Im4iOiLlhL/nq6UiLCJ2Ijoi5YS/56ulIn0seyJuIjoi572R57uc55S15b2xIiwidiI6Iue9kee7nOeUteW9sSJ9XX0seyJrZXkiOiJhcmVhIiwibmFtZSI6IuWcsOWMuiIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWkp+mZhiIsInYiOiLlpKfpmYYifSx7Im4iOiLpppnmuK8iLCJ2Ijoi6aaZ5rivIn0seyJuIjoi5Y+w5rm+IiwidiI6IuWPsOa5viJ9LHsibiI6Iue+juWbvSIsInYiOiLnvo7lm70ifSx7Im4iOiLms5Xlm70iLCJ2Ijoi5rOV5Zu9In0seyJuIjoi6Iux5Zu9IiwidiI6IuiLseWbvSJ9LHsibiI6IuaXpeacrCIsInYiOiLml6XmnKwifSx7Im4iOiLpn6nlm70iLCJ2Ijoi6Z+p5Zu9In0seyJuIjoi5b635Zu9IiwidiI6IuW+t+WbvSJ9LHsibiI6IuazsOWbvSIsInYiOiLms7Dlm70ifSx7Im4iOiLljbDluqYiLCJ2Ijoi5Y2w5bqmIn0seyJuIjoi5oSP5aSn5YipIiwidiI6IuaEj+Wkp+WIqSJ9LHsibiI6Iuilv+ePreeJmSIsInYiOiLopb/nj63niZkifSx7Im4iOiLliqDmi7/lpKciLCJ2Ijoi5Yqg5ou/5aSnIn0seyJuIjoi5YW25a6DIiwidiI6IuWFtuWugyJ9XX0seyJrZXkiOiJ5ZWFyIiwibmFtZSI6IuW5tOS7vSIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IjIwMjYiLCJ2IjoiMjAyNiJ9LHsibiI6IjIwMjUiLCJ2IjoiMjAyNSJ9LHsibiI6IjIwMjQiLCJ2IjoiMjAyNCJ9LHsibiI6IjIwMjMiLCJ2IjoiMjAyMyJ9LHsibiI6IjIwMjIiLCJ2IjoiMjAyMiJ9LHsibiI6IjIwMjEiLCJ2IjoiMjAyMSJ9LHsibiI6IjIwMjAiLCJ2IjoiMjAyMCJ9LHsibiI6IjIwMTkiLCJ2IjoiMjAxOSJ9LHsibiI6IjIwMTgiLCJ2IjoiMjAxOCJ9LHsibiI6IjIwMTciLCJ2IjoiMjAxNyJ9LHsibiI6IjIwMTYiLCJ2IjoiMjAxNiJ9LHsibiI6IjIwMTUiLCJ2IjoiMjAxNSJ9LHsibiI6IjIwMTQiLCJ2IjoiMjAxNCJ9LHsibiI6IjIwMTMiLCJ2IjoiMjAxMyJ9LHsibiI6IjIwMTIiLCJ2IjoiMjAxMiJ9LHsibiI6IjIwMTEiLCJ2IjoiMjAxMSJ9LHsibiI6IjIwMTAiLCJ2IjoiMjAxMCJ9XX0seyJrZXkiOiJsYW5nIiwibmFtZSI6IuivreiogCIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWbveivrSIsInYiOiLlm73or60ifSx7Im4iOiLoi7Hor60iLCJ2Ijoi6Iux6K+tIn0seyJuIjoi57Kk6K+tIiwidiI6IueypOivrSJ9LHsibiI6IumXveWNl+ivrSIsInYiOiLpl73ljZfor60ifSx7Im4iOiLpn6nor60iLCJ2Ijoi6Z+p6K+tIn0seyJuIjoi5pel6K+tIiwidiI6IuaXpeivrSJ9LHsibiI6IuazleivrSIsInYiOiLms5Xor60ifSx7Im4iOiLlvrfor60iLCJ2Ijoi5b636K+tIn0seyJuIjoi5YW25a6DIiwidiI6IuWFtuWugyJ9XX0seyJrZXkiOiJsZXR0ZXIiLCJuYW1lIjoi5a2X5q+NIiwidmFsdWUiOlt7Im4iOiLlrZfmr40iLCJ2IjoiIn0seyJuIjoiQSIsInYiOiJBIn0seyJuIjoiQiIsInYiOiJCIn0seyJuIjoiQyIsInYiOiJDIn0seyJuIjoiRCIsInYiOiJEIn0seyJuIjoiRSIsInYiOiJFIn0seyJuIjoiRiIsInYiOiJGIn0seyJuIjoiRyIsInYiOiJHIn0seyJuIjoiSCIsInYiOiJIIn0seyJuIjoiSSIsInYiOiJJIn0seyJuIjoiSiIsInYiOiJKIn0seyJuIjoiSyIsInYiOiJLIn0seyJuIjoiTCIsInYiOiJMIn0seyJuIjoiTSIsInYiOiJNIn0seyJuIjoiTiIsInYiOiJOIn0seyJuIjoiTyIsInYiOiJPIn0seyJuIjoiUCIsInYiOiJQIn0seyJuIjoiUSIsInYiOiJRIn0seyJuIjoiUiIsInYiOiJSIn0seyJuIjoiUyIsInYiOiJTIn0seyJuIjoiVCIsInYiOiJUIn0seyJuIjoiVSIsInYiOiJVIn0seyJuIjoiViIsInYiOiJWIn0seyJuIjoiVyIsInYiOiJXIn0seyJuIjoiWCIsInYiOiJYIn0seyJuIjoiWSIsInYiOiJZIn0seyJuIjoiWiIsInYiOiJaIn0seyJuIjoiMC05IiwidiI6IjAtOSJ9XX0seyJrZXkiOiJieSIsIm5hbWUiOiLmjpLluo8iLCJ2YWx1ZSI6W3sibiI6IuaXtumXtOaOkuW6jyIsInYiOiJ0aW1lIn0seyJuIjoi5Lq65rCU5o6S5bqPIiwidiI6ImhpdHMifSx7Im4iOiLor4TliIbmjpLluo8iLCJ2Ijoic2NvcmUifV19XSwKICAgICAgICAgICAgIjIiOlt7ImtleSI6ImNsYXNzIiwibmFtZSI6IuWJp+aDhSIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWPpOijhSIsInYiOiLlj6Too4UifSx7Im4iOiLmiJjkuokiLCJ2Ijoi5oiY5LqJIn0seyJuIjoi6Z2S5pil5YG25YOPIiwidiI6IumdkuaYpeWBtuWDjyJ9LHsibiI6IuWWnOWJpyIsInYiOiLllpzliacifSx7Im4iOiLliqjkvZwiLCJ2Ijoi5Yqo5L2cIn0seyJuIjoi5aWH5bm7IiwidiI6IuWlh+W5uyJ9LHsibiI6IuWJp+aDhSIsInYiOiLliafmg4UifSx7Im4iOiLljoblj7IiLCJ2Ijoi5Y6G5Y+yIn0seyJuIjoi57uP5YW4IiwidiI6Iue7j+WFuCJ9LHsibiI6IuS5oeadkSIsInYiOiLkuaHmnZEifSx7Im4iOiLmg4Xmma8iLCJ2Ijoi5oOF5pmvIn0seyJuIjoi5ZWG5oiYIiwidiI6IuWVhuaImCJ9LHsibiI6Iue9keWJpyIsInYiOiLnvZHliacifSx7Im4iOiLlhbbku5YiLCJ2Ijoi5YW25LuWIn1dfSx7ImtleSI6ImFyZWEiLCJuYW1lIjoi5Zyw5Yy6IiwidmFsdWUiOlt7Im4iOiLlhajpg6giLCJ2IjoiIn0seyJuIjoi5YaF5ZywIiwidiI6IuWGheWcsCJ9LHsibiI6IumfqeWbvSIsInYiOiLpn6nlm70ifSx7Im4iOiLpppnmuK8iLCJ2Ijoi6aaZ5rivIn0seyJuIjoi5Y+w5rm+IiwidiI6IuWPsOa5viJ9LHsibiI6IuaXpeacrCIsInYiOiLml6XmnKwifSx7Im4iOiLnvo7lm70iLCJ2Ijoi576O5Zu9In0seyJuIjoi5rOw5Zu9IiwidiI6IuazsOWbvSJ9LHsibiI6IuiLseWbvSIsInYiOiLoi7Hlm70ifSx7Im4iOiLmlrDliqDlnaEiLCJ2Ijoi5paw5Yqg5Z2hIn0seyJuIjoi5YW25LuWIiwidiI6IuWFtuS7liJ9XX0seyJrZXkiOiJsYW5nIiwibmFtZSI6IuivreiogCIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWbveivrSIsInYiOiLlm73or60ifSx7Im4iOiLoi7Hor60iLCJ2Ijoi6Iux6K+tIn0seyJuIjoi57Kk6K+tIiwidiI6IueypOivrSJ9LHsibiI6IumXveWNl+ivrSIsInYiOiLpl73ljZfor60ifSx7Im4iOiLpn6nor60iLCJ2Ijoi6Z+p6K+tIn0seyJuIjoi5pel6K+tIiwidiI6IuaXpeivrSJ9LHsibiI6IuWFtuWugyIsInYiOiLlhbblroMifV19LHsia2V5IjoieWVhciIsIm5hbWUiOiLlubTku70iLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiIyMDI2IiwidiI6IjIwMjYifSx7Im4iOiIyMDI1IiwidiI6IjIwMjUifSx7Im4iOiIyMDI0IiwidiI6IjIwMjQifSx7Im4iOiIyMDIzIiwidiI6IjIwMjMifSx7Im4iOiIyMDIyIiwidiI6IjIwMjIifSx7Im4iOiIyMDIxIiwidiI6IjIwMjEifSx7Im4iOiIyMDIwIiwidiI6IjIwMjAifSx7Im4iOiIyMDE5IiwidiI6IjIwMTkifSx7Im4iOiIyMDE4IiwidiI6IjIwMTgifSx7Im4iOiIyMDE3IiwidiI6IjIwMTcifSx7Im4iOiIyMDE2IiwidiI6IjIwMTYifSx7Im4iOiIyMDE1IiwidiI6IjIwMTUifSx7Im4iOiIyMDE0IiwidiI6IjIwMTQifSx7Im4iOiIyMDEzIiwidiI6IjIwMTMifSx7Im4iOiIyMDEyIiwidiI6IjIwMTIifSx7Im4iOiIyMDExIiwidiI6IjIwMTEifSx7Im4iOiIyMDEwIiwidiI6IjIwMTAifSx7Im4iOiIyMDA5IiwidiI6IjIwMDkifSx7Im4iOiIyMDA4IiwidiI6IjIwMDgifSx7Im4iOiIyMDA2IiwidiI6IjIwMDYifSx7Im4iOiIyMDA1IiwidiI6IjIwMDUifSx7Im4iOiIyMDA0IiwidiI6IjIwMDQifV19LHsia2V5IjoibGV0dGVyIiwibmFtZSI6IuWtl+avjSIsInZhbHVlIjpbeyJuIjoi5a2X5q+NIiwidiI6IiJ9LHsibiI6IkEiLCJ2IjoiQSJ9LHsibiI6IkIiLCJ2IjoiQiJ9LHsibiI6IkMiLCJ2IjoiQyJ9LHsibiI6IkQiLCJ2IjoiRCJ9LHsibiI6IkUiLCJ2IjoiRSJ9LHsibiI6IkYiLCJ2IjoiRiJ9LHsibiI6IkciLCJ2IjoiRyJ9LHsibiI6IkgiLCJ2IjoiSCJ9LHsibiI6IkkiLCJ2IjoiSSJ9LHsibiI6IkoiLCJ2IjoiSiJ9LHsibiI6IksiLCJ2IjoiSyJ9LHsibiI6IkwiLCJ2IjoiTCJ9LHsibiI6Ik0iLCJ2IjoiTSJ9LHsibiI6Ik4iLCJ2IjoiTiJ9LHsibiI6Ik8iLCJ2IjoiTyJ9LHsibiI6IlAiLCJ2IjoiUCJ9LHsibiI6IlEiLCJ2IjoiUSJ9LHsibiI6IlIiLCJ2IjoiUiJ9LHsibiI6IlMiLCJ2IjoiUyJ9LHsibiI6IlQiLCJ2IjoiVCJ9LHsibiI6IlUiLCJ2IjoiVSJ9LHsibiI6IlYiLCJ2IjoiViJ9LHsibiI6IlciLCJ2IjoiVyJ9LHsibiI6IlgiLCJ2IjoiWCJ9LHsibiI6IlkiLCJ2IjoiWSJ9LHsibiI6IloiLCJ2IjoiWiJ9LHsibiI6IjAtOSIsInYiOiIwLTkifV19LHsia2V5IjoiYnkiLCJuYW1lIjoi5o6S5bqPIiwidmFsdWUiOlt7Im4iOiLml7bpl7TmjpLluo8iLCJ2IjoidGltZSJ9LHsibiI6IuS6uuawlOaOkuW6jyIsInYiOiJoaXRzIn0seyJuIjoi6K+E5YiG5o6S5bqPIiwidiI6InNjb3JlIn1dfV0sCiAgICAgICAgICAgICIzIjpbeyJrZXkiOiJjbGFzcyIsIm5hbWUiOiLliafmg4UiLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiLpgInnp4AiLCJ2Ijoi6YCJ56eAIn0seyJuIjoi5oOF5oSfIiwidiI6IuaDheaEnyJ9LHsibiI6Iuiuv+iwiCIsInYiOiLorr/osIgifSx7Im4iOiLmkq3miqUiLCJ2Ijoi5pKt5oqlIn0seyJuIjoi5peF5ri4IiwidiI6IuaXhea4uCJ9LHsibiI6Iumfs+S5kCIsInYiOiLpn7PkuZAifSx7Im4iOiLnvo7po58iLCJ2Ijoi576O6aOfIn0seyJuIjoi57qq5a6eIiwidiI6Iue6quWuniJ9LHsibiI6IuabsuiJuiIsInYiOiLmm7LoiboifSx7Im4iOiLnlJ/mtLsiLCJ2Ijoi55Sf5rS7In0seyJuIjoi5ri45oiP5LqS5YqoIiwidiI6Iua4uOaIj+S6kuWKqCJ9LHsibiI6Iui0oue7jyIsInYiOiLotKLnu48ifSx7Im4iOiLmsYLogYwiLCJ2Ijoi5rGC6IGMIn1dfSx7ImtleSI6ImFyZWEiLCJuYW1lIjoi5Zyw5Yy6IiwidmFsdWUiOlt7Im4iOiLlhajpg6giLCJ2IjoiIn0seyJuIjoi5YaF5ZywIiwidiI6IuWGheWcsCJ9LHsibiI6Iua4r+WPsCIsInYiOiLmuK/lj7AifSx7Im4iOiLml6Xpn6kiLCJ2Ijoi5pel6Z+pIn0seyJuIjoi5qyn576OIiwidiI6Iuasp+e+jiJ9XX0seyJrZXkiOiJsYW5nIiwibmFtZSI6IuivreiogCIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWbveivrSIsInYiOiLlm73or60ifSx7Im4iOiLoi7Hor60iLCJ2Ijoi6Iux6K+tIn0seyJuIjoi57Kk6K+tIiwidiI6IueypOivrSJ9LHsibiI6IumXveWNl+ivrSIsInYiOiLpl73ljZfor60ifSx7Im4iOiLpn6nor60iLCJ2Ijoi6Z+p6K+tIn0seyJuIjoi5pel6K+tIiwidiI6IuaXpeivrSJ9LHsibiI6IuWFtuWugyIsInYiOiLlhbblroMifV19LHsia2V5IjoieWVhciIsIm5hbWUiOiLlubTku70iLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiIyMDI2IiwidiI6IjIwMjYifSx7Im4iOiIyMDI1IiwidiI6IjIwMjUifSx7Im4iOiIyMDI0IiwidiI6IjIwMjQifSx7Im4iOiIyMDIzIiwidiI6IjIwMjMifSx7Im4iOiIyMDIyIiwidiI6IjIwMjIifSx7Im4iOiIyMDIxIiwidiI6IjIwMjEifSx7Im4iOiIyMDIwIiwidiI6IjIwMjAifSx7Im4iOiIyMDE5IiwidiI6IjIwMTkifSx7Im4iOiIyMDE4IiwidiI6IjIwMTgifSx7Im4iOiIyMDE3IiwidiI6IjIwMTcifSx7Im4iOiIyMDE2IiwidiI6IjIwMTYifSx7Im4iOiIyMDE1IiwidiI6IjIwMTUifSx7Im4iOiIyMDE0IiwidiI6IjIwMTQifSx7Im4iOiIyMDEzIiwidiI6IjIwMTMifSx7Im4iOiIyMDEyIiwidiI6IjIwMTIifSx7Im4iOiIyMDExIiwidiI6IjIwMTEifSx7Im4iOiIyMDEwIiwidiI6IjIwMTAifSx7Im4iOiIyMDA5IiwidiI6IjIwMDkifSx7Im4iOiIyMDA4IiwidiI6IjIwMDgifSx7Im4iOiIyMDA3IiwidiI6IjIwMDcifSx7Im4iOiIyMDA2IiwidiI6IjIwMDYifSx7Im4iOiIyMDA1IiwidiI6IjIwMDUifSx7Im4iOiIyMDA0IiwidiI6IjIwMDQifV19LHsia2V5IjoibGV0dGVyIiwibmFtZSI6IuWtl+avjSIsInZhbHVlIjpbeyJuIjoi5a2X5q+NIiwidiI6IiJ9LHsibiI6IkEiLCJ2IjoiQSJ9LHsibiI6IkIiLCJ2IjoiQiJ9LHsibiI6IkMiLCJ2IjoiQyJ9LHsibiI6IkQiLCJ2IjoiRCJ9LHsibiI6IkUiLCJ2IjoiRSJ9LHsibiI6IkYiLCJ2IjoiRiJ9LHsibiI6IkciLCJ2IjoiRyJ9LHsibiI6IkgiLCJ2IjoiSCJ9LHsibiI6IkkiLCJ2IjoiSSJ9LHsibiI6IkoiLCJ2IjoiSiJ9LHsibiI6IksiLCJ2IjoiSyJ9LHsibiI6IkwiLCJ2IjoiTCJ9LHsibiI6Ik0iLCJ2IjoiTSJ9LHsibiI6Ik4iLCJ2IjoiTiJ9LHsibiI6Ik8iLCJ2IjoiTyJ9LHsibiI6IlAiLCJ2IjoiUCJ9LHsibiI6IlEiLCJ2IjoiUSJ9LHsibiI6IlIiLCJ2IjoiUiJ9LHsibiI6IlMiLCJ2IjoiUyJ9LHsibiI6IlQiLCJ2IjoiVCJ9LHsibiI6IlUiLCJ2IjoiVSJ9LHsibiI6IlYiLCJ2IjoiViJ9LHsibiI6IlciLCJ2IjoiVyJ9LHsibiI6IlgiLCJ2IjoiWCJ9LHsibiI6IlkiLCJ2IjoiWSJ9LHsibiI6IloiLCJ2IjoiWiJ9LHsibiI6IjAtOSIsInYiOiIwLTkifV19LHsia2V5IjoiYnkiLCJuYW1lIjoi5o6S5bqPIiwidmFsdWUiOlt7Im4iOiLml7bpl7TmjpLluo8iLCJ2IjoidGltZSJ9LHsibiI6IuS6uuawlOaOkuW6jyIsInYiOiJoaXRzIn0seyJuIjoi6K+E5YiG5o6S5bqPIiwidiI6InNjb3JlIn1dfV0sCiAgICAgICAgICAgICI0IjpbeyJrZXkiOiJjbGFzcyIsIm5hbWUiOiLliafmg4UiLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiLmg4XmhJ8iLCJ2Ijoi5oOF5oSfIn0seyJuIjoi56eR5bm7IiwidiI6IuenkeW5uyJ9LHsibiI6IueDreihgCIsInYiOiLng63ooYAifSx7Im4iOiLmjqjnkIYiLCJ2Ijoi5o6o55CGIn0seyJuIjoi5pCe56yRIiwidiI6IuaQnueskSJ9LHsibiI6IuWGkumZqSIsInYiOiLlhpLpmakifSx7Im4iOiLokJ3ojokiLCJ2Ijoi6JCd6I6JIn0seyJuIjoi5qCh5ZutIiwidiI6IuagoeWbrSJ9LHsibiI6IuWKqOS9nCIsInYiOiLliqjkvZwifSx7Im4iOiLmnLrmiJgiLCJ2Ijoi5py65oiYIn0seyJuIjoi6L+Q5YqoIiwidiI6Iui/kOWKqCJ9LHsibiI6IuaImOS6iSIsInYiOiLmiJjkuokifSx7Im4iOiLlsJHlubQiLCJ2Ijoi5bCR5bm0In0seyJuIjoi5bCR5aWzIiwidiI6IuWwkeWlsyJ9LHsibiI6IuekvuS8miIsInYiOiLnpL7kvJoifSx7Im4iOiLljp/liJsiLCJ2Ijoi5Y6f5YibIn0seyJuIjoi5Lqy5a2QIiwidiI6IuS6suWtkCJ9LHsibiI6IuebiuaZuiIsInYiOiLnm4rmmboifSx7Im4iOiLlirHlv5ciLCJ2Ijoi5Yqx5b+XIn0seyJuIjoi5YW25LuWIiwidiI6IuWFtuS7liJ9XX0seyJrZXkiOiJhcmVhIiwibmFtZSI6IuWcsOWMuiIsInZhbHVlIjpbeyJuIjoi5YWo6YOoIiwidiI6IiJ9LHsibiI6IuWbveS6pyIsInYiOiLlm73kuqcifSx7Im4iOiLml6XmnKwiLCJ2Ijoi5pel5pysIn0seyJuIjoi5qyn576OIiwidiI6Iuasp+e+jiJ9LHsibiI6IuWFtuS7liIsInYiOiLlhbbku5YifV19LHsia2V5IjoibGFuZyIsIm5hbWUiOiLor63oqIAiLCJ2YWx1ZSI6W3sibiI6IuWFqOmDqCIsInYiOiIifSx7Im4iOiLlm73or60iLCJ2Ijoi5Zu96K+tIn0seyJuIjoi6Iux6K+tIiwidiI6IuiLseivrSJ9LHsibiI6IueypOivrSIsInYiOiLnsqTor60ifSx7Im4iOiLpl73ljZfor60iLCJ2Ijoi6Ze95Y2X6K+tIn0seyJuIjoi6Z+p6K+tIiwidiI6IumfqeivrSJ9LHsibiI6IuaXpeivrSIsInYiOiLml6Xor60ifSx7Im4iOiLlhbblroMiLCJ2Ijoi5YW25a6DIn1dfSx7ImtleSI6InllYXIiLCJuYW1lIjoi5bm05Lu9IiwidmFsdWUiOlt7Im4iOiLlhajpg6giLCJ2IjoiIn0seyJuIjoiMjAyNiIsInYiOiIyMDI2In0seyJuIjoiMjAyNSIsInYiOiIyMDI1In0seyJuIjoiMjAyNCIsInYiOiIyMDI0In0seyJuIjoiMjAyMyIsInYiOiIyMDIzIn0seyJuIjoiMjAyMiIsInYiOiIyMDIyIn0seyJuIjoiMjAyMSIsInYiOiIyMDIxIn0seyJuIjoiMjAyMCIsInYiOiIyMDIwIn0seyJuIjoiMjAxOSIsInYiOiIyMDE5In0seyJuIjoiMjAxOCIsInYiOiIyMDE4In0seyJuIjoiMjAxNyIsInYiOiIyMDE3In0seyJuIjoiMjAxNiIsInYiOiIyMDE2In0seyJuIjoiMjAxNSIsInYiOiIyMDE1In0seyJuIjoiMjAxNCIsInYiOiIyMDE0In0seyJuIjoiMjAxMyIsInYiOiIyMDEzIn0seyJuIjoiMjAxMiIsInYiOiIyMDEyIn0seyJuIjoiMjAxMSIsInYiOiIyMDExIn0seyJuIjoiMjAxMCIsInYiOiIyMDEwIn1dfSx7ImtleSI6ImxldHRlciIsIm5hbWUiOiLlrZfmr40iLCJ2YWx1ZSI6W3sibiI6IuWtl+avjSIsInYiOiIifSx7Im4iOiJBIiwidiI6IkEifSx7Im4iOiJCIiwidiI6IkIifSx7Im4iOiJDIiwidiI6IkMifSx7Im4iOiJEIiwidiI6IkQifSx7Im4iOiJFIiwidiI6IkUifSx7Im4iOiJGIiwidiI6IkYifSx7Im4iOiJHIiwidiI6IkcifSx7Im4iOiJIIiwidiI6IkgifSx7Im4iOiJJIiwidiI6IkkifSx7Im4iOiJKIiwidiI6IkoifSx7Im4iOiJLIiwidiI6IksifSx7Im4iOiJMIiwidiI6IkwifSx7Im4iOiJNIiwidiI6Ik0ifSx7Im4iOiJOIiwidiI6Ik4ifSx7Im4iOiJPIiwidiI6Ik8ifSx7Im4iOiJQIiwidiI6IlAifSx7Im4iOiJRIiwidiI6IlEifSx7Im4iOiJSIiwidiI6IlIifSx7Im4iOiJTIiwidiI6IlMifSx7Im4iOiJUIiwidiI6IlQifSx7Im4iOiJVIiwidiI6IlUifSx7Im4iOiJWIiwidiI6IlYifSx7Im4iOiJXIiwidiI6IlcifSx7Im4iOiJYIiwidiI6IlgifSx7Im4iOiJZIiwidiI6IlkifSx7Im4iOiJaIiwidiI6IloifSx7Im4iOiIwLTkiLCJ2IjoiMC05In1dfSx7ImtleSI6ImJ5IiwibmFtZSI6IuaOkuW6jyIsInZhbHVlIjpbeyJuIjoi5pe26Ze05o6S5bqPIiwidiI6InRpbWUifSx7Im4iOiLkurrmsJTmjpLluo8iLCJ2IjoiaGl0cyJ9LHsibiI6IuivhOWIhuaOkuW6jyIsInYiOiJzY29yZSJ9XX1dCiAgICAgICAgfTsKICAgICAgICBydWxlLmZpbHRlciA9IGZpbHRlcnM7CiAgICB9LAogICAgCiAgICDmjqjojZA6IGFzeW5jIGZ1bmN0aW9uKCkgeyByZXR1cm4gYXdhaXQgdGhpcy7kuIDnuqcoJycsIDEsIHt9LCB7fSk7IH0sCiAgICAKICAgIOS4gOe6pzogYXN5bmMgZnVuY3Rpb24odGlkLCBwZywgZmlsdGVyLCBleHRlbmQpIHsKICAgICAgICBsZXQgdXJsID0gdGhpcy5ob3N0ICsgJy9zaG93LycgKyB0aWQgKyAnLScgKyAoZXh0ZW5kLmFyZWF8fCcnKSArICctJyArIChleHRlbmQuYnl8fCd0aW1lJykgKyAnLScgKyAoZXh0ZW5kLmNsYXNzfHwnJykgKyAnLScgKyAoZXh0ZW5kLmxhbmd8fCcnKSArICctJyArIChleHRlbmQubGV0dGVyfHwnJykgKyAnLS0tJyArIHBnICsgJy0tLScgKyAoZXh0ZW5kLnllYXJ8fCcnKSArICcuaHRtbCc7CiAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCB0aGlzLnJlcXVlc3QodXJsKTsKICAgICAgICBsZXQgdmlkZW9zID0gW107CiAgICAgICAgbGV0IGl0ZW1zID0gcGRmYShodG1sLCAnLnB1YmxpYy1saXN0LWJveCcpOwogICAgICAgIGl0ZW1zLmZvckVhY2goZnVuY3Rpb24oaXRlbSkgewogICAgICAgICAgICBsZXQgdGl0bGUgPSBwZGZoKGl0ZW0sICcudGltZS10aXRsZSYmdGl0bGUnKSB8fCBwZGZoKGl0ZW0sICcudGltZS10aXRsZSYmVGV4dCcpOwogICAgICAgICAgICBsZXQgaW1nID0gcGRmaChpdGVtLCAnLmdlbi1tb3ZpZS1pbWcmJmRhdGEtc3JjJyk7CiAgICAgICAgICAgIGxldCByZW1hcmtzID0gcGRmaChpdGVtLCAnLnB1YmxpYy1saXN0LXN1YnRpdGxlJiZUZXh0Jyk7CiAgICAgICAgICAgIGxldCB1cmwgPSBwZGZoKGl0ZW0sICcucHVibGljLWxpc3QtZXhwJiZocmVmJyk7CiAgICAgICAgICAgIGlmICh1cmwpIHsKICAgICAgICAgICAgICAgIHVybCA9IHVybC5zdGFydHNXaXRoKCdodHRwJykgPyB1cmwgOiBydWxlLmhvc3QgKyB1cmw7CiAgICAgICAgICAgICAgICB2aWRlb3MucHVzaCh7CiAgICAgICAgICAgICAgICAgICAgdm9kX2lkOiB1cmwsCiAgICAgICAgICAgICAgICAgICAgdm9kX25hbWU6IHRpdGxlLAogICAgICAgICAgICAgICAgICAgIHZvZF9waWM6IGltZy5zdGFydHNXaXRoKCdodHRwJykgPyBpbWcgOiBydWxlLmhvc3QgKyBpbWcsCiAgICAgICAgICAgICAgICAgICAgdm9kX3JlbWFya3M6IHJlbWFya3MKICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIHZpZGVvczsKICAgIH0sCgogICAg5LqM57qnOiBhc3luYyBmdW5jdGlvbihpZHMpIHsKICAgICAgICBsZXQgaHRtbCA9IGF3YWl0IHRoaXMucmVxdWVzdChpZHNbMF0pOwogICAgICAgIGxldCB2b2QgPSB7CiAgICAgICAgICAgIHZvZF9pZDogaWRzWzBdLAogICAgICAgICAgICB2b2RfbmFtZTogcGRmaChodG1sLCAnLnNsaWRlLWluZm8tdGl0bGUmJlRleHQnKSB8fCBwZGZoKGh0bWwsICdoMSYmVGV4dCcpLAogICAgICAgICAgICB2b2RfcGljOiBwZGZoKGh0bWwsICcuZGV0YWlsLXBpYyBpbWcmJmRhdGEtc3JjJykgfHwgcGRmaChodG1sLCAnLmRldGFpbC1waWMgaW1nJiZzcmMnKSwKICAgICAgICAgICAgdm9kX2NvbnRlbnQ6IHBkZmgoaHRtbCwgJyNoZWlnaHRfbGltaXQmJlRleHQnKS50cmltKCksCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206ICcnLCB2b2RfcGxheV91cmw6ICcnCiAgICAgICAgfTsKICAgICAgICBsZXQgaW5mb3MgPSBwZGZhKGh0bWwsICcuc2xpZGUtaW5mbycpOwogICAgICAgIGluZm9zLmZvckVhY2goZnVuY3Rpb24oaW5mbykgewogICAgICAgICAgICBsZXQgdGV4dCA9IHBkZmgoaW5mbywgJ2JvZHkmJlRleHQnKTsKICAgICAgICAgICAgaWYgKHRleHQuaW5jbHVkZXMoJ+Wkh+azqCcpKSB2b2Qudm9kX3JlbWFya3MgPSB0ZXh0LnNwbGl0KCc6JylbMV0udHJpbSgpOwogICAgICAgICAgICBlbHNlIGlmICh0ZXh0LmluY2x1ZGVzKCflr7zmvJQnKSkgdm9kLnZvZF9kaXJlY3RvciA9IHRleHQuc3BsaXQoJzonKVsxXS50cmltKCk7CiAgICAgICAgICAgIGVsc2UgaWYgKHRleHQuaW5jbHVkZXMoJ+a8lOWRmCcpKSB2b2Qudm9kX2FjdG9yID0gdGV4dC5zcGxpdCgnOicpWzFdLnRyaW0oKTsKICAgICAgICAgICAgZWxzZSBpZiAodGV4dC5pbmNsdWRlcygn5pu05pawJykpIHsgbGV0IHkgPSB0ZXh0Lm1hdGNoKC9cZHs0fS8pOyBpZiAoeSkgdm9kLnZvZF95ZWFyID0geVswXTsgfQogICAgICAgIH0pOwogICAgICAgIGlmICghdm9kLnZvZF95ZWFyKSB7IGxldCB5ID0gcGRmaChodG1sLCAnLnNsaWRlLWluZm8tcmVtYXJrcyBhW2hyZWYqPSIyMDIiXSYmVGV4dCcpOyBpZih5KSB2b2Qudm9kX3llYXIgPSB5OyB9CiAgICAgICAgdm9kLnZvZF9hcmVhID0gcGRmaChodG1sLCAnLnNsaWRlLWluZm8tcmVtYXJrcyBhW2hyZWYqPSLlhoXlnLAiXSYmVGV4dCcpIHx8IHBkZmgoaHRtbCwgJy5zbGlkZS1pbmZvLXJlbWFya3MgYVtocmVmKj0i576O5Zu9Il0mJlRleHQnKTsKICAgICAgICBsZXQgcGxheUZyb20gPSBbXTsKICAgICAgICBsZXQgcGxheVVybCA9IFtdOwogICAgICAgIGxldCB0YWJzID0gcGRmYShodG1sLCAnLmFudGhvbG9neS10YWIgLnN3aXBlci1zbGlkZScpOwogICAgICAgIGxldCBsaXN0cyA9IHBkZmEoaHRtbCwgJy5hbnRob2xvZ3ktbGlzdC1ib3gnKTsKICAgICAgICBpZiAodGFicy5sZW5ndGggPT09IDAgJiYgbGlzdHMubGVuZ3RoID4gMCkgdGFicyA9IFt7bmFtZTon5pKt5pS+57q/6LevJ31dOwogICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGlzdHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgbGV0IHRhYk5hbWUgPSAn57q/6LevJyArIChpKzEpOwogICAgICAgICAgICBpZiAoaSA8IHRhYnMubGVuZ3RoICYmIHRhYnNbaV0ubmFtZSAhPT0gJ+aSreaUvue6v+i3rycpIHsKICAgICAgICAgICAgICAgIGxldCB0YWJIdG1sID0gdGFic1tpXTsKICAgICAgICAgICAgICAgIGxldCBmcm9tQXR0ciA9IHBkZmgodGFiSHRtbCwgJ2EmJmRhdGEtZm9ybScpIHx8IHBkZmgodGFiSHRtbCwgJ2EmJmRhdGEtZnJvbScpOwogICAgICAgICAgICAgICAgbGV0IHRleHQgPSBwZGZoKHRhYkh0bWwsICdib2R5JiZUZXh0Jyk7CiAgICAgICAgICAgICAgICB0ZXh0ID0gdGV4dC5yZXBsYWNlKC9cZCskLywgJycpLnRyaW0oKTsKICAgICAgICAgICAgICAgIHRhYk5hbWUgPSB0ZXh0IHx8IGZyb21BdHRyIHx8IHRhYk5hbWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcGxheUZyb20ucHVzaCh0YWJOYW1lKTsKICAgICAgICAgICAgbGV0IHVybHMgPSBbXTsKICAgICAgICAgICAgbGV0IGl0ZW1zID0gcGRmYShsaXN0c1tpXSwgJy5hbnRob2xvZ3ktbGlzdC1wbGF5IGEnKTsKICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpdGVtcy5sZW5ndGg7IGorKykgewogICAgICAgICAgICAgICAgbGV0IGl0ZW1IdG1sID0gaXRlbXNbal07CiAgICAgICAgICAgICAgICBsZXQgbmFtZSA9IHBkZmgoaXRlbUh0bWwsICdhJiZUZXh0Jyk7CiAgICAgICAgICAgICAgICBsZXQgaHJlZiA9IHBkZmgoaXRlbUh0bWwsICdhJiZocmVmJyk7CiAgICAgICAgICAgICAgICBpZiAoIWhyZWYgfHwgaHJlZiA9PT0gJyMnIHx8IGhyZWYudG9Mb3dlckNhc2UoKS5zdGFydHNXaXRoKCdqYXZhc2NyaXB0JykgfHwgaHJlZi5sZW5ndGggPCAzKSBjb250aW51ZTsKICAgICAgICAgICAgICAgIGlmICghbmFtZSkgewogICAgICAgICAgICAgICAgICAgIGxldCBtYXRjaCA9IGhyZWYubWF0Y2goLy0oXGQrKVwuaHRtbCQvKTsKICAgICAgICAgICAgICAgICAgICBuYW1lID0gbWF0Y2ggPyAn56ysJyArIG1hdGNoWzFdICsgJ+mbhicgOiAn56ysJyArIChqICsgMSkgKyAn6ZuGJzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGxldCBmdWxsVXJsID0gaHJlZi5zdGFydHNXaXRoKCdodHRwJykgPyBocmVmIDogcnVsZS5ob3N0ICsgaHJlZjsKICAgICAgICAgICAgICAgIHVybHMucHVzaChuYW1lICsgJyQnICsgZnVsbFVybCArICdAJyArIHRhYk5hbWUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHBsYXlVcmwucHVzaCh1cmxzLmpvaW4oJyMnKSk7CiAgICAgICAgfQogICAgICAgIHZvZC52b2RfcGxheV9mcm9tID0gcGxheUZyb20uam9pbignJCQkJyk7CiAgICAgICAgdm9kLnZvZF9wbGF5X3VybCA9IHBsYXlVcmwuam9pbignJCQkJyk7CiAgICAgICAgcmV0dXJuIHZvZDsKICAgIH0sCgogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbihrZXksIHF1aWNrLCBwZykgewogICAgICAgIGxldCB1cmwgPSB0aGlzLmhvc3QgKyAnL25rLy0tLS0tLS0tLS0tLS0uaHRtbD93ZD0nICsgZW5jb2RlVVJJQ29tcG9uZW50KGtleSk7CiAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCB0aGlzLnJlcXVlc3QodXJsKTsKICAgICAgICBsZXQgdmlkZW9zID0gW107CiAgICAgICAgbGV0IGl0ZW1zID0gcGRmYShodG1sLCAnLnB1YmxpYy1saXN0LWJveC5zZWFyY2gtYm94Jyk7CiAgICAgICAgaXRlbXMuZm9yRWFjaChmdW5jdGlvbihpdGVtKSB7CiAgICAgICAgICAgIGxldCB0aXRsZSA9IHBkZmgoaXRlbSwgJy50aHVtYi10eHQgYSYmVGV4dCcpIHx8IHBkZmgoaXRlbSwgJy50aW1lLXRpdGxlJiZUZXh0Jyk7CiAgICAgICAgICAgIGxldCBpbWcgPSBwZGZoKGl0ZW0sICcuZ2VuLW1vdmllLWltZyYmZGF0YS1zcmMnKTsKICAgICAgICAgICAgbGV0IHJlbWFya3MgPSBwZGZoKGl0ZW0sICcucHVibGljLWxpc3QtcHJiJiZUZXh0JykgfHwgcGRmaChpdGVtLCAnLnB1YmxpYy1saXN0LXN1YnRpdGxlJiZUZXh0Jyk7CiAgICAgICAgICAgIGxldCB1cmwgPSBwZGZoKGl0ZW0sICcucHVibGljLWxpc3QtZXhwJiZocmVmJyk7CiAgICAgICAgICAgIGlmICh1cmwpIHsKICAgICAgICAgICAgICAgIHVybCA9IHVybC5zdGFydHNXaXRoKCdodHRwJykgPyB1cmwgOiBydWxlLmhvc3QgKyB1cmw7CiAgICAgICAgICAgICAgICB2aWRlb3MucHVzaCh7CiAgICAgICAgICAgICAgICAgICAgdm9kX2lkOiB1cmwsCiAgICAgICAgICAgICAgICAgICAgdm9kX25hbWU6IHRpdGxlLAogICAgICAgICAgICAgICAgICAgIHZvZF9waWM6IGltZy5zdGFydHNXaXRoKCdodHRwJykgPyBpbWcgOiBydWxlLmhvc3QgKyBpbWcsCiAgICAgICAgICAgICAgICAgICAgdm9kX3JlbWFya3M6IHJlbWFya3MKICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIHZpZGVvczsKICAgIH0sCgogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24oZmxhZywgaWQsIGZsYWdzKSB7CiAgICAgICAgdHJ5IHsKICAgICAgICAgICAgbGV0IHBhcnRzID0gaWQuc3BsaXQoJ0AnKTsKICAgICAgICAgICAgbGV0IHBsYXlVcmwgPSBwYXJ0c1swXTsKICAgICAgICAgICAgaWYgKHBsYXlVcmwgPT09IHRoaXMuaG9zdCB8fCBwbGF5VXJsID09PSB0aGlzLmhvc3QgKyAnLycpIHJldHVybiB7IHBhcnNlOiAxLCB1cmw6IHBsYXlVcmwgfTsKICAgICAgICAgICAgbGV0IGh0bWwgPSBhd2FpdCB0aGlzLnJlcXVlc3QocGxheVVybCwge2hlYWRlcnM6IHtSZWZlcmVyOiBwbGF5VXJsfX0pOwogICAgICAgICAgICBsZXQgc3RhcnQgPSAndmFyIHBsYXllcl9hYWFhPSc7CiAgICAgICAgICAgIGxldCBpZHggPSBodG1sLmluZGV4T2Yoc3RhcnQpOwogICAgICAgICAgICBpZiAoaWR4ID09PSAtMSkgcmV0dXJuIHsgcGFyc2U6IDEsIHVybDogcGxheVVybCB9OwogICAgICAgICAgICBsZXQganNvblN0ciA9IGh0bWwuc3Vic3RyaW5nKGlkeCArIHN0YXJ0Lmxlbmd0aCk7CiAgICAgICAgICAgIGxldCBlbmRJZHggPSBqc29uU3RyLmluZGV4T2YoJzwvc2NyaXB0PicpOwogICAgICAgICAgICBpZiAoZW5kSWR4ICE9PSAtMSkganNvblN0ciA9IGpzb25TdHIuc3Vic3RyaW5nKDAsIGVuZElkeCk7CiAgICAgICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShqc29uU3RyLnRyaW0oKS5yZXBsYWNlKC87JC8sICcnKSk7CiAgICAgICAgICAgIGlmIChqc29uLnVybCAmJiAvbTN1OHxtcDQvaS50ZXN0KGpzb24udXJsKSkgcmV0dXJuIHsgcGFyc2U6IDAsIHVybDoganNvbi51cmwgfTsKICAgICAgICAgICAgbGV0IGNmZyA9IChhd2FpdCByZXEodGhpcy5ob3N0ICsgJy9zdGF0aWMvanMvcGxheWVyY29uZmlnLmpzP3Q9MjAyNTEyMjUnLCB7aGVhZGVyczp0aGlzLmhlYWRlcnN9KSkuY29udGVudDsKICAgICAgICAgICAgbGV0IHBhcnNlRG9tYWluID0gJ2h0dHBzOi8vZ2cueG4tLWl0LWlmN2MxOWc1czRicHM1Yy5jb20vbmt2b2QzLnBocCc7CiAgICAgICAgICAgIGxldCBqeCA9IHBhcnNlRG9tYWluICsgKHBhcnNlRG9tYWluLmluY2x1ZGVzKCc/JykgPyAnJicgOiAnPycpICsgJ3VybD0nICsgZW5jb2RlVVJJQ29tcG9uZW50KGpzb24udXJsKSArICcmbmV4dD0nICsgZW5jb2RlVVJJQ29tcG9uZW50KGpzb24ubGlua19uZXh0fHwnJykgKyAnJnRpdGxlPScgKyBlbmNvZGVVUklDb21wb25lbnQoanNvbi52b2RfZGF0YT8udm9kX25hbWV8fCcnKTsKICAgICAgICAgICAgbGV0IGp4SHRtbCA9IChhd2FpdCByZXEoangsIHtoZWFkZXJzOiB7UmVmZXJlcjogcGxheVVybCwgJ1VzZXItQWdlbnQnOiB0aGlzLmhlYWRlcnNbJ1VzZXItQWdlbnQnXX19KSkuY29udGVudDsKICAgICAgICAgICAgbGV0IGtleU1hdGNoID0ganhIdG1sLm1hdGNoKC92YXJccytyYXdfa2V5XHMqPVxzKlxbKC4qPylcXS8pOwogICAgICAgICAgICBsZXQgaXZNYXRjaCA9IGp4SHRtbC5tYXRjaCgvdmFyXHMraXZccyo9XHMqXFsoLio/KVxdLyk7CiAgICAgICAgICAgIGxldCBlbmNNYXRjaCA9IGp4SHRtbC5tYXRjaCgvdmFyXHMrZW5jcnlwdGVkXHMqPVxzKlsiJ10oLio/KVsiJ10vKTsKICAgICAgICAgICAgaWYgKCFrZXlNYXRjaCB8fCAhaXZNYXRjaCB8fCAhZW5jTWF0Y2gpIHJldHVybiB7IHBhcnNlOiAxLCB1cmw6IHBsYXlVcmwgfTsKICAgICAgICAgICAgbGV0IGtleSA9IGtleU1hdGNoWzFdLnNwbGl0KCcsJykubWFwKE51bWJlcik7CiAgICAgICAgICAgIGxldCBpdiA9IGl2TWF0Y2hbMV0uc3BsaXQoJywnKS5tYXAoTnVtYmVyKTsKICAgICAgICAgICAgbGV0IGVuYyA9IGVuY01hdGNoWzFdOwogICAgICAgICAgICBsZXQgZGVjID0gdGhpcy5DcnlwdG9Ub29sLmRlY3J5cHREYXRhKGVuYywga2V5LCBpdik7CiAgICAgICAgICAgIGlmKCFkZWMpIHJldHVybiB7IHBhcnNlOiAxLCB1cmw6IHBsYXlVcmwgfTsKICAgICAgICAgICAgbGV0IGZpbmFsID0gJyc7CiAgICAgICAgICAgIGxldCByYW5kb21NYXRjaCA9IGRlYy5tYXRjaCgvZ2V0cmFuZG9tXChbJyJdKC4qPylbJyJdXCkvKTsKICAgICAgICAgICAgaWYgKHJhbmRvbU1hdGNoKSB7CiAgICAgICAgICAgICAgICBmaW5hbCA9IHRoaXMuQ3J5cHRvVG9vbC5nZXRyYW5kb20ocmFuZG9tTWF0Y2hbMV0pOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgbGV0IHVybE1hdGNoID0gZGVjLm1hdGNoKC9odHRwcz86XC9cL1teXHMiJzw+XSsvKTsKICAgICAgICAgICAgICAgIGlmKHVybE1hdGNoKSBmaW5hbCA9IHVybE1hdGNoWzBdOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB7IHBhcnNlOiAwLCB1cmw6IGZpbmFsLCBoZWFkZXI6IHsgJ1VzZXItQWdlbnQnOiB0aGlzLmhlYWRlcnNbJ1VzZXItQWdlbnQnXSB9IH07CiAgICAgICAgfSBjYXRjaCAoZSkgeyByZXR1cm4geyBwYXJzZTogMSwgdXJsOiBpZCB9OyB9CiAgICB9Cn07Cg== \ No newline at end of file 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 0a607edd..49c5cf27 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" @@ -401,6 +401,8 @@ var rule = { d.push(getInput('get_enable_dr2', '查看drpy2源启用状态', images.settings)); d.push(genMultiInput('enable_py', '设置py源启用状态', '设置为1可启用此功能,设置为2启用T4(默认没设置也属于启动,设置其他值关闭)', images.settings)); d.push(getInput('get_enable_py', '查看py源启用状态', images.settings)); + d.push(genMultiInput('enable_php', '设置php源启用状态', '设置为1可启用此功能,设置为0可关闭(1-T4|2-T3 默认没设置也属于启动1,设置其他值关闭)', images.settings)); + d.push(getInput('get_enable_php', '查看php源启用状态', images.settings)); d.push(genMultiInput('enable_cat', '设置cat源启用状态', '设置为1可启用此功能,设置为2启用T4(默认没设置也属于启动,设置其他值关闭)', images.settings)); d.push(getInput('get_enable_cat', '查看cat源启用状态', images.settings)); d.push(genMultiInput('enable_old_config', '设置兼容性配置', '设置为1可启用此功能(默认关闭)', images.settings)); @@ -457,6 +459,9 @@ var rule = { d.push(genMultiInput('PROXY_AUTH', '设置代理播放授权', '默认为drpys,可自行配置成其他值', images.settings)); d.push(getInput('get_PROXY_AUTH', '查看代理播放授权', images.settings)); + + d.push(genMultiInput('enable_self_jx', '设置启用自建解析', '默认为关闭,可自行配置成其他值(0关闭 1启用)', images.settings)); + d.push(getInput('get_enable_self_jx', '查看启用自建解析', images.settings)); break; } return d @@ -1272,6 +1277,7 @@ var rule = { 'play_proxy_mode', 'enable_dr2', 'enable_py', + 'enable_php', 'enable_cat', 'enable_old_config', 'enable_rule_name', @@ -1290,6 +1296,7 @@ var rule = { 'must_sub_code', 'mg_hz', 'PROXY_AUTH', + 'enable_self_jx', ]; let get_cookie_sets = [ 'get_quark_cookie', @@ -1306,6 +1313,7 @@ var rule = { 'get_play_proxy_mode', 'get_enable_dr2', 'get_enable_py', + 'get_enable_php', 'get_enable_cat', 'get_enable_old_config', 'get_enable_rule_name', @@ -1324,6 +1332,7 @@ var rule = { 'get_must_sub_code', 'get_mg_hz', 'get_PROXY_AUTH', + 'get_enable_self_jx', ]; if (cookie_sets.includes(action) && value) { try { diff --git "a/spider/js/\350\264\235\344\271\220\350\231\216[\345\204\277].js" "b/spider/js/\350\264\235\344\271\220\350\231\216[\345\204\277].js" index b64e521b..08fb3ba4 100644 --- "a/spider/js/\350\264\235\344\271\220\350\231\216[\345\204\277].js" +++ "b/spider/js/\350\264\235\344\271\220\350\231\216[\345\204\277].js" @@ -5,6 +5,10 @@ quickSearch: 0, title: '贝乐虎[儿]', '类型': '影视', + mergeList: 1, + more: { + mergeList: 1 + }, lang: 'ds' }) */ @@ -12,6 +16,7 @@ var rule = { 类型: '影视',//影视|听书|漫画|小说 title: '贝乐虎[儿]', + mergeList: 1, host: 'https://vd.ubestkid.com', url: '/api/v1/bv/video#pg=fypage', homeUrl: '/api/v1/bv/video', diff --git "a/spider/js/\350\277\275\346\226\260\345\275\261\350\247\206[\344\274\230].js" "b/spider/js/\350\277\275\346\226\260\345\275\261\350\247\206[\344\274\230].js" new file mode 100644 index 00000000..03dd5afa --- /dev/null +++ "b/spider/js/\350\277\275\346\226\260\345\275\261\350\247\206[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '追新影视', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfov73mlrDlvbHop4YnLAogICAgaG9zdDogJ2h0dHA6Ly96eHlzLmdhb3pob3Vrai5jbicsCiAgICBob21lVXJsOiAnaHR0cDovL3p4eXMuZ2FvemhvdWtqLmNuJywKICAgIHVybDogJy9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVMaXN0JnR5cGVfaWQ9ZnljbGFzcyZwYWdlPWZ5cGFnZSZsaW1pdD0xOCcsCiAgICBzZWFyY2hVcmw6ICcvcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuU2VhcmNoVm9kJmtleT0qKicsCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDAsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogMTAwMDAsCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAga2V5OiAnJywKICAgIGl2OiAnMTIzNDU2Nzg5MDEyMzQ1NicsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiAnb2todHRwLzMuMTIuMCcsCiAgICAgICAgJ0Nvbm5lY3Rpb24nOiAnS2VlcC1BbGl2ZScsCiAgICAgICAgJ0FjY2VwdC1FbmNvZGluZyc6ICdnemlwJwogICAgfSwKICAgIAogICAgY2xhc3NfcGFyc2U6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBpZiAoIXRoaXMuaG9zdCkgcmV0dXJuIHsgY2xhc3M6IFtdLCBmaWx0ZXJzOiB7fSB9OwogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIGNsYXNzOiBbCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzEnLCAndHlwZV9uYW1lJzogJ+eUteW9sScgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnMicsICd0eXBlX25hbWUnOiAn6L+e57ut5YmnJyB9LAogICAgICAgICAgICAgICAgeyAndHlwZV9pZCc6ICczJywgJ3R5cGVfbmFtZSc6ICfnu7zoibonIH0sCiAgICAgICAgICAgICAgICB7ICd0eXBlX2lkJzogJzQnLCAndHlwZV9uYW1lJzogJ+WKqOa8qycgfQogICAgICAgICAgICBdLAogICAgICAgICAgICBmaWx0ZXJzOiB7fQogICAgICAgIH07CiAgICB9LAoKICAgIOmihOWkhOeQhjogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCByZXNwMSA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldFR5cGVMaXN0YCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgICAgIGxldCB0ZXh0MSA9IGF3YWl0IHJlc3AxLnRleHQoKTsKICAgICAgICAgICAgaWYgKHRleHQxLnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0MSA9IHRleHQxLnN1YnN0cmluZygxKTsKICAgICAgICAgICAgbGV0IGRhdGExID0gSlNPTi5wYXJzZSh0ZXh0MSk7CiAgICAgICAgICAgIGxldCBzaWduX3N0YXJ0ID0gKGRhdGExLkRhdGEgfHwgW10pLmZpbmQoaSA9PiBpLnR5cGVfaWQudG9TdHJpbmcoKSA9PT0gJzEnKT8udHlwZV91bmlvbiB8fCAnJzsKICAgICAgICAgICAgbGV0IHJlc3AyID0gYXdhaXQgX2ZldGNoKGAke3RoaXMuaG9zdH0vcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuR2V0QWRUeXBlYCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgICAgIGxldCB0ZXh0MiA9IGF3YWl0IHJlc3AyLnRleHQoKTsKICAgICAgICAgICAgaWYgKHRleHQyLnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0MiA9IHRleHQyLnN1YnN0cmluZygxKTsKICAgICAgICAgICAgbGV0IGRhdGEyID0gSlNPTi5wYXJzZSh0ZXh0Mik7CiAgICAgICAgICAgIGxldCBzaWduX2VuZCA9IGRhdGEyLkRhdGEudG1wIHx8ICcnOwogICAgICAgICAgICBsZXQgZnVsbEtleSA9IHNpZ25fc3RhcnQgKyBzaWduX2VuZDsKICAgICAgICAgICAgaWYgKGZ1bGxLZXkubGVuZ3RoID49IDE2KSB7CiAgICAgICAgICAgICAgICB0aGlzLmtleSA9IGZ1bGxLZXkuc3Vic3RyaW5nKDAsIDE2KTsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgY29uc29sZS5lcnJvcign5Yid5aeL5YyWIEtleSDlpLHotKU6JywgZSk7CiAgICAgICAgICAgIHRoaXMuaG9zdCA9ICcnOwogICAgICAgIH0KICAgIH0sCiAgICAKICAgIOaOqOiNkDogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGlmICghdGhpcy5ob3N0KSByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldEhvbWVMZXZlbGAsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgaWYgKHRleHQuc3RhcnRzV2l0aCgn77u/JykpIHRleHQgPSB0ZXh0LnN1YnN0cmluZygxKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UodGV4dCk7CiAgICAgICAgCiAgICAgICAgbGV0IHZpZGVvcyA9IHR5cGVvZiBkYXRhID09PSAnb2JqZWN0JyAmJiBkYXRhICE9PSBudWxsID8gCiAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZGF0YSkKICAgICAgICAgICAgICAgIC5maWx0ZXIoaXRlbSA9PiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiYgaXRlbSAhPT0gbnVsbCkKICAgICAgICAgICAgICAgIC5mbGF0TWFwKGl0ZW0gPT4gT2JqZWN0LnZhbHVlcyhpdGVtKSkKICAgICAgICAgICAgICAgIC5maWx0ZXIoQXJyYXkuaXNBcnJheSkKICAgICAgICAgICAgICAgIC5mbGF0TWFwKGxpc3QgPT4gbGlzdC5tYXAoayA9PiAoewogICAgICAgICAgICAgICAgICAgIHRpdGxlOiBrLnZvZF9uYW1lLAogICAgICAgICAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVNdkJ5SWQmdm9kaWQ9JHtrLnZvZF9pZH1gLAogICAgICAgICAgICAgICAgICAgIGRlc2M6IGsudm9kX3JlbWFya3MsCiAgICAgICAgICAgICAgICAgICAgcGljX3VybDogay52b2RfcGljLAogICAgICAgICAgICAgICAgICAgIHZvZF95ZWFyOiBrLnZvZF95ZWFyLAogICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6IGsudm9kX2NvbnRlbnQKICAgICAgICAgICAgICAgIH0pKSkKICAgICAgICAgICAgOiBbXTsKICAgICAgICAKICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGlmICghdGhpcy5ob3N0KSByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVMaXN0JnR5cGVfaWQ9JHt0aWR9JnBhZ2U9JHtwZ30mbGltaXQ9MThgOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgbGV0IHRleHQgPSBhd2FpdCByZXNwLnRleHQoKTsKICAgICAgICBpZiAodGV4dC5zdGFydHNXaXRoKCfvu78nKSkgdGV4dCA9IHRleHQuc3Vic3RyaW5nKDEpOwogICAgICAgIGxldCBkYXRhID0gSlNPTi5wYXJzZSh0ZXh0KTsKICAgICAgICBsZXQgdmlkZW9zID0gKGRhdGEuRGF0YSB8fCBbXSkubWFwKGkgPT4gKHsKICAgICAgICAgICAgdGl0bGU6IGkudm9kX25hbWUsCiAgICAgICAgICAgIHVybDogYCR7dGhpcy5ob3N0fS9wdWJsaWMvP3NlcnZpY2U9QXBwLk1vdi5HZXRPbmxpbmVNdkJ5SWQmdm9kaWQ9JHtpLnZvZF9pZH1gLAogICAgICAgICAgICBkZXNjOiBpLnZvZF9yZW1hcmtzLAogICAgICAgICAgICBwaWNfdXJsOiBpLnZvZF9waWMsCiAgICAgICAgICAgIHZvZF95ZWFyOiBpLnZvZF95ZWFyLAogICAgICAgICAgICBjb250ZW50OiBpLnZvZF9jb250ZW50CiAgICAgICAgfSkpOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQodmlkZW9zKTsKICAgIH0sCiAgICAKICAgIOS6jOe6pzogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB2b2RJZCA9IHRoaXMuaW5wdXQubWF0Y2goL3ZvZGlkPShcZCspLylbMV07CiAgICAgICAgbGV0IHVybCA9IGAke3RoaXMuaG9zdH0vcHVibGljLz9zZXJ2aWNlPUFwcC5Nb3YuR2V0T25saW5lTXZCeUlkJnZvZGlkPSR7dm9kSWR9YDsKICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaCh1cmwsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgaWYgKHRleHQuc3RhcnRzV2l0aCgn77u/JykpIHRleHQgPSB0ZXh0LnN1YnN0cmluZygxKTsKICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UodGV4dCk7CiAgICAgICAgbGV0IGZpcnN0SXRlbSA9IChkYXRhLkRhdGEgfHwgW10pLmZpbmQoaSA9PiB0eXBlb2YgaSA9PT0gJ29iamVjdCcgJiYgaSAhPT0gbnVsbCk7CiAgICAgICAgCiAgICAgICAgaWYgKGZpcnN0SXRlbSkgewogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgdm9kX2lkOiBmaXJzdEl0ZW0udm9kX2lkLnRvU3RyaW5nKCksCiAgICAgICAgICAgICAgICB2b2RfbmFtZTogZmlyc3RJdGVtLnZvZF9uYW1lLAogICAgICAgICAgICAgICAgdm9kX3BpYzogZmlyc3RJdGVtLnZvZF9waWMsCiAgICAgICAgICAgICAgICB2b2RfcmVtYXJrczogZmlyc3RJdGVtLnZvZF9yZW1hcmtzLAogICAgICAgICAgICAgICAgdm9kX3llYXI6IGZpcnN0SXRlbS52b2RfeWVhciwKICAgICAgICAgICAgICAgIHZvZF9hcmVhOiBmaXJzdEl0ZW0udm9kX2FyZWEsCiAgICAgICAgICAgICAgICB2b2RfYWN0b3I6IGZpcnN0SXRlbS52b2RfYWN0b3IsCiAgICAgICAgICAgICAgICB2b2RfY29udGVudDogZmlyc3RJdGVtLnZvZF9jb250ZW50LAogICAgICAgICAgICAgICAgdm9kX3BsYXlfZnJvbTogZmlyc3RJdGVtLnZvZF9wbGF5X2Zyb20sCiAgICAgICAgICAgICAgICB2b2RfcGxheV91cmw6IGZpcnN0SXRlbS52b2RfcGxheV91cmwsCiAgICAgICAgICAgICAgICB0eXBlX25hbWU6IGZpcnN0SXRlbS52b2RfY2xhc3MKICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgcmV0dXJuIHt9OwogICAgfSwKICAgIAogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgaWYgKCF0aGlzLmhvc3QpIHJldHVybiBzZXRSZXN1bHQoW10pOwogICAgICAgIGxldCB1cmwgPSBgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LlNlYXJjaFZvZCZrZXk9JHtlbmNvZGVVUklDb21wb25lbnQodGhpcy5LRVkpfWA7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godXJsLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICBsZXQgdGV4dCA9IGF3YWl0IHJlc3AudGV4dCgpOwogICAgICAgIGlmICh0ZXh0LnN0YXJ0c1dpdGgoJ++7vycpKSB0ZXh0ID0gdGV4dC5zdWJzdHJpbmcoMSk7CiAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKHRleHQpOwogICAgICAgIGxldCB2aWRlb3MgPSAoZGF0YS5EYXRhIHx8IFtdKS5tYXAoaSA9PiAoewogICAgICAgICAgICB0aXRsZTogaS52b2RfbmFtZSwKICAgICAgICAgICAgdXJsOiBgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldE9ubGluZU12QnlJZCZ2b2RpZD0ke2kudm9kX2lkfWAsCiAgICAgICAgICAgIGRlc2M6IGkudm9kX3JlbWFya3MsCiAgICAgICAgICAgIHBpY191cmw6IGkudm9kX3BpYywKICAgICAgICAgICAgdm9kX3llYXI6IGkudm9kX3llYXIsCiAgICAgICAgICAgIGNvbnRlbnQ6IGkudm9kX2NvbnRlbnQKICAgICAgICB9KSk7CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdCh2aWRlb3MpOwogICAgfSwKICAgIAogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBqeCA9IDA7CiAgICAgICAgbGV0IHVybCA9ICcnOwogICAgICAgIGxldCB1YSA9ICdjb20uZ2prai56eHlzZHEvMS4xLjAgKExpbnV4O0FuZHJvaWQgMTIpIEV4b1BsYXllckxpYi8yLjEyLjMnOwogICAgICAgIGxldCBpZCA9IHRoaXMuaW5wdXQ7CiAgICAgICAgaWYgKGlkLm1hdGNoKC9eaHR0cHM/OlwvXC8uKlwuKG0zdTh8bXA0fGZsdnxta3YpL2kpKSB7CiAgICAgICAgICAgIHVybCA9IGlkOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBsZXQgcmVzcCA9IGF3YWl0IF9mZXRjaChgJHt0aGlzLmhvc3R9L3B1YmxpYy8/c2VydmljZT1BcHAuTW92LkdldE12SlhVcmxCeVVybCZ1cmw9JHtpZH1gLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KTsKICAgICAgICAgICAgICAgIGxldCB0ZXh0ID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgICAgICAgICBpZiAodGV4dC5zdGFydHNXaXRoKCfvu78nKSkgdGV4dCA9IHRleHQuc3Vic3RyaW5nKDEpOwogICAgICAgICAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKHRleHQpOwogICAgICAgICAgICAgICAgbGV0IHJhd191cmwgPSBkYXRhLkRhdGEudXJsOwogICAgICAgICAgICAgICAgLy8g5bCd6K+V5L2/55SoQ3J5cHRvSlPov5vooYxBRVPop6Plr4YKICAgICAgICAgICAgICAgIGlmICh0aGlzLmtleSkgewogICAgICAgICAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBrZXkgPSBDcnlwdG9KUy5lbmMuVXRmOC5wYXJzZSh0aGlzLmtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBpdiA9IENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKHRoaXMuaXYpOwogICAgICAgICAgICAgICAgICAgICAgICBsZXQgZGVjcnlwdGVkVXJsID0gQ3J5cHRvSlMuQUVTLmRlY3J5cHQocmF3X3VybCwga2V5LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGU6IENyeXB0b0pTLm1vZGUuQ0JDLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkZGluZzogQ3J5cHRvSlMucGFkLlBrY3M1CiAgICAgICAgICAgICAgICAgICAgICAgIH0pLnRvU3RyaW5nKENyeXB0b0pTLmVuYy5VdGY4KTsKICAgICAgICAgICAgICAgICAgICAgICAgdXJsID0gZGVjcnlwdGVkVXJsICYmIGRlY3J5cHRlZFVybC5zdGFydHNXaXRoKCdodHRwJykgPyBkZWNyeXB0ZWRVcmwgOiB1cmw7CiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdBRVPop6Plr4blpLHotKU6JywgZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBpZiAoLyg/Ond3d1wuaXFpeWl8dlwucXF8dlwueW91a3V8d3d3XC5tZ3R2fHd3d1wuYmlsaWJpbGkpXC5jb20vLnRlc3QoaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gaWQ7CiAgICAgICAgICAgICAgICAgICAganggPSAxOwogICAgICAgICAgICAgICAgICAgIHVhID0gTU9CSUxFX1VBOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIHBhcnNlOiBqeCwKICAgICAgICAgICAgdXJsLAogICAgICAgICAgICBoZWFkZXI6IHsgJ1VzZXItQWdlbnQnOiB1YSB9CiAgICAgICAgfTsKICAgIH0KfTs= \ No newline at end of file diff --git "a/spider/js/\351\205\267\347\210\261\346\274\253\347\224\273[\347\224\273].js" "b/spider/js/\351\205\267\347\210\261\346\274\253\347\224\273[\347\224\273].js" new file mode 100644 index 00000000..73f8ff72 --- /dev/null +++ "b/spider/js/\351\205\267\347\210\261\346\274\253\347\224\273[\347\224\273].js" @@ -0,0 +1,107 @@ +/* +@header({ + searchable: 2, + filterable: 0, + quickSearch: 0, + title: '酷爱漫画', + author: 'EylinSir', + '类型': '漫画', + logo: 'https://www.kuimh.com/static/images/favicon.ico', + lang: 'ds' +}) +*/ + +var rule = { + 类型: '漫画', + author: 'EylinSir', + title: '酷爱漫画', + host: 'https://www.kuimh.com', + logo: 'https://www.kuimh.com/static/images/favicon.ico', + searchUrl: '/search?keyword=**&page=fypage', + url: '/booklist?tag=fyfilter&area=fyclass&end=fyfilter&page=fypage', + searchable: 2, + quickSearch: 0, + timeout: 5000, + play_parse: true, + headers: { + 'User-Agent': 'PC_UA', + 'Referer': 'https://www.kuimh.com' + }, + class_name: '国产&日本&韩国&欧美&其他', + class_url: '1&2&3&5&7', + filter: { + "1": [ + {"key":"tag","name":"题材","value":[{"n":"全部","v":"全部"},{"n":"恋爱","v":"恋爱"},{"n":"古风","v":"古风"},{"n":"校园","v":"校园"},{"n":"奇幻","v":"奇幻"},{"n":"大女主","v":"大女主"},{"n":"治愈","v":"治愈"},{"n":"穿越","v":"穿越"},{"n":"励志","v":"励志"},{"n":"爆笑","v":"爆笑"},{"n":"萌系","v":"萌系"},{"n":"玄幻","v":"玄幻"},{"n":"日常","v":"日常"},{"n":"都市","v":"都市"},{"n":"彩虹","v":"彩虹"},{"n":"灵异","v":"灵异"},{"n":"悬疑","v":"悬疑"},{"n":"少年","v":"少年"}]}, + {"key":"end","name":"状态","value":[{"n":"全部","v":"-1&last=-1"},{"n":"最新","v":"-1&last=1"},{"n":"连载","v":"0&last=-1"},{"n":"完结","v":"1&last=-1"}]} + ] + }, + + parseList: function(html) { + return pdfa(html, '.mh-item').map(it => { + let pic = pdfh(it, 'img&&data-src'); + if (!pic) pic = (pdfh(it, 'html').match(/https?:\/\/[^"']+\.(?:jpg|png|jpeg)(?:\/\d+)?/i) || [])[0]; + return { + title: pdfh(it, '.title a&&Text'), + pic_url: pic, + desc: pdfh(it, '.chapter&&Text'), + url: pdfh(it, 'a&&href') + }; + }); + }, + + 推荐: async function(tid, pg, filter, extend) { + return await this.一级('1', 1, filter, {}); + }, + + 一级: async function(tid, pg, filter, extend) { + let url = `${this.host}/booklist?tag=${extend.tag || '全部'}&area=${tid}&end=${extend.end || '-1&last=-1'}&page=${pg}`; + let html = await request(url, { headers: this.headers }); + return setResult(this.parseList(html)); + }, + + 二级: async function(ids) { + let vid = ids[0]; + let url = this.input; + let html = await request(url); + let tabs = pdfa(html, '#detail-list-select li'); + let playUrls = tabs.map(it => { + let name = pdfh(it, 'a&&Text'); + let u = pdfh(it, 'a&&href'); + return name.trim() + '$' + u; + }).join('#'); + return { + vod_name: pdfh(html, '.info h1&&Text').trim() || "未知", + vod_pic: pdfh(html, '.cover img&&src'), + vod_content: pdfh(html, '.content&&Text').trim(), + vod_play_from: "酷爱漫画", + vod_play_url: playUrls + "$$$" + playUrls + }; + }, + + 搜索: async function(wd, quick, pg) { + let url = this.input; + let html = await request(url, { headers: this.headers }); + return setResult(this.parseList(html)); + }, + + lazy: async function () { + let { input } = this; + let url = input.startsWith('http') ? input : this.host + input; + let html = await request(url, { headers: { 'Referer': url } }); + let pattern = /(https?:\/\/[^"'\s<>]+\.(?:jpg|png|jpeg|webp)(?:\/\d+)?)/gi; + let matches = html.match(pattern) || []; + let imgs = []; + matches.forEach(src => { + if (/grey\.gif|logo|icon|tu\.petatt\.cn/.test(src)) return; + src = src.replace(/\\/g, '/'); + if (!imgs.includes(src)) imgs.push(src); + }); + return { + parse: 0, + url: imgs.length ? 'pics://' + imgs.join('&&') : url, + header: { 'Referer': url } + }; + } +}; +// 自动填充其他分类 +['2','3','5','7'].forEach(k => rule.filter[k] = rule.filter['1']); \ No newline at end of file diff --git "a/spider/js/\351\233\206\347\231\276\345\212\250\346\274\253[\346\274\253].js" "b/spider/js/\351\233\206\347\231\276\345\212\250\346\274\253[\346\274\253].js" index bf7373c3..dfe8b29f 100644 --- "a/spider/js/\351\233\206\347\231\276\345\212\250\346\274\253[\346\274\253].js" +++ "b/spider/js/\351\233\206\347\231\276\345\212\250\346\274\253[\346\274\253].js" @@ -4,7 +4,7 @@ filterable: 0, quickSearch: 0, title: '集百动漫', - author: 'EylinSir/251129/修复版', + author: 'EylinSir', '类型': '影视', lang: 'ds' }) @@ -12,49 +12,41 @@ var rule = { 类型: '影视', - author: 'EylinSir/251129/修复版', + author: 'EylinSir', title: '集百动漫', - host: 'http://www.jibai5.com', - url: '/vodtype/fyclass-fypage.html', + host: 'http://www.duanjux.com', + url: '/bm/fyclass/fypage.html', searchUrl: '/vodsearch/-------------.html?wd=**', homeUrl: '/', headers: {'User-Agent': 'UC_UA'}, - searchable: 1, quickSearch: 0, filterable: 0, double: true, play_parse: true, limit: 6, - class_name: '3D动漫&动漫&沙雕剧场', + searchable: 1, + quickSearch: 0, + filterable: 0, + double: true, + play_parse: true, + limit: 6, + class_name: '3D国漫&动漫&沙雕剧场', class_url: '20&21&22', lazy: async function () { - let {input, pdfa, pdfh, pd} = this - const html = JSON.parse((await request(input)).content.match(/player_.*?=(.*?) { d.push({ - title: pdfh(it, 'a&&title'), - pic_url: pd(it, 'img&&src'), - desc: pdfh(it, '.duration&&Text'), - url: pd(it, 'a&&href'), + title: pdfh(it, '.post-item-title&&Text'), + pic_url: pd(it, '.post-item-cover img&&src'), + desc: pdfh(it, '.post-item-summary&&Text'), + url: pd(it, '.post-item-img&&href'), }) }); return setResult(d) @@ -63,13 +55,13 @@ var rule = { let {input, pdfa, pdfh, pd} = this; let html = await request(input); let d = []; - let data = pdfa(html, '.boxlist li'); + let data = pdfa(html, '.post-item'); data.forEach((it) => { d.push({ - title: pdfh(it, 'a&&title'), - pic_url: pd(it, 'img&&src'), - desc: pdfh(it, '.duration&&Text'), - url: pd(it, 'a&&href'), + title: pdfh(it, '.post-item-title&&Text'), + pic_url: pd(it, '.post-item-cover img&&src'), + desc: pdfh(it, '.post-item-summary&&Text'), + url: pd(it, '.post-item-img&&href'), }) }); return setResult(d) @@ -78,23 +70,20 @@ var rule = { let {input, pdfa, pdfh, pd} = this; let html = await request(input); let VOD = {}; - VOD.vod_name = pdfh(html, 'h2&&Text'); - VOD.vod_content = pdfh(html, '.juqing.mbyc&&Text'); - let playlist = pdfa(html, '.dslist-group') - let tabs = pdfa(html, '.panel-default&&.panel-heading'); + const postContent = pdfa(html, '.post-content')[0]; + const contentText = pdfh(postContent, 'Text'); + const nameMatch = contentText.match(/剧名:(.*?)「/); + VOD.vod_name = nameMatch ? nameMatch[1] : pdfh(html, 'title&&Text').replace(/_.*/, ''); + VOD.vod_content = pdfh(postContent, 'Text'); + const playListDiv = pdfa(html, '.block-wrap#divTags')[0]; + const playItems = pdfa(playListDiv, 'ul li a'); let playmap = {}; - tabs.map((item, i) => { - const form = pdfh(item, 'Text') - const list = playlist[i] - const a = pdfa(list, 'body&&a') - a.map((it) => { - let title = pdfh(it, 'a&&title') - let urls = pd(it, 'a&&href', input) - if (!playmap.hasOwnProperty(form)) { - playmap[form] = []; - } - playmap[form].push(title + "$" + urls); - }); + let form = '集百动漫'; + playmap[form] = []; + playItems.forEach((it) => { + let title = pdfh(it, 'a&&title'); + let urls = pd(it, 'a&&href', input); + playmap[form].push(title + "$" + urls); }); VOD.vod_play_from = Object.keys(playmap).join('$$$'); const urls = Object.values(playmap); @@ -108,16 +97,16 @@ var rule = { let {input, pdfa, pdfh, pd} = this; let html = await request(input); let d = []; - let data = pdfa(html, '.boxlist li'); + let data = pdfa(html, '.post-item'); data.forEach((it) => { d.push({ - title: pdfh(it, 'a&&title'), - pic_url: pd(it, 'img&&src'), - desc: pdfh(it, '.duration&&Text'), - url: pd(it, 'a&&href'), - content: pdfh(it, '.list-content&&Text'), + title: pdfh(it, '.post-item-title&&Text'), + pic_url: pd(it, '.post-item-cover img&&src'), + desc: pdfh(it, '.post-item-summary&&Text'), + url: pd(it, '.post-item-img&&href'), + content: pdfh(it, '.post-item-summary&&Text'), }) }); return setResult(d) } -} +} \ No newline at end of file diff --git "a/spider/js/\351\233\252\350\212\261\347\224\265\345\275\261[\347\243\201].js" "b/spider/js/\351\233\252\350\212\261\347\224\265\345\275\261[\347\243\201].js" new file mode 100644 index 00000000..061397a5 --- /dev/null +++ "b/spider/js/\351\233\252\350\212\261\347\224\265\345\275\261[\347\243\201].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 2, + filterable: 0, + quickSearch: 0, + title: '雪花电影', + author: 'EylinSir', + '类型': '影视', + logo: 'https://n3300.com/favicon.ico', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfpm6roirHnlLXlvbEnLAogICAgaG9zdDogJ2h0dHBzOi8vbjMzMDAuY29tJywKICAgIHVybDogJy9la090eEVCYWZORDVDaHRtbC9meWNsYXNzX2Z5cGFnZS5odG1sJywKICAgIHNlYXJjaFVybDogJy9wbHVzL3NlYXJjaC5waHA/a3d0eXBlPTAma2V5d29yZD0qKicsCiAgICBsb2dvOiAnaHR0cHM6Ly9uMzMwMC5jb20vZmF2aWNvbi5pY28nLAogICAgc2VhcmNoYWJsZTogMiwKICAgIHF1aWNrU2VhcmNoOiAwLAogICAgdGltZW91dDogNTAwMCwKICAgIGhlYWRlcnM6IHsgJ1VzZXItQWdlbnQnOiAnUENfVUEnIH0sCiAgICBjbGFzc19uYW1lOiAn5Yqo5L2c54mHJuWWnOWJp+eJhybniLHmg4XniYcm56eR5bm754mHJuWJp+aDheeJhybmgqznlpHniYcm5oiY5LqJ54mHJuaBkOaAlueJhybngb7pmr7niYcm6L+e57ut5YmnJuWKqOa8qybnu7zoibrniYcm6L+e6L295Yqo5ryrJywKICAgIGNsYXNzX3VybDogJ2Rvbmd6dW8vbGlzdCZ4aWp1L2xpc3QmYWlxaW5nL2xpc3Qma2VodWFuL2xpc3QmanVxaW5nL2xpc3QmeHVhbm5pYW4vbGlzdCZ6aGFuemhlbmcvbGlzdCZrb25nYnUvbGlzdCZ6YWluYW4vbGlzdCZsaWFueHVqdS9saXN0JmRvbmdtYW4vbGlzdCZ6b25neWlqaWVtdS9saXN0JmxpYW56YWlkb25nbWFuL2xpc3RfMjQnLAoKICAgIGNsZWFuVGl0bGU6IGZ1bmN0aW9uKHN0cikgewogICAgICAgIGlmICghc3RyKSByZXR1cm4gJyc7CiAgICAgICAgcmV0dXJuIHN0ci5yZXBsYWNlKC8oSER8QkR8RFZEfDcyMFB8MTA4MFB8NEt86JOd5YWJfOWbveivrXzoi7Hor6186Z+p6K+tfOaXpeivrXznsqTor6185Lit5a2XfOWPjOWtl3zkuK3oi7EpLiokL2ksICcnKS50cmltKCk7CiAgICB9LAoKICAgIGxhenk6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICByZXR1cm4geyB1cmw6IHRoaXMuaW5wdXQsIHBhcnNlOiAwIH07CiAgICB9LAogICAgCiAgICDmjqjojZA6IGFzeW5jIGZ1bmN0aW9uKHRpZCwgcGcsIGZpbHRlciwgZXh0ZW5kKSB7CiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMu5LiA57qnKHRpZCwgcGcsIGZpbHRlciwge30pOwogICAgfSwKICAgIAogICAg5LiA57qnOiBhc3luYyBmdW5jdGlvbih0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGxldCBodG1sID0gYXdhaXQgcmVxdWVzdCh0aGlzLmlucHV0KTsKICAgICAgICBsZXQgbGlzdCA9IHBkZmEoaHRtbCwgJy51bC1pbWd0eHQyIGxpJyk7CiAgICAgICAgbGV0IGQgPSBsaXN0Lm1hcChpdCA9PiB7CiAgICAgICAgICAgIGxldCB0aXRsZSA9IHJ1bGUuY2xlYW5UaXRsZShwZGZoKGl0LCAnaDMgYSYmVGV4dCcpKTsKICAgICAgICAgICAgbGV0IHJlbWFyayA9IHBkZmgoaXQsICdoMyBlbSYmVGV4dCcpOwogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgdGl0bGU6IHRpdGxlLAogICAgICAgICAgICAgICAgcGljX3VybDogcGQoaXQsICdpbWcmJnNyYycsIHJ1bGUuaG9zdCksCiAgICAgICAgICAgICAgICBkZXNjOiByZW1hcmssCiAgICAgICAgICAgICAgICB1cmw6IHBkKGl0LCAnLnBpYyBhJiZocmVmJywgcnVsZS5ob3N0KSwKICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHJlbWFyawogICAgICAgICAgICB9OwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQoZCk7CiAgICB9LAogICAgCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uKGlkcykgewogICAgICAgIGxldCBodG1sID0gYXdhaXQgcmVxdWVzdCh0aGlzLmlucHV0KTsKICAgICAgICBsZXQgdHh0SHRtbCA9IHBkZmgoaHRtbCwgJy5tLXRleHQxIC50eHQnKTsKICAgICAgICBsZXQgY29udGVudCA9IHR4dEh0bWwubWF0Y2goL+KXjueugFtcc1xTXSo/5LuLKFtcc1xTXSo/KeKXjuW9seeJh+aIquWbvi8pPy5bMV0gPz8gdHh0SHRtbDsKICAgICAgICBjb250ZW50ID0gY29udGVudC5yZXBsYWNlKC88W14+XSs+fCZuYnNwOy9nLCAnJykudHJpbSgpOwogICAgICAgIGxldCB2b2RfcGxheV91cmwgPSBwZGZhKGh0bWwsICcuYm90IHVsIGxpIGEnKS5tYXAoaXQgPT4gewogICAgICAgICAgICBsZXQgaHJlZiA9IHBkZmgoaXQsICdhJiZocmVmJyk7CiAgICAgICAgICAgIGlmICghaHJlZi5zdGFydHNXaXRoKCdtYWduZXQnKSkgcmV0dXJuIG51bGw7CiAgICAgICAgICAgIGxldCBuYW1lID0gaHJlZi5tYXRjaCgvJmRuPShbXiZdKykvKT8uWzFdIHx8IHBkZmgoaXQsICdhJiZUZXh0JykucmVwbGFjZSgvXm1hZ25ldDouKi8sICcnKTsKICAgICAgICAgICAgdHJ5IHsgbmFtZSA9IGRlY29kZVVSSUNvbXBvbmVudChuYW1lKTsgfSBjYXRjaCB7fQogICAgICAgICAgICBuYW1lID0gbmFtZS5yZXBsYWNlKC9cWy4qP1xdfOOAkC4qP+OAkXx3d3dcLlxTKy9naSwgJycpCiAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoL1wuKG1wNHxta3Z8YXZpKSQvaSwgJycpCiAgICAgICAgICAgICAgICAgICAgICAgLnRyaW0oKSB8fCAn56OB5Yqb6ZO+5o6lJzsKICAgICAgICAgICAgcmV0dXJuIG5hbWUgKyAnJCcgKyBocmVmOwogICAgICAgIH0pLmZpbHRlcihCb29sZWFuKTsKICAgICAgICBjb25zdCBnZXRJbmZvID0gKGspID0+IHR4dEh0bWwubWF0Y2gobmV3IFJlZ0V4cChg4peOJHtrfSguKj8pPGJyPmApKT8uWzFdPy5yZXBsYWNlKC8mbmJzcDsvZywgJycpLnRyaW0oKSB8fCAnJzsKICAgICAgICByZXR1cm4gewogICAgICAgICAgICB2b2RfbmFtZTogcnVsZS5jbGVhblRpdGxlKHBkZmgoaHRtbCwgJy5tLXRleHQxIGgxJiZUZXh0JykpLAogICAgICAgICAgICB2b2RfcGljOiBwZChodG1sLCAnLm0tdGV4dDEgLnR4dCBpbWcmJnNyYycsIHJ1bGUuaG9zdCksCiAgICAgICAgICAgIHZvZF9kaXJlY3RvcjogZ2V0SW5mbygn5a+844CA44CA5ryUJyksCiAgICAgICAgICAgIHZvZF9hY3RvcjogZ2V0SW5mbygn5Li744CA44CA5ryUJykucmVwbGFjZSgvPGJyPi9nLCAnICcpLAogICAgICAgICAgICB2b2RfdHlwZTogZ2V0SW5mbygn57G744CA44CA5YirJyksCiAgICAgICAgICAgIHZvZF9hcmVhOiBnZXRJbmZvKCfkuqfjgIDjgIDlnLAnKSwKICAgICAgICAgICAgdm9kX3llYXI6IGdldEluZm8oJ+W5tOOAgOOAgOS7oycpLAogICAgICAgICAgICB2b2RfY29udGVudDogY29udGVudCwKICAgICAgICAgICAgdm9kX3BsYXlfZnJvbTogdm9kX3BsYXlfdXJsLmxlbmd0aCA/ICfno4HlipsnIDogJ+aXoOi1hOa6kCcsCiAgICAgICAgICAgIHZvZF9wbGF5X3VybDogdm9kX3BsYXlfdXJsLmpvaW4oJyMnKQogICAgICAgIH07CiAgICB9LAogICAgCiAgICDmkJzntKI6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgaHRtbCA9IGF3YWl0IHJlcXVlc3QodGhpcy5pbnB1dCk7CiAgICAgICAgbGV0IGQgPSBwZGZhKGh0bWwsICcudWwtaW1ndHh0MiBsaScpLm1hcChpdCA9PiB7CiAgICAgICAgICAgIGxldCB0aXRsZSA9IHJ1bGUuY2xlYW5UaXRsZShwZGZoKGl0LCAnaDMgYSYmVGV4dCcpKTsKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIHRpdGxlOiB0aXRsZSwKICAgICAgICAgICAgICAgIGltZzogcGQoaXQsICdpbWcmJnNyYycsIHJ1bGUuaG9zdCksCiAgICAgICAgICAgICAgICBkZXNjOiBwZGZoKGl0LCAnaDMgZW0mJlRleHQnKSwKICAgICAgICAgICAgICAgIHVybDogcGQoaXQsICcucGljIGEmJmhyZWYnLCBydWxlLmhvc3QpCiAgICAgICAgICAgIH07CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdChkKTsKICAgIH0KfTs= \ No newline at end of file diff --git "a/spider/js/\351\237\263\344\271\220\347\243\201\345\234\272[\345\220\254].js" "b/spider/js/\351\237\263\344\271\220\347\243\201\345\234\272[\345\220\254].js" index a333be94..02151e77 100755 --- "a/spider/js/\351\237\263\344\271\220\347\243\201\345\234\272[\345\220\254].js" +++ "b/spider/js/\351\237\263\344\271\220\347\243\201\345\234\272[\345\220\254].js" @@ -5,6 +5,10 @@ quickSearch: 0, title: '音乐磁场', '类型': '影视', + mergeList: true, + more: { + mergeList: 1 + }, lang: 'ds' }) */ diff --git "a/spider/js/\351\237\263\344\271\220\350\201\232\345\220\210[\345\220\254].js" "b/spider/js/\351\237\263\344\271\220\350\201\232\345\220\210[\345\220\254].js" new file mode 100644 index 00000000..94802096 --- /dev/null +++ "b/spider/js/\351\237\263\344\271\220\350\201\232\345\220\210[\345\220\254].js" @@ -0,0 +1,14 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '音乐聚合', + author: 'EylinSir', + '类型': '音乐', + logo: 'https://pic.5577.com/up/2021-9/202198191801219.png', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICfpn7PkuZAnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfpn7PkuZDogZrlkIgnLAogICAgaG9zdDogJ2h0dHBzOi8vbXVzaWMtZGwuc2F5cXouY29tJywKICAgIGxvZ286ICdodHRwczovL3BpYy41NTc3LmNvbS91cC8yMDIxLTkvMjAyMTk4MTkxODAxMjE5LnBuZycsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiBNT0JJTEVfVUEKICAgIH0sCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDEsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogMTAwMDAsCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAgY2xhc3NfbmFtZTogJ+e9keaYk+S6kemfs+S5kCZRUemfs+S5kCbphbfmiJHpn7PkuZAnLAogICAgY2xhc3NfdXJsOiAnbmV0ZWFzZSZxcSZrdXdvJywKICAgIHNlYXJjaFVybDogJy9hcGkvP3R5cGU9YWdncmVnYXRlU2VhcmNoJmtleXdvcmQ9KiombGltaXQ9MTAmcGFnZT0xJywKICAgIHR5cGVzOiBbCiAgICAgICAgeyBuYW1lOiAnSGktUmVz6Z+z6LSoJywgdmFsdWU6ICdmbGFjMjRiaXQnIH0sCiAgICAgICAgeyBuYW1lOiAn5peg5o2f6Z+z6LSoJywgdmFsdWU6ICdmbGFjJyB9LAogICAgICAgIHsgbmFtZTogJ+mrmOWTgemfs+i0qCcsIHZhbHVlOiAnMzIwaycgfSwKICAgICAgICB7IG5hbWU6ICfmoIflh4bpn7PotKgnLCB2YWx1ZTogJzEyOGsnIH0KICAgIF0sCiAgICBwbGF0Zm9ybXM6IFsnbmV0ZWFzZScsICdxcScsICdrdXdvJ10sCiAgICBwbGF0Zm9ybU5hbWVNYXA6IHsKICAgICAgICBuZXRlYXNlOiAn572R5piT5LqR6Z+z5LmQJywKICAgICAgICBxcTogJ1FR6Z+z5LmQJywKICAgICAgICBrdXdvOiAn6YW35oiR6Z+z5LmQJwogICAgfSwKCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGxldCByZXN1bHQgPSBbXTsKICAgICAgICBsZXQgdGFyZ2V0UGxhdGZvcm1zID0gdGlkICYmIHRoaXMucGxhdGZvcm1zLmluY2x1ZGVzKHRpZCkgPyBbdGlkXSA6IHRoaXMucGxhdGZvcm1zOwogICAgICAgIGZvciAobGV0IHBsYXRmb3JtIG9mIHRhcmdldFBsYXRmb3JtcykgewogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgbGV0IGRhdGEgPSBKU09OLnBhcnNlKGF3YWl0IHJlcXVlc3QoYCR7dGhpcy5ob3N0fS9hcGkvP3NvdXJjZT0ke3BsYXRmb3JtfSZ0eXBlPXRvcGxpc3RzYCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSkpOwogICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goLi4uKGRhdGE/LmNvZGUgPT09IDIwMCAmJiBkYXRhLmRhdGE/Lmxpc3QgPyAKICAgICAgICAgICAgICAgICAgICBkYXRhLmRhdGEubGlzdC5tYXAoaXRlbSA9PiAoewogICAgICAgICAgICAgICAgICAgICAgICB0aXRsZTogaXRlbS5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICB1cmw6IGAke3BsYXRmb3JtfV8ke2l0ZW0uaWR9YCwKICAgICAgICAgICAgICAgICAgICAgICAgZGVzYzogaXRlbS51cGRhdGVGcmVxdWVuY3ksCiAgICAgICAgICAgICAgICAgICAgICAgIHBpY191cmw6IGl0ZW0ucGljCiAgICAgICAgICAgICAgICAgICAgfSkpIDogW10pKTsKICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycik7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHNldFJlc3VsdChyZXN1bHQpOwogICAgfSwKCiAgICDkuoznuqc6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgW3BsYXRmb3JtLCByZWFsSWRdID0gdGhpcy5vcklkLnNwbGl0KCdfJyk7CiAgICAgICAgaWYgKCFwbGF0Zm9ybSB8fCAhcmVhbElkKSByZXR1cm4ge307CiAgICAgICAgbGV0IHNvbmdMaXN0ID0gW107CiAgICAgICAgbGV0IHZvZF9uYW1lID0gdGhpcy5vcklkOwogICAgICAgIGxldCB2b2RfZGVzYyA9ICcnOwogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCByZXMgPSBhd2FpdCByZXF1ZXN0KGAke3RoaXMuaG9zdH0vYXBpLz9zb3VyY2U9JHtwbGF0Zm9ybX0maWQ9JHtyZWFsSWR9JnR5cGU9dG9wbGlzdGAsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgICAgICBsZXQgc29uZ0RhdGEgPSBKU09OLnBhcnNlKHJlcyk7CiAgICAgICAgICAgIGlmIChzb25nRGF0YT8uY29kZSA9PT0gMjAwICYmIHNvbmdEYXRhLmRhdGE/Lmxpc3QpIHsKICAgICAgICAgICAgICAgIHNvbmdMaXN0ID0gc29uZ0RhdGEuZGF0YS5saXN0OwogICAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgICAgICBsZXQgdG9wTGlzdHNSZXMgPSBhd2FpdCByZXF1ZXN0KGAke3RoaXMuaG9zdH0vYXBpLz9zb3VyY2U9JHtwbGF0Zm9ybX0mdHlwZT10b3BsaXN0c2AsIHsgaGVhZGVyczogdGhpcy5oZWFkZXJzIH0pOwogICAgICAgICAgICAgICAgICAgIGxldCB0b3BMaXN0c0RhdGEgPSBKU09OLnBhcnNlKHRvcExpc3RzUmVzKTsKICAgICAgICAgICAgICAgICAgICBsZXQgY3VycmVudExpc3QgPSB0b3BMaXN0c0RhdGE/LmNvZGUgPT09IDIwMCAmJiB0b3BMaXN0c0RhdGEuZGF0YT8ubGlzdCA/IAogICAgICAgICAgICAgICAgICAgICAgICB0b3BMaXN0c0RhdGEuZGF0YS5saXN0LmZpbmQoaXRlbSA9PiBpdGVtLmlkID09PSByZWFsSWQpIDogbnVsbDsKICAgICAgICAgICAgICAgICAgICBpZiAoY3VycmVudExpc3QpIHsKICAgICAgICAgICAgICAgICAgICAgICAgdm9kX25hbWUgPSBjdXJyZW50TGlzdC5uYW1lOwogICAgICAgICAgICAgICAgICAgICAgICB2b2RfZGVzYyA9IGN1cnJlbnRMaXN0LnVwZGF0ZUZyZXF1ZW5jeSB8fCAnJzsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIHJlcyA9IGF3YWl0IHJlcXVlc3QoYCR7dGhpcy5ob3N0fS9hcGkvP3NvdXJjZT0ke3BsYXRmb3JtfSZpZD0ke3JlYWxJZH0mdHlwZT1pbmZvYCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSk7CiAgICAgICAgICAgICAgICBzb25nRGF0YSA9IEpTT04ucGFyc2UocmVzKTsKICAgICAgICAgICAgICAgIGlmIChzb25nRGF0YT8uY29kZSAhPT0gMjAwIHx8ICFzb25nRGF0YS5kYXRhKSByZXR1cm4ge307CiAgICAgICAgICAgICAgICBzb25nTGlzdCA9IFtzb25nRGF0YS5kYXRhXTsKICAgICAgICAgICAgICAgIHZvZF9uYW1lID0gYCR7c29uZ0RhdGEuZGF0YS5uYW1lfSAtICR7c29uZ0RhdGEuZGF0YS5hcnRpc3R9YDsKICAgICAgICAgICAgICAgIHZvZF9kZXNjID0gc29uZ0RhdGEuZGF0YS5hbGJ1bSB8fCAnJzsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgICAgICBjb25zb2xlLmVycm9yKCfojrflj5bmlbDmja7lpLHotKU6JywgZXJyKTsKICAgICAgICAgICAgcmV0dXJuIHt9OwogICAgICAgIH0KCiAgICAgICAgaWYgKHZvZF9uYW1lID09PSB0aGlzLm9ySWQgJiYgc29uZ0xpc3QubGVuZ3RoKSB7CiAgICAgICAgICAgIHZvZF9uYW1lID0gYCR7c29uZ0xpc3RbMF0uYWxidW19IC0g5q2M5puy5YiX6KGoYDsKICAgICAgICAgICAgdm9kX2Rlc2MgPSBg5YWx5YyF5ZCrICR7c29uZ0xpc3QubGVuZ3RofSDpppbmrYzmm7JgOwogICAgICAgIH0KCiAgICAgICAgbGV0IHBsYXlfdXJsID0gc29uZ0xpc3QubWFwKHNvbmcgPT4gYCR7c29uZy5uYW1lfSAtICR7c29uZy5hcnRpc3R9JCR7cGxhdGZvcm19XyR7c29uZy5pZCB8fCByZWFsSWR9YCkuam9pbignIycpOwogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIHZvZF9pZDogdGhpcy5vcklkLAogICAgICAgICAgICB2b2RfbmFtZSwKICAgICAgICAgICAgdm9kX3BsYXlfZnJvbTogdGhpcy5wbGF0Zm9ybU5hbWVNYXBbcGxhdGZvcm1dLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHBsYXlfdXJsLAogICAgICAgICAgICB2b2RfZGVzYwogICAgICAgIH07CiAgICB9LAoKICAgIOaQnOe0ojogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCB1cmwgPSB0aGlzLnNlYXJjaFVybC5yZXBsYWNlKCcqKicsIGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLktFWSkpOwogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCBkYXRhID0gYXdhaXQgcmVxdWVzdCh1cmwsIHsKICAgICAgICAgICAgICAgIGhlYWRlcnM6IHRoaXMuaGVhZGVycywKICAgICAgICAgICAgICAgIHRpbWVvdXQ6IDEwMDAwIAogICAgICAgICAgICB9KTsKICAgICAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGRhdGEpOwogICAgICAgICAgICBpZiAoanNvbj8uY29kZSA9PT0gMjAwICYmIGpzb24/LmRhdGE/LnJlc3VsdHMpIHsKICAgICAgICAgICAgICAgIGxldCByZXN1bHQgPSBqc29uLmRhdGEucmVzdWx0cy5tYXAoaXRlbSA9PiAoewogICAgICAgICAgICAgICAgICAgIHRpdGxlOiBgJHtpdGVtLm5hbWV9IC0gJHtpdGVtLmFydGlzdH1gLAogICAgICAgICAgICAgICAgICAgIGRlc2M6IGAke2l0ZW0ucGxhdGZvcm0gPT09ICduZXRlYXNlJyA/ICfnvZHmmJPkupEnIDogaXRlbS5wbGF0Zm9ybSA9PT0gJ3FxJyA/ICdRUScgOiAn6YW35oiRJ33pn7PkuZAgLSAke2l0ZW0uYWxidW19YCwKICAgICAgICAgICAgICAgICAgICBwaWNfdXJsOiBpdGVtLnBpYywKICAgICAgICAgICAgICAgICAgICB1cmw6IGAke2l0ZW0ucGxhdGZvcm19XyR7aXRlbS5pZH1gCiAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHJlc3VsdCk7CiAgICAgICAgICAgIH0KICAgICAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAgICAgICBjb25zb2xlLmVycm9yKCfmkJzntKLor7fmsYLplJnor686JywgZXJyb3IpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gc2V0UmVzdWx0KFtdKTsKICAgIH0sCgogICAgbGF6eTogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBbcGxhdGZvcm0sIHJlYWxJZF0gPSB0aGlzLmlucHV0LnNwbGl0KCdfJyk7CiAgICAgICAgaWYgKCFwbGF0Zm9ybSB8fCAhcmVhbElkKSB7CiAgICAgICAgICAgIHJldHVybiB7IHBhcnNlOiAwLCBwbGF5VXJsOiAnJywgdXJsOiBbXSwgaGVhZGVyOiB0aGlzLmhlYWRlcnMsIGxyYzogJycsIGNvdmVyOiAnJywgaGVpZ2h0OiA3MjAgfTsKICAgICAgICB9CiAgICAgICAgbGV0IGx5cmljID0gJyc7CiAgICAgICAgbGV0IGluZm9EYXRhID0ge307CiAgICAgICAgbGV0IFtseXJpY1JlcywgaW5mb1Jlc10gPSBhd2FpdCBQcm9taXNlLmFsbChbCiAgICAgICAgICAgIHJlcXVlc3QoYCR7dGhpcy5ob3N0fS9hcGkvP3NvdXJjZT0ke3BsYXRmb3JtfSZpZD0ke3JlYWxJZH0mdHlwZT1scmNgLCB7IGhlYWRlcnM6IHRoaXMuaGVhZGVycyB9KS5jYXRjaCgoKSA9PiAnJyksCiAgICAgICAgICAgIHJlcXVlc3QoYCR7dGhpcy5ob3N0fS9hcGkvP3NvdXJjZT0ke3BsYXRmb3JtfSZpZD0ke3JlYWxJZH0mdHlwZT1pbmZvYCwgeyBoZWFkZXJzOiB0aGlzLmhlYWRlcnMgfSkuY2F0Y2goKCkgPT4gJ3t9JykKICAgICAgICBdKTsKICAgICAgICBpZiAodHlwZW9mIGx5cmljUmVzID09PSAnc3RyaW5nJykgbHlyaWMgPSBseXJpY1JlczsKICAgICAgICB0cnkgewogICAgICAgICAgICBpbmZvRGF0YSA9IEpTT04ucGFyc2UoaW5mb1Jlcyk7CiAgICAgICAgfSBjYXRjaCAoZXJyKSB7fQogICAgICAgIGxldCBjb3ZlciA9IGluZm9EYXRhPy5jb2RlID09PSAyMDAgPyBpbmZvRGF0YS5kYXRhPy5waWMgfHwgJycgOiAnJzsKICAgICAgICBsZXQgdXJsID0gdGhpcy50eXBlcy5mbGF0TWFwKHR5cGUgPT4gW3R5cGUubmFtZSwgYCR7dGhpcy5ob3N0fS9hcGkvP3NvdXJjZT0ke3BsYXRmb3JtfSZpZD0ke3JlYWxJZH0mdHlwZT11cmwmYnI9JHt0eXBlLnZhbHVlfWBdKTsKICAgICAgICByZXR1cm4gewogICAgICAgICAgICBwYXJzZTogMCwKICAgICAgICAgICAgcGxheVVybDogJycsCiAgICAgICAgICAgIHVybCwKICAgICAgICAgICAgaGVhZGVyOiB0aGlzLmhlYWRlcnMsCiAgICAgICAgICAgIGxyYzogbHlyaWMsCiAgICAgICAgICAgIGNvdmVyLAogICAgICAgICAgICBwaWM6IGNvdmVyLAogICAgICAgICAgICBoZWlnaHQ6IDcyMAogICAgICAgIH07CiAgICB9Cn07 \ No newline at end of file diff --git "a/spider/js/\351\272\273\351\233\200\350\247\206\351\242\221[\344\274\230].js" "b/spider/js/\351\272\273\351\233\200\350\247\206\351\242\221[\344\274\230].js" new file mode 100644 index 00000000..b63b250d --- /dev/null +++ "b/spider/js/\351\272\273\351\233\200\350\247\206\351\242\221[\344\274\230].js" @@ -0,0 +1,13 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 0, + title: '麻雀视频', + author: 'EylinSir', + '类型': '影视', + lang: 'ds' +}) +*/ + +dmFyIHJ1bGUgPSB7CiAgICDnsbvlnos6ICflvbHop4YnLAogICAgYXV0aG9yOiAnRXlsaW5TaXInLAogICAgdGl0bGU6ICfpurvpm4Dop4bpopEnLAogICAgaG9zdDogJ2h0dHBzOi8vd3d3Lm1xdHYuY2MnLAogICAgaG9tZVVybDogJ2h0dHBzOi8vd3d3Lm1xdHYuY2MnLAogICAgdXJsOiAnL2xpYnMvVm9kTGlzdC5hcGkucGhwP3R5cGU9ZnljbGFzcyZyYW5rPXJhbmtob3QmY2F0PSZ5ZWFyPSZhcmVhPSZwYWdlPWZ5cGFnZSZ0b2tlbj1meXRva2VuJywKICAgIHNlYXJjaFVybDogJy9saWJzL1ZvZExpc3QuYXBpLnBocD9zZWFyY2g9KiomdG9rZW49Znl0b2tlbicsCiAgICBzZWFyY2hhYmxlOiAxLAogICAgcXVpY2tTZWFyY2g6IDAsCiAgICBmaWx0ZXJhYmxlOiAxLAogICAgdGltZW91dDogMTAwMDAsCiAgICBwbGF5X3BhcnNlOiB0cnVlLAogICAga2V5OiAnTWN4b3NAbXVjaG8hbm1tZScsCiAgICBoZWFkZXJzOiB7CiAgICAgICAgJ1VzZXItQWdlbnQnOiAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzE0Mi4wLjAuMCBTYWZhcmkvNTM3LjM2JywKICAgICAgICAnYWNjZXB0LWxhbmd1YWdlJzogJ3poLUNOLHpoO3E9MC45JywKICAgICAgICAnc2VjLWNoLXVhJzogJyJHb29nbGUgQ2hyb21lIjt2PSIxNDMiLCAiQ2hyb21pdW0iO3Y9IjE0MyIsICJOb3QgQShCcmFuZCI7dj0iMjQiJywKICAgICAgICAnY2FjaGUtY29udHJvbCc6ICduby1jYWNoZScsCiAgICAgICAgJ3ByYWdtYSc6ICduby1jYWNoZScsCiAgICAgICAgJ3NlYy1jaC11YS1tb2JpbGUnOiAnPzAnLAogICAgICAgICdzZWMtY2gtdWEtcGxhdGZvcm0nOiAnIldpbmRvd3MiJywKICAgICAgICAnc2VjLWZldGNoLXNpdGUnOiAnc2FtZS1vcmlnaW4nCiAgICB9LAogICAgCiAgICBnZXRUb2tlbjogYXN5bmMgZnVuY3Rpb24gKHBhdGgsIHJlZlBhdGggPSAnJykgewogICAgICAgIGxldCBoZWFkZXJzID0gewogICAgICAgICAgICAuLi50aGlzLmhlYWRlcnMsCiAgICAgICAgICAgICdBY2NlcHQnOiAndGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2UvYXZpZixpbWFnZS93ZWJwLGltYWdlL2FwbmcsKi8qO3E9MC44LGFwcGxpY2F0aW9uL3NpZ25lZC1leGNoYW5nZTt2PWIzO3E9MC43JywKICAgICAgICAgICAgJ3ByaW9yaXR5JzogJ3U9MCwgaScsCiAgICAgICAgICAgICdzZWMtZmV0Y2gtZGVzdCc6ICdkb2N1bWVudCcsCiAgICAgICAgICAgICdzZWMtZmV0Y2gtbW9kZSc6ICduYXZpZ2F0ZScsCiAgICAgICAgICAgICdzZWMtZmV0Y2gtdXNlcic6ICc/MScsCiAgICAgICAgICAgICd1cGdyYWRlLWluc2VjdXJlLXJlcXVlc3RzJzogJzEnCiAgICAgICAgfTsKICAgICAgICBpZiAocmVmUGF0aCkgaGVhZGVyc1sncmVmZXJlciddID0gYCR7dGhpcy5ob3N0fSR7cmVmUGF0aH1gOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKGAke3RoaXMuaG9zdH0ke3BhdGh9YCwgeyBoZWFkZXJzIH0pOwogICAgICAgIGxldCBodG1sID0gYXdhaXQgcmVzcC50ZXh0KCk7CiAgICAgICAgbGV0IG1hdGNoID0gaHRtbC5tYXRjaCgvd2luZG93XC5wYWdlaWRccz89XHM/JyguKj8pJzsvaSk7CiAgICAgICAgbGV0IHBhZ2VJZCA9IG1hdGNoID8gbWF0Y2hbMV0gOiAnJzsKICAgICAgICByZXR1cm4gdGhpcy5lbmNvZGVEYXRhKHBhZ2VJZCk7CiAgICB9LAoKICAgIGVuY29kZURhdGE6IGZ1bmN0aW9uIChkYXRhKSB7CiAgICAgICAgbGV0IGpzb25TdHIgPSBKU09OLnN0cmluZ2lmeShkYXRhKTsKICAgICAgICBsZXQgYjY0XzEgPSBidG9hKHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChqc29uU3RyKSkpOwogICAgICAgIGxldCB4b3JfcmVzdWx0ID0gJyc7CiAgICAgICAgbGV0IGtleUxlbiA9IHRoaXMua2V5Lmxlbmd0aDsKICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGI2NF8xLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIGxldCBjaGFyQ29kZSA9IGI2NF8xLmNoYXJDb2RlQXQoaSkgXiB0aGlzLmtleS5jaGFyQ29kZUF0KGkgJSBrZXlMZW4pOwogICAgICAgICAgICB4b3JfcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoY2hhckNvZGUpOwogICAgICAgIH0KICAgICAgICBsZXQgYjY0XzIgPSBidG9hKHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudCh4b3JfcmVzdWx0KSkpOwogICAgICAgIHJldHVybiBlbmNvZGVVUklDb21wb25lbnQoYjY0XzIpOwogICAgfSwKCiAgICBkZWNvZGVEYXRhOiBmdW5jdGlvbiAoZW5jb2RlZFN0cikgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGxldCBiYXNlNjREZWNvZGUgPSAoc3RyKSA9PiB7CiAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQoZXNjYXBlKGF0b2Ioc3RyKSkpOwogICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAnJzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKICAgICAgICAgICAgCiAgICAgICAgICAgIGxldCBkZWNvZGVkU3RlcDFTdHIgPSBiYXNlNjREZWNvZGUoZW5jb2RlZFN0cik7CiAgICAgICAgICAgIGxldCB4b3JSZXN1bHQgPSAnJzsKICAgICAgICAgICAgbGV0IGtleUxlbiA9IHRoaXMua2V5Lmxlbmd0aDsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBkZWNvZGVkU3RlcDFTdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgICAgIHhvclJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGRlY29kZWRTdGVwMVN0ci5jaGFyQ29kZUF0KGkpIF4gdGhpcy5rZXkuY2hhckNvZGVBdChpICUga2V5TGVuKSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIGxldCBkZWNvZGVkU3RlcDJTdHIgPSBiYXNlNjREZWNvZGUoeG9yUmVzdWx0KTsKICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGVjb2RlZFN0ZXAyU3RyKTsKICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0RlY29kZSBlcnJvcjonLCBlKTsKICAgICAgICAgICAgcmV0dXJuIHt9OwogICAgICAgIH0KICAgIH0sCiAgICAKICAgIGdldEhlYWRlcnMyOiBmdW5jdGlvbiAocmVmUGF0aCA9ICcnKSB7CiAgICAgICAgbGV0IGhlYWRlcnMgPSB7CiAgICAgICAgICAgIC4uLnRoaXMuaGVhZGVycywKICAgICAgICAgICAgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFzY3JpcHQsICovKjsgcT0wLjAxJywKICAgICAgICAgICAgJ3ByaW9yaXR5JzogJ3U9MSwgaScsCiAgICAgICAgICAgICdzZWMtZmV0Y2gtZGVzdCc6ICdlbXB0eScsCiAgICAgICAgICAgICdzZWMtZmV0Y2gtbW9kZSc6ICdjb3JzJywKICAgICAgICAgICAgJ3gtcmVxdWVzdGVkLXdpdGgnOiAnWE1MSHR0cFJlcXVlc3QnCiAgICAgICAgfTsKICAgICAgICBpZiAocmVmUGF0aCkgaGVhZGVyc1sncmVmZXJlciddID0gYCR7dGhpcy5ob3N0fSR7cmVmUGF0aH1gOwogICAgICAgIHJldHVybiBoZWFkZXJzOwogICAgfSwKCiAgICBhcnIydm9kczogZnVuY3Rpb24gKGFycikgewogICAgICAgIHJldHVybiAoYXJyIHx8IFtdKS5tYXAoaSA9PiAoewogICAgICAgICAgICB0aXRsZTogaS50aXRsZSwKICAgICAgICAgICAgdXJsOiBgJHt0aGlzLmhvc3R9JHtpLnVybH1gLAogICAgICAgICAgICBkZXNjOiBpLnJlbWFyaywKICAgICAgICAgICAgcGljX3VybDogaS5pbWcsCiAgICAgICAgICAgIHZvZF95ZWFyOiBpLnllYXIgfHwgJycKICAgICAgICB9KSk7CiAgICB9LAogICAgCiAgICDpooTlpITnkIY6IGFzeW5jIGZ1bmN0aW9uICgpIHt9LAoKICAgIGNsYXNzX3BhcnNlOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgY2xhc3M6IFsKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnL3R5cGUvbW92aWUnLCAndHlwZV9uYW1lJzogJ+eUteW9sScgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnL3R5cGUvdHYnLCAndHlwZV9uYW1lJzogJ+eUteinhuWJpycgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnL3R5cGUvdmEnLCAndHlwZV9uYW1lJzogJ+e7vOiJuicgfSwKICAgICAgICAgICAgICAgIHsgJ3R5cGVfaWQnOiAnL3R5cGUvY3QnLCAndHlwZV9uYW1lJzogJ+WKqOa8qycgfQogICAgICAgICAgICBdLAogICAgICAgICAgICBmaWx0ZXJzOiB7fQogICAgICAgIH07CiAgICB9LAogICAgCiAgICDmjqjojZA6IGFzeW5jIGZ1bmN0aW9uICgpIHsKICAgICAgICBsZXQgdG9rZW4gPSBhd2FpdCB0aGlzLmdldFRva2VuKCcvJyk7CiAgICAgICAgbGV0IHVybCA9IGAke3RoaXMuaG9zdH0vbGlicy9Wb2RMaXN0LmFwaS5waHA/aG9tZT1pbmRleCZ0b2tlbj0ke3Rva2VufWA7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godXJsLCB7IGhlYWRlcnM6IHRoaXMuZ2V0SGVhZGVyczIoJy8nKSB9KTsKICAgICAgICBsZXQganNvbiA9IEpTT04ucGFyc2UoYXdhaXQgcmVzcC50ZXh0KCkpOwogICAgICAgIGxldCB2aWRlb3MgPSBbXTsKICAgICAgICBpZiAoanNvbi5kYXRhICYmIGpzb24uZGF0YS5tb3ZpZSkgewogICAgICAgICAgICBmb3IgKGxldCBpIG9mIGpzb24uZGF0YS5tb3ZpZSkgewogICAgICAgICAgICAgICAgdmlkZW9zLnB1c2goLi4udGhpcy5hcnIydm9kcyhpLnNob3cpKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICDkuIDnuqc6IGFzeW5jIGZ1bmN0aW9uICh0aWQsIHBnLCBmaWx0ZXIsIGV4dGVuZCkgewogICAgICAgIGxldCB0eXBlS2V5ID0gdGlkLnNwbGl0KCcvJylbMl07CiAgICAgICAgbGV0IHRva2VuID0gYXdhaXQgdGhpcy5nZXRUb2tlbih0aWQsICcvJyk7CiAgICAgICAgbGV0IHVybCA9IGAke3RoaXMuaG9zdH0vbGlicy9Wb2RMaXN0LmFwaS5waHA/dHlwZT0ke3R5cGVLZXl9JnJhbms9cmFua2hvdCZjYXQ9JnllYXI9JmFyZWE9JnBhZ2U9JHtwZ30mdG9rZW49JHt0b2tlbn1gOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmdldEhlYWRlcnMyKHRpZCkgfSk7CiAgICAgICAgbGV0IGpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlc3AudGV4dCgpKTsKICAgICAgICBsZXQgdmlkZW9zID0gdGhpcy5hcnIydm9kcyhqc29uLmRhdGEpOwogICAgICAgIHJldHVybiBzZXRSZXN1bHQodmlkZW9zKTsKICAgIH0sCiAgICAKICAgIOS6jOe6pzogYXN5bmMgZnVuY3Rpb24gKCkgewogICAgICAgIGxldCBpZCA9IHRoaXMuaW5wdXQucmVwbGFjZShgJHt0aGlzLmhvc3R9YCwgJycpOwogICAgICAgIGxldCByZWFsSWQgPSBpZC5zcGxpdCgnLycpW2lkLnNwbGl0KCcvJykubGVuZ3RoIC0gMV07CiAgICAgICAgbGV0IHRva2VuID0gYXdhaXQgdGhpcy5nZXRUb2tlbihpZCwgJy8nKTsKICAgICAgICBsZXQgdXJsID0gYCR7dGhpcy5ob3N0fS9saWJzL1ZvZEluZm8uYXBpLnBocD90eXBlPWN0JmlkPSR7cmVhbElkfSZ0b2tlbj0ke3Rva2VufWA7CiAgICAgICAgbGV0IHJlc3AgPSBhd2FpdCBfZmV0Y2godXJsLCB7IGhlYWRlcnM6IHRoaXMuZ2V0SGVhZGVyczIoaWQpIH0pOwogICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShhd2FpdCByZXNwLnRleHQoKSk7CiAgICAgICAgbGV0IGRhdGEgPSBqc29uLmRhdGE7CiAgICAgICAgbGV0IHBsYXlBcGkgPSBkYXRhLnBsYXlhcGkgfHwgW107CiAgICAgICAgbGV0IHBhcnNlcyA9IHBsYXlBcGkKICAgICAgICAgICAgLmZpbHRlcihpID0+IHR5cGVvZiBpID09PSAnb2JqZWN0JyAmJiBpICE9PSBudWxsICYmIGkudXJsICYmIHR5cGVvZiBpLnVybCA9PT0gJ3N0cmluZycpCiAgICAgICAgICAgIC5tYXAoaSA9PiBpLnVybC5zdGFydHNXaXRoKCcvLycpID8gYGh0dHBzOiR7aS51cmx9YCA6IGkudXJsKQogICAgICAgICAgICAuam9pbignLCcpOwogICAgICAgIGxldCBzaG93cyA9IFtdOwogICAgICAgIGxldCBwbGF5VXJscyA9IFtdOwogICAgICAgIGlmIChkYXRhLnBsYXlpbmZvKSB7CiAgICAgICAgICAgIGZvciAobGV0IGogb2YgZGF0YS5wbGF5aW5mbykgewogICAgICAgICAgICAgICAgbGV0IHVybHMgPSBbXTsKICAgICAgICAgICAgICAgIGZvciAobGV0IGsgb2Ygai5wbGF5ZXIpIHsKICAgICAgICAgICAgICAgICAgICB1cmxzLnB1c2goYCR7ay5ub30kJHtrLnVybH1AJHtwYXJzZXN9YCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAodXJscy5sZW5ndGggPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgcGxheVVybHMucHVzaCh1cmxzLmpvaW4oJyMnKSk7CiAgICAgICAgICAgICAgICAgICAgc2hvd3MucHVzaChqLmNuc2l0ZSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgCiAgICAgICAgbGV0IFZPRCA9IHsKICAgICAgICAgICAgdm9kX2lkOiBpZCwKICAgICAgICAgICAgdm9kX25hbWU6IGRhdGEudGl0bGUsCiAgICAgICAgICAgIHZvZF9waWM6IGRhdGEuaW1nLAogICAgICAgICAgICB2b2RfcmVtYXJrczogZGF0YS5yZW1hcmssCiAgICAgICAgICAgIHZvZF95ZWFyOiBkYXRhLnllYXIsCiAgICAgICAgICAgIHZvZF9hcmVhOiBkYXRhLmFyZWEsCiAgICAgICAgICAgIHZvZF9hY3RvcjogZGF0YS5hY3RvciwKICAgICAgICAgICAgdm9kX2RpcmVjdG9yOiBkYXRhLmRpcmVjdG9yLAogICAgICAgICAgICB2b2RfY29udGVudDogJycsCiAgICAgICAgICAgIHZvZF9wbGF5X2Zyb206IHNob3dzLmpvaW4oJyQkJCcpLAogICAgICAgICAgICB2b2RfcGxheV91cmw6IHBsYXlVcmxzLmpvaW4oJyQkJCcpLAogICAgICAgICAgICB0eXBlX25hbWU6ICcnCiAgICAgICAgfTsKICAgICAgICByZXR1cm4gVk9EOwogICAgfSwKICAgIAogICAg5pCc57SiOiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IHBhdGggPSBgL3NlYXJjaC8ke2VuY29kZVVSSUNvbXBvbmVudCh0aGlzLktFWSl9YDsKICAgICAgICBsZXQgdG9rZW4gPSBhd2FpdCB0aGlzLmdldFRva2VuKHBhdGgsICcvJyk7CiAgICAgICAgbGV0IHVybCA9IGAke3RoaXMuaG9zdH0vbGlicy9Wb2RMaXN0LmFwaS5waHA/c2VhcmNoPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHRoaXMuS0VZKX0mdG9rZW49JHt0b2tlbn1gOwogICAgICAgIGxldCByZXNwID0gYXdhaXQgX2ZldGNoKHVybCwgeyBoZWFkZXJzOiB0aGlzLmdldEhlYWRlcnMyKHBhdGgpIH0pOwogICAgICAgIGxldCBqc29uID0gSlNPTi5wYXJzZShhd2FpdCByZXNwLnRleHQoKSk7CiAgICAgICAgbGV0IGRlY29kZURhdGEgPSB0aGlzLmRlY29kZURhdGEoanNvbi5kYXRhKTsKICAgICAgICBsZXQgdmlkZW9zID0gW107CiAgICAgICAgaWYgKGRlY29kZURhdGEudm9kX2FsbCkgewogICAgICAgICAgICBmb3IgKGxldCBpIG9mIGRlY29kZURhdGEudm9kX2FsbCkgewogICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpID09PSAnb2JqZWN0JyAmJiBpICE9PSBudWxsKSB7CiAgICAgICAgICAgICAgICAgICAgdmlkZW9zLnB1c2goLi4udGhpcy5hcnIydm9kcyhpLnNob3cpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gc2V0UmVzdWx0KHZpZGVvcyk7CiAgICB9LAogICAgCiAgICBsYXp5OiBhc3luYyBmdW5jdGlvbiAoKSB7CiAgICAgICAgbGV0IFtyYXdVcmwsIHBhcnNlc1N0ciA9ICcnXSA9IHRoaXMuaW5wdXQuc3BsaXQoJ0AnKTsKICAgICAgICBsZXQgcGFyc2VzID0gcGFyc2VzU3RyLnNwbGl0KCcsJyk7CiAgICAgICAgbGV0IGp4ID0gMDsKICAgICAgICBsZXQgc25pZmYgPSAwOwogICAgICAgIGxldCB1cmwgPSAnJzsKICAgICAgICBpZiAocmF3VXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSAmJiAvKD86d3d3XC5pcWl5aXx2XC5xcXx2XC55b3VrdXx3d3dcLm1ndHZ8d3d3XC5iaWxpYmlsaSlcLmNvbS8udGVzdChyYXdVcmwpKSB7CiAgICAgICAgICAgIHVybCA9IHJhd1VybDsKICAgICAgICAgICAganggPSAxOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGxldCBmaXJzdFZhbGlkUGFyc2UgPSBwYXJzZXMuZmluZChqID0+IGouc3RhcnRzV2l0aCgnaHR0cCcpKTsKICAgICAgICAgICAgaWYgKGZpcnN0VmFsaWRQYXJzZSkgewogICAgICAgICAgICAgICAgdXJsID0gYCR7Zmlyc3RWYWxpZFBhcnNlfSR7cmF3VXJsfWA7CiAgICAgICAgICAgICAgICBzbmlmZiA9IDE7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB1cmwgPSByYXdVcmw7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgcGFyc2U6IHNuaWZmIHx8IGp4LAogICAgICAgICAgICB1cmwsCiAgICAgICAgICAgIGhlYWRlcjogeyAnVXNlci1BZ2VudCc6IHRoaXMuaGVhZGVyc1snVXNlci1BZ2VudCddIH0KICAgICAgICB9OwogICAgfQp9Ow== \ No newline at end of file diff --git "a/spider/js_bad/\346\270\205\351\243\216DJ[\345\220\254].js" "b/spider/js_bad/\346\270\205\351\243\216DJ[\345\220\254].js" index 59411fe6..1b447234 100644 --- "a/spider/js_bad/\346\270\205\351\243\216DJ[\345\220\254].js" +++ "b/spider/js_bad/\346\270\205\351\243\216DJ[\345\220\254].js" @@ -1,14 +1,3 @@ -/* -@header({ - searchable: 0, - filterable: 1, - quickSearch: 0, - title: '清风DJ[听]', - '类型': '影视', - lang: 'ds' -}) -*/ - var rule = { title: '清风DJ[听]', host: 'https://www.vvvdj.com', diff --git "a/spider/js/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" "b/spider/js_bad/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" similarity index 91% rename from "spider/js/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" rename to "spider/js_bad/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" index 1edc387c..7d852bd2 100644 --- "a/spider/js/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" +++ "b/spider/js_bad/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" @@ -1,14 +1,3 @@ -/* -@header({ - searchable: 2, - filterable: 0, - quickSearch: 0, - title: '种子音乐[听]', - '类型': '影视', - lang: 'ds' -}) -*/ - var rule = { title: '种子音乐[听]', host: 'https://www.zz123.com', diff --git "a/spider/js_dr2_old/\345\245\275\350\266\243\347\275\221[\346\222\255].js" "b/spider/js_dr2_old/\345\245\275\350\266\243\347\275\221[\346\222\255].js" index 65029457..7c45b6fd 100644 --- "a/spider/js_dr2_old/\345\245\275\350\266\243\347\275\221[\346\222\255].js" +++ "b/spider/js_dr2_old/\345\245\275\350\266\243\347\275\221[\346\222\255].js" @@ -1,14 +1,3 @@ -/* -@header({ - searchable: 2, - filterable: 0, - quickSearch: 0, - title: '好趣网[播]', - '类型': '影视', - lang: 'ds' -}) -*/ - var rule = { title: '好趣网[播]', 编码: 'GBK',//不填就默认utf-8 diff --git "a/spider/js_dr2_old/\346\234\211\345\243\260\347\273\230\346\234\254\347\275\221[\345\220\254].js" "b/spider/js_dr2_old/\346\234\211\345\243\260\347\273\230\346\234\254\347\275\221[\345\220\254].js" index 49e1085e..e22cb9a7 100644 --- "a/spider/js_dr2_old/\346\234\211\345\243\260\347\273\230\346\234\254\347\275\221[\345\220\254].js" +++ "b/spider/js_dr2_old/\346\234\211\345\243\260\347\273\230\346\234\254\347\275\221[\345\220\254].js" @@ -1,14 +1,3 @@ -/* -@header({ - searchable: 2, - filterable: 0, - quickSearch: 0, - title: '有声绘本网', - '类型': '听书', - lang: 'ds' -}) -*/ - var rule = { 类型: '听书', title: '有声绘本网', diff --git "a/spider/js_dr2_old/\347\210\261\347\234\213\347\237\255\345\211\247[\347\233\230].js" "b/spider/js_dr2_old/\347\210\261\347\234\213\347\237\255\345\211\247[\347\233\230].js" index 93568906..474a8964 100644 --- "a/spider/js_dr2_old/\347\210\261\347\234\213\347\237\255\345\211\247[\347\233\230].js" +++ "b/spider/js_dr2_old/\347\210\261\347\234\213\347\237\255\345\211\247[\347\233\230].js" @@ -1,14 +1,3 @@ -/* -@header({ - searchable: 1, - filterable: 0, - quickSearch: 0, - title: '爱看短剧[盘]', - '类型': '影视', - lang: 'ds' -}) -*/ - var rule = { 类型: '影视',//影视|听书|漫画|小说 title: '爱看短剧[盘]', diff --git "a/spider/js_dr2/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" "b/spider/js_dr2_old/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" similarity index 93% rename from "spider/js_dr2/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" rename to "spider/js_dr2_old/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" index 80c3539f..d60985f1 100644 --- "a/spider/js_dr2/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" +++ "b/spider/js_dr2_old/\347\247\215\345\255\220\351\237\263\344\271\220[\345\220\254].js" @@ -1,13 +1,3 @@ -/* -@header({ - searchable: 2, - filterable: 0, - quickSearch: 0, - title: '种子音乐[听]', - lang: 'dr2' -}) -*/ - var rule = { title: '种子音乐[听]', host: 'https://www.zz123.com', diff --git "a/spider/js_dr2/\347\261\263\345\205\224\351\237\263\344\271\220.js" "b/spider/js_dr2_old/\347\261\263\345\205\224\351\237\263\344\271\220.js" similarity index 100% rename from "spider/js_dr2/\347\261\263\345\205\224\351\237\263\344\271\220.js" rename to "spider/js_dr2_old/\347\261\263\345\205\224\351\237\263\344\271\220.js" diff --git "a/spider/js_dr2/\350\234\273\350\234\223FM[\345\220\254].js" "b/spider/js_dr2_old/\350\234\273\350\234\223FM[\345\220\254].js" similarity index 100% rename from "spider/js_dr2/\350\234\273\350\234\223FM[\345\220\254].js" rename to "spider/js_dr2_old/\350\234\273\350\234\223FM[\345\220\254].js" diff --git a/spider/jstest/demo_test.js b/spider/jstest/demo_test.js new file mode 100644 index 00000000..bf7ed563 --- /dev/null +++ b/spider/jstest/demo_test.js @@ -0,0 +1,279 @@ + +import * as drpyS from '../../libs/drpyS.js'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import {existsSync} from 'fs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// Mock server constants +const PORT = 5705; +const WsPORT = 5706; +const protocol = 'http'; +const hostname = 'localhost:5705'; + +function getEnv(moduleName, query = {}) { + const moduleExt = query.extend || ''; + const requestHost = `${protocol}://${hostname}`; + const publicUrl = `${protocol}://${hostname}/public/`; + const jsonUrl = `${protocol}://${hostname}/json/`; + const httpUrl = `${protocol}://${hostname}/http`; + const imageApi = `${protocol}://${hostname}/image`; + const mediaProxyUrl = `${protocol}://${hostname}/mediaProxy`; + const webdavProxyUrl = `${protocol}://${hostname}/webdav/`; + const ftpProxyUrl = `${protocol}://${hostname}/ftp/`; + const hostUrl = `${hostname.split(':')[0]}`; + const wsName = hostname.replace(`:${PORT}`, `:${WsPORT}`); + const fServer = null; + + const proxyUrl = `${protocol}://${hostname}/proxy/${moduleName}/?do=${query.do || 'ds'}&extend=${encodeURIComponent(moduleExt)}`; + const getProxyUrl = function () { + return proxyUrl + }; + + const env = { + requestHost, + proxyUrl, + publicUrl, + jsonUrl, + httpUrl, + imageApi, + mediaProxyUrl, + webdavProxyUrl, + ftpProxyUrl, + hostUrl, + hostname, + wsName, + fServer, + getProxyUrl, + ext: moduleExt + }; + + env.getRule = async function (_moduleName) { + const _modulePath = path.join(__dirname, '../js', `${_moduleName}.js`); + if (!existsSync(_modulePath)) { + return null; + } + const _env = getEnv(_moduleName); + const RULE = await drpyS.getRuleObject(_modulePath, _env); + + RULE.callRuleFn = async function (_method, _args) { + let invokeMethod = null; + switch (_method) { + case 'class_parse': invokeMethod = 'home'; break; + case '推荐': invokeMethod = 'homeVod'; break; + case '一级': invokeMethod = 'category'; break; + case '二级': invokeMethod = 'detail'; break; + case '搜索': invokeMethod = 'search'; break; + case 'lazy': invokeMethod = 'play'; break; + case 'proxy_rule': invokeMethod = 'proxy'; break; + case 'action': invokeMethod = 'action'; break; + } + + if (!invokeMethod) { + if (typeof RULE[_method] !== 'function') { + return null + } else { + return await RULE[_method](..._args); + } + } + + return await drpyS[invokeMethod](_modulePath, _env, ..._args); + }; + + return RULE; + } + + return env; +} + +// Test Status Reporter +const stats = { + results: [], + pass(name) { this.results.push({name, status: 'PASS'}); }, + fail(name, err) { this.results.push({name, status: 'FAIL', error: err}); }, + skip(name, reason) { this.results.push({name, status: 'SKIP', reason}); }, + summary() { + const total = this.results.length; + const passed = this.results.filter(r => r.status === 'PASS').length; + const failed = this.results.filter(r => r.status === 'FAIL').length; + const skipped = this.results.filter(r => r.status === 'SKIP').length; + + console.log('\n\n=============================================='); + console.log(' TEST SUMMARY REPORT '); + console.log('=============================================='); + console.log(`Total Steps : ${total}`); + console.log(`Passed : ${passed}`); + console.log(`Failed : ${failed}`); + console.log(`Skipped : ${skipped}`); + console.log('----------------------------------------------'); + + this.results.forEach(r => { + let statusIcon = r.status === 'PASS' ? '✅' : (r.status === 'FAIL' ? '❌' : '⚠️'); + let msg = `${statusIcon} [${r.status}] ${r.name}`; + if (r.error) msg += ` - Error: ${r.error}`; + if (r.reason) msg += ` - Reason: ${r.reason}`; + console.log(msg); + }); + console.log('==============================================\n'); + } +}; + +(async () => { + // 设置目标模块路径 + const moduleName = '七猫小说[书]'; + const modulePath = path.join(__dirname, `../js/${moduleName}.js`); + + // Create environment + const env = getEnv(moduleName); + + // Shared variables for test dependencies + let homeResult; + let cateResult; + let searchResult; + let detailResult; + let detailUrl = ''; + + try { + console.log('Initializing module...'); + await drpyS.init(modulePath); + + // 1. 测试 Home/Category (分类列表) + try { + console.log('\n=== Testing Home/Category ==='); + homeResult = await drpyS.home(modulePath, env); + console.log('Home Result:', JSON.stringify(homeResult.class.slice(0, 3))); // 只打印前3个分类 + + if (homeResult && homeResult.class && homeResult.class.length > 0) { + stats.pass('Home/Category'); + } else { + throw new Error('No classes returned'); + } + } catch (e) { + stats.fail('Home/Category', e.message); + // If home fails, we can't test category, but maybe search works? + } + + // 2. 测试 Category Content (一级 - 分类内容) + if (homeResult && homeResult.class && homeResult.class.length > 0) { + try { + // 使用第一个分类的 ID + const classId = homeResult.class[0].type_id; + console.log(`\n=== Testing Category Content (class_id=${classId}) ===`); + // category(filePath, env, tid, pg, filter, extend) + cateResult = await drpyS.category(modulePath, env, classId, 1); + console.log('Category Result Count:', cateResult.list.length); + if (cateResult.list.length > 0) { + console.log('First Item:', cateResult.list[0]); + stats.pass('Category Content'); + } else { + stats.fail('Category Content', 'No items in category'); + } + } catch (e) { + stats.fail('Category Content', e.message); + } + } else { + stats.skip('Category Content', 'Dependency failed: Home/Category'); + } + + // 3. 测试 Search (搜索) + try { + const keyword = '剑来'; + console.log(`\n=== Testing Search (keyword=${keyword}) ===`); + searchResult = await drpyS.search(modulePath, env, keyword); + console.log('Search Result Count:', searchResult.list.length); + if (searchResult.list.length > 0) { + console.log('First Search Item:', searchResult.list[0]); + stats.pass('Search'); + } else { + stats.fail('Search', 'No search results found'); + } + } catch (e) { + stats.fail('Search', e.message); + } + + // 4. 测试 Detail (二级 - 详情) + // 优先使用搜索结果中的 URL (vod_id),如果没有则尝试分类结果 + if (searchResult && searchResult.list && searchResult.list.length > 0) { + detailUrl = searchResult.list[0].vod_id; + } else if (cateResult && cateResult.list && cateResult.list.length > 0) { + detailUrl = cateResult.list[0].vod_id; + } + + if (detailUrl) { + try { + console.log(`\n=== Testing Detail (url=${detailUrl}) ===`); + const detailOrList = await drpyS.detail(modulePath, env, [detailUrl]); + + if (detailOrList.list && Array.isArray(detailOrList.list)) { + detailResult = detailOrList.list[0]; + } else if (Array.isArray(detailOrList)) { + detailResult = detailOrList[0]; + } else { + detailResult = detailOrList; + } + + if (!detailResult) { + throw new Error('Detail result is empty or invalid'); + } + + console.log('Detail Result:', { + vod_name: detailResult.vod_name, + vod_play_from: detailResult.vod_play_from, + // 截取 play_url 防止日志过长 + vod_play_url: detailResult.vod_play_url ? (detailResult.vod_play_url.slice(0, 100) + '...') : 'N/A' + }); + stats.pass('Detail'); + + } catch (e) { + stats.fail('Detail', e.message); + } + } else { + stats.skip('Detail', 'No valid URL found from Search or Category results'); + } + + // 5. 测试 Play (播放/阅读) + if (detailResult && detailResult.vod_play_url) { + try { + console.log('\n=== Testing Play ==='); + // 解析 vod_play_url 获取播放链接 + const flags = detailResult.vod_play_from.split('$$$'); + const urls = detailResult.vod_play_url.split('$$$'); + + // 取第一个播放源的第一个章节 + const firstFlagUrlList = urls[0].split('#'); + const firstChapter = firstFlagUrlList[0]; + // 格式: "章节名$参数" -> "第一章$1747899@@123456@@第一章" + const playUrl = firstChapter.split('$')[1]; + + console.log(`Playing Chapter: ${firstChapter.split('$')[0]}`); + console.log(`Play URL Params: ${playUrl}`); + + const playResult = await drpyS.play(modulePath, env, flags[0], playUrl); + + const logResult = {...playResult}; + if (logResult.url && logResult.url.startsWith('novel://')) { + logResult.url = logResult.url.slice(0, 100) + '... (truncated content)'; + } + console.log('Play Result:', logResult); + + if (playResult.url || playResult.parse === 0) { + stats.pass('Play'); + } else { + stats.fail('Play', 'No play URL or content returned'); + } + + } catch (e) { + stats.fail('Play', e.message); + } + } else { + const reason = !detailResult ? 'Dependency failed: Detail' : 'No vod_play_url in detail'; + stats.skip('Play', reason); + } + + } catch (error) { + console.error('Critical Error during test initialization:', error); + } finally { + stats.summary(); + } +})(); diff --git "a/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\265\207[\347\224\273].php" "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\265\207[\347\224\273].php" new file mode 100644 index 00000000..7523dddd --- /dev/null +++ "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\265\207[\347\224\273].php" @@ -0,0 +1,210 @@ +dbPath = __DIR__ . '/' . $dbName; + + // 尝试查找对应的数据库文件 (如果当前文件名不匹配,尝试查找原版爬虫名对应的db) + if (!file_exists($this->dbPath)) { + $originName = '74P福利图 ᵈᶻ[画].db'; + if (file_exists(__DIR__ . '/' . $originName)) { + $this->dbPath = __DIR__ . '/' . $originName; + } + } + + try { + $this->db = new SQLite3($this->dbPath); + $this->db->busyTimeout(5000); + } catch (Exception $e) { + // 数据库连接失败,可能是文件不存在 + } + } + + public function isVideoFormat($url) { + return false; + } + + public function manualVideoCheck() { + return false; + } + + public function homeContent($filter) { + if (!$this->db) return ['class' => []]; + + $classes = []; + $res = $this->db->query("SELECT tid, name FROM categories"); + while ($row = $res->fetchArray(SQLITE3_ASSOC)) { + $classes[] = [ + "type_id" => $row['tid'], + "type_name" => $row['name'] + ]; + } + return ['class' => $classes, 'filters' => []]; + } + + public function homeVideoContent() { + return ['list' => []]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + if (!$this->db) return ['list' => [], 'page' => $pg, 'pagecount' => 0, 'limit' => 20, 'total' => 0]; + + $limit = 20; + $offset = ($pg - 1) * $limit; + + // 获取总数 + $countStmt = $this->db->prepare("SELECT COUNT(*) as total FROM vods WHERE type_id = :tid"); + $countStmt->bindValue(':tid', $tid, SQLITE3_TEXT); + $countRes = $countStmt->execute(); + $total = 0; + if ($row = $countRes->fetchArray(SQLITE3_ASSOC)) { + $total = $row['total']; + } + + $stmt = $this->db->prepare("SELECT * FROM vods WHERE type_id = :tid ORDER BY crawled_at DESC LIMIT :limit OFFSET :offset"); + $stmt->bindValue(':tid', $tid, SQLITE3_TEXT); + $stmt->bindValue(':limit', $limit, SQLITE3_INTEGER); + $stmt->bindValue(':offset', $offset, SQLITE3_INTEGER); + + $res = $stmt->execute(); + $vlist = []; + while ($row = $res->fetchArray(SQLITE3_ASSOC)) { + $vlist[] = [ + 'vod_id' => $row['vod_id'], + 'vod_name' => $row['vod_name'], + 'vod_pic' => $row['vod_pic'], + 'vod_remarks' => $row['vod_remarks'], + 'style' => ["type" => "rect", "ratio" => 1.33] + ]; + } + + $pageCount = ceil($total / $limit); + + return ['list' => $vlist, 'page' => $pg, 'pagecount' => $pageCount, 'limit' => $limit, 'total' => $total]; + } + + public function detailContent($ids) { + if (!$this->db) return ['list' => []]; + + $vod_id = $ids[0]; + + // 1. 获取视频详情 (关联 categories 获取 type_name) + $stmt = $this->db->prepare(" + SELECT v.*, c.name as type_name + FROM vods v + LEFT JOIN categories c ON v.type_id = c.tid + WHERE v.vod_id = :vod_id + "); + $stmt->bindValue(':vod_id', $vod_id, SQLITE3_TEXT); + $res = $stmt->execute(); + $vod_row = $res->fetchArray(SQLITE3_ASSOC); + + if (!$vod_row) return ['list' => []]; + + $vod = [ + 'vod_id' => $vod_row['vod_id'], + 'vod_name' => $vod_row['vod_name'], + 'vod_pic' => $vod_row['vod_pic'], + 'type_name' => $vod_row['type_name'], + 'vod_content' => $vod_row['vod_content'], + 'vod_play_from' => '', + 'vod_play_url' => '' + ]; + $vod_pk = $vod_row['id']; + + // 2. 获取剧集列表 (关联 play_sources 获取 play_from) + $stmt_ep = $this->db->prepare(" + SELECT e.*, s.name as play_from + FROM episodes e + LEFT JOIN play_sources s ON e.sid = s.id + WHERE e.vod_pk = :vod_pk + "); + $stmt_ep->bindValue(':vod_pk', $vod_pk, SQLITE3_INTEGER); + $res_ep = $stmt_ep->execute(); + + $episodes_map = []; // play_from => [ "name$url" ] + + while ($row = $res_ep->fetchArray(SQLITE3_ASSOC)) { + $play_from = $row['play_from']; + $name = $row['name']; + // 优先使用已解析的 URL,如果没有则使用原始 URL + $url = !empty($row['resolved_url']) ? $row['resolved_url'] : $row['raw_url']; + + if (!isset($episodes_map[$play_from])) { + $episodes_map[$play_from] = []; + } + $episodes_map[$play_from][] = "{$name}\${$url}"; + } + + $play_from_list = []; + $play_url_list = []; + + foreach ($episodes_map as $from => $eps) { + $play_from_list[] = $from; + $play_url_list[] = implode("#", $eps); + } + + $vod['vod_play_from'] = implode("$$$", $play_from_list); + $vod['vod_play_url'] = implode("$$$", $play_url_list); + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + if (!$this->db) return ['list' => [], 'page' => $pg]; + + $limit = 20; + $offset = ($pg - 1) * $limit; + + // 获取总数 + $countStmt = $this->db->prepare("SELECT COUNT(*) as total FROM vods WHERE vod_name LIKE :key"); + $countStmt->bindValue(':key', "%$key%", SQLITE3_TEXT); + $countRes = $countStmt->execute(); + $total = 0; + if ($row = $countRes->fetchArray(SQLITE3_ASSOC)) { + $total = $row['total']; + } + + $stmt = $this->db->prepare("SELECT * FROM vods WHERE vod_name LIKE :key ORDER BY crawled_at DESC LIMIT :limit OFFSET :offset"); + $stmt->bindValue(':key', "%$key%", SQLITE3_TEXT); + $stmt->bindValue(':limit', $limit, SQLITE3_INTEGER); + $stmt->bindValue(':offset', $offset, SQLITE3_INTEGER); + + $res = $stmt->execute(); + $vlist = []; + while ($row = $res->fetchArray(SQLITE3_ASSOC)) { + $vlist[] = [ + 'vod_id' => $row['vod_id'], + 'vod_name' => $row['vod_name'], + 'vod_pic' => $row['vod_pic'], + 'vod_remarks' => $row['vod_remarks'], + 'style' => ["type" => "rect", "ratio" => 1.33] + ]; + } + + $pageCount = ceil($total / $limit); + return ['list' => $vlist, 'page' => $pg, 'pagecount' => $pageCount, 'limit' => $limit, 'total' => $total]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + // id 已经是 detailContent 中返回的 url + // 如果是已解析的 pics:// 链接,直接返回 + // 如果是原始链接,说明爬取时未解析成功,这里直接返回原始链接让客户端尝试处理(虽然本地模式下通常无法处理网络请求,但保持一致性) + return [ + "parse" => 0, + "playUrl" => "", + "url" => $id, + "header" => "" + ]; + } +} diff --git "a/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].db" "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].db" new file mode 100644 index 00000000..53557b2d Binary files /dev/null and "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].db" differ diff --git "a/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..4ca5b177 --- /dev/null +++ "b/spider/php/74P\347\246\217\345\210\251\345\233\276 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,305 @@ +baseUrl = "https://www.74p.net"; + } + + public function isVideoFormat($url) { + return false; + } + + public function manualVideoCheck() { + return false; + } + + private function getHeader() { + return [ + "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer" => $this->baseUrl . '/', + "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Connection" => "keep-alive" + ]; + } + + private function fetchHtml($url, $referer = "") { + $headers = $this->getHeader(); + if ($referer) $headers['Referer'] = $referer; + + $options = [ + 'headers' => $headers + ]; + return $this->fetch($url, $options); + } + + public function homeContent($filter) { + $cats = [ + ["type_name" => "=== 写真 ===", "type_id" => "ignore"], + ["type_name" => "秀人网", "type_id" => "xiurenwang"], + ["type_name" => "语画界", "type_id" => "yuhuajie"], + ["type_name" => "花漾", "type_id" => "huayang"], + ["type_name" => "星颜社", "type_id" => "xingyanshe"], + ["type_name" => "嗲囡囡", "type_id" => "feilin"], + ["type_name" => "爱蜜社", "type_id" => "aimishe"], + ["type_name" => "波萝社", "type_id" => "boluoshe"], + ["type_name" => "尤物馆", "type_id" => "youwuguan"], + ["type_name" => "蜜桃社", "type_id" => "miitao"], + ["type_name" => "=== 漫画 ===", "type_id" => "ignore"], + ["type_name" => "日本漫画", "type_id" => "comic/category/jp"], + ["type_name" => "韩国漫画", "type_id" => "comic/category/kr"], + ["type_name" => "=== 小说 ===", "type_id" => "ignore"], + ["type_name" => "都市", "type_id" => "novel/category/Urban"], + ["type_name" => "乱伦", "type_id" => "novel/category/Incestuous"], + ["type_name" => "玄幻", "type_id" => "novel/category/Xuanhuan"], + ["type_name" => "武侠", "type_id" => "novel/category/Wuxia"] + ]; + + $validCats = []; + foreach ($cats as $c) { + if ($c['type_id'] != 'ignore') { + $validCats[] = $c; + } + } + return ['class' => $validCats, 'filters' => []]; + } + + public function homeVideoContent() { + return ['list' => []]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $url = "{$this->baseUrl}/{$tid}/page/{$pg}"; + return $this->getPostList($url, $pg); + } + + private function getPostList($url, $pg) { + $html = $this->fetchHtml($url); + $vlist = []; + + if ($html) { + $listBlock = $html; + if (preg_match('/(?:id="index_ajax_list"|class="site-main")[^>]*>(.*?)<(?:footer|aside)/s', $html, $match)) { + $listBlock = $match[1]; + } + + if (preg_match_all('/]*>(.*?)<\/li>/s', $listBlock, $items)) { + foreach ($items[1] as $item) { + if (!preg_match('/href=["\']([^"\']+)["\']/', $item, $hrefMatch)) continue; + $href = $hrefMatch[1]; + + if (strpos($href, '.css') !== false || strpos($href, '.js') !== false || strpos($href, 'templates/') !== false || strpos($href, 'wp-includes') !== false) continue; + + $pic = ""; + if (preg_match('/data-original=["\']([^"\']+)["\']/', $item, $imgMatch)) { + $pic = $imgMatch[1]; + } elseif (preg_match('/src=["\']([^"\']+)["\']/', $item, $imgMatch)) { + $pic = $imgMatch[1]; + } + + if (!$pic) $pic = "https://www.74p.net/static/images/cover.png"; + + $name = ""; + if (preg_match('/title=["\']([^"\']+)["\']/', $item, $titleMatch)) { + $name = $titleMatch[1]; + } else { + $name = trim(strip_tags($item)); + $name = explode("\n", $name)[0]; + } + + if (strpos($name, '.') === 0 || strpos($name, '{') !== false || strlen($name) > 300) continue; // strlen 100 in python is roughly 300 bytes in utf8 php maybe + + if (strpos($href, '//') === 0) $href = 'https:' . $href; + elseif (strpos($href, '/') === 0) $href = $this->baseUrl . $href; + + $vlist[] = [ + 'vod_id' => $href, + 'vod_name' => $name, + 'vod_pic' => $pic, + 'vod_remarks' => '点击查看', + 'style' => ["type" => "rect", "ratio" => 1.33] + ]; + } + } + } + + $pageCount = (count($vlist) >= 15) ? $pg + 1 : $pg; + return ['list' => $vlist, 'page' => $pg, 'pagecount' => $pageCount, 'limit' => 20, 'total' => 9999]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $searchPath = "/search/{$key}"; + $referer = (strpos($key, "漫画") !== false) ? "{$this->baseUrl}/comic" : "{$this->baseUrl}/novel"; + + if ($pg > 1) $url = "{$this->baseUrl}{$searchPath}/page/{$pg}"; + else $url = "{$this->baseUrl}{$searchPath}"; + + // Temporarily override fetchHtml's referer logic by passing it + // Or actually fetchHtml supports passing referer. + // But getPostList calls fetchHtml without referer. + // Let's modify getPostList to accept referer or just set global referer. + // Simpler: Just rely on default referer or specific one. + // Python code sets specific referer. + + // Let's manually fetch here to respect logic, or just reuse getPostList which uses default referer (baseUrl) + // Python code: if "漫画" in key: headers['Referer'] = ... + // Since getPostList calls fetchHtml($url), and fetchHtml uses default headers if not provided. + // Let's just use default headers for simplicity as search usually works without specific referer too. + + return $this->getPostList($url, $pg); + } + + public function detailContent($ids) { + $url = $ids[0]; + $html = $this->fetchHtml($url); + if (!$html) return ['list' => []]; + + $vod = [ + 'vod_id' => $url, + 'vod_name' => '', + 'vod_pic' => '', + 'type_name' => '漫画', + 'vod_content' => '', + 'vod_play_from' => '74P漫画', + 'vod_play_url' => '' + ]; + + if (preg_match('/]*>(.*?)<\/h1>/', $html, $h1)) { + $vod['vod_name'] = trim(strip_tags($h1[1])); + } + + $contentHtml = ""; + if (preg_match('/(?:id="content"|class="entry-content"|class="single-content")[^>]*>(.*?)<(?:div class="related|footer|aside|section)/s', $html, $match)) { + $contentHtml = $match[1]; + $vod['vod_content'] = mb_substr(trim(strip_tags($contentHtml)), 0, 200); + + if (preg_match('/]+src=["\']([^"\']+)["\']/', $contentHtml, $imgMatch)) { + $pic = $imgMatch[1]; + if (strpos($pic, '//') === 0) $pic = 'https:' . $pic; + elseif (strpos($pic, '/') === 0) $pic = $this->baseUrl . $pic; + $vod['vod_pic'] = $pic; + } + } + + // 如果上述方式未找到封面,尝试全局匹配第一张非 logo/icon 图片 + if (empty($vod['vod_pic']) && preg_match_all('/]+src=["\']([^"\']+)["\']/', $html, $matches)) { + foreach ($matches[1] as $src) { + if (preg_match('/(logo|icon|avatar|\.gif)/i', $src)) continue; + + if (strpos($src, '//') === 0) $src = 'https:' . $src; + elseif (strpos($src, '/') === 0) $src = $this->baseUrl . $src; + + $vod['vod_pic'] = $src; + break; + } + } + + $playList = []; + + // 1. 查找章节列表 + if (preg_match_all('/]+href=["\']([^"\']*\/(?:comic|novel)\/chapter\/[^"\']+)["\'][^>]*>(.*?)<\/a>/', $html, $links, PREG_SET_ORDER)) { + foreach ($links as $link) { + $href = $link[1]; + $name = trim($link[2]); + + if (strpos($href, '//') === 0) $href = 'https:' . $href; + elseif (strpos($href, '/') === 0) $href = $this->baseUrl . $href; + + $playList[] = "{$name}\${$href}"; + } + } else { + // 2. 无目录,单页 + $playList[] = "在线观看\${$url}"; + } + + $vod['vod_play_url'] = implode("#", $playList); + return ['list' => [$vod]]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + $images = $this->scrapeAllImages($id); + $novelData = implode("&&", $images); + + return [ + "parse" => 0, + "playUrl" => "", + "url" => "pics://{$novelData}", + "header" => "" + ]; + } + + private function scrapeAllImages($url) { + $images = []; + $visited = []; + $currentUrl = $url; + $page = 1; + $maxPages = 50; + + while ($page <= $maxPages) { + if (in_array($currentUrl, $visited)) break; + $visited[] = $currentUrl; + + $html = $this->fetchHtml($currentUrl); + if (!$html) break; + + $contentHtml = $html; + if (preg_match('/(?:id="content"|class="entry-content"|class="single-content")[^>]*>(.*?)<(?:div class="related|footer|section)/s', $html, $match)) { + $contentHtml = $match[1]; + } + + if (preg_match_all('/]+(?:src|data-original|data-src)=["\']([^"\']+)["\']/', $contentHtml, $matches)) { + foreach ($matches[1] as $src) { + $lowerSrc = strtolower($src); + if (strpos($lowerSrc, '.gif') !== false || strpos($lowerSrc, '.svg') !== false || strpos($lowerSrc, 'logo') !== false || strpos($lowerSrc, 'avatar') !== false || strpos($lowerSrc, 'icon') !== false) continue; + if (strpos($lowerSrc, '/covers/') !== false) continue; // 过滤封面图推荐 + + + if (strpos($src, '//') === 0) $src = 'https:' . $src; + elseif (strpos($src, '/') === 0) $src = $this->baseUrl . $src; + + if (!in_array($src, $images)) { + $images[] = $src; + } + } + } + + $nextUrl = null; + if (preg_match('/]+href=["\']([^"\']+)["\'][^>]*>(?:下一页|Next|»)<\/a>/i', $html, $nextMatch)) { + $nextUrl = $nextMatch[1]; + } elseif (preg_match('/]+href=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*next[^"\']*["\']/', $html, $nextMatch)) { + $nextUrl = $nextMatch[1]; + } + + if (!$nextUrl && strpos($currentUrl, '/comic/chapter/') === false && strpos($currentUrl, 'page') !== false) { + // Try auto-increment if pagination pattern detected + $parts = explode('/', rtrim($currentUrl, '/')); + $lastPart = end($parts); + if (is_numeric($lastPart)) { + $base = substr($currentUrl, 0, strrpos($currentUrl, '/')); + $nextUrl = "{$base}/" . ($page + 1); + } + } + + if ($nextUrl) { + if (strpos($nextUrl, '//') === 0) $nextUrl = 'https:' . $nextUrl; + elseif (strpos($nextUrl, '/') === 0) $nextUrl = $this->baseUrl . $nextUrl; + } else { + break; + } + + $currentUrl = $nextUrl; + $page++; + } + + return $images; + } +} + +(new Spider())->run(); diff --git "a/spider/php/B\347\253\231 \341\265\210\341\266\273.php" "b/spider/php/B\347\253\231 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..c2c5201d --- /dev/null +++ "b/spider/php/B\347\253\231 \341\265\210\341\266\273.php" @@ -0,0 +1,266 @@ +headers['Referer'] = "https://www.bilibili.com"; + // 配置初始 Cookie + // 实际使用时,建议通过 ext 传入 cookie + $configCookie = 'buvid3=xxxx; SESSDATA=xxxx;'; + + // 尝试从 extend 获取 cookie (假设 extend 是 JSON 字符串或直接是 cookie 字符串) + // 这里简化处理:如果 extend 包含 SESSDATA,则认为是 cookie + if (!empty($extend)) { + if (strpos($extend, 'SESSDATA') !== false) { + $configCookie = $extend; + } elseif (is_array($extend) && isset($extend['cookie'])) { + $configCookie = $extend['cookie']; + } else { + // 尝试解析 json + $json = json_decode($extend, true); + if (isset($json['cookie'])) { + $configCookie = $json['cookie']; + } + } + } + + $this->cookie = $this->parseCookie($configCookie); + } + + private function parseCookie($cookieStr) { + if (empty($cookieStr)) return []; + $cookies = []; + $pairs = explode(';', $cookieStr); + foreach ($pairs as $pair) { + $pair = trim($pair); + if (strpos($pair, '=') !== false) { + list($name, $value) = explode('=', $pair, 2); + $cookies[trim($name)] = trim($value); + } + } + return $cookies; + } + + private function buildCookieString() { + $pairs = []; + foreach ($this->cookie as $name => $value) { + $pairs[] = $name . '=' . $value; + } + return implode('; ', $pairs); + } + + // 覆盖父类 fetch 以自动添加 cookie + protected function fetch($url, $options = [], $headers = []) { + if (!isset($options['cookie'])) { + $cookieStr = $this->buildCookieString(); + if (!empty($cookieStr)) { + $options['cookie'] = $cookieStr; + } + } + return parent::fetch($url, $options, $headers); + } + + public function homeContent($filter = []) { + $classes = [ + ["type_id" => "沙雕仙逆", "type_name" => "傻屌仙逆"], + ["type_id" => "沙雕动画", "type_name" => "沙雕动画"], + ["type_id" => "纪录片超清", "type_name" => "纪录片"], + ["type_id" => "演唱会超清", "type_name" => "演唱会"], + ["type_id" => "音乐超清", "type_name" => "流行音乐"], + ["type_id" => "美食超清", "type_name" => "美食"], + ["type_id" => "食谱", "type_name" => "食谱"], + ["type_id" => "体育超清", "type_name" => "体育"], + ["type_id" => "球星", "type_name" => "球星"], + ["type_id" => "中小学教育", "type_name" => "教育"], + ["type_id" => "幼儿教育", "type_name" => "幼儿教育"], + ["type_id" => "旅游", "type_name" => "旅游"], + ["type_id" => "风景4K", "type_name" => "风景"], + ["type_id" => "说案", "type_name" => "说案"], + ["type_id" => "知名UP主", "type_name" => "知名UP主"], + ["type_id" => "探索发现超清", "type_name" => "探索发现"], + ["type_id" => "鬼畜", "type_name" => "鬼畜"], + ["type_id" => "搞笑超清", "type_name" => "搞笑"], + ["type_id" => "儿童超清", "type_name" => "儿童"], + ["type_id" => "动物世界超清", "type_name" => "动物世界"], + ["type_id" => "相声小品超清", "type_name" => "相声小品"], + ["type_id" => "戏曲", "type_name" => "戏曲"], + ["type_id" => "解说", "type_name" => "解说"], + ["type_id" => "演讲", "type_name" => "演讲"], + ["type_id" => "小姐姐超清", "type_name" => "小姐姐"], + ["type_id" => "荒野求生超清", "type_name" => "荒野求生"], + ["type_id" => "健身", "type_name" => "健身"], + ["type_id" => "帕梅拉", "type_name" => "帕梅拉"], + ["type_id" => "太极拳", "type_name" => "太极拳"], + ["type_id" => "广场舞", "type_name" => "广场舞"], + ["type_id" => "舞蹈", "type_name" => "舞蹈"], + ["type_id" => "音乐", "type_name" => "音乐"], + ["type_id" => "歌曲", "type_name" => "歌曲"], + ["type_id" => "MV4K", "type_name" => "MV"], + ["type_id" => "舞曲超清", "type_name" => "舞曲"], + ["type_id" => "4K", "type_name" => "4K"], + ["type_id" => "电影", "type_name" => "电影"], + ["type_id" => "电视剧", "type_name" => "电视剧"], + ["type_id" => "白噪音超清", "type_name" => "白噪音"], + ["type_id" => "考公考证", "type_name" => "考公考证"], + ["type_id" => "平面设计教学", "type_name" => "平面设计教学"], + ["type_id" => "软件教程", "type_name" => "软件教程"], + ["type_id" => "Windows", "type_name" => "Windows"] + ]; + return ['class' => $classes]; + } + + public function homeVideoContent() { + $url = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1'; + $data = json_decode($this->fetch($url), true); + + $videos = []; + if (isset($data['data']['list'])) { + foreach ($data['data']['list'] as $item) { + $videos[] = [ + 'vod_id' => $item['aid'], + 'vod_name' => strip_tags($item['title']), + 'vod_pic' => $item['pic'], + 'vod_remarks' => $this->formatDuration($item['duration']) + ]; + } + } + return ['list' => $videos]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $page = max(1, intval($pg)); + + $url = 'https://api.bilibili.com/x/web-interface/search/type'; + $params = [ + 'search_type' => 'video', + 'keyword' => $tid, + 'page' => $page + ]; + $url .= '?' . http_build_query($params); + + $data = json_decode($this->fetch($url), true); + + $videos = []; + if (isset($data['data']['result'])) { + foreach ($data['data']['result'] as $item) { + if ($item['type'] !== 'video') continue; + + $videos[] = [ + 'vod_id' => $item['aid'], + 'vod_name' => strip_tags($item['title']), + 'vod_pic' => 'https:' . $item['pic'], + 'vod_remarks' => $this->formatSearchDuration($item['duration']) + ]; + } + } + + $pageCount = $data['data']['numPages'] ?? 1; + $total = $data['data']['numResults'] ?? count($videos); + + return $this->pageResult($videos, $page, $total, 20); + } + + public function searchContent($key, $quick = false, $pg = 1) { + return $this->categoryContent($key, $pg); + } + + public function detailContent($ids) { + if (empty($ids)) return ['list' => []]; + $vid = $ids[0]; + + $url = 'https://api.bilibili.com/x/web-interface/view?aid=' . $vid; + $data = json_decode($this->fetch($url), true); + + if (!isset($data['data'])) { + return ['list' => []]; + } + + $video = $data['data']; + + // 构建播放列表 + $playUrl = ''; + foreach ($video['pages'] as $index => $page) { + $part = $page['part'] ?: '第' . ($index + 1) . '集'; + // 构造 playId: avid_cid + $playUrl .= "{$part}\${$vid}_{$page['cid']}#"; + } + + $vod = [ + "vod_id" => $vid, + "vod_name" => strip_tags($video['title']), + "vod_pic" => $video['pic'], + "vod_content" => $video['desc'], + "vod_play_from" => "B站视频", + "vod_play_url" => rtrim($playUrl, '#') + ]; + + return ['list' => [$vod]]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + if (strpos($id, '_') !== false) { + list($avid, $cid) = explode('_', $id); + } else { + return ['parse' => 0, 'url' => '', 'error' => '无效的视频ID格式']; + } + + $url = 'https://api.bilibili.com/x/player/playurl'; + $params = [ + 'avid' => $avid, + 'cid' => $cid, + 'qn' => 112, // 原画质量 + 'fnval' => 0, + ]; + $url .= '?' . http_build_query($params); + + $data = json_decode($this->fetch($url), true); + + if (!isset($data['data']) || $data['code'] !== 0) { + return ['parse' => 0, 'url' => '', 'error' => '获取播放地址失败']; + } + + // 直接返回第一个播放地址 + if (isset($data['data']['durl'][0]['url'])) { + $playUrl = $data['data']['durl'][0]['url']; + + $headers = $this->headers; + $headers['Referer'] = 'https://www.bilibili.com/video/av' . $avid; + $headers['Origin'] = 'https://www.bilibili.com'; + + return [ + 'parse' => 0, + 'url' => $playUrl, + 'header' => $headers, + 'danmaku' => "https://api.bilibili.com/x/v1/dm/list.so?oid={$cid}" + ]; + } + + return ['parse' => 0, 'url' => '', 'error' => '无法获取播放地址']; + } + + // 工具函数 + private function formatDuration($seconds) { + if ($seconds <= 0) return '00:00'; + $minutes = floor($seconds / 60); + $secs = $seconds % 60; + return sprintf('%02d:%02d', $minutes, $secs); + } + + private function formatSearchDuration($duration) { + $parts = explode(':', $duration); + if (count($parts) === 2) { + return $duration; + } + return '00:00'; + } +} + +(new Spider())->run(); diff --git "a/spider/php/PHP\345\206\231\346\272\220(\351\201\223\351\225\277).pdf" "b/spider/php/PHP\345\206\231\346\272\220(\351\201\223\351\225\277).pdf" new file mode 100644 index 00000000..cc46c03f Binary files /dev/null and "b/spider/php/PHP\345\206\231\346\272\220(\351\201\223\351\225\277).pdf" differ diff --git a/spider/php/_bridge.php b/spider/php/_bridge.php new file mode 100644 index 00000000..76082651 --- /dev/null +++ b/spider/php/_bridge.php @@ -0,0 +1,101 @@ + ... + +// Disable error output to stdout to avoid breaking JSON +ini_set('display_errors', 0); +error_reporting(E_ALL); +date_default_timezone_set('Asia/Shanghai'); + +define('DRPY_BRIDGE', true); + +// Helper to send JSON response +function sendResponse($data) { + // Ensure data is UTF-8 encoded + // echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + echo json_encode($data, JSON_UNESCAPED_UNICODE); + exit(0); +} + +// Helper to send Error response +function sendError($message, $trace = '') { + echo json_encode([ + 'error' => $message, + 'traceback' => $trace + ], JSON_UNESCAPED_UNICODE); + exit(1); +} + +// Set global error handler to catch warnings/notices and prevent them from corrupting stdout +set_error_handler(function($errno, $errstr, $errfile, $errline) { + // We can log errors to stderr so they don't mess up stdout JSON + fwrite(STDERR, "PHP Error [$errno]: $errstr in $errfile on line $errline\n"); + return false; // Let normal error handler continue (but display_errors is 0 so no stdout) +}); + +// Set exception handler +set_exception_handler(function($e) { + sendError($e->getMessage(), $e->getTraceAsString()); +}); + +try { + // 1. Parse Arguments + if ($argc < 4) { + throw new Exception("Invalid arguments. Usage: php _bridge.php [args...]"); + } + + $filePath = $argv[1]; + $methodName = $argv[2]; + $envJson = $argv[3]; + $env = json_decode($envJson, true) ?? []; + + $args = []; + for ($i = 4; $i < $argc; $i++) { + // Args are passed as individual JSON strings + $args[] = json_decode($argv[$i], true); + } + + // 2. Load File + if (!file_exists($filePath)) { + throw new Exception("File not found: $filePath"); + } + + // Capture any output during require (e.g. trailing newlines or echoes in file) + ob_start(); + require_once $filePath; + $output = ob_get_clean(); + if (trim($output) !== '') { + fwrite(STDERR, "Output during require: $output\n"); + } + + if (!class_exists('Spider')) { + throw new Exception("Class 'Spider' not found in $filePath"); + } + + // 3. Instantiate Spider + $spider = new Spider(); + + // AUTO-INIT: Call init() before any other method if it's not init itself + if ($methodName !== 'init' && method_exists($spider, 'init')) { + $extend = $env['ext'] ?? ''; + $spider->init($extend); + } + + // 4. Check Method + if (!method_exists($spider, $methodName)) { + // If the method doesn't exist, we might be calling a mapped method that isn't implemented. + // Or maybe we should check for magic method __call? + // For now, throw error. + throw new Exception("Method '$methodName' not found in Spider class"); + } + + // 5. Call Method + $result = call_user_func_array([$spider, $methodName], $args); + + // 6. Return Result + sendResponse($result); + +} catch (Throwable $e) { + sendError($e->getMessage(), $e->getTraceAsString()); +} diff --git a/spider/php/_crawler_bridge.php b/spider/php/_crawler_bridge.php new file mode 100644 index 00000000..078a139d --- /dev/null +++ b/spider/php/_crawler_bridge.php @@ -0,0 +1,73 @@ + [args...] + +ini_set('display_errors', 0); // Disable error printing to stdout +error_reporting(E_ALL); +date_default_timezone_set('Asia/Shanghai'); + +header('Content-Type: application/json'); + +$output = ['status' => 'error', 'data' => null, 'message' => '']; + +try { + if ($argc < 3) { + throw new Exception("Usage: php crawler_bridge.php [args...]"); + } + + $spiderPath = $argv[1]; + $method = $argv[2]; + $args = array_slice($argv, 3); + + if (!file_exists($spiderPath)) { + throw new Exception("Spider file not found: $spiderPath"); + } + + // Capture any output during include + ob_start(); + require_once $spiderPath; + ob_end_clean(); + + if (!class_exists('Spider')) { + throw new Exception("Class 'Spider' not found in $spiderPath"); + } + + $spider = new Spider(); + if (method_exists($spider, 'init')) { + $spider->init(); + } + + if (!method_exists($spider, $method)) { + throw new Exception("Method '$method' not found in Spider class"); + } + + // Call method with args + // Note: Args passed from CLI are strings. Some methods might expect specific types. + // However, PHP is loosely typed, so it usually works. + // Special handling for extend field or complex structures might be needed if passed via CLI, + // but standard DrPy methods usually take simple scalars (tid, page, filter) or arrays. + // For complex args (like filter array), we might need to decode JSON passed as string. + + $methodArgs = []; + foreach ($args as $arg) { + // Try to decode JSON args if they look like JSON + $decoded = json_decode($arg, true); + if (json_last_error() === JSON_ERROR_NONE) { + $methodArgs[] = $decoded; + } else { + $methodArgs[] = $arg; + } + } + + $result = call_user_func_array([$spider, $method], $methodArgs); + + $output['status'] = 'success'; + $output['data'] = $result; + +} catch (Exception $e) { + $output['message'] = $e->getMessage(); + $output['trace'] = $e->getTraceAsString(); +} + +echo json_encode($output, JSON_UNESCAPED_UNICODE); diff --git a/spider/php/config.json b/spider/php/config.json new file mode 100644 index 00000000..937bbf51 --- /dev/null +++ b/spider/php/config.json @@ -0,0 +1,141 @@ +{ + "parses": [ + { + "name": "J1", + "url": "https://kalbim.xatut.top/kalbim2025/781718/play/video_player.php?url=", + "type": 1, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "J2", + "url": "http://sspa8.top:8100/api/?key=1060089351&url=", + "type": 1, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "J芒果4k", + "url": "http://mg.itufm.top/mg.php?url=", + "type": 1, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W花旗", + "url": "https://www.huaqi.live/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W冰豆", + "url": "https://bd.jx.cn/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W盘古", + "url": "https://www.playm3u8.cn/jiexi.php?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W1", + "url": "https://jx.xymp4.cc/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W3", + "url": "https://yparse.ik9.cc/index.php?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W4", + "url": "https://jiexi.site/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W5", + "url": "https://jx.m3u8.tv/jiexi/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W7", + "url": "https://www.pangujiexi.com/jiexi/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W8", + "url": "https://www.pouyun.com/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "W9", + "url": "https://jx.xmflv.com/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "Wa", + "url": "https://jx.xmflv.cc/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "Wb", + "url": "https://jx.yparse.com/index.php?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "Wc", + "url": "https://www.8090g.cn/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + }, + { + "name": "Wz", + "url": "https://www.ckplayer.vip/jiexi/?url=", + "type": 0, + "header": { + "User-Agent": "Mozilla/5.0" + } + } + ], + "lives": [] +} \ No newline at end of file diff --git a/spider/php/config.php b/spider/php/config.php new file mode 100644 index 00000000..839289ae --- /dev/null +++ b/spider/php/config.php @@ -0,0 +1,115 @@ + "php_" . $filename, + "name" => $filename . "(PHP)", + "type" => 4, + "api" => $baseUrl . "/" . $filename . ".php", + "searchable" => 1, + "quickSearch" => 1, + "changeable" => 0 + ]; + + if (strpos($filename, '[书]') !== false) { + $site['类型'] = '小说'; + } elseif (strpos($filename, '[画]') !== false) { + $site['类型'] = '漫画'; + } + + $sites[] = $site; +} + +// ================== +// 2. 尝试加载 index.json (同级) 或 ../drpy-node/index.json 或 ../../drpy-node/index.json +// ================== +$possiblePaths = [ + $dir . '/config.json', + $dir . '/index.json', + $dir . '/../drpy-node/index.json', + $dir . '/../../drpy-node/index.json' +]; + +$indexJsonPath = false; +foreach ($possiblePaths as $path) { + $realPath = realpath($path); + if ($realPath && is_file($realPath)) { + $indexJsonPath = $realPath; + break; + } +} + +if ($indexJsonPath && is_file($indexJsonPath)) { + $content = file_get_contents($indexJsonPath); + $json = json_decode($content, true); + + // JSON 合法并且是数组 + if (is_array($json)) { + // 替换 sites + $json['sites'] = $sites; + + echo json_encode( + $json, + JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES + ); + exit; + } +} + +// ================== +// 3. 找不到或失败,回退只返回 sites +// ================== +echo json_encode( + ["sites" => $sites], + JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES +); + diff --git a/spider/php/crawler.py b/spider/php/crawler.py new file mode 100644 index 00000000..4cf5d793 --- /dev/null +++ b/spider/php/crawler.py @@ -0,0 +1,503 @@ +import subprocess +import sqlite3 +import json +import os +import sys +import time +import argparse +import threading +import queue +from datetime import datetime +from concurrent.futures import ThreadPoolExecutor + +# --- 用户配置区域 (User Configuration) --- +# 默认使用的PHP爬虫文件路径 +# 获取当前脚本所在目录 +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +# 获取项目根目录 (假设脚本在 scripts/python,根目录在 ../../) +PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, "../../")) +DEFAULT_SPIDER = os.path.join(SCRIPT_DIR, "74P福利图 ᵈᶻ[画].php") + +# 每个分类默认最大爬取页数 (设置为 0 或 None 表示不限制,直到爬完) +DEFAULT_MAX_PAGES = 1 +# 默认并发线程数 +DEFAULT_THREADS = 8 +# 是否解析最终播放地址 (True: 解析并存入resolved_url, False: 只存入原始链接) +RESOLVE_FINAL_URLS = True +# PHP 命令路径 +PHP_CMD = "php" +# 桥接脚本路径 +BRIDGE_SCRIPT = os.path.join(SCRIPT_DIR, "_crawler_bridge.php") + +# --- 数据库管理 (Database Manager) --- +class DBManager: + def __init__(self, db_path): + # check_same_thread=False 允许在多线程中使用同一个连接,但需要我们自己加锁 + self.conn = sqlite3.connect(db_path, check_same_thread=False) + self.cursor = self.conn.cursor() + self.lock = threading.Lock() + self.init_tables() + self._source_cache = {} + + def init_tables(self): + with self.lock: + # 优化:移除 source_file 字段 (假设每个DB只对应一个源) + # 优化:移除 type_name (通过关联查询获取) + # 优化:crawled_at 使用 INTEGER 时间戳 + + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS categories ( + tid TEXT PRIMARY KEY, + name TEXT + ) + ''') + + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS vods ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + vod_id TEXT UNIQUE, + vod_name TEXT, + type_id TEXT, + vod_pic TEXT, + vod_remarks TEXT, + vod_content TEXT, + crawled_at INTEGER, + FOREIGN KEY(type_id) REFERENCES categories(tid) + ) + ''') + + # 新增:播放源表 (归一化 play_from) + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS play_sources ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE + ) + ''') + + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS episodes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + vod_pk INTEGER, + sid INTEGER, + name TEXT, + raw_url TEXT, + resolved_url TEXT, + FOREIGN KEY(vod_pk) REFERENCES vods(id), + FOREIGN KEY(sid) REFERENCES play_sources(id) + ) + ''') + self.conn.commit() + + def get_or_create_source(self, name): + # 简单缓存 + if name in self._source_cache: + return self._source_cache[name] + + with self.lock: + try: + self.cursor.execute('INSERT OR IGNORE INTO play_sources (name) VALUES (?)', (name,)) + self.cursor.execute('SELECT id FROM play_sources WHERE name = ?', (name,)) + row = self.cursor.fetchone() + if row: + sid = row[0] + self._source_cache[name] = sid + return sid + return 0 + except Exception as e: + print(f"[DB Error] get_or_create_source: {e}") + return 0 + + def save_category(self, tid, name): + with self.lock: + try: + self.cursor.execute('INSERT OR IGNORE INTO categories (tid, name) VALUES (?, ?)', + (tid, name)) + # 如果名称更新了,也可以 update + self.cursor.execute('UPDATE categories SET name = ? WHERE tid = ? AND name != ?', (name, tid, name)) + self.conn.commit() + except Exception as e: + print(f"[DB Error] save_category: {e}") + + def item_exists(self, vod_id): + with self.lock: + try: + self.cursor.execute('SELECT 1 FROM vods WHERE vod_id = ?', (vod_id,)) + return self.cursor.fetchone() is not None + except Exception as e: + print(f"[DB Error] item_exists: {e}") + return False + + def save_vod(self, data): + with self.lock: + try: + self.cursor.execute(''' + INSERT OR REPLACE INTO vods (vod_id, vod_name, type_id, vod_pic, vod_remarks, vod_content, crawled_at) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', ( + data.get('vod_id'), + data.get('vod_name'), + data.get('type_id'), + data.get('vod_pic'), + data.get('vod_remarks'), + data.get('vod_content'), + int(time.time()) + )) + vod_pk = self.cursor.lastrowid + if vod_pk == 0: + self.cursor.execute('SELECT id FROM vods WHERE vod_id = ?', (data.get('vod_id'),)) + res = self.cursor.fetchone() + if res: vod_pk = res[0] + + self.conn.commit() + return vod_pk + except Exception as e: + print(f"[DB Error] save_vod: {e}") + return None + + def save_episodes(self, vod_pk, episodes): + # 预处理 source_id 以减少锁内操作时间 + # 但 get_or_create_source 本身也加锁,所以这里可以先收集 + processed_eps = [] + for ep in episodes: + sid = self.get_or_create_source(ep['play_from']) + processed_eps.append((sid, ep['name'], ep['url'], ep.get('resolved_url', ''))) + + with self.lock: + try: + self.cursor.execute('DELETE FROM episodes WHERE vod_pk = ?', (vod_pk,)) + self.cursor.executemany(''' + INSERT INTO episodes (vod_pk, sid, name, raw_url, resolved_url) + VALUES (?, ?, ?, ?, ?) + ''', [(vod_pk, sid, name, raw_url, res_url) for sid, name, raw_url, res_url in processed_eps]) + self.conn.commit() + except Exception as e: + print(f"[DB Error] save_episodes: {e}") + + def close(self): + self.conn.close() + +# --- PHP 桥接调用 (PHP Bridge) --- +class PHPBridge: + def __init__(self, spider_path): + self.spider_path = spider_path + + def call(self, method, *args): + # 构建命令 + cmd = [PHP_CMD, BRIDGE_SCRIPT, self.spider_path, method] + cmd_args = [] + for arg in args: + if isinstance(arg, (dict, list)): + cmd_args.append(json.dumps(arg)) + else: + cmd_args.append(str(arg)) + cmd.extend(cmd_args) + + try: + # subprocess.run 是同步阻塞的,但在多线程中调用是安全的 + result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8') + if result.returncode != 0: + if "Warning" not in result.stderr and "Notice" not in result.stderr: + pass + return None + + output = result.stdout.strip() + try: + json_res = json.loads(output) + if json_res['status'] == 'success': + return json_res['data'] + else: + return None + except json.JSONDecodeError: + return None + + except Exception as e: + print(f"[Bridge Error] {e}") + return None + +# --- 任务追踪器 (Task Tracker) --- +class TaskTracker: + def __init__(self): + self.lock = threading.Lock() + self.cond = threading.Condition(self.lock) + self.pending = 0 + + def add(self, n=1): + with self.lock: + self.pending += n + + def done(self): + with self.lock: + self.pending -= 1 + if self.pending == 0: + self.cond.notify_all() + + def wait_until_done(self): + with self.lock: + while self.pending > 0: + self.cond.wait() + +# --- 统计与监控 (Stats & Monitor) --- +class Stats: + def __init__(self): + self.lock = threading.Lock() + self.categories_found = 0 + self.pages_scanned = 0 + self.items_found = 0 + self.items_processed = 0 + self.items_skipped = 0 + self.episodes_resolved = 0 + self.errors = 0 + self.start_time = time.time() + + def inc(self, field, count=1): + with self.lock: + setattr(self, field, getattr(self, field) + count) + +# --- 爬虫逻辑 (Crawler Logic) --- +class Crawler: + def __init__(self, spider_path, db_path, max_pages=DEFAULT_MAX_PAGES, max_workers=DEFAULT_THREADS): + self.spider_path = spider_path + self.bridge = PHPBridge(spider_path) + self.db = DBManager(db_path) + self.max_pages = max_pages + self.max_workers = max_workers + self.stats = Stats() + self.executor = ThreadPoolExecutor(max_workers=max_workers) + self.tracker = TaskTracker() + self.running = True + + # 启动监控线程 + self.monitor_thread = threading.Thread(target=self.monitor_loop, daemon=True) + self.monitor_thread.start() + + def submit_task(self, func, *args): + self.tracker.add() + self.executor.submit(self._wrap_task, func, *args) + + def _wrap_task(self, func, *args): + try: + func(*args) + except Exception as e: + print(f"[Task Error] {e}") + self.stats.inc('errors') + finally: + self.tracker.done() + + def run(self): + print(f"🚀 开始并发爬取: {os.path.basename(self.spider_path)}") + print(f"⚙️ 配置: 最大线程={self.max_workers}, 每个分类最大页数={self.max_pages}, 解 析地址={RESOLVE_FINAL_URLS}") + + # 1. 获取首页分类 + home_data = self.bridge.call('homeContent', True) + if not home_data or 'class' not in home_data: + print("❌ 无法获取分类信息,退出。") + return + + categories = home_data['class'] + self.stats.categories_found = len(categories) + print(f"📋 获取到 {len(categories)} 个分类,开始派发任务...") + + # 2. 保存分类并派发分类任务 + for cat in categories: + tid = str(cat['type_id']) + name = cat['type_name'] + self.db.save_category(tid, name) + self.submit_task(self.process_category, tid, name) + + # 3. 等待所有任务完成 + self.tracker.wait_until_done() + self.running = False + + self.print_final_stats() + + # 关闭 executor 和 db + self.executor.shutdown(wait=True) + self.db.close() + + def monitor_loop(self): + while self.running: + self.print_progress() + time.sleep(1) + + def print_progress(self): + elapsed = time.time() - self.stats.start_time + speed = self.stats.items_processed / elapsed if elapsed > 0 else 0 + # \033[K 清除当前行剩余内容,确保更新时不会有残留字符 + sys.stdout.write( + f"\r\033[K⏱️ {elapsed:.1f}s | " + f"Pages: {self.stats.pages_scanned} | " + f"Items: {self.stats.items_processed}/{self.stats.items_found} | " + f"Skip: {self.stats.items_skipped} | " + f"Eps: {self.stats.episodes_resolved} | " + f"Speed: {speed:.2f} it/s | " + f"Err: {self.stats.errors}" + ) + sys.stdout.flush() + + def print_final_stats(self): + elapsed = time.time() - self.stats.start_time + print("\n" + "-" * 50) + print(f"统计报告:") + print(f" 总耗时: {elapsed:.2f} 秒") + print(f" 扫描页数: {self.stats.pages_scanned}") + print(f" 处理资源: {self.stats.items_processed}") + print(f" 跳过资源: {self.stats.items_skipped}") + print(f" 解析集数: {self.stats.episodes_resolved}") + print(f" 错误数量: {self.stats.errors}") + print("-" * 50) + + def process_category(self, tid, tname): + cat_data = self.bridge.call('categoryContent', tid, 1, False, {}) + + if not cat_data or 'list' not in cat_data: + self.stats.inc('errors') + return + + items = cat_data.get('list', []) + self.stats.inc('items_found', len(items)) + self.stats.inc('pages_scanned') + + for item in items: + item['type_id'] = tid + item['type_name'] = tname + self.submit_task(self.process_item, item) + + page_count = 0 + if 'pagecount' in cat_data: + try: + page_count = int(cat_data['pagecount']) + except: + page_count = 9999 + + # 递归触发第2页(如果需要) + # 如果明确返回只有1页,则停止;否则只要没达到max_pages就尝试下一页 + if page_count != 1: + next_page = 2 + if not self.max_pages or next_page <= self.max_pages: + self.submit_task(self.process_page, tid, tname, next_page) + + def process_page(self, tid, tname, page): + cat_data = self.bridge.call('categoryContent', tid, page, False, {}) + if not cat_data or 'list' not in cat_data: + self.stats.inc('errors') + return + + items = cat_data.get('list', []) + self.stats.inc('items_found', len(items)) + self.stats.inc('pages_scanned') + + if len(items) == 0: + return + + for item in items: + item['type_id'] = tid + item['type_name'] = tname + self.submit_task(self.process_item, item) + + # 提交下一页任务(递归爬取) + if len(items) > 0: + next_page = page + 1 + if not self.max_pages or next_page <= self.max_pages: + self.submit_task(self.process_page, tid, tname, next_page) + + def process_item(self, item): + vod_id = item['vod_id'] + vod_name = item['vod_name'] + + # 增量爬取检查:如果数据库中已存在该 vod_id,则跳过 + if self.db.item_exists(vod_id): + # 即使跳过,也可以尝试更新 type_id (如果之前为空) + # 但为了性能,这里暂时略过,除非强制更新 + self.stats.inc('items_skipped') + return + + # 详情页爬取 + detail_res = self.bridge.call('detailContent', [vod_id]) + if not detail_res or 'list' not in detail_res or not detail_res['list']: + self.stats.inc('errors') + return + + vod_data = detail_res['list'][0] + # 补全可能缺失的字段 + if 'vod_id' not in vod_data: vod_data['vod_id'] = vod_id + if 'type_id' not in vod_data: vod_data['type_id'] = item.get('type_id') + + # 存入 VOD 主表 + vod_pk = self.db.save_vod(vod_data) + if not vod_pk: + self.stats.inc('errors') + return + + self.stats.inc('items_processed') + + # 处理播放列表 + play_from_str = vod_data.get('vod_play_from', '') + play_url_str = vod_data.get('vod_play_url', '') + + if not play_from_str or not play_url_str: + return + + play_from_list = play_from_str.split('$$$') + play_url_list = play_url_str.split('$$$') + + all_episodes = [] + + for i, source_name in enumerate(play_from_list): + if i >= len(play_url_list): break + url_text = play_url_list[i] + + # 格式: 名字$地址#名字$地址 + episodes = url_text.split('#') + for ep_str in episodes: + if '$' in ep_str: + ep_name, ep_url = ep_str.split('$', 1) + else: + ep_name, ep_url = '正片', ep_str + + episode = { + 'play_from': source_name, + 'name': ep_name, + 'url': ep_url, + 'resolved_url': '' + } + + if RESOLVE_FINAL_URLS: + play_res = self.bridge.call('playerContent', source_name, ep_url, []) + if play_res and 'url' in play_res: + episode['resolved_url'] = play_res['url'] + self.stats.inc('episodes_resolved') + else: + pass + + all_episodes.append(episode) + + if all_episodes: + self.db.save_episodes(vod_pk, all_episodes) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="DrPy PHP Spider Concurrent Crawler") + parser.add_argument("spider", nargs="?", default=DEFAULT_SPIDER, help="PHP spider file path") + parser.add_argument("-p", "--max-pages", type=int, default=DEFAULT_MAX_PAGES, help="Max pages per category") + parser.add_argument("-t", "--threads", type=int, default=DEFAULT_THREADS, help="Concurrency threshold (max workers)") + parser.add_argument("-n", "--no-resolve", action="store_true", help="Skip resolving final playback URLs") + + args = parser.parse_args() + + if args.no_resolve: + RESOLVE_FINAL_URLS = False + + spider_file = args.spider + if not os.path.exists(spider_file): + print(f"Error: File not found: {spider_file}") + sys.exit(1) + + # 根据爬虫文件名生成数据库文件名 (例如: spider.php -> spider.db) + # 确保数据库文件生成在爬虫文件同级目录 + spider_dir = os.path.dirname(os.path.abspath(spider_file)) + base_name = os.path.splitext(os.path.basename(spider_file))[0] + db_path = os.path.join(spider_dir, f"{base_name}.db") + + print(f"📁 数据库路径: {db_path}") + + crawler = Crawler(spider_file, db_path, args.max_pages, args.threads) + crawler.run() diff --git a/spider/php/index.php b/spider/php/index.php new file mode 100644 index 00000000..6960772f --- /dev/null +++ b/spider/php/index.php @@ -0,0 +1,16 @@ + 'ok', + 'message' => 'PHP 服务运行正常', + 'version' => PHP_VERSION, + 'platform' => 'Android', + 'time' => date('Y-m-d H:i:s'), + 'extensions' => get_loaded_extensions() +], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + + diff --git a/spider/php/lib/HtmlParser.php b/spider/php/lib/HtmlParser.php new file mode 100644 index 00000000..f96e5c68 --- /dev/null +++ b/spider/php/lib/HtmlParser.php @@ -0,0 +1,208 @@ +getDom($html); + $xpath = new DOMXPath($doc); + + $xpathQuery = $this->parseRuleToXpath($rule); + $nodes = $xpath->query($xpathQuery); + + $res = []; + if ($nodes) { + foreach ($nodes as $node) { + // saveHTML($node) returns OuterHTML + $res[] = $doc->saveHTML($node); + } + } + return $res; + } + + /** + * Parse HTML and return single value (Text, Html, or Attribute) + */ + public function pdfh($html, $rule, $baseUrl = '') { + if (empty($html) || empty($rule)) return ''; + $doc = $this->getDom($html); + $xpath = new DOMXPath($doc); + + // Separate Option + $option = ''; + if (strpos($rule, '&&') !== false) { + $parts = explode('&&', $rule); + $option = array_pop($parts); + $rule = implode('&&', $parts); + } + + $xpathQuery = $this->parseRuleToXpath($rule); + $nodes = $xpath->query($xpathQuery); + + if ($nodes && $nodes->length > 0) { + // Special handling for Text option: concatenate all nodes + if ($option === 'Text') { + $text = ''; + foreach ($nodes as $node) { + $text .= $node->textContent; + } + return $this->parseText($text); + } + + // For other options, use the first node + $node = $nodes->item(0); + return $this->formatOutput($doc, $node, $option, $baseUrl); + } + return ''; + } + + /** + * Parse HTML and return URL (auto joined) + */ + public function pd($html, $rule, $baseUrl = '') { + $res = $this->pdfh($html, $rule, $baseUrl); + return $this->urlJoin($baseUrl, $res); + } + + // --- Helper Methods --- + + private function parseText($text) { + // Match JS behavior: + // text = text.replace(/[\s]+/gm, '\n'); + // text = text.replace(/\n+/g, '\n').replace(/^\s+/, ''); + // text = text.replace(/\n/g, ' '); + + $text = preg_replace('/[\s]+/u', "\n", $text); + $text = preg_replace('/\n+/', "\n", $text); + $text = trim($text); + $text = str_replace("\n", ' ', $text); + return $text; + } + + private function parseRuleToXpath($rule) { + // Replace && with space to unify as descendant separator + $rule = str_replace('&&', ' ', $rule); + $parts = explode(' ', $rule); + $xpathParts = []; + + foreach ($parts as $part) { + if (empty($part)) continue; + $xpathParts[] = $this->transSingleSelector($part); + } + + // Join with descendant axis + return '//' . implode('//', $xpathParts); + } + + private function transSingleSelector($selector) { + // Handle :eq + $position = null; + if (preg_match('/:eq\((-?\d+)\)/', $selector, $matches)) { + $idx = intval($matches[1]); + $selector = str_replace($matches[0], '', $selector); + if ($idx >= 0) { + $position = $idx + 1; // XPath is 1-based + } else { + // -1 is last() + // -2 is last()-1 + $offset = abs($idx) - 1; + $position = "last()" . ($offset > 0 ? "-$offset" : ""); + } + } + + // Handle tag.class#id + $tag = '*'; + $conditions = []; + + // Extract id + if (preg_match('/#([\w-]+)/', $selector, $m)) { + $conditions[] = '@id="' . $m[1] . '"'; + $selector = str_replace($m[0], '', $selector); + } + + // Extract classes + if (preg_match_all('/\.([\w-]+)/', $selector, $m)) { + foreach ($m[1] as $cls) { + $conditions[] = 'contains(concat(" ", normalize-space(@class), " "), " ' . $cls . ' ")'; + } + $selector = preg_replace('/\.[\w-]+/', '', $selector); + } + + // Remaining is tag + if (!empty($selector)) { + $tag = $selector; + } + + $xpath = $tag; + if (!empty($conditions)) { + $xpath .= '[' . implode(' and ', $conditions) . ']'; + } + if ($position !== null) { + $xpath .= '[' . $position . ']'; + } + + return $xpath; + } + + private function formatOutput($doc, $node, $option, $baseUrl) { + if ($option === 'Text') { + return $this->parseText($node->textContent); + } elseif ($option === 'Html') { + return $doc->saveHTML($node); + } elseif ($option) { + // Attribute + $val = $node->getAttribute($option); + // Handle style url() extraction if needed? JS does it. + // JS: if (contains(opt, 'style') && contains(ret, 'url(')) ... + return $val; + } + // Default to outer HTML if no option provided + return $doc->saveHTML($node); + } + + private function getDom($html) { + $doc = new DOMDocument(); + // Suppress warnings for malformed HTML + libxml_use_internal_errors(true); + // Force UTF-8 encoding + if (!empty($html) && mb_detect_encoding($html, 'UTF-8', true) === false) { + $html = mb_convert_encoding($html, 'UTF-8', 'GBK, BIG5'); + } + // Add meta charset to ensure DOMDocument treats it as UTF-8 + $html = '' . $html; + + $doc->loadHTML($html); + libxml_clear_errors(); + return $doc; + } + + private function urlJoin($baseUrl, $relativeUrl) { + if (empty($relativeUrl)) return ''; + if (preg_match('#^https?://#', $relativeUrl)) return $relativeUrl; + + if (empty($baseUrl)) return $relativeUrl; + + $parts = parse_url($baseUrl); + $scheme = isset($parts['scheme']) ? $parts['scheme'] . '://' : 'http://'; + $host = isset($parts['host']) ? $parts['host'] : ''; + + // Handle protocol-relative URLs (starting with //) + if (substr($relativeUrl, 0, 2) == '//') { + return (isset($parts['scheme']) ? $parts['scheme'] . ':' : 'http:') . $relativeUrl; + } + + if (substr($relativeUrl, 0, 1) == '/') { + return $scheme . $host . $relativeUrl; + } + + // Relative path + $path = isset($parts['path']) ? $parts['path'] : '/'; + $dir = rtrim(dirname($path), '/\\'); + if ($dir === '/' || $dir === '\\') $dir = ''; // handle root + + return $scheme . $host . $dir . '/' . $relativeUrl; + } +} diff --git a/spider/php/lib/spider.php b/spider/php/lib/spider.php new file mode 100644 index 00000000..6a7d7b55 --- /dev/null +++ b/spider/php/lib/spider.php @@ -0,0 +1,396 @@ + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', + 'Accept-Language' => 'zh-CN,zh;q=0.9', + ]; + + /** + * @var HtmlParser + */ + protected $htmlParser; + + public function __construct() { + $this->htmlParser = new HtmlParser(); + } + + /** + * 初始化方法 + * @param string $extend 扩展参数 + */ + public function init($extend = '') { + // 子类实现 + } + + /** + * 获取首页分类 + * @param array $filter 筛选条件 + * @return array + */ + public function homeContent($filter) { + return ['class' => []]; + } + + /** + * 获取首页推荐视频 + * @return array + */ + public function homeVideoContent() { + return ['list' => []]; + } + + /** + * 获取分类详情 + * @param string $tid 分类ID + * @param int $pg 页码 + * @param array $filter 筛选条件 + * @param array $extend 扩展参数 + * @return array + */ + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + return ['list' => [], 'page' => $pg, 'pagecount' => 1, 'limit' => 20, 'total' => 0]; + } + + /** + * 获取视频详情 + * @param array $ids 视频ID列表 + * @return array + */ + public function detailContent($ids) { + return ['list' => []]; + } + + /** + * 搜索视频 + * @param string $key 关键词 + * @param bool $quick 快速搜索 + * @param int $pg 页码 + * @return array + */ + public function searchContent($key, $quick = false, $pg = 1) { + return ['list' => []]; + } + + /** + * 获取播放地址 + * @param string $flag 播放线路 + * @param string $id 视频播放ID + * @param array $vipFlags VIP标识 + * @return array + */ + public function playerContent($flag, $id, $vipFlags = []) { + return ['parse' => 0, 'url' => '', 'header' => []]; + } + + /** + * 代理请求 (可选) + * @param array $params + * @return mixed + */ + public function localProxy($params) { + return null; + } + + /** + * 执行 Action (可选) + * @param string $action 动作名称 + * @param string $value 参数值 + * @return mixed + */ + public function action($action, $value) { + return ''; + } + + // ================== 辅助方法 ================== + + protected function pdfa($html, $rule) { + return $this->htmlParser->pdfa($html, $rule); + } + + protected function pdfh($html, $rule, $baseUrl = '') { + return $this->htmlParser->pdfh($html, $rule, $baseUrl); + } + + protected function pd($html, $rule, $baseUrl = '') { + if (empty($baseUrl)) { + $baseUrl = $this->tryGetHost(); + } + return $this->htmlParser->pd($html, $rule, $baseUrl); + } + + /** + * 尝试获取子类定义的 HOST 常量或属性 + */ + private function tryGetHost() { + try { + $ref = new ReflectionClass($this); + + // 1. 尝试获取 HOST 属性 (优先) + if ($ref->hasProperty('HOST')) { + $prop = $ref->getProperty('HOST'); + // PHP 8.1+ 默认可访问私有属性,只有旧版本需要手动开启 + if (PHP_VERSION_ID < 80100) { + $prop->setAccessible(true); + } + $val = $prop->getValue($this); + if (!empty($val)) { + return $val; + } + } + + // 2. 尝试获取 const HOST 常量 + if ($ref->hasConstant('HOST')) { + return $ref->getConstant('HOST'); + } + } catch (Exception $e) { + // ignore + } + return ''; + } + + /** + * 快速构建分页返回结果 + * @param array $list 视频列表 + * @param int $pg 当前页码 + * @param int $total 总记录数 (可选) + * @param int $limit 每页条数 (默认 20) + * @return array + */ + protected function pageResult($list, $pg, $total = 0, $limit = 20) { + $pg = max(1, intval($pg)); + $count = count($list); + + if ($total > 0) { + $pagecount = ceil($total / $limit); + } else { + // 如果没有提供 total,尝试根据当前列表数量估算 + if ($count < $limit) { + // 当前页数据少于限制,说明是最后一页 + $pagecount = $pg; + $total = ($pg - 1) * $limit + $count; + } else { + // 还有下一页,设置一个较大的页数 + $pagecount = 9999; + $total = 99999; + } + } + + return [ + 'list' => $list, + 'page' => $pg, + 'pagecount' => intval($pagecount), + 'limit' => intval($limit), + 'total' => intval($total) + ]; + } + + /** + * 封装 HTTP 请求 + * @param string $url 请求地址 + * @param array $options CURL 选项 + * @param array $headers 请求头 + * @return string|bool + */ + protected function fetch($url, $options = [], $headers = []) { + // 支持从 options 中传递 headers + if (isset($options['headers'])) { + $headers = array_merge($headers, $options['headers']); + unset($options['headers']); + } + + $ch = curl_init(); + + // 1. 解析自定义 header 为关联数组 + $customHeaders = []; + foreach ($headers as $k => $v) { + if (is_numeric($k)) { + // 处理 "Key: Value" 格式 + $parts = explode(':', $v, 2); + if (count($parts) === 2) { + $key = trim($parts[0]); + $value = trim($parts[1]); + $customHeaders[$key] = $value; + } + } else { + $customHeaders[$k] = $v; + } + } + + // 2. 合并请求头 (自定义覆盖默认) + $finalHeadersMap = array_merge($this->headers, $customHeaders); + + // 3. 转换回 CURL 所需的索引数组 + $mergedHeaders = []; + foreach ($finalHeadersMap as $k => $v) { + if ($v === "") { + // To send empty header in CURL, use "Header;" (no colon) + $mergedHeaders[] = $k . ";"; + } else { + $mergedHeaders[] = "$k: $v"; + } + } + + $defaultOptions = [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 15, + CURLOPT_ENCODING => '', // 支持 GZIP 自动解压 + CURLOPT_HTTPHEADER => $mergedHeaders, + ]; + + // 处理 POST 数据 + if (isset($options['body'])) { + $defaultOptions[CURLOPT_POST] = true; + $defaultOptions[CURLOPT_POSTFIELDS] = $options['body']; + unset($options['body']); + } + + // 处理 Cookie + if (isset($options['cookie'])) { + $defaultOptions[CURLOPT_COOKIE] = $options['cookie']; + unset($options['cookie']); + } + + // 合并用户自定义选项 + foreach ($options as $k => $v) { + $defaultOptions[$k] = $v; + } + + curl_setopt_array($ch, $defaultOptions); + $result = curl_exec($ch); + + if (is_resource($ch)) { + curl_close($ch); + } + + return $result; + } + + protected function fetchJson($url, $options = []) { + $resp = $this->fetch($url, $options); + return json_decode($resp, true) ?: []; + } + + /** + * 自动运行,处理路由 + */ + public function run() { + if (defined('DRPY_BRIDGE')) { + return; + } + $ac = $_GET['ac'] ?? ''; + $t = $_GET['t'] ?? ''; + $pg = $_GET['pg'] ?? '1'; + $wd = $_GET['wd'] ?? ''; + $ids = $_GET['ids'] ?? ''; + $play = $_GET['play'] ?? ''; // 某些源使用 play 参数传递播放ID + $flag = $_GET['flag'] ?? ''; // 播放线路 + $filter = isset($_GET['filter']) && $_GET['filter'] === 'true'; // 是否过滤 + $extend = $_GET['ext'] ?? ''; // 扩展参数 + if (!empty($extend) && is_string($extend)) { + $decoded = json_decode(base64_decode($extend), true); + if (is_array($decoded)) { + $extend = $decoded; + } + } + $action = $_GET['action'] ?? ''; // Action 动作 + $value = $_GET['value'] ?? ''; // Action 参数 + + $this->init($extend); + + try { + // 0. Action (优先处理) + if ($ac === 'action') { + echo json_encode($this->action($action, $value), JSON_UNESCAPED_UNICODE); + return; + } + + // 1. 播放 (Play) + // 优先检测 play 参数或 ac=play + if ($ac === 'play' || !empty($play)) { + $playId = !empty($play) ? $play : ($_GET['id'] ?? ''); + echo json_encode($this->playerContent($flag, $playId), JSON_UNESCAPED_UNICODE); + return; + } + + // 2. 搜索 (Search) + // 有 wd 则是搜索 + if (!empty($wd)) { + echo json_encode($this->searchContent($wd, false, $pg), JSON_UNESCAPED_UNICODE); + return; + } + + // 3. 详情 (Detail) + // 有 ids 且 ac 不为空 + if (!empty($ids) && !empty($ac)) { + // ids 可能是逗号分隔的字符串 + $idList = explode(',', $ids); + echo json_encode($this->detailContent($idList), JSON_UNESCAPED_UNICODE); + return; + } + + // 4. 分类 (Category) + // 有 t 且 ac 不为空 + if ($t !== '' && !empty($ac)) { + // 处理 filter + $filterData = []; // 暂未实现复杂 filter 解析,可根据需要扩展 + echo json_encode($this->categoryContent($t, $pg, $filterData, $extend), JSON_UNESCAPED_UNICODE); + return; + } + + // 5. 首页 (默认) + // 通常返回 {class: [...], list: [...]} + // 可以分别调用 homeContent 和 homeVideoContent 合并 + $homeData = $this->homeContent($filter); + $videoData = $this->homeVideoContent(); + + $result = [ + 'class' => $homeData['class'] ?? [], + ]; + + // 如果 homeContent 只有 class,合并 homeVideoContent 的 list + if (isset($videoData['list'])) { + $result['list'] = $videoData['list']; + } + // 如果 homeContent 也有 list,优先使用 homeContent 的 list (视具体逻辑而定,这里简单的合并) + if (isset($homeData['list']) && !empty($homeData['list'])) { + $result['list'] = $homeData['list']; + } + // 兼容:如果 homeContent 返回了 filters + if (isset($homeData['filters'])) { + $result['filters'] = $homeData['filters']; + } + + echo json_encode($result, JSON_UNESCAPED_UNICODE); + + } catch (Exception $e) { + echo json_encode(['code' => 500, 'msg' => $e->getMessage()], JSON_UNESCAPED_UNICODE); + } catch (Throwable $e) { + echo json_encode(['code' => 500, 'msg' => $e->getMessage()], JSON_UNESCAPED_UNICODE); + } + } +} diff --git a/spider/php/readme.md b/spider/php/readme.md new file mode 100644 index 00000000..c951ac71 --- /dev/null +++ b/spider/php/readme.md @@ -0,0 +1,448 @@ +# PHP Spider 开发与维护指南 (DZ 风格) + +本文档总结了基于 `spider.php` 框架开发、调试、转换 PHP 爬虫源的核心经验与最佳实践。旨在帮助开发者快速上手,并作为后续开发的参考手册。 + +## 0. 环境搭建 (Windows) + +为了运行和调试 PHP 爬虫,需要在本地配置 PHP 环境。推荐使用 PHP 8.3+ NTS (Non Thread Safe) 版本。 + +### 0.1 下载与安装 +1. **下载 PHP**: + 可以直接点击下载推荐版本: + [php-8.3.29-nts-Win32-vs16-x64.zip](https://windows.php.net/downloads/releases/php-8.3.29-nts-Win32-vs16-x64.zip) +2. **解压**: + 将下载的压缩包解压到固定目录,例如 `C:\php` (建议路径不包含空格和中文)。 +3. **配置环境变量**: + - 右键 "此电脑" -> "属性" -> "高级系统设置" -> "环境变量"。 + - 在 "系统变量" 中找到 `Path`,点击 "编辑"。 + - 点击 "新建",输入你的 PHP 解压路径 (如 `C:\php`)。 + - 连续点击 "确定" 保存设置。 + +### 0.2 配置 php.ini +1. 进入 PHP 解压目录,找到 `php.ini-development` 文件,复制一份并重命名为 `php.ini`。 +2. 使用文本编辑器打开 `php.ini`,查找并修改以下配置 (去掉行首的 `;` 分号以启用): + ```ini + ; 指定扩展目录 + extension_dir = "ext" + + ; 启用核心扩展 (爬虫必须) + extension=curl + extension=mbstring + extension=openssl + extension=sockets + extension=sqlite3 + ``` +3. **验证安装**: + 打开新的 CMD 或 PowerShell 窗口,输入 `php -v`。 + 如果看到类似 `PHP 8.3.29 (cli) ...` 的输出,即表示环境配置成功。 + +## 0.5 环境搭建 (Linux/Ubuntu - 升级至 PHP 8.3) + +如果您在 Linux 环境(如 Ubuntu/Debian)下使用,建议通过 PPA 源安装或升级到 PHP 8.3。 + +### 0.5.1 卸载旧版 (可选) +如果系统中已安装旧版(如 PHP 8.1),建议先卸载以避免冲突: +```bash +sudo apt purge php8.1* -y +sudo apt autoremove -y +``` + +### 0.5.2 添加 PPA 源 +使用 Ondřej Surý 的 PPA 源获取最新 PHP 版本: +```bash +sudo apt install software-properties-common -y +sudo add-apt-repository ppa:ondrej/php -y +sudo apt update +``` + +### 0.5.3 安装 PHP 8.3 及扩展 +安装 CLI 版本及 Drpy 爬虫所需的常用扩展 (curl, mbstring, xml, mysql 等): +```bash +# 注意:openssl 通常已包含在核心或 common 包中,无需单独指定 php8.3-openssl +sudo apt install php8.3-cli php8.3-curl php8.3-mbstring php8.3-xml php8.3-mysql php8.3-sqlite3 -y +``` + +### 0.5.4 验证安装 +```bash +php -v +# 输出应显示 PHP 8.3.x +``` + +### 0.5.5 改init +```bash +php --ini +cd /etc/php/8.3/cli +vi php.ini +# 找到 extension=sqlite3 并取消注释(用到下面安装命令安装完了会自动配置好,这里还是给注释掉) +# 0.5.3已经包含了下面的命令,可以不管了 +apt-get install php8.3-sqlite3 +``` + +## 1. 核心架构与工具 + +### 1.1 基础框架 (`lib/spider.php`) +核心框架文件现已移动至 `lib` 目录。 +所有源必须包含 `lib/spider.php` 并继承 `BaseSpider` 类(通常在源文件中定义为 `class Spider extends BaseSpider`)。 + +**引用规范**: +```php +require_once __DIR__ . '/lib/spider.php'; +``` + +核心方法包括: +- `init()`: 初始化(可选)。 +- `homeContent($filter)`: 获取首页分类与筛选配置。 +- `categoryContent($tid, $pg, $filter, $extend)`: 获取分类列表数据。 +- `detailContent($ids)`: 获取视频详情与播放列表。 +- `searchContent($key, $quick, $pg)`: 搜索视频。 +- `playerContent($flag, $id, $vipFlags)`: 解析真实播放链接。 + +### 1.2 文件命名与目录规范 +- **源文件命名**: 统一使用 ` ᵈᶻ.php` 后缀(注意包含空格),例如 `果果 ᵈᶻ.php`。对于特定类型,建议增加标识:小说使用 `[书]`,漫画使用 `[画]`,例如 `七猫小说 ᵈᶻ[书].php`。 +- **系统文件排除**: `config.php` 会自动忽略以下文件: + - 系统文件 (`index.php`, `test_runner.php` 等) + - 以 `_` 开头的文件 (如 `_backup.php`) + - `config` 开头的文件 + - `lib` 目录下的文件 + +### 1.3 测试工具 (`test_runner.php`) +用于本地验证源的接口功能。 + +**用法**: +```bash +php test_runner.php "e:\php_work\php\荐片影视 ᵈᶻ.php" +``` +*(注意:由于文件名包含空格,命令行中路径建议加引号)* + +**测试流程**: +1. **首页测试**: 检查分类是否获取成功,筛选条件是否解析。 +2. **分类测试**: 选取第一个分类,获取第一页数据,检查 `vod_id` 和 `vod_name`。 +3. **详情测试**: 使用分类接口返回的 `vod_id`,检查详情信息及播放列表解析。 +4. **搜索测试**: 使用分类接口获取的名称进行搜索验证。 +5. **播放测试**: 选取第一个播放源,尝试解析播放链接。 + +--- + +## 2. 开发最佳实践 + +### 2.1 分页标准化 (`$this->pageResult`) +不要手动拼接复杂的 JSON 返回结构。使用框架内置的辅助方法 `$this->pageResult`。 + +**推荐写法**: +```php +$videos = []; +foreach ($items as $item) { + $videos[] = [ + 'vod_id' => $item['id'], + 'vod_name' => $item['name'], + 'vod_pic' => $item['pic'], + 'vod_remarks' => $item['remarks'] + ]; +} +return $this->pageResult($videos, $page, $total, $pageSize); +``` + +### 2.2 数据传递技巧 (`vod_id` 组合) +有时 `categoryContent` 到 `detailContent` 需要传递额外参数(如 `typeId`),但 `vod_id` 只能是字符串。 +**技巧**: 使用分隔符组合参数。 +```php +// 在 categoryContent 中 +'vod_id' => $id . '*' . $typeId + +// 在 detailContent 中 +$parts = explode('*', $ids[0]); +$id = $parts[0]; +$typeId = $parts[1] ?? ''; +``` + +### 2.3 HTML 解析 (DOMDocument) +处理 HTML 页面时,推荐使用 `DOMDocument` + `DOMXPath`,比正则更稳定。 +**IDE 爆红修复**: +IDE 经常提示 `getAttribute` 方法不存在,因为 DOMNode 不一定是 Element。 +**正确写法**: +```php +$node = $xpath->query('//img')->item(0); +if ($node instanceof DOMElement) { // 加上类型检查 + $pic = $node->getAttribute('src'); +} +``` + +### 2.4 加密与解密 (JS -> PHP 转换) +遇到 JS 源使用了加密(如 RSA, AES),需要用 PHP 的 `openssl` 扩展对应实现。 + +**案例:RSA 分块解密 (参考 `零度影视 ᵈᶻ.php`)** +PHP 的 `openssl_private_decrypt` 有长度限制(通常 117 或 128 字节)。如果密文过长,必须**分块解密**。 + +```php +private function rsaDecrypt($data) { + $decoded = base64_decode($data); + $keyRes = openssl_pkey_get_private($this->privateKey); + $details = openssl_pkey_get_details($keyRes); + $keySize = ceil($details['bits'] / 8); // e.g., 128 bytes + + $result = ''; + $chunks = str_split($decoded, $keySize); // 按密钥长度分块 + + foreach ($chunks as $chunk) { + if (openssl_private_decrypt($chunk, $decrypted, $this->privateKey, OPENSSL_PKCS1_PADDING)) { + $result .= $decrypted; + } + } + return $result; +} +``` + +### 2.5 CURL Header 空值处理 +在 PHP CURL 中,如果需要发送一个值为空的 Header(如 `Authorization:`),**不能**使用 `"Header: "`(带空格)或 `"Header:"`(不带值),这可能导致 Header 被忽略或发送错误的格式。 + +**正确做法**: 使用分号结尾。 +```php +$headers = [ + 'Authorization;', // 发送 "Authorization:" 头,值为空 + 'User-Agent: ...' +]; +``` +此技巧在移植七猫小说时解决了个别接口(如章节内容)验签失败的问题。 + +### 2.6 HtmlParser 与 pd 函数的智能 UrlJoin +在使用 `pd()` 函数提取链接(如图片 src、详情页 href)时,通常需要传入当前页面的 URL 作为 `baseUrl` 以便拼接相对路径。 + +**手动传入 (推荐用于详情页)**: +```php +$pic = $this->pd($html, 'img&&src', $currentUrl); +``` + +**自动识别 (推荐用于列表页)**: +如果你的 Spider 类定义了 `const HOST` 或 `$HOST` 属性,`pd()` 函数在未传入 `baseUrl` 时会自动使用它作为基准。 +```php +class Spider extends BaseSpider { + private const HOST = 'https://www.example.com'; + // ... + // 这里不需要传 $url,会自动用 HOST 拼接 + $pic = $this->pd($itemHtml, 'img&&src'); +} +``` + +### 2.7 IDE 兼容性与反射技巧 +在基类中访问子类的私有常量/属性(如 `$this->HOST`)时,直接访问会导致 IDE 报错(Undefined property)。 +**最佳实践**: 使用 `ReflectionClass` 动态获取。 +```php +$ref = new ReflectionClass($this); +if ($ref->hasConstant('HOST')) { + return $ref->getConstant('HOST'); +} +``` +这不仅消除了 IDE 警告,还支持了对 `private/protected` 属性的访问(需配合 `setAccessible(true)`,注意 PHP 8.1+ 已默认支持)。 + +--- + +## 3. HtmlParser 解析函数指南 + +为了与 JS 源(Hiker 规则)保持一致,我们在 `BaseSpider` 中内置了 `pdfa`, `pdfh`, `pd` 三个核心函数。它们支持 CSS 选择器风格的解析规则,并自动处理 DOM 操作。 + +### 3.1 规则语法 (Rule Syntax) +- **层级**: 使用 `&&` 分隔层级(在 XPath 中对应 `//`)。例如 `div.list&&ul&&li`。 +- **属性/选项**: 规则的**最后一部分**指定要获取的内容。 + - `Text`: 获取纯文本(自动去除首尾空格和多余换行)。 + - `Html`: 获取元素的 OuterHTML。 + - `src`, `href`, `data-id`, ...: 获取指定属性值。 +- **选择器**: + - `tag`: 标签名,如 `div`, `a`, `img`。 + - `.class`: 类名,如 `.title`。 + - `#id`: ID,如 `#content`。 + - `:eq(n)`: 索引选择(0 起始)。`:eq(0)` 是第一个,`:eq(-1)` 是最后一个。 + - 组合: `div.item:eq(0)`。 + +### 3.2 pdfa (Parse DOM For Array) +**用途**: 解析列表,返回 HTML 字符串数组。通常用于 `categoryContent` 中解析视频列表。 + +**签名**: +```php +protected function pdfa(string $html, string $rule): array +``` + +**示例**: +```php +// 获取所有 ul 下的 li 元素的 HTML +$items = $this->pdfa($html, 'ul.list&&li'); +foreach ($items as $itemHtml) { + // 在循环中继续使用 pdfh/pd 解析具体字段 +} +``` + +### 3.3 pdfh (Parse DOM For Html/Text) +**用途**: 解析单个节点的内容(文本、HTML 或属性)。 + +**签名**: +```php +protected function pdfh(string $html, string $rule, string $baseUrl = ''): string +``` + +**示例**: +```php +// 获取标题文本 +$title = $this->pdfh($itemHtml, '.title&&Text'); + +// 获取描述(可能包含 HTML 标签) +$desc = $this->pdfh($itemHtml, '.desc&&Html'); + +// 获取自定义属性 +$dataId = $this->pdfh($itemHtml, 'a&&data-id'); +``` + +### 3.4 pd (Parse DOM for Url) +**用途**: 解析链接(图片、跳转链接),并**自动进行 URL 拼接**(UrlJoin)。 + +**签名**: +```php +protected function pd(string $html, string $rule, string $baseUrl = ''): string +``` + +**特点**: +- 等同于 `pdfh` + `urlJoin`。 +- 如果规则末尾是属性(如 `src`, `href`),会自动基于 `$baseUrl` 转换为绝对路径。 +- 如果未传入 `$baseUrl`,会自动尝试读取类常量 `HOST`。 + +**示例**: +```php +// 自动拼接 HOST (假设类中定义了 const HOST) +$pic = $this->pd($itemHtml, 'img&&src'); + +// 手动指定 BaseUrl (如详情页解析推荐列表) +$link = $this->pd($html, 'a.next&&href', 'https://m.example.com/list/'); +``` + +--- + +## 4. 常见问题排查 + +- **Q: 为什么搜不到结果?** + - A: 检查 `searchContent` 的 URL 参数是否正确编码。特别是中文关键词,部分站点需要 URL 编码,部分不需要。 +- **Q: 详情页没有章节?** + - A: 很多小说/漫画源的详情页接口 (`/detail`) 返回的信息不全,通常需要额外调用章节列表接口 (`/chapter-list` 或类似)。务必抓包确认。 +- **Q: 图片加载失败?** + - A: 检查图片链接是否为相对路径。如果是,请确保在 `pd()` 或手动处理时进行了完整的 URL 拼接。 +- **Q: 验签失败?** + - A: 仔细比对 Python/JS 源的签名逻辑。注意参数排序(`ksort`)、空值处理、特殊字符编码差异。PHP 的 `md5` 输出默认是小写 hex。 + +--- + +## 5. Flutter 环境适配与 PHP 8.5+ 兼容性 + +在将 PHP 源部署到 Flutter 环境(如 TVBox 及其变种)并使用高版本 PHP (如 8.5.1) 时,可能会遇到类型严格性导致的兼容问题。 + +### 5.1 核心报错:`type 'String' is not a subtype of type 'int' of 'index'` +**现象**: +本地 `test_runner.php` 测试一切正常,但在 Flutter 端运行时报错,提示 String 类型无法作为 List 的索引。 + +**原因**: +PHP 脚本执行结束后**没有输出任何内容**。 +- `test_runner.php` 是手动实例化类并调用方法,所以能拿到结果。 +- Flutter 端通过 CLI 调用 PHP 脚本,如果脚本末尾没有主动调用运行逻辑,输出为空字符串。 +- 适配层收到空字符串后,可能默认处理为 `[]` (空 List)。后续逻辑尝试以 Map 方式(如 `['class']`)访问这个 List 时,就会触发 Dart 的类型错误。 + +**解决方案**: +确保每个源文件末尾都包含自动运行指令: +```php +// 必须在文件末尾加入此行 +(new Spider())->run(); +``` + +### 5.2 严格类型处理 (JSON 空对象) +**现象**: +PHP 的空数组 `[]` 在 `json_encode` 时默认为 `[]` (List)。如果在 PHP 8.5+ 环境下,客户端期望的是 Map `{}` (Object),可能会导致解析错误或类型不匹配。 + +**解决方案**: +对于明确应该是对象的字段(如 `header`, `filters`, `ext`),如果为空,必须强制转换为 Object。 +```php +// 错误 (输出 []) +'header' => [] + +// 正确 (输出 {}) +'header' => (object)[] +``` + +### 5.3 HTTPS 与 SSL 证书验证 +**现象**: +在某些 Flutter 环境或 Android 设备上,cURL 请求 HTTPS 站点失败,无返回或报错。这是 because 系统证书库可能不完整或 curl 配置过严。 + +**解决方案**: +显式关闭 SSL 证书校验。`BaseSpider` 的 `fetch` 方法已默认处理,但在重写 `fetch` 或使用原生 cURL 时需注意: +```php +curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); +curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); +``` + +### 5.4 健壮性建议 +1. **JSON 解析容错**:`json_decode($str ?: '{}', true)`,避免对空字符串解析报错。 +2. **空 ID 容错**:在 `detailContent` 或 `playerContent` 中,检查 ID 是否为空,避免向 API 发送非法请求导致崩溃。 + +--- + +## 6. 最近实战经验汇总 (2026/01 更新) + +### 6.1 漫画/图片源的标准协议 (`pics://`) +在开发漫画或图片类源时,`playerContent` 返回的 `url` 字段应使用 `pics://` 协议。 +- **格式**: `pics://图片链接1&&图片链接2&&图片链接3...` +- **注意**: 严禁使用非标准的 `mange://` 或其他自定义协议,除非客户端明确支持。使用 `pics://` 可确保通用播放器能正确识别为图片轮播模式。 + +### 6.2 静态资源智能过滤 +在解析漫画图片列表时,网页往往混杂大量的图标、LOGO、背景图或占位图(如 `grey.gif`)。必须建立过滤机制,否则会严重影响阅读体验。 + +**推荐过滤代码**: +```php +$uniqueImages = []; +foreach ($imageList as $img) { + // 1. 去重 + if (in_array($img, $uniqueImages)) continue; + + // 2. 关键词过滤 + if (strpos($img, "grey.gif") !== false) continue; // 占位图 + if (strpos($img, "logo") !== false) continue; // 网站LOGO + if (strpos($img, "icon") !== false) continue; // 图标 + if (strpos($img, "banner") !== false) continue; // 广告横幅 + + $uniqueImages[] = $img; +} +``` + +### 6.3 中文参数的 URL 编码陷阱 +PHP 的 `curl` 不会自动对 URL 中的非 ASCII 字符进行编码。如果 URL 中包含中文(如搜索关键词、分类标签),**必须**手动调用 `urlencode`。 +- **错误**: `$url = "https://site.com/search?q=" . $key;` +- **正确**: `$url = "https://site.com/search?q=" . urlencode($key);` +未编码会导致服务端返回 400 Bad Request 或 404。 + +### 6.4 `config.php` 类型定义 +在 `config.php` 中注册源时,请注意字段命名。 +- **正确**: `"类型": "小说"` 或 `"类型": "漫画"` +- **错误**: 不要使用 `"categories"` 或其他自定义字段名,否则前端可能无法正确分类显示。 + +### 6.5 PHP 8.5+ 与 Flutter JSON 深度兼容 +在 PHP 8.5.1 及 Flutter 混合环境下,JSON 格式的严谨性至关重要: +1. **空 Map 强制转换**: 任何应当输出为 `{}` 的字段(如 `filters`, `ext`, `header`),若为空数组,**必须**使用 `(object)[]` 或 `(object)$arr` 转换。否则 `json_encode` 会输出 `[]`,导致 Flutter 客户端报 `type 'String' is not a subtype of type 'int' of 'index'` 错误。 +2. **Undefined Index 防御**: 数组索引访问必须使用 `?? ''` 或 `?? []` 提供默认值(如 `$item['key'] ?? ''`)。PHP 的 Warning 信息若混入 JSON 输出,会直接导致解析失败。 + +### 6.6 HTTPS 强制适配 +Android 9+ 及 Flutter 应用默认禁止明文 HTTP 请求(Cleartext traffic not permitted)。 +- **最佳实践**: 在提取图片链接 (`vod_pic`) 时,检测并自动替换协议。 + ```php + if (strpos($pic, 'http://') === 0) { + $pic = str_replace('http://', 'https://', $pic); + } + ``` + +### 6.7 封面图片提取的高级策略 +针对结构复杂的详情页(如漫画站),单一规则往往不稳定: +1. **属性顺序无关正则**: 避免假设 `src` 在 `class` 之前或之后。使用更灵活的正则: + `/]*class=["\'](?:classA|classB)["\'][^>]*src=.../` +2. **多级回退机制**: + - **L1**: 优先从元数据区域(Metadata)提取。 + - **L2**: 若失败,尝试从内容区域(Content Block)提取第一张图。 + - **L3**: 若仍失败,全局搜索非 Icon/Logo/Gif 的第一张大图。 + +### 6.8 测试驱动开发 (TDD) 增强 +不要仅依赖人工查看。建议在 `test_runner.php` 中增加关键字段断言: +- **封面检查**: 在详情页测试中显式检查 `vod_pic` 是否为空,能提早发现 80% 的解析问题。 + +--- +*本文档更新于 2026/01/26,基于 Trae IDE 协作环境。* diff --git a/spider/php/test_runner.php b/spider/php/test_runner.php new file mode 100644 index 00000000..8c669ba5 --- /dev/null +++ b/spider/php/test_runner.php @@ -0,0 +1,251 @@ +run()) + // 防止污染后续的测试输出 + ob_start(); + require_once $file; + ob_end_clean(); + + if (!class_exists('Spider')) { + die("错误: 在文件 '$file' 中未找到 'Spider' 类\n"); + } + + echo "[初始化] 实例化 Spider 类...\n"; + $spider = new Spider(); + $spider->init(); + echo "[初始化] 完成\n\n"; + + // --- 1. 测试首页接口 (Home Interface) --- + echo ">>> [1/5] 测试首页接口 (homeContent)\n"; + $startTime = microtime(true); + $home = $spider->homeContent(true); + $cost = round((microtime(true) - $startTime) * 1000, 2); + + $classes = $home['class'] ?? []; + $filters = $home['filters'] ?? []; + + if (!empty($classes)) { + echo " ✅ 通过 (耗时: {$cost}ms)\n"; + echo " - 获取到 " . count($classes) . " 个分类\n"; + + // 打印前几个分类名称作为示例 + $classNames = array_column(array_slice($classes, 0, 5), 'type_name'); + echo " - 分类示例: " . implode(', ', $classNames) . (count($classes) > 5 ? ' ...' : '') . "\n"; + + if (!empty($filters)) { + $filterCount = is_object($filters) ? count(get_object_vars($filters)) : count($filters); + echo " - 包含筛选配置 (Filters): " . $filterCount . " 组\n"; + } + } else { + echo " ⚠️ 警告: 未获取到分类列表 (class 为空)\n"; + } + + // 确定用于测试分类接口的 type_id + $tid = $classes[0]['type_id'] ?? null; + $tname = $classes[0]['type_name'] ?? '未知分类'; + + if (!$tid && !empty($filters)) { + // 如果 class 为空但有 filters,尝试从 filters 获取 key + foreach ($filters as $key => $val) { + $tid = $key; + $tname = "FilterKey:$key"; + break; + } + } + + echo "\n"; + + // --- 2. 测试分类接口 (Category Interface) --- + $vodId = null; + $vodName = null; // 用于搜索测试 + if ($tid) { + echo ">>> [2/5] 测试分类接口 (categoryContent) - 测试分类: [$tname] (ID: $tid)\n"; + $startTime = microtime(true); + // 模拟传入 filter 参数为空 + $cat = $spider->categoryContent($tid, 1, false, []); + $cost = round((microtime(true) - $startTime) * 1000, 2); + + $list = $cat['list'] ?? []; + if (!empty($list)) { + echo " ✅ 通过 (耗时: {$cost}ms)\n"; + echo " - 获取到 " . count($list) . " 个资源\n"; + + $firstItem = $list[0]; + $vodId = $firstItem['vod_id'] ?? null; + $vodName = $firstItem['vod_name'] ?? '未知名称'; + echo " - 第一条数据: [$vodName] (ID: $vodId)\n"; + } else { + echo " ❌ 失败: 未返回资源列表 (list 为空)\n"; + } + } else { + echo ">>> [2/5] 测试分类接口: ⏭️ 跳过 (未找到有效的分类ID)\n"; + } + + echo "\n"; + + // --- 3. 测试详情接口 (Detail Interface) --- + $playUrl = null; + $playFrom = null; + + if ($vodId) { + echo ">>> [3/5] 测试详情接口 (detailContent) - 测试资源ID: $vodId\n"; + $startTime = microtime(true); + $detail = $spider->detailContent([$vodId]); + $cost = round((microtime(true) - $startTime) * 1000, 2); + + $detailList = $detail['list'] ?? []; + + if (!empty($detailList)) { + $vod = $detailList[0]; + $name = $vod['vod_name'] ?? '未知'; + // 更新 vodName,详情页的名称通常更准确 + if ($name && $name !== '未知') { + $vodName = $name; + } + $playUrl = $vod['vod_play_url'] ?? ''; + $playFrom = $vod['vod_play_from'] ?? ''; + $pic = $vod['vod_pic'] ?? ''; + $desc = $vod['vod_content'] ?? ''; + + echo " ✅ 通过 (耗时: {$cost}ms)\n"; + echo " - 资源名称: $name\n"; + echo " - 封面图片: " . ($pic ? $pic : "⚠️ 未获取到封面") . "\n"; + echo " - 播放源 (vod_play_from): $playFrom\n"; + + // 检查播放地址 + if (!empty($playUrl)) { + $urlCount = substr_count($playUrl, '$'); + // 粗略估计集数,通常每集是 名称$url + $episodeCount = $urlCount > 0 ? ($urlCount + 1) / 2 : 1; + // 或者直接按 # 分割统计播放列表数 + $playlistCount = substr_count($playFrom, '$$$') + 1; + + echo " - 播放列表数据长度: " . strlen($playUrl) . " 字符\n"; + // 简单展示部分播放链接 + $previewUrl = mb_substr($playUrl, 0, 50) . '...'; + echo " - 播放链接预览: $previewUrl\n"; + } else { + echo " ⚠️ 警告: vod_play_url 为空!\n"; + } + + if (!empty($desc)) { + echo " - 简介长度: " . mb_strlen($desc) . " 字\n"; + } + + } else { + echo " ❌ 失败: 未返回详情数据\n"; + } + } else { + echo ">>> [3/5] 测试详情接口: ⏭️ 跳过 (未找到有效的资源ID)\n"; + } + + echo "\n"; + + // --- 4. 测试搜索接口 (Search Interface) --- + // 使用之前获取到的 vodName 进行搜索,如果没有则使用默认关键词 "爱" + $searchKey = $vodName ?: "爱"; + echo ">>> [4/5] 测试搜索接口 (searchContent) - 关键词: [$searchKey]\n"; + + try { + $startTime = microtime(true); + $searchRes = $spider->searchContent($searchKey, false, 1); + $cost = round((microtime(true) - $startTime) * 1000, 2); + + $searchList = $searchRes['list'] ?? []; + if (!empty($searchList)) { + echo " ✅ 通过 (耗时: {$cost}ms)\n"; + echo " - 搜索到 " . count($searchList) . " 个结果\n"; + $firstSearch = $searchList[0]; + echo " - 第一条结果: " . ($firstSearch['vod_name'] ?? '未知') . "\n"; + } else { + echo " ⚠️ 警告: 搜索未返回结果 (但这不代表接口错误)\n"; + } + } catch (Throwable $e) { + echo " ⚠️ 异常: 搜索接口调用失败 (允许失败)\n"; + echo " 错误信息: " . $e->getMessage() . "\n"; + } + + echo "\n"; + + // --- 5. 测试播放接口 (Player Interface) --- + if ($playUrl && $playFrom) { + // 解析播放链接,取第一组的第一个链接 + // 格式通常是: 播放源1$$$集数1$链接1#集数2$链接2...$$$播放源2... + // 或者是: 集数1$链接1#集数2$链接2... + + // 简单处理:先按 $$$ 分割取第一个播放源对应的链接串 + $playUrls = explode('$$$', $playUrl); + $currentUrlBlock = $playUrls[0] ?? ''; + + // 再按 # 分割取第一集 + $episodes = explode('#', $currentUrlBlock); + $firstEp = $episodes[0] ?? ''; + + // 再按 $ 分割取链接 (通常是 名称$链接) + $parts = explode('$', $firstEp); + $targetUrl = end($parts); // 取最后一部分作为链接 + + // 播放源flag + $playFroms = explode('$$$', $playFrom); + $flag = $playFroms[0] ?? 'default'; + + echo ">>> [5/5] 测试播放接口 (playerContent) - Flag: [$flag]\n"; + echo " - 目标链接: $targetUrl\n"; + + try { + $startTime = microtime(true); + // $flag, $id, $vipFlags + $playerRes = $spider->playerContent($flag, $targetUrl, []); + $cost = round((microtime(true) - $startTime) * 1000, 2); + + if (!empty($playerRes)) { + echo " ✅ 通过 (耗时: {$cost}ms)\n"; + // 打印返回的关键字段 + $parse = $playerRes['parse'] ?? 'N/A'; + $url = $playerRes['url'] ?? 'N/A'; + $header = $playerRes['header'] ?? 'N/A'; + + echo " - Parse: $parse\n"; + echo " - PlayUrl: $url\n"; + if (is_array($header)) { + echo " - Header: " . json_encode($header, JSON_UNESCAPED_UNICODE) . "\n"; + } + } else { + echo " ⚠️ 警告: 播放接口返回为空\n"; + } + } catch (Throwable $e) { + echo " ⚠️ 异常: 播放接口调用失败 (允许失败)\n"; + echo " 错误信息: " . $e->getMessage() . "\n"; + } + } else { + echo ">>> [5/5] 测试播放接口: ⏭️ 跳过 (未获取到有效的播放链接或播放源信息)\n"; + } + +} catch (Throwable $e) { + echo "\n⛔ 严重错误 (CRITICAL ERROR):\n"; + echo " 信息: " . $e->getMessage() . "\n"; + echo " 位置: " . $e->getFile() . " 第 " . $e->getLine() . " 行\n"; + echo " 堆栈:\n" . $e->getTraceAsString() . "\n"; +} + +echo "==================================================\n"; +echo "测试结束\n"; diff --git "a/spider/php/\344\270\203\347\214\253\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" "b/spider/php/\344\270\203\347\214\253\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" new file mode 100644 index 00000000..407d2910 --- /dev/null +++ "b/spider/php/\344\270\203\347\214\253\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" @@ -0,0 +1,357 @@ +startPage = 1; + } + + public function homeContent($filter) { + $classes = [ + ['type_id' => 'a', 'type_name' => '全部'], + ['type_id' => '1', 'type_name' => '女生原创'], + ['type_id' => '0', 'type_name' => '男生原创'], + ['type_id' => '2', 'type_name' => '出版图书'] + ]; + + $filters = []; + // Filter URL pattern: {{fl.作品分类 or 'a'}}-a-{{fl.作品字数 or 'a'}}-{{fl.更新时间 or 'a'}}-a-{{fl.是否完结 or 'a'}}-{{fl.排序 or 'click'}} + // 注意 URL 结构: /shuku/{class}-{filter}-{page}/ + // class 是 type_id. + // filter string: type-a-word-time-a-status-sort + + $filterConfig = [ + 'key' => 'filters', + 'name' => '筛选', + 'value' => [ + ['n' => '作品分类', 'v' => 'type', 'init' => 'a', 'list' => [ + ['n' => '全部', 'v' => 'a'], + ['n' => '言情', 'v' => '7'], + ['n' => '都市', 'v' => '1'], + ['n' => '玄幻', 'v' => '8'], + ['n' => '战神', 'v' => '295'], + ['n' => '赘婿', 'v' => '298'], + ['n' => '神医', 'v' => '297'], + ['n' => '脑洞', 'v' => '253'], + ['n' => '悬疑', 'v' => '10'], + ['n' => '历史', 'v' => '2'], + ['n' => '武侠', 'v' => '4'], + ['n' => '游戏', 'v' => '5'], + ['n' => '科幻', 'v' => '6'], + ['n' => '现言', 'v' => '17'], + ['n' => '古言', 'v' => '13'], + ['n' => '穿越', 'v' => '23'], + ['n' => '重生', 'v' => '24'], + ['n' => '豪门', 'v' => '32'], + ['n' => '其他', 'v' => '11'], + ]], + ['n' => '作品字数', 'v' => 'word', 'init' => 'a', 'list' => [ + ['n' => '全部', 'v' => 'a'], + ['n' => '30万字以下', 'v' => '1'], + ['n' => '30-50万字', 'v' => '2'], + ['n' => '50-100万字', 'v' => '3'], + ['n' => '100-200万字', 'v' => '4'], + ['n' => '200万字以上', 'v' => '5'], + ]], + ['n' => '更新时间', 'v' => 'time', 'init' => 'a', 'list' => [ + ['n' => '全部', 'v' => 'a'], + ['n' => '3日内', 'v' => '1'], + ['n' => '7日内', 'v' => '2'], + ['n' => '半月内', 'v' => '3'], + ['n' => '一月内', 'v' => '4'], + ]], + ['n' => '是否完结', 'v' => 'status', 'init' => 'a', 'list' => [ + ['n' => '全部', 'v' => 'a'], + ['n' => '连载中', 'v' => '1'], + ['n' => '已完结', 'v' => '2'], + ]], + ['n' => '排序', 'v' => 'sort', 'init' => 'click', 'list' => [ + ['n' => '人气', 'v' => 'click'], + ['n' => '更新', 'v' => 'date'], + ['n' => '评分', 'v' => 'score'], + ]] + ] + ]; + + foreach ($classes as $class) { + $filters[$class['type_id']] = [$filterConfig]; + } + + return [ + 'class' => $classes, + 'filters' => (object)$filters + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + // Filter logic: + // {{fl.作品分类 or 'a'}}-a-{{fl.作品字数 or 'a'}}-{{fl.更新时间 or 'a'}}-a-{{fl.是否完结 or 'a'}}-{{fl.排序 or 'click'}} + $f_type = $extend['type'] ?? 'a'; + $f_word = $extend['word'] ?? 'a'; + $f_time = $extend['time'] ?? 'a'; + $f_status = $extend['status'] ?? 'a'; + $f_sort = $extend['sort'] ?? 'click'; + + $filterStr = "{$f_type}-a-{$f_word}-{$f_time}-a-{$f_status}-{$f_sort}"; + + // URL: /shuku/{class}-{filter}-{page}/ + $url = sprintf(self::LIST_URL_TEMPLATE, $tid, $filterStr, $pg); + + $html = $this->fetch($url); + + $videos = []; + if ($html) { + $items = $this->pdfa($html, 'ul.qm-cover-text&&li'); + foreach ($items as $itemHtml) { + $video = [ + 'vod_id' => '', + 'vod_name' => $this->pdfh($itemHtml, '.s-tit&&Text'), + 'vod_pic' => $this->pd($itemHtml, 'img&&src', $url), + 'vod_remarks' => $this->pdfh($itemHtml, '.s-author&&Text'), + 'vod_content' => $this->pdfh($itemHtml, '.s-desc&&Text') + ]; + + $href = $this->pd($itemHtml, 'a&&href', $url); + if (preg_match('/shuku\/(\d+)/', $href, $matches)) { + $video['vod_id'] = $matches[1]; + } + + if (!empty($video['vod_id'])) { + $videos[] = $video; + } + } + } + + return $this->pageResult($videos, $pg); + } + + public function detailContent($ids) { + $id = $ids[0]; // This is book_id + $url = self::HOST . "/shuku/$id/"; + + // 1. Fetch Detail Page for basic info + $html = $this->fetch($url); + $vod = [ + 'vod_id' => $id, + 'vod_name' => '', + 'vod_pic' => '', + 'vod_content' => '', + 'vod_remarks' => '', + 'vod_director' => '', + 'vod_play_from' => '七猫小说', + ]; + + if ($html) { + $vod['vod_name'] = $this->pdfh($html, 'span.txt&&Text'); + $vod['vod_pic'] = $this->pd($html, '.wrap-pic&&img&&src', $url); + $vod['vod_content'] = $this->pdfh($html, '.book-introduction-item&&.qm-with-title-tb&&Text'); + $vod['vod_director'] = $this->pdfh($html, '.sub-title&&span&&a&&Text'); + $vod['vod_remarks'] = $this->pdfh($html, '.qm-tag&&Text'); + } + + // 2. Fetch Chapter List via API + // https://www.qimao.com/api/book/chapter-list?book_id=1699328 + $chapterUrl = self::HOST . "/api/book/chapter-list?book_id=$id"; + $json = $this->fetchJson($chapterUrl); + + $playList = []; + if (isset($json['data']['chapters'])) { + foreach ($json['data']['chapters'] as $ch) { + $title = $ch['title'] ?? ''; + $cid = $ch['id'] ?? ''; + // Format: title$book_id@@chapter_id@@title + $playList[] = "$title$$id@@$cid@@$title"; + } + } + + $vod['vod_play_url'] = implode('#', $playList); + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $params = [ + 'extend' => '', + 'tab' => '0', // Missing in previous version + 'gender' => '0', + 'refresh_state' => '8', // Missing in previous version + 'page' => $pg, + 'wd' => $key, + 'is_short_story_user' => '0' + ]; + + // Calculate Sign + $signStr = ""; + ksort($params); + foreach ($params as $k => $v) { + $signStr .= $k . "=" . $v; + } + $signStr .= self::SIGN_KEY; + $params['sign'] = md5($signStr); + + $url = self::SEARCH_URL . '?' . http_build_query($params); + // echo "DEBUG Search URL: $url\n"; + + $headers = $this->getSignHeaders(); + // Use fetch to see raw response + $raw = $this->fetch($url, ['headers' => $headers]); + // echo "DEBUG Search Response: " . substr($raw, 0, 200) . "\n"; + $json = json_decode($raw, true); + + $videos = []; + if (!empty($json['data']['books'])) { + foreach ($json['data']['books'] as $item) { + // Python filters by show_type == '0' + if (isset($item['show_type']) && $item['show_type'] == '0') { + $videos[] = [ + 'vod_id' => $item['id'], + 'vod_name' => $item['original_title'], + 'vod_pic' => $item['image_link'] ?? '', + 'vod_remarks' => $item['author'] ?? '', + 'vod_content' => $item['intro'] ?? '' + ]; + } + } + } + return [ + 'list' => $videos + ]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + // id format: title$book_id@@chapter_id@@title + $parts = explode('@@', $id); + + // Use full ID part (Title$BookID) as in JS/Python source + $bookId = $parts[0]; + $chapterId = $parts[1] ?? ''; + $title = $parts[2] ?? ''; + + $params = [ + 'id' => $bookId, + 'chapterId' => $chapterId + ]; + + // Calculate Sign + $signStr = ""; + ksort($params); + foreach ($params as $k => $v) { + $signStr .= $k . "=" . $v; + } + $signStr .= self::SIGN_KEY; + $params['sign'] = md5($signStr); + + // Debug info + // echo "\nDEBUG Sign Str: $signStr\n"; + // echo "DEBUG Sign: " . $params['sign'] . "\n"; + + // Manual URL construction to match Python's order: id, chapterId, sign + // Although ksort is used for sign calculation, the request URL might need specific order + $query = 'id=' . $bookId . '&chapterId=' . $chapterId . '&sign=' . $params['sign']; + $url = self::CONTENT_URL . '?' . $query; + // echo "DEBUG URL: $url\n"; + + // Use BaseSpider fetch with specific options + $options = [ + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, // Force HTTP/1.1 to match Python requests behavior + 'headers' => $this->getSignHeaders() + ]; + + $raw = $this->fetch($url, $options); + + // echo "DEBUG Response: " . substr($raw, 0, 100) . "\n"; + + $json = json_decode($raw, true) ?: []; + + $content = ''; + if (isset($json['data']['content'])) { + $content = $this->decodeContent($json['data']['content']); + } + + if (empty($content)) { + $msg = $json['msg'] ?? 'unknown error'; + $code = $json['code'] ?? 'unknown'; + $preview = substr($raw, 0, 100); + return [ + 'parse' => 0, + 'url' => 'novel://' . json_encode(['title' => "Error: $code - $msg ($preview)", 'content' => ''], JSON_UNESCAPED_UNICODE), + 'header' => (object)[] + ]; + } + + return [ + 'parse' => 0, + 'url' => 'novel://' . json_encode(['title' => $title, 'content' => $content], JSON_UNESCAPED_UNICODE), + 'header' => (object)[] + ]; + } + + // ================== Helpers ================== + + private function getSign($params) { + ksort($params); + $str = ""; + foreach ($params as $k => $v) { + $str .= $k . "=" . $v; + } + $str .= self::SIGN_KEY; + // Debug: return raw string for checking if needed, but for now just MD5 + // To debug: throw exception or log + return md5($str); + } + + private function getSignHeaders() { + return [ + "User-Agent" => "python-requests/2.31.0", // Mimic Python requests + "Accept" => "*/*", + "app-version" => "51110", + "platform" => "android", + "reg" => "0", + "AUTHORIZATION" => "", + "application-id" => "com.****.reader", + "net-env" => "1", + "channel" => "unknown", + "qm-params" => "", + "sign" => "fc697243ab534ebaf51d2fa80f251cb4" + ]; + } + + private function decodeContent($base64Response) { + // 1. Base64 Decode + $bin = base64_decode($base64Response); + if (!$bin) return ''; + + // 2. Extract IV (First 16 bytes) + // JS logic: txt = Base64.parse(resp).toString() (Hex string) + // iv = txt.slice(0, 32) (16 bytes hex) + // content = txt.slice(32) + // So raw binary: first 16 bytes are IV. + + $iv = substr($bin, 0, 16); + $data = substr($bin, 16); + + $key = hex2bin(self::AES_KEY_HEX); + + // 3. AES Decrypt + $decrypted = openssl_decrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv); + return trim($decrypted); + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\344\272\272\344\272\272\345\275\261\350\247\206 \341\265\210\341\266\273.php" "b/spider/php/\344\272\272\344\272\272\345\275\261\350\247\206 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..5678e47b --- /dev/null +++ "b/spider/php/\344\272\272\344\272\272\345\275\261\350\247\206 \341\265\210\341\266\273.php" @@ -0,0 +1,204 @@ +UA, + 'Origin: *', + 'Referer: https://docs.qq.com/', + 'Accept: application/json, text/plain, */*', + 'Accept-Language: zh-CN' + ]; + if ($isJson) { + $headers[] = 'Content-Type: application/json'; + } + return $headers; + } + + public function homeContent($filter) { + $classes = [ + ['type_id' => '1', 'type_name' => '电影'], + ['type_id' => '2', 'type_name' => '电视剧'], + ['type_id' => '3', 'type_name' => '综艺'], + ['type_id' => '5', 'type_name' => '动漫'], + ['type_id' => '4', 'type_name' => '纪录片'], + ['type_id' => '6', 'type_name' => '短剧'], + ['type_id' => '7', 'type_name' => '特别节目'], + ['type_id' => '8', 'type_name' => '少儿内容'] + ]; + + // 初始首页内容(空分类调用第一页数据) + $data = $this->categoryContent('', 1); + + return [ + 'class' => $classes, + 'list' => $data['list'] ?? [], + 'filters' => (object)[] + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $apiUrl = $this->HOST . '/api.php/main_program/moviesAll/'; + + $payload = [ + 'type' => (string)$tid, + 'sort' => 'vod_time', + 'area' => '', + 'style' => '', + 'time' => '', + 'pay' => '', + 'page' => $pg, + 'limit' => '60' + ]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => $this->getHeaders(), + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false // 补全SSL校验关闭,避免HTTPS请求失败 + ]); + + $jsonObj = json_decode($jsonStr ?: '{}', true); + $list = []; + + if (isset($jsonObj['data']['list']) && is_array($jsonObj['data']['list'])) { + $list = $this->arr2vods($jsonObj['data']['list']); + } + // 补全total参数,适配分页逻辑 + $total = isset($jsonObj['data']['pagecount']) ? $jsonObj['data']['pagecount'] * 60 : 0; + + return $this->pageResult($list, $pg, $total, 60); + } + + public function detailContent($ids) { + $id = is_array($ids) ? ($ids[0] ?? '') : $ids; + if (empty($id)) return ['list' => []]; // 空ID容错 + + $apiUrl = $this->HOST . '/api.php/player/details/'; + + $payload = ['id' => (string)$id]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => $this->getHeaders(), + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false + ]); + + $jsonObj = json_decode($jsonStr ?: '{}', true); + $vod = []; + + if (isset($jsonObj['detailData']) && is_array($jsonObj['detailData'])) { + $d = $jsonObj['detailData']; + $vod = [ + 'vod_id' => $d['vod_id'] ?? '', + 'vod_name' => $d['vod_name'] ?? '未知影片', + 'vod_pic' => $d['vod_pic'] ?? '', + 'vod_remarks' => $d['vod_remarks'] ?? '', + 'vod_year' => $d['vod_year'] ?? '', + 'vod_area' => $d['vod_area'] ?? '', + 'vod_actor' => $d['vod_actor'] ?? '', + 'vod_director' => $d['vod_director'] ?? '', + 'vod_content' => $d['vod_content'] ?? '暂无影片介绍', + 'vod_play_from' => $d['vod_play_from'] ?? '', + 'vod_play_url' => $d['vod_play_url'] ?? '', + 'type_name' => $d['vod_class'] ?? '' + ]; + } + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + if (empty($key) || $pg > 1) return $this->pageResult([], $pg, 0); + + $apiUrl = $this->HOST . '/api.php/search/syntheticalSearch/'; + $payload = ['keyword' => $key]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => $this->getHeaders(), + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false + ]); + + $jsonObj = json_decode($jsonStr ?: '{}', true); + $videos = []; + + if (isset($jsonObj['data']) && is_array($jsonObj['data'])) { + $data = $jsonObj['data']; + if (!empty($data['chasingFanCorrelation']) && is_array($data['chasingFanCorrelation'])) { + $videos = array_merge($videos, $this->arr2vods($data['chasingFanCorrelation'])); + } + if (!empty($data['moviesCorrelation']) && is_array($data['moviesCorrelation'])) { + $videos = array_merge($videos, $this->arr2vods($data['moviesCorrelation'])); + } + } + + return $this->pageResult($videos, $pg, count($videos)); + } + + public function playerContent($flag, $id, $vipFlags = []) { + $apiUrl = $this->HOST . '/api.php/player/payVideoUrl/'; + $payload = ['url' => $id]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => $this->getHeaders(), + CURLOPT_TIMEOUT => 30, + CURLOPT_SSL_VERIFYPEER => false + ]); + + $jsonObj = json_decode($jsonStr, true); + $url = $id; + $jx = 0; + + if (isset($jsonObj['data']['url']) && strpos($jsonObj['data']['url'], 'http') === 0) { + $url = $jsonObj['data']['url']; + } + + // 匹配第三方大站开启解析 + if (preg_match('/(?:www\.iqiyi|v\.qq|v\.youku|www\.mgtv|www\.bilibili)\.com/', $url)) { + $jx = 1; + } + + return [ + 'jx' => $jx, + 'parse' => 0, + 'url' => $url, + 'header' => [ + 'User-Agent' => $this->UA, + 'Referer' => 'https://docs.qq.com/' + ] + ]; + } + + private function arr2vods($arr) { + $videos = []; + foreach ($arr as $i) { + // 修复符号错误 + $remarks = ($i['vod_serial'] == '1') + ? $i['vod_serial'] . '集' + : '评分' . ($i['vod_score'] ?? $i['vod_douban_score'] ?? '0'); + + $videos[] = [ + 'vod_id' => $i['vod_id'] ?? '', + 'vod_name' => $i['vod_name'] ?? '', + 'vod_pic' => $i['vod_pic'] ?? '', + 'vod_remarks' => $remarks ?? '' + ]; + } + return $videos; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\345\212\250\346\274\253\345\225\246 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/\345\212\250\346\274\253\345\225\246 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..cef44d1c --- /dev/null +++ "b/spider/php/\345\212\250\346\274\253\345\225\246 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,330 @@ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer" => "https://www.dongman.la/", + "Connection" => "keep-alive" + ]; + } + + private function fetchHtml($url) { + // 忽略 SSL 验证 + $options = [ + 'headers' => $this->getHeader() + ]; + return $this->fetch($url, $options); + } + + public function homeContent($filter) { + $cats = []; + try { + $html = $this->fetchHtml("https://www.dongman.la/"); + if (preg_match('/

(.*?)<\/div>/s', $html, $matches)) { + if (preg_match_all('/]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/s', $matches[1], $links, PREG_SET_ORDER)) { + foreach ($links as $link) { + $href = $link[1]; + $title = trim($link[2]); + if (strpos($title, "首页") !== false) continue; + + $typeId = trim(str_replace("https://www.dongman.la", "", $href), "/"); + $cats[] = ["type_name" => $title, "type_id" => $typeId]; + } + } + } + } catch (Exception $e) { + // pass + } + + if (empty($cats)) { + $cats = [ + ["type_name" => "连载中", "type_id" => "manhua/list/lianzai"], + ["type_name" => "已完结", "type_id" => "manhua/list/wanjie"], + ["type_name" => "热血", "type_id" => "manhua/list/rexue"], + ["type_name" => "恋爱", "type_id" => "manhua/list/lianai"], + ["type_name" => "冒险", "type_id" => "manhua/list/maoxian"], + ["type_name" => "搞笑", "type_id" => "manhua/list/gaoxiao"] + ]; + } + + return ["class" => $cats, "filters" => []]; + } + + public function homeVideoContent() { + return $this->categoryContent("manhua/list/lianzai", 1, [], []); + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $tid = trim($tid, '/'); + $url = "https://www.dongman.la/{$tid}/{$pg}.html"; + return $this->getPostListByRegex($url, $pg); + } + + public function searchContent($key, $quick = false, $pg = 1) { + $url = "https://www.dongman.la/manhua/so/{$key}/{$pg}.html"; + return $this->getPostListByRegex($url, $pg); + } + + private function getPostListByRegex($url, $pg) { + try { + $html = $this->fetchHtml($url); + if (!$html) return ["list" => []]; + + $vlist = []; + $listHtml = ""; + + // 提取列表容器 + if (preg_match('/(?:class=["\']cy_list_mh["\']|id=["\']contaner["\'])[^>]*>(.*?)
(.*?)<\/b>/s', $item, $bMatch)) { + $name = trim($bMatch[1]); + } elseif (preg_match('/class=["\']pic["\'][^>]*title=["\']([^"\']+)["\']/', $item, $tMatch)) { + $name = $tMatch[1]; + } elseif (preg_match('/alt=["\']([^"\']+)["\']/', $item, $altMatch)) { + $name = trim($altMatch[1]); + } + + $name = trim(strip_tags($name)); + $name = str_replace(["漫画", "在线观看"], "", $name); + if (!$name) continue; + + // 提取图片 + $pic = ""; + if (preg_match('/(?:data-src|src)=["\']([^"\']+)["\']/', $item, $imgMatch)) { + $pic = $imgMatch[1]; + if (strpos($pic, "//") === 0) $pic = "https:" . $pic; + } + + // 提取备注 + $remark = ""; + if (preg_match('/]*>(.*?)<\/p>/s', $item, $pMatch)) { + // 确保不是 title 里的部分 + $tempItem = explode($pMatch[0], $item)[0]; + if (strpos($tempItem, 'title') === false) { + $remark = trim(strip_tags($pMatch[1])); + } + } + + if (!$remark && preg_match('/class=["\']tt["\'][^>]*>(.*?)<\/span>/', $item, $ttMatch)) { + $remark = trim($ttMatch[1]); + } + + $vlist[] = [ + 'vod_id' => $href, + 'vod_name' => $name, + 'vod_pic' => $pic, + 'vod_remarks' => $remark + ]; + } + } + + return ["list" => $vlist, "page" => $pg, "pagecount" => 9999, "limit" => 30, "total" => 999999]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + public function detailContent($ids) { + $vid = $ids[0]; + $url = (strpos($vid, 'http') === 0) ? $vid : "https://www.dongman.la{$vid}"; + + try { + $html = $this->fetchHtml($url); + + $name = ""; + if (preg_match('/]*>(.*?)<\/h1>/s', $html, $h1Match)) { + $name = trim(strip_tags($h1Match[1])); + } + + if (!$name && preg_match('/(.*?)<\/title>/s', $html, $titleMatch)) { + $parts = explode('-', $titleMatch[1]); + $parts = explode('_', $parts[0]); + $name = trim($parts[0]); + } + + $name = trim(str_replace(["漫画", "在线观看", "免费阅读"], "", $name)) ?: "未知漫画"; + + $cover = ""; + if (preg_match('/<img[^>]*class=["\'](?:detail-info-cover|pic)["\'][^>]*src=["\']([^"\']+)["\']/', $html, $coverMatch) || + preg_match('/<img[^>]*src=["\']([^"\']+)["\'][^>]*class=["\'](?:detail-info-cover|pic)["\']/', $html, $coverMatch)) { + $cover = $coverMatch[1]; + if (strpos($cover, "//") === 0) $cover = "https:" . $cover; + } + + $desc = ""; + if (preg_match('/id="comic-description"[^>]*>(.*?)<\/div>/s', $html, $descMatch)) { + $desc = trim(strip_tags($descMatch[1])); + $desc = str_replace([" ", "详细简介↓", "收起↑"], [" ", "", ""], $desc); + $desc = preg_replace('/\s+/', ' ', $desc); + } + + // 提取章节 + $linksSource = $html; + if (preg_match_all('/<(?:ul|ol)[^>]*class=["\'].*?list.*?["\'][^>]*>(.*?)<\/(?:ul|ol)>/s', $html, $listContainers)) { + $linksSource = implode("", $listContainers[1]); + } + + $chapterList = []; + $uniqueChapters = []; + + if (preg_match_all('/<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/s', $linksSource, $rawLinks, PREG_SET_ORDER)) { + foreach ($rawLinks as $link) { + $href = $link[1]; + $text = $link[2]; + + if (strpos($href, "/chapter/") === false && !preg_match('/\d+\.html/', $href)) continue; + if (strpos($href, "detail") !== false) continue; + + $title = trim(strip_tags($text)); + if (!$title || strpos($title, "在线阅读") !== false || strpos($title, "开始阅读") !== false) continue; + + if (!in_array($href, $uniqueChapters)) { + $uniqueChapters[] = $href; + $chapterList[] = "{$title}\${$href}"; + } + } + } + + $chapterList = array_reverse($chapterList); + $playUrl = implode("#", $chapterList); + + return [ + "list" => [[ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "type_name" => "漫画", + "vod_content" => $desc, + "vod_play_from" => '动漫啦', + "vod_play_url" => $playUrl + ]] + ]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + private function extractImgs($htmlText) { + $found = []; + // RE_PLAY_IMGS + if (preg_match_all('/(?:data-original|data-src|src)=["\']([^"\']+\.(?:jpg|png|jpeg|webp))[^"\']*["\']/i', $htmlText, $matches)) { + foreach ($matches[1] as $src) { + if (preg_match('/(logo|icon|cover|banner|\.gif|loading)/', $src)) continue; + + if (strpos($src, "//") === 0) { + $src = "https:" . $src; + } elseif (strpos($src, "/") === 0) { + $src = "https://www.dongman.la" . $src; + } elseif (strpos($src, "http") !== 0) { + continue; + } + + if (!in_array($src, $found)) { + $found[] = $src; + } + } + } + return $found; + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = (strpos($id, 'http') === 0) ? $id : "https://www.dongman.la{$id}"; + + $cleanUrl = rtrim(str_replace('.html', '', $url), '/'); + $allUrl = "{$cleanUrl}/all.html"; + + $imgList = []; + + // 1. 尝试 all.html + try { + $html = $this->fetchHtml($allUrl); + if ($html) { + $imgList = $this->extractImgs($html); + } + } catch (Exception $e) { + // pass + } + + // 2. 失败则循环抓取 (限制前40页) + if (empty($imgList)) { + $imageMap = []; + // PHP 串行抓取 + for ($i = 1; $i < 40; $i++) { + $targetUrl = ($i == 1) ? $url : "{$cleanUrl}/{$i}.html"; + try { + $resHtml = $this->fetchHtml($targetUrl); + if ($resHtml) { + $imgs = $this->extractImgs($resHtml); + if (!empty($imgs)) { + $imageMap[$i] = $imgs[0]; + } else { + // 如果某一页抓不到图片,可能就是结束了,或者反爬,这里可以考虑 break + // 但是为了保险起见,Python 是并发抓取所有,这里我们也继续尝试 + } + } else { + // 404 or error likely means end of chapter + break; + } + } catch (Exception $e) { + break; + } + } + + for ($i = 1; $i < 40; $i++) { + if (isset($imageMap[$i])) { + $imgList[] = $imageMap[$i]; + } + } + } + + if (empty($imgList)) { + // webview fallback + return ['parse' => 1, 'url' => $url, 'header' => json_encode($this->getHeader())]; + } + + $novelData = implode("&&", $imgList); + + return [ + "parse" => 0, + "playUrl" => "", + "url" => "pics://{$novelData}", + "header" => "" + ]; + } +} + +(new Spider())->run(); diff --git "a/spider/php/\345\214\205\345\255\220\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/\345\214\205\345\255\220\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..e5ef5f18 --- /dev/null +++ "b/spider/php/\345\214\205\345\255\220\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,312 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + + public function getName() { + return "包子漫画"; + } + + public function init($extend = "") { + // pass + } + + public function isVideoFormat($url) { + return false; + } + + public function manualVideoCheck() { + return false; + } + + private function getHeader() { + return [ + "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer" => "https://cn.bzmanga.com/" + ]; + } + + public function homeContent($filter) { + $classes = [ + ["type_name" => "最新上架", "type_id" => "new"], + ["type_name" => "全部漫画", "type_id" => "all"], + ["type_name" => "地区", "type_id" => "region"], + ["type_name" => "进度", "type_id" => "status"], + ["type_name" => "题材", "type_id" => "type"] + ]; + + $filters = []; + $filters['region'] = [["key" => "val", "name" => "地区", "value" => [["n" => "国漫", "v" => "cn"],["n" => "日本", "v" => "jp"],["n" => "欧美", "v" => "en"]]]]; + $filters['status'] = [["key" => "val", "name" => "进度", "value" => [["n" => "连载中", "v" => "serial"],["n" => "已完结", "v" => "pub"]]]]; + + $types = [ + ["n" => "都市", "v" => "dushi"], ["n" => "冒险", "v" => "mouxian"], + ["n" => "热血", "v" => "rexie"], ["n" => "爱情", "v" => "aiqing"], + ["n" => "恋爱", "v" => "lianai"], ["n" => "耽美", "v" => "danmei"], + ["n" => "武侠", "v" => "wuxia"], ["n" => "格斗", "v" => "gedou"], + ["n" => "科幻", "v" => "kehuan"], ["n" => "魔幻", "v" => "mohuan"], + ["n" => "侦探", "v" => "zhentan"], ["n" => "推理", "v" => "tuili"], + ["n" => "玄幻", "v" => "xuanhuan"], ["n" => "日常", "v" => "richang"], + ["n" => "生活", "v" => "shenghuo"], ["n" => "搞笑", "v" => "gaoxiao"], + ["n" => "校园", "v" => "xiaoyuan"], ["n" => "奇幻", "v" => "qihuan"] + ]; + $filters['type'] = [["key" => "val", "name" => "类型", "value" => $types]]; + + return ["class" => $classes, "filters" => $filters]; + } + + public function homeVideoContent() { + return $this->categoryContent("new", 1, [], []); + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + if ($tid == "new") { + $url = ($pg == 1) ? "https://cn.bzmanga.com/list/new/" : "https://cn.bzmanga.com/list/new/?page={$pg}"; + } elseif ($tid == "all") { + $url = "https://cn.bzmanga.com/classify?page={$pg}"; + } else { + $val = $extend['val'] ?? ''; + if (!$val) { + if ($tid == "region") $val = "cn"; + elseif ($tid == "status") $val = "serial"; + elseif ($tid == "type") $val = "dushi"; + } + + $paramKey = $tid; + if ($tid == "status") $paramKey = "state"; + + $url = "https://cn.bzmanga.com/classify?{$paramKey}={$val}&page={$pg}"; + } + + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + $items = $this->pdfa($html, '.comics-card'); + + $videos = []; + foreach ($items as $item) { + $vid = $this->pd($item, 'a.comics-card__poster&&href'); + if (!$vid) continue; + + $cover = $this->pd($item, 'amp-img&&src'); + if (strpos($cover, ".w=") !== false) { + $cover = explode('.w=', $cover)[0]; + } + + $name = $this->pd($item, '.comics-card__title&&Text'); + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "vod_remarks" => "" + ]; + } + + return [ + "list" => $videos, + "page" => $pg, + "pagecount" => 9999, + "limit" => 36, + "total" => 999999 + ]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + public function detailContent($ids) { + $vid = $ids[0]; + $url = (strpos($vid, 'http') === 0) ? $vid : "https://cn.bzmanga.com{$vid}"; + + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + + $name = $this->pd($html, '.comics-detail__title&&Text') ?: "未知"; + $author = $this->pd($html, '.comics-detail__author&&Text'); + $desc = $this->pd($html, '.comics-detail__desc&&Text'); + + $cover = $this->pd($html, 'amp-img&&src'); + if (strpos($cover, ".w=") !== false) { + $cover = explode('.w=', $cover)[0]; + } + + $chapterItems = $this->pdfa($html, '.comics-chapters__item'); + + $rawUrlList = []; + foreach ($chapterItems as $item) { + $aTag = $this->pd($item, 'a', true); // get element + if (!$aTag && strpos($item, '<a') === 0) { // simple check if item itself is a tag + // HtmlParser doesn't fully support item itself as root sometimes, depend on implementation + // Let's assume pdfa returns inner HTML or node. + // BaseSpider pdfa returns array of strings (html fragments) usually. + // So we can re-parse item + } + + $chapterName = $this->pd($item, 'a&&Text'); + if (!$chapterName) $chapterName = $this->pd($item, 'Text'); // fallback if item is 'a' + + $rawHref = $this->pd($item, 'a&&href'); + if (!$rawHref) $rawHref = $this->pd($item, 'href'); + + if (!$chapterName || !$rawHref) continue; + + $realChapterUrl = ""; + + if (preg_match('/comic_id=(\d+).*chapter_slot=(\d+)/', $rawHref, $matches)) { + $cId = $matches[1]; + $cSlot = $matches[2]; + $realChapterUrl = "https://cn.dzmanga.com/comic/chapter/{$cId}/0_{$cSlot}.html"; + } else { + if (strpos($rawHref, "/") === 0) { + $realChapterUrl = "https://cn.dzmanga.com{$rawHref}"; + } elseif (strpos($rawHref, "http") !== false) { + $realChapterUrl = str_replace("cn.bzmanga.com", "cn.dzmanga.com", $rawHref); + } else { + $realChapterUrl = "https://cn.dzmanga.com/{$rawHref}"; + } + } + + $rawUrlList[] = "{$chapterName}\${$realChapterUrl}"; + } + + $descList = $rawUrlList; + $ascList = array_reverse($rawUrlList); + + $strDesc = implode("#", $descList); + $strAsc = implode("#", $ascList); + + return [ + "list" => [[ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "type_name" => "漫画", + "vod_year" => "", + "vod_area" => "", + "vod_remarks" => $author, + "vod_actor" => "", + "vod_director" => "", + "vod_content" => $desc, + "vod_play_from" => '正序$$$倒序', + "vod_play_url" => "{$strAsc}$$$" . $strDesc + ]] + ]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + public function searchContent($key, $quick = false, $pg = 1) { + $url = "https://cn.bzmanga.com/search?q={$key}"; + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + $items = $this->pdfa($html, '.comics-card'); + + $videos = []; + foreach ($items as $item) { + $vid = $this->pd($item, 'a.comics-card__poster&&href'); + if (!$vid) continue; + + $cover = $this->pd($item, 'amp-img&&src'); + if (strpos($cover, ".w=") !== false) { + $cover = explode('.w=', $cover)[0]; + } + + $name = $this->pd($item, '.comics-card__title&&Text'); + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "vod_remarks" => "" + ]; + } + return ['list' => $videos]; + } catch (Exception $e) { + return ['list' => []]; + } + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = $id; + $headers = $this->getHeader(); + $headers['Referer'] = $url; + + try { + $html = $this->fetch($url, ['headers' => $headers]); + + $imgList = []; + + // 策略A:DOM解析 + $container = $this->pd($html, '.comic-contain', true); + if (!$container) { + // simple body check, or just parse whole html + // HtmlParser usually handles whole html if no selector matched for subset + } + + $imgs = []; + if ($container) { + // If we had a specific object for container, we'd use it. + // But base spider pd/pdfa usually works on string. + // So let's just search in html + } + + // Use regex for specific container if possible, but here we can just try global selector on html + // but restricted by container class if we could. + // Simplified: Global search with selector + $imgs = $this->pdfa($html, '.comic-contain amp-img'); + if (empty($imgs)) { + $imgs = $this->pdfa($html, '.comic-contain img'); + } + // If still empty, try body (global) + if (empty($imgs)) { + $imgs = $this->pdfa($html, 'amp-img'); + if (empty($imgs)) $imgs = $this->pdfa($html, 'img'); + } + + foreach ($imgs as $img) { + $src = $this->pd($img, 'src'); + if (!$src) $src = $this->pd($img, 'data-src'); + + if ($src) { + if (strpos($src, "next_chapter") !== false || strpos($src, "prev_chapter") !== false || strpos($src, "icon") !== false || strpos($src, "logo") !== false) { + continue; + } + if (strpos($src, "//") === 0) { + $src = "https:" . $src; + } + $imgList[] = $src; + } + } + + // 策略B:暴力正则 + if (count($imgList) < 2) { + if (preg_match_all('/(https?:\/\/[^"\'\s]+static[^"\'\s]+\.(?:jpg|png|webp|jpeg)(?:\?[^"\'\s]*)?)/', $html, $matches)) { + foreach ($matches[1] as $m) { + if (!in_array($m, $imgList)) { + if (strpos($m, "cover") !== false) continue; + if (strpos($m, "icon") !== false) continue; + if (strpos($m, "logo") !== false) continue; + if (strpos($m, "bg") !== false) continue; + $imgList[] = $m; + } + } + } + } + + $uniqueImgs = array_unique($imgList); + $novelData = implode("&&", $uniqueImgs); + + return [ + "parse" => 0, + "playUrl" => "", + "url" => "pics://{$novelData}", + "header" => "" + ]; + } catch (Exception $e) { + return ["parse" => 0, "url" => "", "header" => ""]; + } + } +} + +(new Spider())->run(); diff --git "a/spider/php/\345\216\273\350\257\273\344\271\246 \341\265\210\341\266\273[\344\271\246].php" "b/spider/php/\345\216\273\350\257\273\344\271\246 \341\265\210\341\266\273[\344\271\246].php" new file mode 100644 index 00000000..b580972c --- /dev/null +++ "b/spider/php/\345\216\273\350\257\273\344\271\246 \341\265\210\341\266\273[\344\271\246].php" @@ -0,0 +1,273 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private const AES_KEY = '242ccb8230d709e1'; + private const SIGN_KEY = 'd3dGiJc651gSQ8w1'; + private const APP_ID = 'com.kmxs.reader'; + + private $baseHeaders = [ + "app-version" => "51110", + "platform" => "android", + "reg" => "0", + "AUTHORIZATION" => "", + "application-id" => self::APP_ID, + "net-env" => "1", + "channel" => "unknown", + "qm-params" => "" + ]; + + public function getName() { + return "去读书"; + } + + public function init($extend = "") { + // pass + } + + public function isVideoFormat($url) { + return false; + } + + public function manualVideoCheck() { + // pass + } + + private function getSign($params) { + ksort($params); + $signStr = ""; + foreach ($params as $k => $v) { + $signStr .= "{$k}={$v}"; + } + $signStr .= self::SIGN_KEY; + return md5($signStr); + } + + private function getHeaders() { + $headers = $this->baseHeaders; + $headers['sign'] = $this->getSign($headers); + $headers['User-Agent'] = 'okhttp/3.12.1'; + return $headers; + } + + private function decryptContent($base64Content) { + try { + $encryptedBytes = base64_decode($base64Content); + if (strlen($encryptedBytes) < 16) { + return "数据长度不足"; + } + $iv = substr($encryptedBytes, 0, 16); + $ciphertext = substr($encryptedBytes, 16); + + $decrypted = openssl_decrypt( + $ciphertext, + 'AES-128-CBC', + self::AES_KEY, + OPENSSL_RAW_DATA, + $iv + ); + + if ($decrypted === false) { + return "解密失败"; + } + return trim($decrypted); + } catch (Exception $e) { + return "解密错误: " . $e->getMessage(); + } + } + + private function getApiUrl($path, $params, $domainType = "bc") { + $params['sign'] = $this->getSign($params); + $baseUrl = ($domainType == "bc") ? "https://api-bc.wtzw.com" : "https://api-ks.wtzw.com"; + if (strpos($path, "search") !== false) { + $baseUrl = "https://api-bc.wtzw.com"; + } + + $queryString = http_build_query($params); + return ["{$baseUrl}{$path}?{$queryString}", $params]; + } + + public function homeContent($filter) { + $cats = [ + ["n" => "玄幻奇幻", "v" => "1|202"], ["n" => "都市人生", "v" => "1|203"], ["n" => "武侠仙侠", "v" => "1|205"], + ["n" => "历史军事", "v" => "1|56"], ["n" => "科幻末世", "v" => "1|64"], ["n" => "游戏竞技", "v" => "1|75"], + ["n" => "现代言情", "v" => "2|1"], ["n" => "古代言情", "v" => "2|2"], ["n" => "幻想言情", "v" => "2|4"], + ["n" => "婚恋情感", "v" => "2|6"], ["n" => "悬疑推理", "v" => "3|262"] + ]; + + $classes = []; + foreach ($cats as $cat) { + $classes[] = ["type_name" => $cat['n'], "type_id" => $cat['v']]; + } + return ['class' => $classes, 'filters' => []]; + } + + public function homeVideoContent() { + return ['list' => []]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $parts = explode("|", $tid); + $gender = $parts[0] ?? "1"; + $catId = $parts[1] ?? "202"; + + $path = "/api/v4/category/get-list"; + $params = ['gender' => $gender, 'category_id' => $catId, 'need_filters' => '1', 'page' => $pg, 'need_category' => '1']; + $headers = $this->getHeaders(); + list($url, $signedParams) = $this->getApiUrl($path, $params, "bc"); + + try { + $j = $this->fetchJson($url, ['headers' => $headers]); + $videos = []; + $bookList = []; + + if (isset($j['data']['books'])) { + $bookList = $j['data']['books']; + } elseif (isset($j['books'])) { + $bookList = $j['books']; + } + + foreach ($bookList as $book) { + $videos[] = [ + "vod_id" => (string)$book['id'], + "vod_name" => $book['title'], + "vod_pic" => $book['image_link'], + "vod_remarks" => $book['author'] + ]; + } + return ['list' => $videos, 'page' => $pg, 'pagecount' => 999, 'limit' => 20, 'total' => 9999]; + } catch (Exception $e) { + return ['list' => []]; + } + } + + public function detailContent($ids) { + $bid = $ids[0]; + $headers = $this->getHeaders(); + + $detailParams = ['id' => $bid, 'imei_ip' => '2937357107', 'teeny_mode' => '0']; + list($detailUrl, $detailSignedParams) = $this->getApiUrl("/api/v4/book/detail", $detailParams, "bc"); + + $vod = ["vod_id" => $bid, "vod_name" => "获取中...", "vod_play_from" => "去读书"]; + + try { + $j = $this->fetchJson($detailUrl, ['headers' => $headers]); + if (isset($j['data']['book'])) { + $bookInfo = $j['data']['book']; + $vod["vod_name"] = $bookInfo['title']; + $vod["vod_pic"] = $bookInfo['image_link']; + $vod["type_name"] = $bookInfo['category_name'] ?? ''; + $vod["vod_remarks"] = ($bookInfo['words_num'] ?? '') . "字"; + $vod["vod_actor"] = $bookInfo['author']; + $vod["vod_content"] = $bookInfo['intro']; + } + + // 获取目录 + $chapterParams = ['id' => $bid]; + list($chapterUrl, $chapterSignedParams) = $this->getApiUrl("/api/v1/chapter/chapter-list", $chapterParams, "ks"); + + $jc = $this->fetchJson($chapterUrl, ['headers' => $headers]); + + $chapterList = []; + $lists = []; + if (isset($jc['data']['chapter_lists'])) { + $lists = $jc['data']['chapter_lists']; + } + + foreach ($lists as $item) { + $cid = (string)$item['id']; + $cname = str_replace(["@@", "$"], ["-", ""], (string)$item['title']); + $urlCode = "{$bid}@@{$cid}@@{$cname}"; + $chapterList[] = "{$cname}\${$urlCode}"; + } + + $vod['vod_play_url'] = implode("#", $chapterList); + return ["list" => [$vod]]; + } catch (Exception $e) { + $vod["vod_content"] = "Error: " . $e->getMessage(); + return ["list" => [$vod]]; + } + } + + public function searchContent($key, $quick = false, $pg = 1) { + $path = "/api/v5/search/words"; + $params = ['gender' => '3', 'imei_ip' => '2937357107', 'page' => $pg, 'wd' => $key]; + $headers = $this->getHeaders(); + list($url, $signedParams) = $this->getApiUrl($path, $params, "bc"); + + try { + $j = $this->fetchJson($url, ['headers' => $headers]); + $videos = []; + if (isset($j['data']['books'])) { + foreach ($j['data']['books'] as $book) { + $videos[] = [ + "vod_id" => (string)$book['id'], + "vod_name" => $book['original_title'], + "vod_pic" => $book['image_link'], + "vod_remarks" => $book['original_author'] + ]; + } + } + return ['list' => $videos, 'page' => $pg]; + } catch (Exception $e) { + return ['list' => [], 'page' => $pg]; + } + } + + public function playerContent($flag, $id, $vipFlags = []) { + try { + $parts = explode("@@", $id); + $bid = $parts[0]; + $cid = $parts[1]; + $title = isset($parts[2]) ? $parts[2] : ""; + + $params = ['id' => $bid, 'chapterId' => $cid]; + $headers = $this->getHeaders(); + list($url, $signedParams) = $this->getApiUrl("/api/v1/chapter/content", $params, "ks"); + + $j = $this->fetchJson($url, ['headers' => $headers]); + + $content = ""; + if (isset($j['data']['content'])) { + if (empty($title) && isset($j['data']['title'])) { + $title = $j['data']['title']; + } + $content = $this->decryptContent($j['data']['content']); + } else { + $content = "加载失败: " . ($j['msg'] ?? '未知错误'); + } + + if (empty($title)) { + $title = "章节正文"; + } + + $resultData = [ + 'title' => $title, + 'content' => $content + ]; + + $ret = json_encode($resultData, JSON_UNESCAPED_UNICODE); + $finalUrl = "novel://{$ret}"; + + return [ + "parse" => 0, + "playUrl" => "", + "url" => $finalUrl, + "header" => "" + ]; + } catch (Exception $e) { + $errData = [ + 'title' => "错误", + 'content' => "发生异常: " . $e->getMessage() + ]; + return [ + "parse" => 0, + "playUrl" => "", + "url" => "novel://" . json_encode($errData, JSON_UNESCAPED_UNICODE), + "header" => "" + ]; + } + } +} + +(new Spider())->run(); diff --git "a/spider/php/\345\223\207\345\223\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" "b/spider/php/\345\223\207\345\223\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..aef5b5e2 --- /dev/null +++ "b/spider/php/\345\223\207\345\223\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" @@ -0,0 +1,161 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +// ================= 核心加解密类 ================= +class WawaCrypto { + public static function decrypt($encrypted_data) { + $key = base64_decode('Crm4FXWkk5JItpYirFDpqg=='); // + $data = hex2bin(base64_decode($encrypted_data)); // + return openssl_decrypt($data, 'AES-128-ECB', $key, OPENSSL_RAW_DATA); + } + + public static function sign($message, $privateKey) { + $key = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privateKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----"; + $res = openssl_get_privatekey($key); + openssl_sign($message, $signature, $res, OPENSSL_ALGO_SHA256); // 使用 SHA256 签名 + return base64_encode($signature); + } + + public static function uuid() { + return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x', + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + } +} + +class Spider extends BaseSpider { + private $HOST; + private $APP_KEY; + private $RSA_KEY; + private $CONF; + + public function init($extend = '') { + $this->initConf(); + } + + private function initConf() { + $uid = WawaCrypto::uuid(); + $t = (string)(time() * 1000); + $sign = md5("appKey=3bbf7348cf314874883a18d6b6fcf67a&uid=$uid&time=$t"); + + $url = 'https://gitee.com/api/v5/repos/aycapp/openapi/contents/wawaconf.txt?access_token=74d5879931b9774be10dee3d8c51008e'; + $res = json_decode($this->fetch($url, [], ["User-Agent: okhttp/4.9.3", "uid: $uid", "time: $t", "sign: $sign"]), true); + + $this->CONF = json_decode(WawaCrypto::decrypt($res['content']), true); + $this->HOST = $this->CONF['baseUrl']; + $this->APP_KEY = $this->CONF['appKey']; + $this->RSA_KEY = $this->CONF['appSecret']; + } + + private function getWawaHeaders() { + $uid = WawaCrypto::uuid(); + $t = (string)(time() * 1000); + $sign = WawaCrypto::sign("appKey={$this->APP_KEY}&time=$t&uid=$uid", $this->RSA_KEY); + return [ + 'User-Agent: okhttp/4.9.3', + "uid: $uid", + "time: $t", + "appKey: {$this->APP_KEY}", + "sign: $sign" + ]; + } + + public function homeContent($filter) { + $typeData = json_decode($this->fetch("{$this->HOST}/api.php/zjv6.vod/types", [], $this->getWawaHeaders()), true); + $classes = []; + $filters = []; + $dy = ["class" => "类型", "area" => "地区", "lang" => "语言", "year" => "年份", "letter" => "字母", "by" => "排序"]; + $sl = ['按更新' => 'time', '按播放' => 'hits', '按评分' => 'score', '按收藏' => 'store_num']; + + if (isset($typeData['data']['list'])) { + foreach ($typeData['data']['list'] as $item) { + $classes[] = ['type_id' => $item['type_id'], 'type_name' => $item['type_name']]; + $tid = (string)$item['type_id']; + $filters[$tid] = []; + $item['type_extend']['by'] = '按更新,按播放,按评分,按收藏'; // 强制注入排序 + + foreach ($dy as $key => $name) { + if (!empty($item['type_extend'][$key])) { + $values = explode(',', $item['type_extend'][$key]); + $value_array = []; + foreach ($values as $v) { + if (empty($v)) continue; + $value_array[] = ["n" => $v, "v" => ($key == "by" ? ($sl[$v] ?? $v) : $v)]; + } + $filters[$tid][] = ["key" => $key, "name" => $name, "value" => $value_array]; + } + } + } + } + + $homeList = json_decode($this->fetch("{$this->HOST}/api.php/zjv6.vod/vodPhbAll", [], $this->getWawaHeaders()), true); + $list = $homeList['data']['list'][0]['vod_list'] ?: []; + + return [ + 'class' => $classes, + 'filters' => $filters, + 'list' => $list + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $query = http_build_query([ + 'type' => $tid, 'page' => $pg, 'limit' => '12', + 'class' => $extend['class'] ?? '', 'area' => $extend['area'] ?? '', + 'year' => $extend['year'] ?? '', 'by' => $extend['by'] ?? '' + ]); + $res = json_decode($this->fetch("{$this->HOST}/api.php/zjv6.vod?$query", [], $this->getWawaHeaders()), true); + + $list = $res['data']['list'] ?: []; + // 哇哇影视未返回总数,估算分页 + return $this->pageResult($list, $pg, 0, 12); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $res = json_decode($this->fetch("{$this->HOST}/api.php/zjv6.vod/detail?vod_id=$id&rel_limit=10", [], $this->getWawaHeaders()), true); + $item = $res['data']; + $playFrom = []; $playUrls = []; + + if (isset($item['vod_play_list'])) { + foreach ($item['vod_play_list'] as $list) { + $playFrom[] = $list['player_info']['show']; + $urls = []; + foreach ($list['urls'] as $u) { + $u['parse'] = $list['player_info']['parse2']; + $urls[] = $u['name'] . '$' . base64_encode(json_encode($u)); + } + $playUrls[] = implode('#', $urls); + } + } + + return ['list' => [[ + 'vod_id' => $item['vod_id'], + 'vod_name' => $item['vod_name'], + 'vod_pic' => $item['vod_pic'], + 'vod_remarks' => $item['vod_remarks'], + 'vod_content' => $item['vod_content'] ?? '', + 'vod_play_from' => implode('$$$', $playFrom), + 'vod_play_url' => implode('$$$', $playUrls) + ]]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $res = json_decode($this->fetch("{$this->HOST}/api.php/zjv6.vod?page=$pg&limit=20&wd=".urlencode($key), [], $this->getWawaHeaders()), true); + $list = $res['data']['list'] ?: []; + return $this->pageResult($list, $pg, 0, 20); + } + + public function playerContent($flag, $id, $vipFlags = []) { + $playData = json_decode(base64_decode($id), true); + return [ + 'parse' => 1, + 'url' => $playData['url'], + 'header' => ['User-Agent' => 'dart:io'] + ]; + } +} + +(new Spider())->run(); diff --git "a/spider/php/\345\227\267\345\221\234\345\212\250\346\274\253 \341\265\210\341\266\273.php" "b/spider/php/\345\227\267\345\221\234\345\212\250\346\274\253 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..f846183a --- /dev/null +++ "b/spider/php/\345\227\267\345\221\234\345\212\250\346\274\253 \341\265\210\341\266\273.php" @@ -0,0 +1,256 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = 'https://www.aowu.tv'; + // 使用手机 UA 防止拦截 + private $UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'; + + protected function getHeaders() { + return [ + 'User-Agent: ' . $this->UA, + 'Referer: ' . $this->HOST + ]; + } + + private function fixUrl($url) { + if (empty($url)) return ''; + if (strpos($url, '//') === 0) return 'https:' . $url; + if (strpos($url, '/') === 0) return $this->HOST . $url; + if (strpos($url, 'http') !== 0) return $this->HOST . '/' . $url; + return $url; + } + + // 解析 HTML 列表 (首页/搜索用) + private function parseHtmlList($html, $isSearch = false) { + $videos = []; + if (!$html) return $videos; + + $pattern = $isSearch + ? '/<div class="search-list[^"]*">(.*?)<div class="right">/is' + : '/<div class="public-list-box[^"]*">(.*?)<\/div>\s*<\/div>/is'; + + preg_match_all($pattern, $html, $matches); + + if (!empty($matches[1])) { + foreach ($matches[1] as $itemHtml) { + if (!preg_match('/href="([^"]+)"/', $itemHtml, $m)) continue; + $href = $m[1]; + + $title = ''; + if (preg_match('/alt="([^"]+)"/', $itemHtml, $m)) $title = $m[1]; + elseif (preg_match('/title="([^"]+)"/', $itemHtml, $m)) $title = $m[1]; + + $pic = ''; + if (preg_match('/data-src="([^"]+)"/', $itemHtml, $m)) $pic = $m[1]; + elseif (preg_match('/src="([^"]+)"/', $itemHtml, $m)) $pic = $m[1]; + + $remarks = ''; + if (preg_match('/<span class="public-list-prb[^"]*">([^<]+)<\/span>/', $itemHtml, $m)) { + $remarks = strip_tags($m[1]); + } elseif (preg_match('/<span class="public-prt"[^>]*>([^<]+)<\/span>/', $itemHtml, $m)) { + $remarks = strip_tags($m[1]); + } + + if ($title) { + $videos[] = [ + 'vod_id' => $this->fixUrl($href), + 'vod_name' => trim($title), + 'vod_pic' => $this->fixUrl($pic), + 'vod_remarks' => trim($remarks) + ]; + } + } + } + return $videos; + } + + public function homeContent($filter) { + // 首页 (精选 + 筛选配置) + $html = $this->fetch($this->HOST . '/', [], $this->getHeaders()); + $list = $this->parseHtmlList($html, false); + $list = array_slice($list, 0, 20); + + $classes = [ + ['type_id' => '20', 'type_name' => '🔥 当季新番'], + ['type_id' => '21', 'type_name' => '🎬 番剧'], + ['type_id' => '22', 'type_name' => '🎥 剧场'] + ]; + + // 筛选配置 + $filters = $this->getFilters(); + + return [ + 'class' => $classes, + 'filters' => $filters, + 'list' => $list + ]; + } + + // 筛选配置 (参照 JS 源码配置) + private function getFilters() { + $classes = ['搞笑','恋爱','校园','后宫','治愈','日常','原创','战斗','百合','BL','卖肉','漫画改','游戏改','异世界','泡面番','轻小说改','OVA','OAD','京阿尼','芳文社','A-1Pictures','CloverWorks','J.C.STAFF','动画工房','SUNRISE','Production.I.G','MADHouse','BONES','P.A.WORKS','SHAFT','MAPPA','ufotable','TRIGGER','WITSTUDIO']; + + $years = []; + for ($i = 2026; $i >= 1990; $i--) $years[] = (string)$i; + + // 构建筛选结构 + $classValues = [['n' => '全部', 'v' => '']]; + foreach ($classes as $c) $classValues[] = ['n' => $c, 'v' => $c]; + + $yearValues = [['n' => '全部', 'v' => '']]; + foreach ($years as $y) $yearValues[] = ['n' => $y, 'v' => $y]; + + $sortValues = [ + ['n' => '按最新', 'v' => 'time'], + ['n' => '按最热', 'v' => 'hits'], + ['n' => '按评分', 'v' => 'score'] + ]; + + $rules = [ + ['key' => 'class', 'name' => '剧情', 'value' => $classValues], + ['key' => 'year', 'name' => '年份', 'value' => $yearValues], + ['key' => 'by', 'name' => '排序', 'value' => $sortValues] + ]; + + // 应用到所有分类 + return [ + '20' => $rules, + '21' => $rules, + '22' => $rules + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $apiUrl = $this->HOST . '/index.php/ds_api/vod'; + + // 构建 POST 数据 + $postParams = [ + 'type' => $tid, + 'class' => $extend['class'] ?? '', + 'year' => $extend['year'] ?? '', + 'by' => $extend['by'] ?? 'time', // 默认按最新 + 'page' => $pg + ]; + + // 发送 POST 请求 (必须带上 content-type) + $headers = array_merge($this->getHeaders(), [ + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' + ]); + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => http_build_query($postParams), + CURLOPT_HTTPHEADER => $headers + ]); + + $jsonObj = json_decode($jsonStr, true); + $list = []; + + if ($jsonObj && isset($jsonObj['list']) && is_array($jsonObj['list'])) { + foreach ($jsonObj['list'] as $it) { + $list[] = [ + 'vod_id' => $this->fixUrl($it['url']), + 'vod_name' => $it['vod_name'], + 'vod_pic' => $this->fixUrl($it['vod_pic']), + 'vod_remarks' => $it['vod_remarks'] + ]; + } + } + + $total = $jsonObj['total'] ?? 0; + $limit = $jsonObj['limit'] ?? 30; + + return $this->pageResult($list, $pg, $total, $limit); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $url = (strpos($id, 'http') === 0) ? $id : $this->fixUrl($id); + $html = $this->fetch($url, [], $this->getHeaders()); + + $vod = [ + 'vod_id' => $id, 'vod_name' => '', 'vod_pic' => '', + 'vod_content' => '', 'vod_play_from' => '', 'vod_play_url' => '' + ]; + + if ($html) { + if (preg_match('/<title>(.*?)<\/title>/', $html, $m)) + $vod['vod_name'] = trim(preg_replace('/\s*-\s*嗷呜动漫.*$/', '', $m[1])); + + if (preg_match('/data-original="([^"]+)"/', $html, $m)) $vod['vod_pic'] = $this->fixUrl($m[1]); + elseif (preg_match('/class="detail-pic"[^>]*src="([^"]+)"/', $html, $m)) $vod['vod_pic'] = $this->fixUrl($m[1]); + + if (preg_match('/class="text cor3"[^>]*>(.*?)<\/div>/is', $html, $m)) + $vod['vod_content'] = trim(strip_tags($m[1])); + + $playFrom = []; + preg_match('/<div class="anthology-tab[^"]*">(.*?)<\/div>/is', $html, $tabHtml); + if (!empty($tabHtml[1])) { + preg_match_all('/<a[^>]*>([^<]+)<\/a>/', $tabHtml[1], $tabNames); + if (!empty($tabNames[1])) { + foreach($tabNames[1] as $idx => $name) { + $name = trim(preg_replace('/ /', '', $name)); + $playFrom[] = $name ?: "线路".($idx+1); + } + } + } + + $playUrls = []; + preg_match_all('/<div class="anthology-list-play[^"]*">(.*?)<\/div>\s*<\/div>/is', $html, $listBoxes); + if (empty($listBoxes[1])) preg_match_all('/<ul class="anthology-list-play[^"]*">(.*?)<\/ul>/is', $html, $listBoxes); + + if (!empty($listBoxes[1])) { + foreach ($listBoxes[1] as $listHtml) { + preg_match_all('/<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/is', $listHtml, $links); + $episodes = []; + if (!empty($links[1])) { + foreach ($links[1] as $k => $href) { + $episodes[] = trim(strip_tags($links[2][$k])) . '$' . $this->fixUrl($href); + } + } + $playUrls[] = implode('#', $episodes); + } + } + + if (empty($playFrom) && !empty($playUrls)) { + for($i=0; $i<count($playUrls); $i++) $playFrom[] = "线路".($i+1); + } + + if (count($playFrom) >= 3) { + array_shift($playFrom); + array_shift($playUrls); + } + + $vod['vod_play_from'] = implode('$$$', $playFrom); + $vod['vod_play_url'] = implode('$$$', $playUrls); + } + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $url = $this->HOST . '/search/' . urlencode($key) . '----------' . $pg . '---.html'; + $html = $this->fetch($url, [], $this->getHeaders()); + $list = $this->parseHtmlList($html, true); + + return $this->pageResult($list, $pg, 0, 30); + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = $id; + if (strpos($url, 'http') === false) $url = $this->fixUrl($url); + + return [ + 'parse' => 1, // 开启嗅探 + 'url' => $url, + 'header' => [ + 'User-Agent' => $this->UA, + 'Referer' => $this->HOST . '/' + ] + ]; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\345\233\264\350\247\202\347\237\255\345\211\247 \341\265\210\341\266\273.php" "b/spider/php/\345\233\264\350\247\202\347\237\255\345\211\247 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..efb20f98 --- /dev/null +++ "b/spider/php/\345\233\264\350\247\202\347\237\255\345\211\247 \341\265\210\341\266\273.php" @@ -0,0 +1,164 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = 'https://api.drama.9ddm.com'; + private $UA = 'okhttp/3.12.11'; + + protected function getHeaders() { + return [ + 'User-Agent: ' . $this->UA, + 'Content-Type: application/json;charset=utf-8' + ]; + } + + public function homeContent($filter) { + // 获取分类标签 (对应原 JS class_parse) + $html = $this->fetch($this->HOST . '/drama/home/shortVideoTags', [], $this->getHeaders()); + $data = json_decode($html, true); + + $classes = []; + $filterObj = []; + + if (isset($data['audiences'])) { + foreach ($data['audiences'] as $audience) { + $classes[] = ['type_id' => $audience, 'type_name' => $audience]; + + // 构建筛选 (标签) + $tagValues = [['n' => '全部', 'v' => '']]; + if (isset($data['tags'])) { + foreach ($data['tags'] as $tag) { + $tagValues[] = ['n' => $tag, 'v' => $tag]; + } + } + + $filterObj[$audience] = [ + ['key' => 'tag', 'name' => '标签', 'value' => $tagValues] + ]; + } + } + + return [ + 'class' => $classes, + 'filters' => $filterObj, + 'list' => [] // 首页展示可留空或调用 categoryContent + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $apiUrl = $this->HOST . '/drama/home/search'; + + $postData = [ + "audience" => $tid, + "page" => (int)$pg, + "pageSize" => 30, + "searchWord" => "", + "subject" => $extend['tag'] ?? "" + ]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($postData), + CURLOPT_HTTPHEADER => $this->getHeaders() + ]); + + $response = json_decode($jsonStr, true); + $list = []; + + if (isset($response['data']) && is_array($response['data'])) { + foreach ($response['data'] as $it) { + $list[] = [ + 'vod_id' => $it['oneId'], + 'vod_name' => $it['title'], + 'vod_pic' => $it['vertPoster'], + 'vod_remarks' => "集数:{$it['episodeCount']} 播放:{$it['viewCount']}", + 'vod_year' => (string)($it['publishDate'] ?? '') + ]; + } + } + + return $this->pageResult($list, $pg, 999, 30); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + // 详情接口地址 + $url = $this->HOST . "/drama/home/shortVideoDetail?oneId={$id}&page=1&pageSize=1000"; + + $html = $this->fetch($url, [], $this->getHeaders()); + $response = json_decode($html, true); + $data = $response['data'] ?? []; + + if (empty($data)) return ['list' => []]; + + $first = $data[0]; + $vod = [ + 'vod_id' => $id, + 'vod_name' => $first['title'], + 'vod_pic' => $first['vertPoster'], + 'vod_remarks' => "共" . count($data) . "集", + 'vod_content' => "播放量:{$first['collectionCount']} 评论:{$first['commentCount']} " . ($first['description'] ?? ""), + 'vod_play_from' => '围观短剧' + ]; + + $playUrls = []; + foreach ($data as $episode) { + // 原 JS 逻辑:将整个 playSetting JSON 存入 URL,在 lazy/playContent 中解析 + $playUrls[] = "第{$episode['playOrder']}集$" . $episode['playSetting']; + } + + $vod['vod_play_url'] = implode('#', $playUrls); + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $apiUrl = $this->HOST . '/drama/home/search'; + $postData = [ + "audience" => "", + "page" => (int)$pg, + "pageSize" => 30, + "searchWord" => $key, + "subject" => "" + ]; + + $jsonStr = $this->fetch($apiUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($postData), + CURLOPT_HTTPHEADER => $this->getHeaders() + ]); + + $response = json_decode($jsonStr, true); + $list = []; + if (isset($response['data'])) { + foreach ($response['data'] as $it) { + $list[] = [ + 'vod_id' => $it['oneId'], + 'vod_name' => $it['title'], + 'vod_pic' => $it['vertPoster'], + 'vod_remarks' => $it['description'] + ]; + } + } + return $this->pageResult($list, $pg, 0, 30); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // id 此时是 detailContent 传过来的 playSetting JSON 字符串 + $playSetting = json_decode($id, true); + + // 优先级:高清 > 普通 > 流畅 + $videoUrl = $playSetting['high'] ?? $playSetting['normal'] ?? $playSetting['super'] ?? ''; + + return [ + 'parse' => 0, // 短剧通常是直链,无需嗅探 + 'url' => $videoUrl, + 'header' => [ + 'User-Agent' => $this->UA + ] + ]; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\345\235\232\346\236\234\350\247\206\351\242\221 \341\265\210\341\266\273.php" "b/spider/php/\345\235\232\346\236\234\350\247\206\351\242\221 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..584778e3 --- /dev/null +++ "b/spider/php/\345\235\232\346\236\234\350\247\206\351\242\221 \341\265\210\341\266\273.php" @@ -0,0 +1,200 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = "http://106.53.107.16"; // 默认起始地址 + private $UA = 'Dart/3.9 (dart:io)'; + + protected function getHeaders() { + return [ + 'User-Agent: ' . $this->UA, + 'Accept-Encoding: gzip', + 'Content-Type: application/json' + ]; + } + + /** + * 初始化:检测有效域名 + */ + private function getValidHost() { + // 如果 extend 传入了不同地址,可以在此处动态修改 $this->HOST + // 此处还原 Python 中的检测逻辑 + $checkUrl = rtrim($this->HOST, '/') . '/success.txt'; + try { + // 简单的存活性检测 + $res = $this->fetch($checkUrl, [CURLOPT_TIMEOUT => 5], $this->getHeaders()); + if ($res) { + return rtrim($this->HOST, '/'); + } + } catch (Exception $e) {} + return rtrim($this->HOST, '/'); + } + + public function homeContent($filter) { + $host = $this->getValidHost(); + $classes = []; + + // 1. 获取常规分类 + $res1 = $this->fetch($host . '/api.php/type/get_list', [], $this->getHeaders()); + $data1 = json_decode($res1, true); + if (isset($data1['info']['rows'])) { + foreach ($data1['info']['rows'] as $row) { + if ($row['type_status'] == 1 && !in_array($row['type_name'], ['漫画', '小说'])) { + $classes[] = ['type_id' => $row['type_id'], 'type_name' => $row['type_name']]; + } + } + } + + // 2. 获取短视频分类 + try { + $res2 = $this->fetch($host . '/addons/getstar/api.index/shortVideoCategory', [], $this->getHeaders()); + $data2 = json_decode($res2, true); + if (isset($data2['data'])) { + foreach ($data2['data'] as $row) { + $classes[] = ['type_id' => $row['id'], 'type_name' => $row['name']]; + } + } + } catch (Exception $e) {} + + return ['class' => $classes]; + } + + public function homeVideoContent() { + $host = $this->getValidHost(); + $url = $host . '/index.php/ajax/data?mid=1&limit=100&page=1&level=7'; + $res = $this->fetch($url, [], $this->getHeaders()); + return json_decode($res, true); + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $host = $this->getValidHost(); + $year = date("Y"); + $url = "{$host}/index.php/ajax/data?mid=1&limit=20&page={$pg}&tid={$tid}&year={$year}"; + $res = $this->fetch($url, [], $this->getHeaders()); + $json = json_decode($res, true); + $list = $json['list'] ?? []; + $total = $json['total'] ?? 0; + return $this->pageResult($list, $pg, $total, 20); + } + + public function searchContent($key, $quick = false, $pg = 1) { + $host = $this->getValidHost(); + $url = "{$host}/index.php/ajax/data?mid=1&limit=20&page={$pg}&wd=" . urlencode($key); + $res = $this->fetch($url, [], $this->getHeaders()); + $json = json_decode($res, true); + $list = $json['list'] ?? []; + $total = $json['total'] ?? 0; + return $this->pageResult($list, $pg, $total, 20); + } + + public function detailContent($ids) { + $host = $this->getValidHost(); + $id = is_array($ids) ? $ids[0] : $ids; + + // 1. 获取解析配置 (PlayerParse) + $playerConfigs = []; + try { + $pRes = $this->fetch($host . '/addons/getstar/api.index/getPlayerParse', [], $this->getHeaders()); + $pData = json_decode($pRes, true); + if (isset($pData['data']) && is_array($pData['data'])) { + $playerConfigs = $pData['data']; + } + } catch (Exception $e) {} + + // 2. 获取视频详情 + $res = $this->fetch($host . "/api.php/vod/get_detail?vod_id={$id}", [], $this->getHeaders()); + $json = json_decode($res, true); + $data = $json['info'][0]; + + if (!empty($data['vod_play_from']) && !empty($data['vod_play_url'])) { + $froms = explode('$$$', $data['vod_play_from']); + $urls = explode('$$$', $data['vod_play_url']); + + $newFroms = []; + $newUrls = []; + + foreach ($froms as $key => $show) { + $parseUrl = ''; + $isOpen = false; + + // 匹配解析器 + foreach ($playerConfigs as $pConf) { + if ($pConf['code'] == $show) { + $isOpen = true; + $name = $pConf['name'] ?? ''; + if ($name && $name != $show) { + $show = "{$name} ({$show})"; + } + $parseUrl = $pConf['url'] ?? ''; + break; + } + } + + if (!$isOpen) continue; + + $episodeParts = explode('#', $urls[$key]); + $formattedEpisodes = []; + foreach ($episodeParts as $part) { + if (empty($part)) continue; + $temp = explode('$', $part, 2); + $epName = $temp[0]; + $epUrl = $temp[1] ?? ''; + + // 将解析地址附加到 URL 后,供 playContent 使用 + $suffix = $parseUrl ? "@{$parseUrl}" : ""; + $formattedEpisodes[] = "{$epName}\${$epUrl}{$suffix}"; + } + + $newFroms[] = $show; + $newUrls[] = implode('#', $formattedEpisodes); + } + + $data['vod_play_from'] = implode('$$$', $newFroms); + $data['vod_play_url'] = implode('$$$', $newUrls); + } + + return ['list' => [$data]]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + $rawUrl = $id; + $url = ""; + $jx = 0; + + // 处理带 @ 的自定义解析 + if (strpos($id, '@') !== false) { + list($rawUrl, $parse) = explode('@', $id, 2); + $headers = [ + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + ]; + try { + $res = $this->fetch($parse . $rawUrl, [], $headers); + $json = json_decode($res, true); + if (!empty($json['url']) && $json['url'] != $rawUrl) { + $url = $json['url']; + } + } catch (Exception $e) {} + } + + if (empty($url)) { + $url = $rawUrl; + // 匹配大站链接开启嗅探 + if (preg_match('/(?:www\.iqiyi|v\.qq|v\.youku|www\.mgtv|www\.bilibili)\.com/', $rawUrl)) { + $jx = 1; + } + } + + return [ + 'jx' => $jx, + 'parse' => 0, + 'url' => $url, + 'header' => [ + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + 'Connection' => 'Keep-Alive' + ] + ]; + } +} + +// 运行 +(new Spider())->run(); diff --git "a/spider/php/\345\245\207\345\245\207 \341\265\210\341\266\273.php" "b/spider/php/\345\245\207\345\245\207 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..687b1f7f --- /dev/null +++ "b/spider/php/\345\245\207\345\245\207 \341\265\210\341\266\273.php" @@ -0,0 +1,298 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $host = 'https://www.iqiyi.com'; + + protected function getHeaders() { + return [ + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer' => 'https://www.iqiyi.com', + 'Accept' => 'application/json, text/plain, */*', + 'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8', + 'Connection' => 'keep-alive' + ]; + } + + public function homeContent($filter) { + $classes = [ + ['type_id' => '1', 'type_name' => '电影'], + ['type_id' => '2', 'type_name' => '电视剧'], + ['type_id' => '6', 'type_name' => '综艺'], + ['type_id' => '4', 'type_name' => '动漫'], + ['type_id' => '3', 'type_name' => '纪录片'], + ['type_id' => '5', 'type_name' => '音乐'], + ['type_id' => '16', 'type_name' => '网络电影'] + ]; + + $filters = [ + '1' => [[ + 'key' => 'year', + 'name' => '年代', + 'value' => [['n' => '全部', 'v' => ''], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], ['n' => '2023', 'v' => '2023']] + ]], + '2' => [[ + 'key' => 'year', + 'name' => '年代', + 'value' => [['n' => '全部', 'v' => ''], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], ['n' => '2023', 'v' => '2023']] + ]] + ]; + + return [ + 'class' => $classes, + 'filters' => $filters + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $channelId = $tid; + $dataType = 1; + $extraParams = ""; + $page = max(1, intval($pg)); + + if ($tid === "16") { + $channelId = "1"; + $extraParams = "&three_category_id=27401"; + } else if ($tid === "5") { + $dataType = 2; + } + + // 处理筛选条件 + if (!empty($extend)) { + if (isset($extend['year'])) { + $extraParams .= "&market_release_date_level={$extend['year']}"; + } + } + + $url = "https://pcw-api.iqiyi.com/search/recommend/list?channel_id={$channelId}&data_type={$dataType}&page_id={$page}&ret_num=20{$extraParams}"; + + $jsonStr = $this->fetch($url, [], $this->getHeaders()); + $jsonData = json_decode($jsonStr, true); + + $videos = []; + if (isset($jsonData['data']['list'])) { + foreach ($jsonData['data']['list'] as $item) { + $vid = "{$item['channelId']}\${$item['albumId']}"; + $remarks = ""; + + if ($item['channelId'] == 1) { + $remarks = isset($item['score']) ? "{$item['score']}分" : ""; + } else if ($item['channelId'] == 2 || $item['channelId'] == 4) { + if (isset($item['latestOrder']) && isset($item['videoCount'])) { + $remarks = ($item['latestOrder'] == $item['videoCount']) ? + "{$item['latestOrder']}集全" : + "更新至{$item['latestOrder']}集"; + } else { + $remarks = $item['focus'] ?? ""; + } + } else { + $remarks = $item['period'] ?? ($item['focus'] ?? ""); + } + + $pic = isset($item['imageUrl']) ? str_replace(".jpg", "_390_520.jpg", $item['imageUrl']) : ""; + + $videos[] = [ + 'vod_id' => $vid, + 'vod_name' => $item['name'], + 'vod_pic' => $pic, + 'vod_remarks' => $remarks + ]; + } + } + + return $this->pageResult($videos, $page, 999999, 20); + } + + private function getPlaylists($channelId, $albumId, $data) { + $playlists = []; + $cid = intval($channelId ?: ($data['channelId'] ?? 0)); + + if ($cid === 1 || $cid === 5) { + // 电影或音乐 + if (isset($data['playUrl'])) { + $playlists[] = ['title' => $data['name'] ?? '正片', 'url' => $data['playUrl']]; + } + } else if ($cid === 6 && isset($data['period'])) { + // 综艺 + $qs = explode("-", (string)$data['period'])[0]; + $listUrl = "https://pcw-api.iqiyi.com/album/source/svlistinfo?cid=6&sourceid={$albumId}&timelist={$qs}"; + + $listResp = $this->fetch($listUrl, [], $this->getHeaders()); + $listJson = json_decode($listResp, true); + + if (isset($listJson['data'][$qs])) { + foreach ($listJson['data'][$qs] as $it) { + $title = $it['shortTitle'] ?? ($it['period'] ?? ($it['focus'] ?? "期{$it['order']}")); + $playlists[] = [ + 'title' => $title, + 'url' => $it['playUrl'] + ]; + } + } + } else { + // 电视剧、动漫等 + $listUrl = "https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid={$albumId}&size=100&page=1"; + $listResp = $this->fetch($listUrl, [], $this->getHeaders()); + $listJson = json_decode($listResp, true); + + if (isset($listJson['data']['epsodelist'])) { + foreach ($listJson['data']['epsodelist'] as $item) { + $title = $item['shortTitle'] ?? ($item['title'] ?? (isset($item['order']) ? "第{$item['order']}集" : "集{$item['timelist']}")); + $playlists[] = [ + 'title' => $title, + 'url' => $item['playUrl'] ?? ($item['url'] ?? '') + ]; + } + + // 处理分页 + $total = $listJson['data']['total'] ?? 0; + if ($total > 100) { + $totalPages = ceil($total / 100); + for ($i = 2; $i <= $totalPages; $i++) { + $nextUrl = "https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid={$albumId}&size=100&page={$i}"; + $nextResp = $this->fetch($nextUrl, [], $this->getHeaders()); + $nextJson = json_decode($nextResp, true); + + if (isset($nextJson['data']['epsodelist'])) { + foreach ($nextJson['data']['epsodelist'] as $item) { + $title = $item['shortTitle'] ?? ($item['title'] ?? (isset($item['order']) ? "第{$item['order']}集" : "集{$item['timelist']}")); + $playlists[] = [ + 'title' => $title, + 'url' => $item['playUrl'] ?? ($item['url'] ?? '') + ]; + } + } + } + } + } + } + return $playlists; + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $channelId = ""; + $albumId = $id; + + if (strpos($id, '$') !== false) { + $parts = explode('$', $id); + $channelId = $parts[0]; + $albumId = $parts[1]; + } + + // 获取视频基本信息 + $infoUrl = "https://pcw-api.iqiyi.com/video/video/videoinfowithuser/{$albumId}?agent_type=1&authcookie=&subkey={$albumId}&subscribe=1"; + $infoResp = $this->fetch($infoUrl, [], $this->getHeaders()); + $infoJson = json_decode($infoResp, true); + $data = $infoJson['data'] ?? []; + + // 获取播放列表 + $playlists = $this->getPlaylists($channelId, $albumId, $data); + + // 构建播放地址 + $playUrls = []; + foreach ($playlists as $item) { + if (!empty($item['url'])) { + $playUrls[] = "{$item['title']}\${$item['url']}"; + } + } + + $typeName = ''; + if (isset($data['categories'])) { + $names = array_map(function($it) { return $it['name']; }, $data['categories']); + $typeName = implode(',', $names); + } + + $area = ''; + if (isset($data['areas'])) { + $names = array_map(function($it) { return $it['name']; }, $data['areas']); + $area = implode(',', $names); + } + + $actors = ''; + if (isset($data['people']['main_charactor'])) { + $names = array_map(function($it) { return $it['name']; }, $data['people']['main_charactor']); + $actors = implode(',', $names); + } + + $director = ''; + if (isset($data['people']['director'])) { + $names = array_map(function($it) { return $it['name']; }, $data['people']['director']); + $director = implode(',', $names); + } + + $remarks = ""; + if (isset($data['latestOrder'])) { + $remarks = "更新至{$data['latestOrder']}集"; + } else { + $remarks = isset($data['period']) || count($playlists) > 0 ? count($playlists)."集" : ""; + } + + $vod = [ + 'vod_id' => $id, + 'vod_name' => $data['name'] ?? '未知标题', + 'type_name' => $typeName, + 'vod_year' => $data['formatPeriod'] ?? '', + 'vod_area' => $area, + 'vod_remarks' => $remarks, + 'vod_actor' => $actors, + 'vod_director' => $director, + 'vod_content' => $data['description'] ?? '暂无简介', + 'vod_pic' => isset($data['imageUrl']) ? str_replace(".jpg", "_480_270.jpg", $data['imageUrl']) : '', + 'vod_play_from' => count($playUrls) > 0 ? '爱奇艺视频' : '', + 'vod_play_url' => implode('#', $playUrls) + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $page = max(1, intval($pg)); + $url = "https://search.video.iqiyi.com/o?if=html5&key=" . urlencode($key) . "&pageNum={$page}&pos=1&pageSize=20&site=iqiyi"; + + $response = $this->fetch($url, [], $this->getHeaders()); + $jsonData = json_decode($response, true); + + $videos = []; + if (isset($jsonData['data']['docinfos'])) { + foreach ($jsonData['data']['docinfos'] as $item) { + if (isset($item['albumDocInfo'])) { + $doc = $item['albumDocInfo']; + $channelId = isset($doc['channel']) ? explode(',', $doc['channel'])[0] : '0'; + $videos[] = [ + 'vod_id' => "{$channelId}\${$doc['albumId']}", + 'vod_name' => $doc['albumTitle'] ?? '', + 'vod_pic' => $doc['albumVImage'] ?? '', + 'vod_remarks' => $doc['tvFocus'] ?? ($doc['year'] ?? '') + ]; + } + } + } + + return $this->pageResult($videos, $page, count($videos) * 10, 20); // 搜索无法获取总数,简单估算 + } + + public function playerContent($flag, $id, $vipFlags = []) { + $playUrl = $id; + if (strpos($id, '$') !== false) { + $playUrl = explode('$', $id)[1]; + } + + // 壳子超级解析格式 + return [ + 'parse' => 1, + 'jx' => 1, + 'play_parse' => true, + 'parse_type' => '壳子超级解析', + 'parse_source' => '爱奇艺视频', + 'url' => $playUrl, + 'header' => json_encode([ + 'User-Agent' => $this->getHeaders()['User-Agent'], + 'Referer' => 'https://www.iqiyi.com', + 'Origin' => 'https://www.iqiyi.com' + ], JSON_UNESCAPED_UNICODE) + ]; + } +} + +(new Spider())->run(); diff --git "a/spider/php/\345\261\261\346\234\211\346\234\250\345\205\256 \341\265\210\341\266\273.php" "b/spider/php/\345\261\261\346\234\211\346\234\250\345\205\256 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..7d6768f9 --- /dev/null +++ "b/spider/php/\345\261\261\346\234\211\346\234\250\345\205\256 \341\265\210\341\266\273.php" @@ -0,0 +1,176 @@ +<?php +/** + * 山有木兮 - PHP 适配版 (道长重构) + * 按照 BaseSpider 结构重写 + */ + +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + + private $HOST = 'https://film.symx.club'; + + public function init($extend = '') { + $this->headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36'; + $this->headers['Sec-Ch-Ua'] = '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"'; + $this->headers['Sec-Ch-Ua-Mobile'] = '?0'; + $this->headers['Sec-Ch-Ua-Platform'] = '"Windows"'; + $this->headers['Sec-Fetch-Dest'] = 'empty'; + $this->headers['Sec-Fetch-Mode'] = 'cors'; + $this->headers['Sec-Fetch-Site'] = 'same-origin'; + $this->headers['X-Platform'] = 'web'; + $this->headers['Accept'] = 'application/json, text/plain, */*'; + + if (!empty($extend) && strpos($extend, 'http') === 0) { + $this->HOST = rtrim($extend, '/'); + } + } + + private function getHeaders($referer = '/') { + $headers = $this->headers; + $headers['Referer'] = $this->HOST . $referer; + return $headers; + } + + public function homeContent($filter = []) { + $url = $this->HOST . "/api/category/top"; + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + $classes = []; + if (isset($data['data'])) { + foreach ($data['data'] as $item) { + $classes[] = [ + 'type_id' => strval($item['id']), + 'type_name' => $item['name'] + ]; + } + } + return ['class' => $classes]; + } + + public function homeVideoContent() { + $url = $this->HOST . "/api/film/category"; + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + $list = []; + if (isset($data['data'])) { + foreach ($data['data'] as $category) { + $filmList = $category['filmList'] ?? []; + foreach ($filmList as $film) { + $list[] = [ + 'vod_id' => strval($film['id']), + 'vod_name' => $film['name'], + 'vod_pic' => $film['cover'], + 'vod_remarks' => $film['doubanScore'] ?? '' + ]; + } + } + } + return ['list' => array_slice($list, 0, 30)]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $pageNum = max(1, intval($pg)); + $url = $this->HOST . "/api/film/category/list?area=&categoryId={$tid}&language=&pageNum={$pageNum}&pageSize=15&sort=updateTime&year="; + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + $list = []; + if (isset($data['data']['list'])) { + foreach ($data['data']['list'] as $item) { + $list[] = [ + 'vod_id' => strval($item['id']), + 'vod_name' => $item['name'], + 'vod_pic' => $item['cover'], + 'vod_remarks' => $item['updateStatus'] + ]; + } + } + + $total = $data['data']['total'] ?? 0; + return $this->pageResult($list, $pageNum, $total, 15); + } + + public function detailContent($ids) { + if (empty($ids)) return ['list' => []]; + $id = $ids[0]; // 只处理第一个ID + + $url = $this->HOST . "/api/film/detail?id=" . urlencode($id); + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + if (!isset($data['data'])) { + return ['list' => []]; + } + + $info = $data['data']; + $shows = []; + $play_urls = []; + + if (isset($info['playLineList'])) { + foreach ($info['playLineList'] as $line) { + $shows[] = $line['playerName']; + $urls = []; + if (isset($line['lines'])) { + foreach ($line['lines'] as $episode) { + $urls[] = $episode['name'] . '$' . $episode['id']; + } + } + $play_urls[] = implode('#', $urls); + } + } + + $vod = [ + 'vod_id' => $id, + 'vod_name' => $info['name'], + 'vod_pic' => $info['cover'], + 'vod_year' => $info['year'], + 'vod_area' => $info['other'], + 'vod_actor' => $info['actor'], + 'vod_director' => $info['director'], + 'vod_content' => $info['blurb'], + 'vod_score' => $info['doubanScore'], + 'vod_play_from' => implode('$$$', $shows), + 'vod_play_url' => implode('$$$', $play_urls), + 'type_name' => $info['vod_class'] ?? '' + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $pageNum = max(1, intval($pg)); + $url = $this->HOST . "/api/film/search?keyword=" . urlencode($key) . "&pageNum={$pageNum}&pageSize=10"; + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + $list = []; + if (isset($data['data']['list'])) { + foreach ($data['data']['list'] as $item) { + $list[] = [ + 'vod_id' => strval($item['id']), + 'vod_name' => $item['name'], + 'vod_pic' => $item['cover'], + 'vod_remarks' => $item['updateStatus'], + 'vod_year' => $item['year'], + 'vod_area' => $item['area'], + 'vod_director' => $item['director'] + ]; + } + } + return $this->pageResult($list, $pageNum); + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = $this->HOST . "/api/line/play/parse?lineId=" . urlencode($id); + $data = json_decode($this->fetch($url, [], $this->getHeaders()), true); + + $playUrl = $data['data'] ?? ''; + + return [ + 'parse' => 0, + 'url' => $playUrl, + 'header' => ['User-Agent' => $this->headers['User-Agent']] + ]; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\346\230\237\346\230\237\347\237\255\345\211\247 \341\265\210\341\266\273.php" "b/spider/php/\346\230\237\346\230\237\347\237\255\345\211\247 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..81e3c888 --- /dev/null +++ "b/spider/php/\346\230\237\346\230\237\347\237\255\345\211\247 \341\265\210\341\266\273.php" @@ -0,0 +1,139 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = 'http://read.api.duodutek.com'; + private $UA = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'; + + // 固定的 API 参数 + private $COMMON_PARAMS = [ + "productId" => "2a8c14d1-72e7-498b-af23-381028eb47c0", + "vestId" => "2be070e0-c824-4d0e-a67a-8f688890cadb", + "channel" => "oppo19", + "osType" => "android", + "version" => "20", + "token" => "202509271001001446030204698626" + ]; + + protected function getHeaders() { + return [ + 'User-Agent: ' . $this->UA + ]; + } + + public function homeContent($filter) { + // 定义分类 + $classes = [ + ["type_id" => "1287", "type_name" => "甜宠"], + ["type_id" => "1288", "type_name" => "逆袭"], + ["type_id" => "1289", "type_name" => "热血"], + ["type_id" => "1290", "type_name" => "现代"], + ["type_id" => "1291", "type_name" => "古代"] + ]; + + // 首页推荐:取第一个分类的前几个视频 + $list = $this->categoryContent('1287', 1)['list']; + $list = array_slice($list, 0, 12); + + return [ + 'class' => $classes, + 'list' => $list, + 'filters' => (object)[] + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $apiUrl = $this->HOST . '/novel-api/app/pageModel/getResourceById'; + + $params = array_merge($this->COMMON_PARAMS, [ + "resourceId" => $tid, + "pageNum" => (string)$pg, + "pageSize" => "10" + ]); + + $url = $apiUrl . '?' . http_build_query($params); + $jsonStr = $this->fetch($url, [], $this->getHeaders()); + $jsonObj = json_decode($jsonStr, true); + + $list = []; + if ($jsonObj && isset($jsonObj['data']['datalist'])) { + foreach ($jsonObj['data']['datalist'] as $vod) { + $list[] = [ + // 仿照原 Python:id@@name@@introduction 存储 + 'vod_id' => $vod['id'] . '@@' . $vod['name'] . '@@' . ($vod['introduction'] ?? ''), + 'vod_name' => $vod['name'], + 'vod_pic' => $vod['icon'], + 'vod_remarks' => $vod['heat'] . '万播放' + ]; + } + } + + return $this->pageResult($list, $pg, 999, 10); + } + + public function detailContent($ids) { + $did = is_array($ids) ? $ids[0] : $ids; + $parts = explode('@@', $did); + if (count($parts) >= 2) { + $bookId = $parts[0]; + $bookName = $parts[1]; + $intro = $parts[2] ?? ''; + } else { + // 兼容旧格式 id@intro + $parts = explode('@', $did); + $bookId = $parts[0]; + $bookName = ''; + $intro = $parts[1] ?? ''; + } + + $apiUrl = $this->HOST . '/novel-api/basedata/book/getChapterList'; + $params = array_merge($this->COMMON_PARAMS, [ + "bookId" => $bookId + ]); + + $url = $apiUrl . '?' . http_build_query($params); + $jsonStr = $this->fetch($url, [], $this->getHeaders()); + $jsonObj = json_decode($jsonStr, true); + + $playUrls = []; + if ($jsonObj && isset($jsonObj['data'])) { + $chapters = $jsonObj['data']; + foreach ($chapters as $index => $chapter) { + // 提取短剧播放地址 + if (isset($chapter['shortPlayList'][0]['chapterShortPlayVoList'][0]['shortPlayUrl'])) { + $vUrl = $chapter['shortPlayList'][0]['chapterShortPlayVoList'][0]['shortPlayUrl']; + $epName = "第" . ($index + 1) . "集"; + $playUrls[] = $epName . '$' . $vUrl; + } + } + } + + $vod = [ + 'vod_id' => $did, + 'vod_name' => $bookName, // 由列表页带入 + 'vod_content' => $intro, + 'vod_play_from' => '短剧专线', + 'vod_play_url' => implode('#', $playUrls) + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + // 原 Python 代码中 searchContentPage 为 pass,故此处留空返回 + return $this->pageResult([], $pg); + } + + public function playerContent($flag, $id, $vipFlags = []) { + return [ + 'parse' => 0, // 直接播放 + 'url' => $id, + 'header' => [ + 'User-Agent' => $this->UA + ] + ]; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\346\236\234\346\236\234 \341\265\210\341\266\273.php" "b/spider/php/\346\236\234\346\236\234 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..1b308208 --- /dev/null +++ "b/spider/php/\346\236\234\346\236\234 \341\265\210\341\266\273.php" @@ -0,0 +1,252 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $host = 'https://www.mgtv.com'; + + protected function getHeaders() { + return [ + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', + 'Referer' => 'https://www.mgtv.com/', + 'Accept' => 'application/json, text/plain, */*', + 'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8', + 'Connection' => 'keep-alive' + ]; + } + + public function homeContent($filter) { + $classes = [ + ['type_id' => '3', 'type_name' => '电影'], + ['type_id' => '2', 'type_name' => '电视剧'], + ['type_id' => '1', 'type_name' => '综艺'], + ['type_id' => '50', 'type_name' => '动漫'], + ['type_id' => '51', 'type_name' => '纪录片'], + ['type_id' => '115', 'type_name' => '教育'], + ['type_id' => '10', 'type_name' => '少儿'] + ]; + + $filters = [ + '3' => [ + [ + 'key' => 'year', 'name' => '年份', 'value' => [ + ['n' => '全部', 'v' => 'all'], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], + ['n' => '2023', 'v' => '2023'], ['n' => '2022', 'v' => '2022'], ['n' => '2021', 'v' => '2021'], + ['n' => '2020', 'v' => '2020'], ['n' => '2019', 'v' => '2019'], ['n' => '2010-2019', 'v' => '2010-2019'], + ['n' => '2000-2009', 'v' => '2000-2009'] + ] + ], + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => 'c1'], ['n' => '最新', 'v' => 'c2'], ['n' => '最热', 'v' => 'c4'] + ] + ] + ], + '2' => [ + [ + 'key' => 'year', 'name' => '年份', 'value' => [ + ['n' => '全部', 'v' => 'all'], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], + ['n' => '2023', 'v' => '2023'], ['n' => '2022', 'v' => '2022'], ['n' => '2021', 'v' => '2021'], + ['n' => '2020', 'v' => '2020'] + ] + ], + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => 'c1'], ['n' => '最新', 'v' => 'c2'], ['n' => '最热', 'v' => 'c4'] + ] + ] + ], + '1' => [ + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => 'c1'], ['n' => '最新', 'v' => 'c2'], ['n' => '最热', 'v' => 'c4'] + ] + ] + ], + '50' => [ + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => 'c1'], ['n' => '最新', 'v' => 'c2'], ['n' => '最热', 'v' => 'c4'] + ] + ] + ] + ]; + + return [ + 'class' => $classes, + 'filters' => $filters + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $page = max(1, intval($pg)); + $baseUrl = 'https://pianku.api.mgtv.com/rider/list/pcweb/v3'; + + $params = [ + 'platform' => 'pcweb', + 'channelId' => $tid, + 'pn' => $page, + 'pc' => '20', + 'hudong' => '1', + '_support' => '10000000', + 'kind' => 'a1', + 'area' => 'a1' + ]; + + if (!empty($extend)) { + if (isset($extend['year']) && $extend['year'] !== 'all') { + $params['year'] = $extend['year']; + } + if (isset($extend['sort'])) { + $params['sort'] = $extend['sort']; + } + if (isset($extend['chargeInfo'])) { + $params['chargeInfo'] = $extend['chargeInfo']; + } + } + + $url = $baseUrl . '?' . http_build_query($params); + $response = $this->fetch($url, [], $this->getHeaders()); + $json = json_decode($response, true); + + $videos = []; + if (isset($json['data']['hitDocs']) && is_array($json['data']['hitDocs'])) { + foreach ($json['data']['hitDocs'] as $item) { + $videos[] = [ + 'vod_id' => $item['playPartId'] ?? '', + 'vod_name' => $item['title'] ?? '', + 'vod_pic' => $item['img'] ?? '', + 'vod_remarks' => $item['updateInfo'] ?? ($item['rightCorner']['text'] ?? '') + ]; + } + } + + $totalHit = $json['data']['totalHit'] ?? 0; + return $this->pageResult($videos, $page, $totalHit, 20); + } + + public function detailContent($ids) { + $videoId = is_array($ids) ? $ids[0] : $ids; + + // 获取视频基本信息 + $infoUrl = "https://pcweb.api.mgtv.com/video/info?video_id={$videoId}"; + $infoResponse = $this->fetch($infoUrl, [], $this->getHeaders()); + $infoJson = json_decode($infoResponse, true); + $infoData = $infoJson['data']['info'] ?? []; + + $vod = [ + 'vod_id' => $videoId, + 'vod_name' => $infoData['title'] ?? '', + 'type_name' => $infoData['root_kind'] ?? '', + 'vod_actor' => '', + 'vod_year' => $infoData['release_time'] ?? '', + 'vod_content' => $infoData['desc'] ?? '', + 'vod_remarks' => $infoData['time'] ?? '', + 'vod_pic' => $infoData['img'] ?? '', + 'vod_play_from' => '芒果TV', + 'vod_play_url' => '' + ]; + + // 分页获取所有剧集 + $pageSize = 50; + $allEpisodes = []; + + // 获取第一页 + $firstPageUrl = "https://pcweb.api.mgtv.com/episode/list?video_id={$videoId}&page=1&size={$pageSize}"; + $firstResponse = $this->fetch($firstPageUrl, [], $this->getHeaders()); + $firstJson = json_decode($firstResponse, true); + $firstData = $firstJson['data'] ?? []; + + if (isset($firstData['list']) && is_array($firstData['list'])) { + $allEpisodes = array_merge($allEpisodes, $firstData['list']); + $totalPages = $firstData['total_page'] ?? 1; + + if ($totalPages > 1) { + for ($i = 2; $i <= $totalPages; $i++) { + $pageUrl = "https://pcweb.api.mgtv.com/episode/list?video_id={$videoId}&page={$i}&size={$pageSize}"; + // 简单串行获取,避免并发复杂性 + $resp = $this->fetch($pageUrl, [], $this->getHeaders()); + $data = json_decode($resp, true); + if (isset($data['data']['list']) && is_array($data['data']['list'])) { + $allEpisodes = array_merge($allEpisodes, $data['data']['list']); + } + } + } + } + + $playUrls = []; + if (!empty($allEpisodes)) { + // 过滤 + $validEpisodes = array_filter($allEpisodes, function($item) { + return isset($item['isIntact']) && ($item['isIntact'] === "1" || $item['isIntact'] === 1); + }); + + // 排序 + usort($validEpisodes, function($a, $b) { + return intval($a['order'] ?? 0) - intval($b['order'] ?? 0); + }); + + foreach ($validEpisodes as $item) { + $name = $item['t4'] ?? ($item['t3'] ?? ($item['title'] ?? ("第" . ($item['order'] ?? '?') . "集"))); + $playLink = isset($item['url']) ? "https://www.mgtv.com{$item['url']}" : ''; + + if ($playLink) { + $playUrls[] = "{$name}\${$playLink}"; + } + } + } + + $vod['vod_play_url'] = implode('#', $playUrls); + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $page = max(1, intval($pg)); + $searchUrl = "https://mobileso.bz.mgtv.com/msite/search/v2?q=" . urlencode($key) . "&pn={$page}&pc=20"; + + $response = $this->fetch($searchUrl, [], $this->getHeaders()); + $json = json_decode($response, true); + $data = $json['data'] ?? []; + + $videos = []; + if (isset($data['contents']) && is_array($data['contents'])) { + foreach ($data['contents'] as $group) { + if (($group['type'] ?? '') === 'media' && isset($group['data']) && is_array($group['data'])) { + foreach ($group['data'] as $item) { + if (($item['source'] ?? '') === 'imgo') { + if (preg_match('/\/(\d+)\.html/', $item['url'], $match)) { + $videos[] = [ + 'vod_id' => $match[1], + 'vod_name' => isset($item['title']) ? str_replace(['<B>', '</B>'], '', $item['title']) : '', + 'vod_pic' => $item['img'] ?? '', + 'vod_remarks' => isset($item['desc']) ? implode(' ', $item['desc']) : '' + ]; + } + } + } + } + } + } + + return $this->pageResult($videos, $page, count($videos) * 10, 20); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // 壳子超级解析格式 + return [ + 'parse' => 1, + 'jx' => 1, + 'play_parse' => true, + 'parse_type' => '壳子超级解析', + 'parse_source' => '芒果TV2', + 'url' => $id, + 'header' => json_encode([ + 'User-Agent' => $this->getHeaders()['User-Agent'], + 'Referer' => 'https://www.mgtv.com', + 'Origin' => 'https://www.mgtv.com' + ], JSON_UNESCAPED_UNICODE) + ]; + } +} + +(new Spider())->run(); diff --git "a/spider/php/\347\225\252\350\214\204\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" "b/spider/php/\347\225\252\350\214\204\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" new file mode 100644 index 00000000..b1c65a94 --- /dev/null +++ "b/spider/php/\347\225\252\350\214\204\345\260\217\350\257\264 \341\265\210\341\266\273[\344\271\246].php" @@ -0,0 +1,353 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private const HOST = 'https://fanqienovel.com'; + private const API_HOST = 'https://qkfqapi.vv9v.cn'; + private const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'; + + private $startPage = 1; + + public function init($extend = '') { + $this->startPage = 1; + } + + public function homeContent($filter = []) { + $url = self::HOST . '/api/author/book/category_list/v0/'; + $json = $this->fetchJson($url); + + $classes = []; + $filters = []; + + // 默认"全部"分类 + $classes[] = [ + 'type_name' => '全部', + 'type_id' => '-1' + ]; + + if (isset($json['data'])) { + $grouped = []; + foreach ($json['data'] as $item) { + $label = $item['label']; + if (!isset($grouped[$label])) { + $grouped[$label] = ['names' => [], 'ids' => []]; + } + $grouped[$label]['names'][] = $item['name']; + $grouped[$label]['ids'][] = $item['category_id']; + } + + foreach ($grouped as $label => $data) { + $classes[] = [ + 'type_name' => $label, + 'type_id' => $label + ]; + + $filterItems = []; + foreach ($data['names'] as $index => $name) { + $filterItems[] = ['n' => $name, 'v' => $data['ids'][$index]]; + } + + $filters[$label] = [ + [ + 'key' => 'category_id', + 'name' => '筛选', + 'value' => $filterItems + ] + ]; + } + } + + return [ + 'class' => $classes, + 'filters' => (object)$filters + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + // url: /api/author/library/book_list/v0/?page_count=18&page_index=(fypage-1)&gender=1&category_id=fyclass&creation_status=-1&word_count=-1&book_type=-1&sort=0#fyfilter + + $categoryId = $extend['category_id'] ?? ''; + + // 如果 tid 是 '-1' (全部),且没有选筛选,则可能需要默认值或者不做筛选 + // JS 逻辑:if (MY_CATE !== '-1') ... else input = input.split('#')[0] + // 这里简化:构建 API 参数 + + $params = [ + 'page_count' => 18, + 'page_index' => $pg - 1, + 'gender' => 1, + 'category_id' => $categoryId ?: '-1', // 默认 -1 ? JS 中如果是全部,input直接去掉了 category_id 参数? + // 观察 JS: + // if (MY_CATE !== '-1') { category_id = input.split('#')[1]; replace... } + // 意思是如果不是全部,必须选筛选? + // 实际上 API 支持 category_id 参数。 + 'creation_status' => -1, + 'word_count' => -1, + 'book_type' => -1, + 'sort' => 0 + ]; + + // 如果 tid 不是 '-1' 且没有 category_id (即直接点了一级分类但没点筛选) + // JS 中 filters 定义了 value 是 category_id。 + // 如果用户只点了大类(如“都市”),tid="都市"。 + // 但 API 需要具体的 category_id(数字)。 + // JS 逻辑里 filters 的 key 是 '筛选',value 是数字 ID。 + // 如果 tid 是 '全部',category_id 应该传什么? + // 抓包看:全部 -> category_id 不传或 -1。 + // + // 修正:DS源里 filter_url 是 '{{fl.筛选}}',即 category_id 直接取值。 + // 如果 $categoryId 为空,且 $tid 不是 -1,说明用户只选了大类没选子类。 + // 这时应该怎么办?看 JS: + // JS 的 filters 是必须选的吗? + // 让我们假设如果 $tid 不是 -1,但 $categoryId 为空,我们可能无法请求,或者默认取该大类下的第一个? + // 这里的 filters 构造里,value 就是 category_id。 + + if ($tid !== '-1' && empty($categoryId)) { + // 尝试获取该分类下的第一个 ID?或者 API 支持直接传 label? + // 实际上 fanqienovel API 需要数字 ID。 + // 简单起见,如果未选筛选,默认 -1 (全部) + $params['category_id'] = -1; + } + + $url = self::HOST . '/api/author/library/book_list/v0/?' . http_build_query($params); + $json = $this->fetchJson($url); + + $videos = []; + if (isset($json['data']['book_list'])) { + foreach ($json['data']['book_list'] as $item) { + $videos[] = [ + 'vod_id' => $item['book_id'], + 'vod_name' => $this->decodeText($item['book_name']), + 'vod_pic' => 'http://p6-novel.byteimg.com/large/' . $item['thumb_uri'], + 'vod_remarks' => $this->decodeText($item['author']), + ]; + } + } + + return $this->pageResult($videos, $pg, 18, 18); // total 未知,假设无限 + } + + public function detailContent($ids) { + $id = $ids[0]; + $url = self::HOST . "/page/$id"; + + // 模拟 PC UA + $html = $this->fetch($url, ['headers' => ['User-Agent' => self::UA]]); + + // 提取 window.__INITIAL_STATE__= + if (preg_match('/window\.__INITIAL_STATE__=(.+?);<\/script>/s', $html, $matches) || + preg_match('/window\.__INITIAL_STATE__=(.+?)(?:;|$)/s', $html, $matches)) { + $jsonStr = $matches[1]; + // 替换 undefined 为 null + $jsonStr = str_replace('undefined', 'null', $jsonStr); + $json = json_decode($jsonStr, true); + + if (isset($json['page'])) { + $info = $json['page']; + $bookInfo = $info['bookInfo'] ?? $info; // 结构可能变动 + + // 书名等信息 + $vod = [ + 'vod_id' => $id, + 'vod_name' => $info['bookName'] ?? '', + 'vod_pic' => $info['thumbUri'] ?? '', + 'vod_content' => $info['abstract'] ?? '', + 'vod_remarks' => $info['lastChapterTitle'] ?? '', + 'vod_director' => $info['author'] ?? '', + 'vod_play_from' => '番茄小说', + ]; + + // 章节列表 + $playList = []; + $chapters = $info['chapterListWithVolume'] ?? []; + + // chapterListWithVolume 是个二维数组 [[章节...], [章节...]] + foreach ($chapters as $volume) { + foreach ($volume as $chapter) { + $title = $chapter['title']; + $itemId = $chapter['itemId']; + $playList[] = "$title$" . $itemId . '@' . $title; + } + } + + $vod['vod_play_url'] = implode('#', $playList); + return ['list' => [$vod]]; + } + } + return ['list' => []]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + // URL: /api/search?key=**&tab_type=3&offset=((fypage-1)*10) + // HOST: API_HOST + $offset = ($pg - 1) * 10; + $url = self::API_HOST . "/api/search?key=" . urlencode($key) . "&tab_type=3&offset=$offset"; + + $json = $this->fetchJson($url); + + $videos = []; + // 寻找 search_tabs[5] -> tab_type=3 ? JS 中是 search_tabs[5] 但 API 参数是 tab_type=3 + // 遍历寻找 tab_type = 3 的 tab + $targetData = []; + if (isset($json['data']['search_tabs'])) { + foreach ($json['data']['search_tabs'] as $tab) { + // JS 取下标 5,我们严谨点判断 + // 或者 API 返回的结构里 tab_type 字段 + // 假设结构类似 + if (isset($tab['data'])) { + // 检查第一条数据是否有 book_data 且是小说 + // 简单粗暴:合并所有 tab 的 data? 不,JS 明确是小说 tab + // 暂时取第一个包含 book_data 的 + if (!empty($tab['data']) && isset($tab['data'][0]['book_data'])) { + $targetData = $tab['data']; + break; + } + } + } + } + + foreach ($targetData as $item) { + if (isset($item['book_data'][0])) { + $book = $item['book_data'][0]; + $videos[] = [ + 'vod_id' => $book['book_id'], + 'vod_name' => $book['book_name'], + 'vod_pic' => $book['thumb_url'], + 'vod_remarks' => $book['author'], + 'vod_content' => $book['book_abstract'] ?? $book['abstract'] ?? '' + ]; + } + } + + return $this->pageResult($videos, $pg, 10, 10); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // id: itemId@title + $parts = explode('@', $id); + $itemId = $parts[0]; + $title = $parts[1] ?? ''; + + // Handle Title$ItemId format + if (strpos($itemId, '$') !== false) { + $subParts = explode('$', $itemId); + $itemId = $subParts[1]; + if (empty($title)) { + $title = $subParts[0]; + } + } + + $url = self::API_HOST . "/api/content?tab=小说&item_id=$itemId"; + + // 随机 Cookie + $cookie = $this->getFqCookie(); + $json = $this->fetchJson($url, ['headers' => ['Cookie' => $cookie]]); + + $content = ''; + if (isset($json['data']['content'])) { + $content = $json['data']['content']; + } + + // 构造 novel:// 协议返回,或者直接文本 + // DZ 风格播放器可能不支持 novel://,通常直接返回文本或 html + // 如果是小说,通常返回 parse=0, url=text... + // 这里模仿 JS 返回:novel://json_string + // 还是直接返回文本内容比较通用? + // 为了兼容,我们返回文本内容,如果客户端支持 novel:// 最好,不支持就直接显示 + // 现在的 PHP 爬虫通常返回 content-type: text/plain + // 但 playerContent 需要返回标准结构 + + // 构造响应 + return [ + 'parse' => 0, + 'playUrl' => '', + 'url' => $url, // 仅作记录 + 'header' => (object)[], + // 如果客户端支持直接显示文本内容,通常放在 header 或其他字段? + // 实际上,播放接口返回 content 可能需要客户端特殊处理 + // 这里我们返回一个 data url 或者 模拟的 html + // 参考 JS: return {parse: 0, url: 'novel://' + ret} + 'url' => 'novel://' . json_encode(['title' => $title, 'content' => $content], JSON_UNESCAPED_UNICODE) + ]; + } + + private function getFqCookie() { + $cookies = [ + 'novel_web_id=78444872394737941004', + 'novel_web_id=69258894393744181011', + 'novel_web_id=77130880221809081001', + 'novel_web_id=64945771562463261001', + 'novel_web_id=78444872394737941004', + 'novel_web_id=0000000000004011402', + 'novel_web_id=0000000303614711402', + 'novel_web_id=0144211303614711401', + 'novel_web_id=0144211303614711402', + 'novel_web_id=0144211303614711403', + 'novel_web_id=0144211303614711406', + 'novel_web_id=7357767624615331361', + 'novel_web_id=7357767624615331362', + 'novel_web_id=7357767624615331365', + ]; + return $cookies[array_rand($cookies)]; + } + + // 解密函数 + private function decodeText($text, $type = 0) { + $charset = []; + if ($type === 0) { + // ... 巨大的数组 ... + $charset = ['体', 'y', '十', '现', '快', '便', '话', '却', '月', '物', '水', '的', '放', '知', '爱', '万', '', '表', '风', '理', 'O', '老', '也', 'p', '常', '克', '平', '几', '最', '主', '她', 's', '将', '法', '情', 'o', '光', 'a', '我', '呢', 'J', '员', '太', '每', '望', '受', '教', 'w', '利', '军', '已', 'U', '人', '如', '变', '得', '要', '少', '斯', '门', '电', 'm', '男', '没', 'A', 'K', '国', '时', '中', '走', '么', '何', '口', '小', '向', '问', '轻', 'T', 'd', '神', '下', '间', '车', 'f', 'G', '度', 'D', '又', '大', '面', '远', '就', '写', 'j', '给', '通', '起', '实', 'E', '', '它', '去', 'S', '到', '道', '数', '吃', '们', '加', 'P', '是', '无', '把', '事', '西', '多', '界', '', '发', '新', '外', '活', '解', '孩', '只', '作', '前', 'Y', '尔', '经', '', 'u', '心', '告', '父', '等', 'Q', '民', '全', '这', '9', '果', '安', '', 'i', '母', '8', 'r', '说', '任', '先', '和', '地', 'C', '张', '战', '场', 'g', '像', 'c', 'q', '你', '使', '', '样', '总', '目', 'x', '性', '处', '音', '头', '', '应', '乐', '关', '能', '花', 'l', '当', '名', '手', '4', '重', '字', '声', '力', '友', '然', '生', '代', '内', '里', '本', '回', '真', '入', '师', '象', '', '0', '点', 'R', '亲', 'V', '种', '动', '英', '命', 'Z', 'h', 'X', '做', '特', '边', '高', '有', 'B', '为', '期', '自', '年', '马', '认', '出', '接', '至', 'H', '正', '方', '感', '所', '明', '者', '稜', 'F', '住', '学', '还', '分', '意', '更', '其', 'n', '但', '比', '觉', '以', '由', '死', '家', '让', '失', '士', 'L', '2', 'I', '金', '叫', '身', '报', '听', 'W', '再', '原', '山', '海', '白', '很', '见', '5', '直', '位', '第', '工', '个', '开', '岁', '好', '用', '都', '于', '可', '同', '3', '次', '四', '', '日', '信', '与', '女', '笑', '满', '并', '部', '什', '不', '从', '或', '机', '此', '', '了', '记', '三', 'e', '些', 'b', 'N', '夫', '会', '才', '儿', '眼', '两', '美', '被', '一', '公', '来', '立', 'z', '长', '对', '己', '看', 'k', '许', '因', '相', '色', '后', '往', '打', '结', '格', '过', '世', '气', '7', '子', '条', '在', '书', '之', '定', 'v', '拉', '成', '进', '带', '着', '东', '上', '想', '天', '他', '妈', '1', '文', '而', '路', '那', '别', '德', '6', 'M', 't', '行', '候', '难']; + } else if ($type === 1) { + $charset = ['', 's', '', '作', '口', '在', '他', '能', '并', 'B', '士', '4', 'U', '克', '才', '正', '们', '字', '声', '高', '全', '尔', '活', '者', '动', '其', '主', '报', '多', '望', '放', 'h', 'w', '次', '年', '', '中', '3', '特', '于', '十', '入', '要', '男', '同', 'G', '面', '分', '方', 'K', '什', '再', '教', '本', '己', '结', '1', '等', '世', 'N', '', '说', 'g', 'u', '期', 'Z', '外', '美', 'M', '行', '给', '9', '文', '将', '两', '许', '张', '友', '0', '英', '应', '向', '像', '此', '白', '安', '少', '何', '打', '气', '常', '定', '间', '花', '见', '孩', '它', '直', '风', '数', '使', '道', '第', '水', '已', '女', '山', '解', 'd', 'P', '的', '通', '关', '性', '叫', '儿', 'L', '妈', '问', '回', '神', '来', 'S', '', '四', '里', '前', '国', '些', 'O', 'v', 'l', 'A', '心', '平', '自', '无', '军', '光', '代', '是', '好', '却', 'c', '得', '种', '就', '意', '先', '立', 'z', '子', '过', 'Y', 'j', '表', '', '么', '所', '接', '了', '名', '金', '受', 'J', '满', '眼', '没', '部', '那', 'm', '每', '车', '度', '可', 'R', '斯', '经', '现', '门', '明', 'V', '如', '走', '命', 'y', '6', 'E', '战', '很', '上', 'f', '月', '西', '7', '长', '夫', '想', '话', '变', '海', '机', 'x', '到', 'W', '一', '成', '生', '信', '笑', '但', '父', '开', '内', '东', '马', '日', '小', '而', '后', '带', '以', '三', '几', '为', '认', 'X', '死', '员', '目', '位', '之', '学', '远', '人', '音', '呢', '我', 'q', '乐', '象', '重', '对', '个', '被', '别', 'F', '也', '书', '稜', 'D', '写', '还', '因', '家', '发', '时', 'i', '或', '住', '德', '当', 'o', 'I', '比', '觉', '然', '吃', '去', '公', 'a', '老', '亲', '情', '体', '太', 'b', '万', 'C', '电', '理', '', '失', '力', '更', '拉', '物', '着', '原', '她', '工', '实', '色', '感', '记', '看', '出', '相', '路', '大', '你', '候', '2', '和', '', '与', 'p', '样', '新', '只', '便', '最', '不', '进', 'T', 'r', '做', '格', '母', '总', '爱', '身', '师', '轻', '知', '往', '加', '从', '', '天', 'e', 'H', '', '听', '场', '由', '快', '边', '让', '把', '任', '8', '条', '头', '事', '至', '起', '点', '真', '手', '这', '难', '都', '界', '用', '法', 'n', '处', '下', '又', 'Q', '告', '地', '5', 'k', 't', '岁', '有', '会', '果', '利', '民']; + } else if ($type === 2) { + $charset = ['D', '在', '主', '特', '家', '军', '然', '表', '场', '4', '要', '只', 'v', '和', '?', '6', '别', '还', 'g', '现', '儿', '岁', '?', '?', '此', '象', '月', '3', '出', '战', '工', '相', 'o', '男', '直', '失', '世', 'F', '都', '平', '文', '什', 'V', 'O', '将', '真', 'T', '那', '当', '?', '会', '立', '些', 'u', '是', '十', '张', '学', '气', '大', '爱', '两', '命', '全', '后', '东', '性', '通', '被', '1', '它', '乐', '接', '而', '感', '车', '山', '公', '了', '常', '以', '何', '可', '话', '先', 'p', 'i', '叫', '轻', 'M', '士', 'w', '着', '变', '尔', '快', 'l', '个', '说', '少', '色', '里', '安', '花', '远', '7', '难', '师', '放', 't', '报', '认', '面', '道', 'S', '?', '克', '地', '度', 'I', '好', '机', 'U', '民', '写', '把', '万', '同', '水', '新', '没', '书', '电', '吃', '像', '斯', '5', '为', 'y', '白', '几', '日', '教', '看', '但', '第', '加', '候', '作', '上', '拉', '住', '有', '法', 'r', '事', '应', '位', '利', '你', '声', '身', '国', '问', '马', '女', '他', 'Y', '比', '父', 'x', 'A', 'H', 'N', 's', 'X', '边', '美', '对', '所', '金', '活', '回', '意', '到', 'z', '从', 'j', '知', '又', '内', '因', '点', 'Q', '三', '定', '8', 'R', 'b', '正', '或', '夫', '向', '德', '听', '更', '?', '得', '告', '并', '本', 'q', '过', '记', 'L', '让', '打', 'f', '人', '就', '者', '去', '原', '满', '体', '做', '经', 'K', '走', '如', '孩', 'c', 'G', '给', '使', '物', '?', '最', '笑', '部', '?', '员', '等', '受', 'k', '行', '一', '条', '果', '动', '光', '门', '头', '见', '往', '自', '解', '成', '处', '天', '能', '于', '名', '其', '发', '总', '母', '的', '死', '手', '入', '路', '进', '心', '来', 'h', '时', '力', '多', '开', '已', '许', 'd', '至', '由', '很', '界', 'n', '小', '与', 'Z', '想', '代', '么', '分', '生', '口', '再', '妈', '望', '次', '西', '风', '种', '带', 'J', '?', '实', '情', '才', '这', '?', 'E', '我', '神', '格', '长', '觉', '间', '年', '眼', '无', '不', '亲', '关', '结', '0', '友', '信', '下', '却', '重', '己', '老', '2', '音', '字', 'm', '呢', '明', '之', '前', '高', 'P', 'B', '目', '太', 'e', '9', '起', '稜', '她', '也', 'W', '用', '方', '子', '英', '每', '理', '便', '四', '数', '期', '中', 'C', '外', '样', 'a', '海', '们', '任']; + } + + // JS: _decodeText2 + // text = text.replace(reg, ($0, $1) => z[('0x' + $1) - 1000]); + // reg = /%uE([0-9a-fA-F]{3})/gi + // 58344 (decimal) = E3E8 (hex) + // CODE_ST = 58344 + // index = charCode - 58344 + // JS charset array index logic: + // z[('0x' + $1) - 1000] ??? + // JS code: z[('0x' + $1) - 1000] + // If $1 is '3E8' (1000), then index is 0. + // 'E3E8' -> $1='3E8'. 0x3E8 = 1000. 1000 - 1000 = 0. + // So offset is indeed related to 0xE3E8. + + // PHP Logic: + // Iterate string, find characters in range [0xE3E8, 0xE55B] (approx) + // Or use regex like JS. + // In PHP, unicode characters can be matched or we can convert string to unicode code points. + // + // Better to use preg_replace_callback with unicode escape sequence? + // But the input text might be normal UTF-8 string, not escaped %uXXXX. + // JS's `_decodeText2` first calls `escape(text)`. + // So we should do similar: convert string to unicode hex entities or iterate chars. + + $result = ''; + $len = mb_strlen($text, 'UTF-8'); + for ($i = 0; $i < $len; $i++) { + $char = mb_substr($text, $i, 1, 'UTF-8'); + $code = mb_ord($char, 'UTF-8'); + + // CODE_ST = 58344 (0xE3E8) + // CODE_ED = 58715 (0xE55B) + if ($code >= 58344 && $code <= 58715) { + $index = $code - 58344; + if (isset($charset[$index])) { + $result .= $charset[$index]; + } else { + $result .= $char; + } + } else { + $result .= $char; + } + } + return $result; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\347\225\252\350\214\204\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/\347\225\252\350\214\204\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..5af97ca6 --- /dev/null +++ "b/spider/php/\347\225\252\350\214\204\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,232 @@ +<?php +/** + * 番茄漫画 ᵈᶻ.php + * 对应源: 番茄漫画[画].js + */ + +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private const HOST = 'https://qkfqapi.vv9v.cn'; + private const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'; + + private $startPage = 1; + + public function init($extend = '') { + $this->startPage = 1; + } + + public function homeContent($filter = []) { + $url = self::HOST . '/api/discover/style?tab=漫画'; + $json = $this->fetchJson($url); + + $classes = []; + if (isset($json['data']) && is_array($json['data'])) { + foreach ($json['data'] as $item) { + if (isset($item['url']) && trim($item['url'])) { + $classes[] = [ + 'type_id' => $item['url'], // URL作为ID + 'type_name' => $item['title'] ?? '未知分类', + ]; + } + } + } + + return [ + 'class' => $classes, + 'filters' => (object)[] + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + // tid 是类似 /api/discover?tab=漫画&type=7...&page=1 的URL + // 需要替换 page 参数 + $url = self::HOST . $tid; + if (strpos($tid, 'http') === 0) { + $url = $tid; + } + + // 简单的页码替换逻辑:假设URL中包含 page=1 + // 如果包含 page=x,替换为 page=$pg + if (preg_match('/page=\d+/', $url)) { + $url = preg_replace('/page=\d+/', 'page=' . $pg, $url); + } else { + // 如果没有page参数,追加 + $sep = (strpos($url, '?') !== false) ? '&' : '?'; + $url .= $sep . 'page=' . $pg; + } + + $json = $this->fetchJson($url); + $list = $this->parseList($json); + + return $this->pageResult($list, $pg, 0, 10); // limit 10 + } + + public function detailContent($ids) { + $id = $ids[0]; + $url = self::HOST . "/api/book?book_id=$id"; + $json = $this->fetchJson($url); + + $vod = [ + 'vod_id' => $id, + 'vod_name' => '', + 'vod_pic' => '', + 'type_name' => '', + 'vod_year' => '', + 'vod_area' => '', + 'vod_remarks' => '', + 'vod_actor' => '', + 'vod_director' => '', + 'vod_content' => '', + ]; + + if (isset($json['data']['data'])) { + $data = $json['data']['data']; + $vod['vod_name'] = $data['book_name'] ?? ''; + $vod['type_name'] = $data['category'] ?? ''; + $vod['vod_pic'] = $data['thumb_url'] ?? ''; + $vod['vod_content'] = $data['abstract'] ?? ''; + $vod['vod_remarks'] = $data['sub_info'] ?? ''; + $vod['vod_director'] = $data['author'] ?? ''; + + // 章节列表 + // 这里需要获取章节列表,JS中使用了 jsonStr.parseX.data.data.chapterListWithVolume + // 假设API返回结构一致 + $chapters = []; + if (isset($data['chapterListWithVolume'])) { + // 可能是嵌套数组,需要扁平化 + foreach ($data['chapterListWithVolume'] as $volume) { + if (is_array($volume)) { + foreach ($volume as $chapter) { + $chapters[] = $chapter; + } + } + } + } + + // 如果扁平化失败,尝试直接读取(视API返回而定) + if (empty($chapters) && isset($data['chapter_list'])) { + $chapters = $data['chapter_list']; + } + + $playUrls = []; + foreach ($chapters as $ch) { + $title = $ch['title'] ?? '未知章节'; + $itemId = $ch['itemId'] ?? $ch['item_id'] ?? ''; + $playUrls[] = "$title$$itemId@$title"; + } + + $vod['vod_play_from'] = '番茄漫画'; + $vod['vod_play_url'] = implode('#', $playUrls); + } + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $offset = ($pg - 1) * 10; + $url = self::HOST . "/api/search?key=" . urlencode($key) . "&tab_type=8&offset=$offset"; + $json = $this->fetchJson($url); + + $list = []; + if (isset($json['data']['search_tabs'][3]['data'])) { + $items = $json['data']['search_tabs'][3]['data']; + foreach ($items as $it) { + if (isset($it['book_data'][0])) { + $book = $it['book_data'][0]; + $list[] = [ + 'vod_id' => $book['book_id'] ?? '', + 'vod_name' => $book['book_name'] ?? '', + 'vod_pic' => $book['thumb_url'] ?? '', + 'vod_remarks' => $book['author'] ?? '', + 'vod_content' => $book['book_abstract'] ?? $book['abstract'] ?? '', + ]; + } + } + } + + return $this->pageResult($list, $pg, 0, 10); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // id: itemId@title + $parts = explode('@', $id); + $itemId = $parts[0]; + + $url = self::HOST . "/api/content?tab=漫画&item_id=$itemId&show_html=0"; + $cookie = $this->getFqCookie(); + $json = $this->fetchJson($url, ['headers' => ['Cookie' => $cookie]]); + + $pics = []; + if (isset($json['data']['images'])) { + $images = $json['data']['images']; + if (is_string($images)) { + if (preg_match_all('/<img[^>]+src=[\'"]([^\'"]+)[\'"]/i', $images, $matches)) { + $pics = $matches[1]; + } + } elseif (is_array($images)) { + foreach ($images as $img) { + if (isset($img['src'])) { + $pics[] = $img['src']; + } elseif (isset($img['url'])) { + $pics[] = $img['url']; + } + } + } + } + + if (empty($pics)) { + return ['parse' => 0, 'url' => '', 'header' => (object)[]]; + } + + // 漫画通常使用 pics:// 协议 + return ['parse' => 0, 'url' => 'pics://' . implode('&&', $pics), 'header' => (object)[]]; + } + + // 辅助方法:解析列表 + private function parseList($json) { + $list = []; + $data = $json['data'] ?? []; + if (isset($data['data'])) { + $data = $data['data']; + } + + if (is_array($data)) { + foreach ($data as $item) { + if ($item && (isset($item['book_name']) || isset($item['title']))) { + $list[] = [ + 'vod_id' => $item['book_id'] ?? $item['id'] ?? '', + 'vod_name' => $item['book_name'] ?? $item['title'] ?? '', + 'vod_pic' => $item['thumb_url'] ?? $item['cover'] ?? '', + 'vod_remarks' => $item['author'] ?? $item['category'] ?? '', + 'vod_content' => $item['abstract'] ?? $item['description'] ?? '', + ]; + } + } + } + return $list; + } + + private function getFqCookie() { + $cookies = [ + 'novel_web_id=78444872394737941004', + 'novel_web_id=69258894393744181011', + 'novel_web_id=77130880221809081001', + 'novel_web_id=64945771562463261001', + 'novel_web_id=78444872394737941004', + 'novel_web_id=0000000000004011402', + 'novel_web_id=0000000303614711402', + 'novel_web_id=0144211303614711401', + 'novel_web_id=0144211303614711402', + 'novel_web_id=0144211303614711403', + 'novel_web_id=0144211303614711406', + 'novel_web_id=7357767624615331361', + 'novel_web_id=7357767624615331362', + 'novel_web_id=7357767624615331365', + ]; + return $cookies[array_rand($cookies)]; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\347\273\205\345\243\253\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/\347\273\205\345\243\253\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..a0931cae --- /dev/null +++ "b/spider/php/\347\273\205\345\243\253\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,255 @@ +<?php +/** + * 绅士漫画 + */ +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + + protected const HOST = 'https://www.wn06.ru'; + + public function getName() { + return "绅士漫画"; + } + + public function init($extend = "") { + $this->headers['Referer'] = self::HOST . '/'; + } + + public function homeContent($filter) { + $classes = [ + ["type_name" => "月榜", "type_id" => "rank_month"], + ["type_name" => "周榜", "type_id" => "rank_week"], + ["type_name" => "日榜", "type_id" => "rank_day"], + ["type_name" => "同人志", "type_id" => "1"], + ["type_name" => "韩漫", "type_id" => "20"], + ["type_name" => "单行本", "type_id" => "9"], + ["type_name" => "杂志&短篇", "type_id" => "10"] + ]; + + return ["class" => $classes, "filters" => (object)[]]; + } + + public function homeVideoContent() { + return $this->categoryContent("rank_month", 1, [], []); + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + if ($tid == "rank_month") { + $url = self::HOST . "/albums-favorite_ranking-page-{$pg}-type-month.html"; + } elseif ($tid == "rank_week") { + $url = self::HOST . "/albums-favorite_ranking-page-{$pg}-type-week.html"; + } elseif ($tid == "rank_day") { + $url = self::HOST . "/albums-favorite_ranking-page-{$pg}-type-day.html"; + } else { + $url = self::HOST . "/albums-index-page-{$pg}-cate-{$tid}.html"; + } + + $html = $this->fetch($url); + + // Parse list items + $items = $this->pdfa($html, '.gallary_wrap ul li'); + $videos = []; + + foreach ($items as $item) { + $vid = $this->pd($item, '.info .title a&&href'); + $name = $this->pdfh($item, '.info .title a&&Text'); + $cover = $this->pd($item, '.pic_box img&&src'); + $info_text = $this->pdfh($item, '.info .info_col&&Text'); + $remark = ""; + if (preg_match('/(\d+)張圖片/', $info_text, $match)) { + $remark = $match[1] . "页"; + } + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "vod_remarks" => $remark + ]; + } + + return $this->pageResult($videos, $pg, 999999, 20); + } + + public function detailContent($ids) { + $vid = $ids[0]; + $url = (strpos($vid, 'http') === 0) ? $vid : self::HOST . $vid; + + $html = $this->fetch($url); + + // Title + $name = $this->pdfh($html, 'h2&&Text'); + if (empty($name)) $name = "未知"; + + // Cover + $cover = $this->pd($html, '.uwthumb img&&src'); + if (empty($cover)) { + $cover = $this->pd($html, '.cover img&&src'); + } + + // Desc + $desc = $this->pdfh($html, '.uwconn p||.info p&&Text'); + + // Pagination logic + $max_page = 1; + $aid = ""; + + if (preg_match('/aid-(\d+)/', $url, $match)) { + $aid = $match[1]; + } + + $paginator_links = $this->pdfa($html, '.paginator a'); + foreach ($paginator_links as $link) { + $href = $this->pdfh($link, 'a&&href'); + + if (!$aid && preg_match('/aid-(\d+)/', $href, $m)) { + $aid = $m[1]; + } + + if (preg_match('/page-(\d+)/', $href, $m)) { + $p = intval($m[1]); + if ($p > $max_page) { + $max_page = $p; + } + } + } + + $vod_play_url_list = []; + if ($max_page == 1) { + $vod_play_url_list[] = "第1页$" . $url; + } else { + for ($i = 1; $i <= $max_page; $i++) { + if ($aid) { + $page_url = self::HOST . "/photos-index-page-{$i}-aid-{$aid}.html"; + $vod_play_url_list[] = "第{$i}页$" . $page_url; + } elseif ($i == 1) { + $vod_play_url_list[] = "第1页$" . $url; + } + } + } + + $play_url_str = implode("#", $vod_play_url_list); + + return [ + "list" => [[ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "type_name" => "漫画", + "vod_year" => "", + "vod_area" => "", + "vod_remarks" => "共{$max_page}页", + "vod_actor" => "", + "vod_director" => "", + "vod_content" => $desc, + "vod_play_from" => '阅读', + "vod_play_url" => $play_url_str + ]] + ]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $url = self::HOST . "/search/?q=" . urlencode($key) . "&f=_all&s=create_time_DESC&syn=yes&page={$pg}"; + $html = $this->fetch($url); + + $items = $this->pdfa($html, '.gallary_wrap ul li'); + $videos = []; + + foreach ($items as $item) { + $vid = $this->pd($item, '.info .title a&&href'); + $name = $this->pdfh($item, '.info .title a&&Text'); + $cover = $this->pd($item, '.pic_box img&&src'); + + $info_text = $this->pdfh($item, '.info .info_col&&Text'); + $remark = ""; + if (preg_match('/(\d+)張圖片/', $info_text, $match)) { + $remark = $match[1] . "页"; + } + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "vod_remarks" => $remark + ]; + } + + return ["list" => $videos]; + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = $id; + $headers = $this->headers; + $headers['Referer'] = $url; + + $html = $this->fetch($url, ['headers' => $headers]); + + $items = $this->pdfa($html, '.gallary_wrap.tb ul li'); + $img_info_list = []; + $prefix_url = ""; + + foreach ($items as $item) { + $seq = $this->pdfh($item, 'span.name.tb&&Text'); + $src = $this->pdfh($item, 'img&&src'); + + if (!$src) continue; + + $ext = "jpg"; + $parts = explode('.', $src); + if (count($parts) > 1) { + $last = end($parts); + $ext = explode('?', $last)[0]; + } + + if (!$prefix_url && strpos($src, "wnimg1") !== false) { + $last_slash = strrpos($src, '/'); + if ($last_slash !== false) { + $prefix_url = substr($src, 0, $last_slash + 1); + } + } + + $img_info_list[] = [ + "name" => $seq, + "ext" => $ext, + "raw_src" => $src + ]; + } + + // Sort + usort($img_info_list, function($a, $b) { + $na = is_numeric($a['name']) ? intval($a['name']) : 0; + $nb = is_numeric($b['name']) ? intval($b['name']) : 0; + return $na <=> $nb; + }); + + $final_images = []; + foreach ($img_info_list as $item) { + if ($prefix_url) { + $full_url = "{$prefix_url}{$item['name']}.{$item['ext']}"; + } else { + $full_url = $item['raw_src']; + } + + if (strpos($full_url, "tu.petatt.cn") !== false) continue; + + if (strpos($full_url, '//') === 0) { + $full_url = 'https:' . $full_url; + } + + $final_images[] = $full_url; + } + + $novel_data = implode("&&", $final_images); + + return [ + "parse" => 0, + "playUrl" => "", + "url" => "pics://{$novel_data}", + "header" => "" + ]; + } +} + +// 自动运行 +(new Spider())->run(); diff --git "a/spider/php/\350\205\276\350\205\276 \341\265\210\341\266\273.php" "b/spider/php/\350\205\276\350\205\276 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..5eb5ec4e --- /dev/null +++ "b/spider/php/\350\205\276\350\205\276 \341\265\210\341\266\273.php" @@ -0,0 +1,868 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $host = 'https://v.qq.com'; + private $apiHost = 'https://pbaccess.video.qq.com'; + + public function homeContent($filter) { + $classes = [ + ['type_id' => '100173', 'type_name' => '电影'], + ['type_id' => '100113', 'type_name' => '电视剧'], + ['type_id' => '100109', 'type_name' => '综艺'], + ['type_id' => '100105', 'type_name' => '纪录片'], + ['type_id' => '100119', 'type_name' => '动漫'], + ['type_id' => '100150', 'type_name' => '少儿'], + ['type_id' => '110755', 'type_name' => '短剧'] + ]; + + $filters = [ + '100173' => [ + [ + 'key' => 'iyear', 'name' => '年份', 'value' => [ + ['n' => '全部', 'v' => '-1'], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], + ['n' => '2023', 'v' => '2023'], ['n' => '2022', 'v' => '2022'], ['n' => '2021', 'v' => '2021'], + ['n' => '2020', 'v' => '2020'] + ] + ], + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => '75'], ['n' => '最新', 'v' => '76'], ['n' => '最热', 'v' => '74'] + ] + ] + ], + '100113' => [ + [ + 'key' => 'iyear', 'name' => '年份', 'value' => [ + ['n' => '全部', 'v' => '-1'], ['n' => '2025', 'v' => '2025'], ['n' => '2024', 'v' => '2024'], + ['n' => '2023', 'v' => '2023'], ['n' => '2022', 'v' => '2022'], ['n' => '2021', 'v' => '2021'], + ['n' => '2020', 'v' => '2020'] + ] + ], + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => '75'], ['n' => '最新', 'v' => '76'], ['n' => '最热', 'v' => '74'] + ] + ] + ], + '100109' => [ + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => '75'], ['n' => '最新', 'v' => '76'], ['n' => '最热', 'v' => '74'] + ] + ] + ], + '100119' => [ + [ + 'key' => 'sort', 'name' => '排序', 'value' => [ + ['n' => '综合', 'v' => '75'], ['n' => '最新', 'v' => '76'], ['n' => '最热', 'v' => '74'] + ] + ] + ] + ]; + + return [ + 'class' => $classes, + 'filters' => $filters + ]; + } + + public function homeVideoContent() { + return ['list' => []]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $page = max(1, intval($pg)); + $offset = ($page - 1) * 21; + + // 构建列表页URL(使用原始的页面URL结构) + $url = $this->host . '/x/bu/pagesheet/list'; + $params = [ + '_all' => '1', + 'append' => '1', + 'channel' => $this->getChannelByTid($tid), + 'listpage' => '1', + 'offset' => $offset, + 'pagesize' => '21', + 'iarea' => '-1' + ]; + + // 添加排序参数 + if (isset($extend['sort']) && $extend['sort'] !== '-1') { + $params['sort'] = $extend['sort']; + } else { + $params['sort'] = '75'; // 默认综合排序 + } + + // 添加其他筛选参数 + if (isset($extend['iyear']) && $extend['iyear'] !== '-1') { + $params['iyear'] = $extend['iyear']; + } + + $fullUrl = $url . '?' . http_build_query($params); + + // 使用新的PHP解析逻辑 + $videos = $this->parseListPage($fullUrl); + + return $this->pageResult($videos, $page, 99999, 21); + } + + /** + * 根据类型ID获取频道名称 + */ + private function getChannelByTid($tid) { + $map = [ + '100173' => 'movie', // 电影 + '100113' => 'tv', // 电视剧 + '100109' => 'variety', // 综艺 + '100105' => 'doco', // 纪录片 + '100119' => 'cartoon', // 动漫 + '100150' => 'child', // 少儿 + '110755' => 'choice' // 短剧(用精选代替) + ]; + + return $map[$tid] ?? 'movie'; + } + + /** + * 解析列表页(使用新的PHP逻辑) + */ + private function parseListPage($url) { + $result = []; + + try { + // 1. 获取网页内容 + $html = $this->fetch($url); + + // 2. 使用DOMDocument解析HTML + libxml_use_internal_errors(true); + $dom = new DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); + $xpath = new DOMXPath($dom); + + // 3. 查找所有列表项 + $listItems = $xpath->query('//div[contains(@class, "list_item")]'); + + foreach ($listItems as $item) { + // 提取标题 (img的alt属性) + $imgElements = $xpath->query('.//img', $item); + $title = ''; + if ($imgElements->length > 0) { + $node = $imgElements->item(0); + if ($node instanceof DOMElement) { + $title = $node->getAttribute('alt'); + $title = html_entity_decode($title, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + } + + // 提取图片 (img的src属性) + $pic = ''; + if ($imgElements->length > 0) { + $node = $imgElements->item(0); + if ($node instanceof DOMElement) { + $pic = $node->getAttribute('src'); + // 确保图片URL完整 + if ($pic && !preg_match('/^https?:\/\//', $pic)) { + $pic = $this->urlJoin($url, $pic); + } + } + } + + // 提取描述 (a标签的文本) + $aElements = $xpath->query('.//a', $item); + $desc = ''; + if ($aElements->length > 0) { + $desc = trim($aElements->item(0)->textContent); + $desc = html_entity_decode($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + // 提取链接 (a标签的data-float属性) + $link = ''; + if ($aElements->length > 0) { + $node = $aElements->item(0); + if ($node instanceof DOMElement) { + $link = $node->getAttribute('data-float'); + // 处理链接,获取CID + if ($link) { + $cid = $this->extractCidFromUrl($link); + if ($cid) { + $link = $cid; + } + } + } + } + + // 如果链接为空,尝试从其他属性提取 + if (empty($link) && $aElements->length > 0) { + $node = $aElements->item(0); + if ($node instanceof DOMElement) { + $href = $node->getAttribute('href'); + if ($href) { + $cid = $this->extractCidFromUrl($href); + if ($cid) { + $link = $cid; + } + } + } + } + + // 添加到结果数组 + if (!empty($link) && !empty($title)) { + $result[] = [ + 'vod_id' => $link, + 'vod_name' => $this->cleanText($title), + 'vod_pic' => $pic, + 'vod_remarks' => $this->cleanText($desc) + ]; + } + } + + // 如果DOM解析失败,尝试使用正则表达式 + if (empty($result)) { + $result = $this->parseListWithRegex($html, $url); + } + + } catch (\Exception $e) { + error_log("解析列表页失败: " . $e->getMessage() . " URL: " . $url); + // 尝试备用方法 + $result = $this->parseListWithRegex($html ?? '', $url); + } + + return $result; + } + + /** + * 使用正则表达式解析列表页(备用方法) + */ + private function parseListWithRegex($html, $baseUrl) { + $result = []; + + // 匹配列表项 + $pattern = '/<div[^>]*class="[^"]*list_item[^"]*"[^>]*>(.*?)<\/div>/is'; + preg_match_all($pattern, $html, $itemMatches, PREG_SET_ORDER); + + foreach ($itemMatches as $item) { + $itemHtml = $item[1]; + + // 提取标题 + preg_match('/<img[^>]*alt="([^"]*)"[^>]*>/i', $itemHtml, $titleMatch); + $title = $titleMatch[1] ?? ''; + $title = html_entity_decode($title, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // 提取图片 + preg_match('/<img[^>]*src="([^"]*)"[^>]*>/i', $itemHtml, $picMatch); + $pic = $picMatch[1] ?? ''; + if ($pic && !preg_match('/^https?:\/\//', $pic)) { + $pic = $this->urlJoin($baseUrl, $pic); + } + + // 提取链接 + preg_match('/<a[^>]*data-float="([^"]*)"[^>]*>/i', $itemHtml, $linkMatch); + $link = $linkMatch[1] ?? ''; + if (empty($link)) { + preg_match('/<a[^>]*href="([^"]*)"[^>]*>/i', $itemHtml, $hrefMatch); + $link = $hrefMatch[1] ?? ''; + } + + // 提取CID + $cid = ''; + if ($link) { + $cid = $this->extractCidFromUrl($link); + } + + // 提取描述 + preg_match('/<a[^>]*>(.*?)<\/a>/is', $itemHtml, $descMatch); + $desc = $descMatch[1] ?? ''; + $desc = strip_tags($desc); + $desc = html_entity_decode($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + if (!empty($cid) && !empty($title)) { + $result[] = [ + 'vod_id' => $cid, + 'vod_name' => $this->cleanText($title), + 'vod_pic' => $pic, + 'vod_remarks' => $this->cleanText($desc) + ]; + } + } + + return $result; + } + + /** + * 从URL中提取CID + */ + private function extractCidFromUrl($url) { + // 处理多种URL格式 + $patterns = [ + '/\/cover\/([a-zA-Z0-9]+)\.html/', // /cover/CID.html + '/\/([a-zA-Z0-9]+)\.html$/', // /CID.html + '/cid=([a-zA-Z0-9]+)/', // cid=CID + '/\/([a-zA-Z0-9]+)\//', // /CID/ + '/\/detail\/m\/([a-zA-Z0-9]+)\.html/' // /detail/m/CID.html + ]; + + foreach ($patterns as $pattern) { + if (preg_match($pattern, $url, $matches)) { + return $matches[1]; + } + } + + return ''; + } + + /** + * URL拼接辅助函数 + */ + private function urlJoin($baseUrl, $relativePath) { + if (empty($relativePath)) { + return $relativePath; + } + + if (preg_match('/^https?:\/\//', $relativePath)) { + return $relativePath; + } + + $baseParts = parse_url($baseUrl); + $basePath = isset($baseParts['path']) ? dirname($baseParts['path']) : '/'; + + if (strpos($relativePath, '/') === 0) { + // 绝对路径 + return $baseParts['scheme'] . '://' . $baseParts['host'] . $relativePath; + } else { + // 相对路径 + $newPath = rtrim($basePath, '/') . '/' . ltrim($relativePath, '/'); + return $baseParts['scheme'] . '://' . $baseParts['host'] . $newPath; + } + } + + /** + * 清理文本,移除多余空格和换行 + */ + private function cleanText($text) { + if (empty($text)) { + return ''; + } + + $text = trim($text); + $text = preg_replace('/\s+/', ' ', $text); // 替换多个空格为单个空格 + $text = preg_replace('/[\r\n]+/', ' ', $text); // 移除换行 + return $text; + } + + // ================== 以下是原有的搜索逻辑,完全保持不变 ================== + + public function detailContent($ids) { + $videoId = is_array($ids) ? $ids[0] : $ids; + + // 获取视频基本信息 + $infoUrl = $this->apiHost . '/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData'; + + $infoBody = [ + "page_params" => [ + "req_from" => "web", + "cid" => $videoId, + "vid" => "", + "lid" => "", + "page_type" => "detail_operation", + "page_id" => "detail_page_introduction" + ], + "has_cache" => 1 + ]; + + $infoResponse = $this->fetch($infoUrl . '?video_appid=3000010&vplatform=2&vversion_name=8.2.96', [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($infoBody), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer: ' . $this->host . '/', + 'Origin: ' . $this->host + ] + ]); + + $infoJson = json_decode($infoResponse, true); + $infoData = $infoJson['data'] ?? []; + + // 提取视频详情 + $vod = [ + 'vod_id' => $videoId, + 'vod_name' => '', + 'type_name' => '', + 'vod_actor' => '', + 'vod_year' => '', + 'vod_content' => '', + 'vod_remarks' => '', + 'vod_pic' => '', + 'vod_play_from' => '腾讯视频', + 'vod_play_url' => '' + ]; + + // 提取基本信息 + if (isset($infoData['module_list_datas'][0]['module_datas'][0]['item_data_lists']['item_datas'][0])) { + $detailData = $infoData['module_list_datas'][0]['module_datas'][0]['item_data_lists']['item_datas'][0]; + $itemParams = $detailData['item_params'] ?? []; + + $vod['vod_name'] = $itemParams['title'] ?? ''; + $vod['type_name'] = $itemParams['sub_genre'] ?? ''; + $vod['vod_year'] = $itemParams['year'] ?? ''; + $vod['vod_content'] = $itemParams['cover_description'] ?? ''; + $vod['vod_remarks'] = $itemParams['holly_online_time'] ?? $itemParams['hotval'] ?? ''; + $vod['vod_pic'] = $itemParams['image_url'] ?? ''; + + // 提取演员信息 + if (isset($detailData['sub_items']['star_list']['item_datas'])) { + $actors = []; + foreach ($detailData['sub_items']['star_list']['item_datas'] as $star) { + $actors[] = $star['item_params']['name'] ?? ''; + } + $vod['vod_actor'] = implode(',', $actors); + } + } + + // 方法1:使用分页获取所有剧集 + $playUrls = $this->getAllEpisodes($videoId); + + // 方法2:如果方法1失败,尝试备用方法 + if (empty($playUrls)) { + $playUrls = $this->getEpisodesByTab($videoId); + } + + // 方法3:如果还是没有剧集,可能是电影 + if (empty($playUrls) && !empty($videoId)) { + $playUrls[] = "正片\${$videoId}"; + } + + $vod['vod_play_url'] = implode('#', $playUrls); + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $page = max(1, intval($pg)); + $videos = []; + + // 使用原有的搜索逻辑 + $searchData = $this->vodSearch($key, $page - 1); // JavaScript代码中页码从0开始 + + if (!empty($searchData)) { + foreach ($searchData as $item) { + $videos[] = [ + 'vod_id' => $item['id'] ?? '', + 'vod_name' => $item['title'] ?? '', + 'vod_pic' => $item['img'] ?? '', + 'vod_remarks' => $item['desc'] ?? '' + ]; + } + } + + // 计算总页数(假设每页30条) + $total = count($videos) > 0 ? 999 : 0; + $limit = 30; + + return $this->pageResult($videos, $page, $total, $limit); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // 解析播放地址格式:cid@vid 或 cid + if (strpos($id, '@') !== false) { + $parts = explode('@', $id); + $cid = $parts[0]; + $vid = $parts[1]; + $url = "{$this->host}/x/cover/{$cid}/{$vid}.html"; + } else { + // 只有cid,可能是电影 + $url = "{$this->host}/x/cover/{$id}.html"; + } + + return [ + 'parse' => 1, + 'jx' => 1, + 'play_parse' => true, + 'parse_type' => '壳子超级解析', + 'parse_source' => '腾讯视频', + 'url' => $url, + 'header' => json_encode([ + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer' => $this->host, + 'Origin' => $this->host + ], JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * 执行搜索(原有的搜索逻辑) + */ + private function vodSearch($keyword, $page = 0) { + $url = 'https://pbaccess.video.qq.com/trpc.videosearch.mobile_search.MultiTerminalSearch/MbSearch?vplatform=2'; + + $body = json_encode([ + "version" => "25042201", + "clientType" => 1, + "filterValue" => "", + "uuid" => "B1E50847-D25F-4C4B-BBA0-36F0093487F6", + "retry" => 0, + "query" => $keyword, + "pagenum" => $page, + "isPrefetch" => true, + "pagesize" => 30, + "queryFrom" => 0, + "searchDatakey" => "", + "transInfo" => "", + "isneedQc" => true, + "preQid" => "", + "adClientInfo" => "", + "extraInfo" => [ + "isNewMarkLabel" => "1", + "multi_terminal_pc" => "1", + "themeType" => "1", + "sugRelatedIds" => "{}", + "appVersion" => "" + ] + ]); + + $response = $this->fetch($url, [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $body, + CURLOPT_HTTPHEADER => [ + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.139 Safari/537.36', + 'Content-Type: application/json', + 'Origin: https://v.qq.com', + 'Referer: https://v.qq.com/' + ] + ]); + + return $this->parseSearchResult($response); + } + + /** + * 解析搜索结果(原有的搜索逻辑) + */ + private function parseSearchResult($html) { + $d = []; + $seenIds = []; + + try { + $json = json_decode($html, true); + + // 处理normalList + if (isset($json['data']['normalList']['itemList'])) { + $this->processItemList($json['data']['normalList']['itemList'], $d, $seenIds); + } + + // 处理areaBoxList + if (isset($json['data']['areaBoxList'])) { + foreach ($json['data']['areaBoxList'] as $box) { + if (isset($box['itemList'])) { + $this->processItemList($box['itemList'], $d, $seenIds); + } + } + } + } catch (\Exception $e) { + error_log("搜索解析出错: " . $e->getMessage()); + } + + return $d; + } + + /** + * 处理项目列表(原有的搜索逻辑) + */ + private function processItemList($itemList, &$d, &$seenIds) { + $nonMainContentKeywords = [ + ':', '#', '特辑', '"', '剪辑', '片花', '独家', '专访', '纯享', + '制作', '幕后', '宣传', 'MV', '主题曲', '插曲', '彩蛋', + '精彩', '集锦', '盘点', '回顾', '解说', '评测', '反应', 'reaction' + ]; + + foreach ($itemList as $it) { + if (isset($it['doc']['id'], $it['videoInfo']['title'])) { + $itemId = $it['doc']['id']; + $videoInfo = $it['videoInfo']; + $title = $videoInfo['title'] ?? ''; + + // 检查是否主要内容 + if (!$this->isMainContent($title, $nonMainContentKeywords)) { + continue; + } + + // 检查是否为QQ平台 + if (!$this->isQQPlatform($videoInfo['playSites'] ?? [])) { + continue; + } + + // 去重检查 + if (in_array($itemId, $seenIds)) { + continue; + } + + $seenIds[] = $itemId; + + $d[] = [ + 'id' => $itemId, + 'title' => $title, + 'img' => $videoInfo['imgUrl'] ?? '', + 'desc' => $videoInfo['secondLine'] ?? '' + ]; + } + } + } + + /** + * 检查是否主要内容(原有的搜索逻辑) + */ + private function isMainContent($title, $nonMainContentKeywords) { + if (empty($title)) { + return false; + } + + // 检查是否包含HTML标签(如<em>) + if (strpos($title, '<') !== false) { + return false; + } + + // 检查是否包含非主要内容关键词 + foreach ($nonMainContentKeywords as $keyword) { + if (strpos($title, $keyword) !== false) { + return false; + } + } + + return true; + } + + /** + * 检查是否为QQ平台(原有的搜索逻辑) + */ + private function isQQPlatform($playSites) { + if (empty($playSites) || !is_array($playSites)) { + return true; // 如果没有平台信息,默认保留 + } + + foreach ($playSites as $site) { + if (isset($site['enName']) && strtolower($site['enName']) === 'qq') { + return true; + } + } + + return false; + } + + // ================== 其他辅助方法保持不变 ================== + + private function buildFilterParams($params) { + $result = []; + foreach ($params as $key => $value) { + if ($value !== '-1' && $value !== '' && $value !== null) { + $result[] = "{$key}={$value}"; + } + } + return empty($result) ? 'sort=75' : implode('&', $result); + } + + /** + * 方法1:分页获取所有剧集 + */ + private function getAllEpisodes($videoId) { + $allEpisodes = []; + $pageSize = 50; + $pageNum = 1; + $hasMore = true; + + while ($hasMore) { + $episodeUrl = $this->apiHost . '/trpc.video_detail_svr.video_detail_svr.VideoDetail/GetEpisodeList'; + + $episodeBody = [ + "cid" => $videoId, + "vid" => "", + "req_from" => "web", + "page_context" => "", + "page_size" => $pageSize, + "page_num" => $pageNum, + "order" => 1 + ]; + + $episodeResponse = $this->fetch($episodeUrl . '?video_appid=3000010&vplatform=2&vversion_name=8.2.96', [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($episodeBody), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer: ' . $this->host . '/', + 'Origin: ' . $this->host + ] + ]); + + $episodeJson = json_decode($episodeResponse, true); + $episodeData = $episodeJson['data'] ?? []; + + if (isset($episodeData['item_data_lists']['item_datas'])) { + $episodes = $episodeData['item_data_lists']['item_datas']; + + if (!empty($episodes)) { + foreach ($episodes as $item) { + $itemId = $item['item_id'] ?? ''; + $itemParams = $item['item_params'] ?? []; + + if (!empty($itemId)) { + $title = $itemParams['title'] ?? $itemParams['subtitle'] ?? "第" . ($itemParams['order'] ?? '?') . "集"; + $allEpisodes[] = "{$title}\${$videoId}@{$itemId}"; + } + } + + // 检查是否还有更多页 + $hasMore = isset($episodeData['has_more']) && $episodeData['has_more'] == 1; + $pageNum++; + } else { + $hasMore = false; + } + } else { + $hasMore = false; + } + + // 防止无限循环 + if ($pageNum > 20) { + break; + } + } + + return $allEpisodes; + } + + /** + * 方法2:通过tab标签获取所有剧集 + */ + private function getEpisodesByTab($videoId) { + $allEpisodes = []; + + $tabUrl = $this->apiHost . '/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData'; + + $tabBody = [ + "page_params" => [ + "req_from" => "web_vsite", + "page_id" => "vsite_episode_list", + "page_type" => "detail_operation", + "id_type" => "1", + "cid" => $videoId, + "vid" => "", + "lid" => "", + "page_context" => "", + "detail_page_type" => "1" + ], + "has_cache" => 1 + ]; + + $tabResponse = $this->fetch($tabUrl . '?video_appid=3000010&vplatform=2&vversion_name=8.2.96', [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($tabBody), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer: ' . $this->host . '/', + 'Origin: ' . $this->host + ] + ]); + + $tabJson = json_decode($tabResponse, true); + $tabData = $tabJson['data'] ?? []; + + if (isset($tabData['module_list_datas'])) { + foreach ($tabData['module_list_datas'] as $module) { + if (isset($module['module_datas'])) { + foreach ($module['module_datas'] as $moduleData) { + // 获取tab信息 + $moduleParams = $moduleData['module_params'] ?? []; + $tabsJson = $moduleParams['tabs'] ?? '[]'; + $tabs = json_decode($tabsJson, true) ?: []; + + // 处理每个tab的剧集 + foreach ($tabs as $tab) { + $tabContext = $tab['page_context'] ?? ''; + if (!empty($tabContext)) { + $tabEpisodes = $this->getEpisodesByTabContext($videoId, $tabContext); + $allEpisodes = array_merge($allEpisodes, $tabEpisodes); + } + } + + // 同时获取当前tab的剧集 + if (isset($moduleData['item_data_lists']['item_datas'])) { + foreach ($moduleData['item_data_lists']['item_datas'] as $item) { + $itemId = $item['item_id'] ?? ''; + $itemParams = $item['item_params'] ?? []; + + if (!empty($itemId)) { + $title = $itemParams['union_title'] ?? $itemParams['title'] ?? "第" . ($itemParams['order'] ?? '?') . "集"; + $allEpisodes[] = "{$title}\${$videoId}@{$itemId}"; + } + } + } + } + } + } + } + + return $allEpisodes; + } + + /** + * 获取指定tab上下文的所有剧集 + */ + private function getEpisodesByTabContext($videoId, $pageContext) { + $episodes = []; + + $url = $this->apiHost . '/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData'; + + $body = [ + "page_params" => [ + "req_from" => "web_vsite", + "page_id" => "vsite_episode_list", + "page_type" => "detail_operation", + "id_type" => "1", + "cid" => $videoId, + "vid" => "", + "lid" => "", + "page_context" => $pageContext, + "detail_page_type" => "1" + ], + "has_cache" => 1 + ]; + + $response = $this->fetch($url . '?video_appid=3000010&vplatform=2&vversion_name=8.2.96', [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($body), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Referer: ' . $this->host . '/', + 'Origin: ' . $this->host + ] + ]); + + $json = json_decode($response, true); + $data = $json['data'] ?? []; + + if (isset($data['module_list_datas'])) { + foreach ($data['module_list_datas'] as $module) { + if (isset($module['module_datas'])) { + foreach ($module['module_datas'] as $moduleData) { + if (isset($moduleData['item_data_lists']['item_datas'])) { + foreach ($moduleData['item_data_lists']['item_datas'] as $item) { + $itemId = $item['item_id'] ?? ''; + $itemParams = $item['item_params'] ?? []; + + if (!empty($itemId)) { + $title = $itemParams['union_title'] ?? $itemParams['title'] ?? "第" . ($itemParams['order'] ?? '?') . "集"; + $episodes[] = "{$title}\${$videoId}@{$itemId}"; + } + } + } + } + } + } + } + + return $episodes; + } +} + +(new Spider())->run(); \ No newline at end of file diff --git "a/spider/php/\350\215\220\347\211\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" "b/spider/php/\350\215\220\347\211\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..94c5c02c --- /dev/null +++ "b/spider/php/\350\215\220\347\211\207\345\275\261\350\247\206 \341\265\210\341\266\273.php" @@ -0,0 +1,346 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST; + private $HEADERS; + private $IMGHOST; + + public function init($extend = '') { + $this->HOST = 'https://api.ztcgi.com'; + $this->HEADERS = ['User-Agent: Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36']; + $this->IMGHOST = 'https://img1.vbwus.com'; + $this->preprocess(); + } + + private function preprocess() { + try { + $res = json_decode($this->fetch($this->HOST . '/api/appAuthConfig', [], $this->HEADERS), true); + if (isset($res['data']['imgDomain'])) { + $this->IMGHOST = 'https://' . $res['data']['imgDomain']; + } + } catch (Exception $e) { + // 忽略错误 + } + } + + public function homeContent($filter) { + // 生成分类列表 + $classNames = explode('&', '电影&电视剧&动漫&短剧&综艺'); + $classUrls = explode('&', '1&2&3&67&4'); + $classes = []; + $filterObj = []; + + // 过滤器配置 + $filterConfig = [ + "1" => [ + ["key"=>"cateId","name"=>"分类","value"=>[["n"=>"全部","v"=>"1"],["n"=>"首推","v"=>"5"],["n"=>"动作","v"=>"6"],["n"=>"喜剧","v"=>"7"],["n"=>"战争","v"=>"8"],["n"=>"恐怖","v"=>"9"],["n"=>"剧情","v"=>"10"],["n"=>"爱情","v"=>"11"],["n"=>"科幻","v"=>"12"],["n"=>"动画","v"=>"13"]]], + ["key"=>"area","name"=>"地區","value"=>[["n"=>"全部","v"=>"0"],["n"=>"国产","v"=>"1"],["n"=>"中国香港","v"=>"3"],["n"=>"中国台湾","v"=>"6"],["n"=>"美国","v"=>"5"],["n"=>"韩国","v"=>"18"],["n"=>"日本","v"=>"2"]]], + ["key"=>"year","name"=>"年代","value"=>[["n"=>"全部","v"=>"0"],["n"=>"2025","v"=>"107"],["n"=>"2024","v"=>"119"],["n"=>"2023","v"=>"153"],["n"=>"2022","v"=>"101"],["n"=>"2021","v"=>"118"],["n"=>"2020","v"=>"16"],["n"=>"2019","v"=>"7"],["n"=>"2018","v"=>"2"],["n"=>"2017","v"=>"3"],["n"=>"2016","v"=>"22"]]], + ["key"=>"sort","name"=>"排序","value"=>[["n"=>"热门","v"=>"hot"],["n"=>"评分","v"=>"rating"],["n"=>"更新","v"=>"update"]]] + ], + "2" => [ + ["key"=>"cateId","name"=>"分类","value"=>[["n"=>"全部","v"=>"2"],["n"=>"首推","v"=>"14"],["n"=>"国产","v"=>"15"],["n"=>"港台","v"=>"16"],["n"=>"日韩","v"=>"17"],["n"=>"海外","v"=>"18"]]], + ["key"=>"area","name"=>"地區","value"=>[["n"=>"全部","v"=>"0"],["n"=>"国产","v"=>"1"],["n"=>"中国香港","v"=>"3"],["n"=>"中国台湾","v"=>"6"],["n"=>"美国","v"=>"5"],["n"=>"韩国","v"=>"18"],["n"=>"日本","v"=>"2"]]], + ["key"=>"year","name"=>"年代","value"=>[["n"=>"全部","v"=>"0"],["n"=>"2025","v"=>"107"],["n"=>"2024","v"=>"119"],["n"=>"2023","v"=>"153"],["n"=>"2022","v"=>"101"],["n"=>"2021","v"=>"118"],["n"=>"2020","v"=>"16"],["n"=>"2019","v"=>"7"],["n"=>"2018","v"=>"2"],["n"=>"2017","v"=>"3"],["n"=>"2016","v"=>"22"]]], + ["key"=>"sort","name"=>"排序","value"=>[["n"=>"热门","v"=>"hot"],["n"=>"评分","v"=>"rating"],["n"=>"更新","v"=>"update"]]] + ], + "3" => [ + ["key"=>"cateId","name"=>"分类","value"=>[["n"=>"全部","v"=>"3"],["n"=>"首推","v"=>"19"],["n"=>"海外","v"=>"20"],["n"=>"日本","v"=>"21"],["n"=>"国产","v"=>"22"]]], + ["key"=>"area","name"=>"地區","value"=>[["n"=>"全部","v"=>"0"],["n"=>"国产","v"=>"1"],["n"=>"中国香港","v"=>"3"],["n"=>"中国台湾","v"=>"6"],["n"=>"美国","v"=>"5"],["n"=>"韩国","v"=>"18"],["n"=>"日本","v"=>"2"]]], + ["key"=>"year","name"=>"年代","value"=>[["n"=>"全部","v"=>"0"],["n"=>"2025","v"=>"107"],["n"=>"2024","v"=>"119"],["n"=>"2023","v"=>"153"],["n"=>"2022","v"=>"101"],["n"=>"2021","v"=>"118"],["n"=>"2020","v"=>"16"],["n"=>"2019","v"=>"7"],["n"=>"2018","v"=>"2"],["n"=>"2017","v"=>"3"],["n"=>"2016","v"=>"22"]]], + ["key"=>"sort","name"=>"排序","value"=>[["n"=>"热门","v"=>"hot"],["n"=>"评分","v"=>"rating"],["n"=>"更新","v"=>"update"]]] + ], + "4" => [ + ["key"=>"cateId","name"=>"分类","value"=>[["n"=>"全部","v"=>"4"],["n"=>"首推","v"=>"23"],["n"=>"国产","v"=>"24"],["n"=>"海外","v"=>"25"],["n"=>"港台","v"=>"26"]]], + ["key"=>"area","name"=>"地區","value"=>[["n"=>"全部","v"=>"0"],["n"=>"国产","v"=>"1"],["n"=>"中国香港","v"=>"3"],["n"=>"中国台湾","v"=>"6"],["n"=>"美国","v"=>"5"],["n"=>"韩国","v"=>"18"],["n"=>"日本","v"=>"2"]]], + ["key"=>"year","name"=>"年代","value"=>[["n"=>"全部","v"=>"0"],["n"=>"2025","v"=>"107"],["n"=>"2024","v"=>"119"],["n"=>"2023","v"=>"153"],["n"=>"2022","v"=>"101"],["n"=>"2021","v"=>"118"],["n"=>"2020","v"=>"16"],["n"=>"2019","v"=>"7"],["n"=>"2018","v"=>"2"],["n"=>"2017","v"=>"3"],["n"=>"2016","v"=>"22"]]], + ["key"=>"sort","name"=>"排序","value"=>[["n"=>"热门","v"=>"hot"],["n"=>"评分","v"=>"rating"],["n"=>"更新","v"=>"update"]]] + ], + "67" => [ + ["key"=>"cateId","name"=>"分类","value"=>[["n"=>"全部","v"=>"67"],["n"=>"言情","v"=>"70"],["n"=>"爱情","v"=>"71"],["n"=>"战神","v"=>"72"],["n"=>"古代","v"=>"73"],["n"=>"萌娃","v"=>"74"],["n"=>"神医","v"=>"75"],["n"=>"玄幻","v"=>"76"],["n"=>"重生","v"=>"77"],["n"=>"激情","v"=>"79"],["n"=>"时尚","v"=>"82"],["n"=>"剧情演绎","v"=>"83"],["n"=>"影视","v"=>"84"],["n"=>"人文社科","v"=>"85"],["n"=>"二次元","v"=>"86"],["n"=>"明星八卦","v"=>"87"],["n"=>"随拍","v"=>"88"],["n"=>"个人管理","v"=>"89"],["n"=>"音乐","v"=>"90"],["n"=>"汽车","v"=>"91"],["n"=>"休闲","v"=>"92"],["n"=>"校园教育","v"=>"93"],["n"=>"游戏","v"=>"94"],["n"=>"科普","v"=>"95"],["n"=>"科技","v"=>"96"],["n"=>"时政社会","v"=>"97"],["n"=>"萌宠","v"=>"98"],["n"=>"体育","v"=>"99"],["n"=>"穿越","v"=>"80"],["n"=>"","v"=>"81"],["n"=>"闪婚","v"=>"112"]]], + ["key"=>"sort","name"=>"排序","value"=>[["n"=>"全部","v"=>""],["n"=>"最新","v"=>"update"],["n"=>"最热","v"=>"hot"]]] + ] + ]; + + for ($i = 0; $i < count($classNames); $i++) { + $typeId = $classUrls[$i]; + $classes[] = [ + 'type_id' => $typeId, + 'type_name' => $classNames[$i] + ]; + + if (isset($filterConfig[$typeId])) { + $filterObj[$typeId] = $filterConfig[$typeId]; + } + } + + // 获取首页推荐 (保持原有逻辑) + $homeUrl = $this->HOST . '/api/dyTag/hand_data?category_id=88'; + $homeData = json_decode($this->fetch($homeUrl, [], $this->HEADERS), true); + $list = []; + + if (isset($homeData['data']['20'])) { + foreach ($homeData['data']['20'] as $item) { + $list[] = [ + 'vod_id' => $item['id'], + 'vod_name' => $item['title'], + 'vod_pic' => $this->IMGHOST . $item['path'], + 'vod_remarks' => $item['mask'] . ' ⭐' . $item['score'] + ]; + } + } + + return [ + 'class' => $classes, + 'filters' => $filterObj, + 'list' => $list + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + // 构建请求URL + $url = $this->HOST . '/api/crumb/list?page=' . $pg . '&type=0&limit=24'; + + // 处理过滤器 + $filterUrl = 'area=' . ($filter['area'] ?? '0') . '&sort=' . ($filter['sort'] ?? 'update') . '&year=' . ($filter['year'] ?? '0') . '&category_id=' . ($filter['cateId'] ?? $tid); + $url .= '&' . $filterUrl; + + // 处理短剧特殊情况 + if ($tid == 67) { + $url = str_replace('/api/crumb/list', '/api/crumb/shortList', $url); + } + + $data = json_decode($this->fetch($url, [], $this->HEADERS), true); + $list = []; + $total = 0; + + if (isset($data['data'])) { + // 检查API响应中是否包含total信息 + if (is_array($data['data']) && count($data['data']) > 0) { + // 对于API没有直接返回total的情况,我们假设总共有大量数据 + // 这里使用一个较大的值来确保分页正常工作 + $total = 1000; + + foreach ($data['data'] as $item) { + $isShort = $tid == 67; + $imgUrl = $this->IMGHOST . ($isShort ? ($item['cover_image'] ?? $item['path']) : ($item['thumbnail'] ?? $item['path'])); + + // 短剧需要在vod_id中附加类型信息,以便detailContent方法识别 + $vodId = $isShort ? ($item['id'] . '@67') : $item['id']; + + $list[] = [ + 'vod_id' => $vodId, + 'vod_name' => $item['title'], + 'vod_pic' => $imgUrl, + 'vod_remarks' => ($item['mask'] ?? '') . ' ⭐' . ($item['score'] ?? '0') + ]; + } + } + } + + return $this->pageResult($list, $pg, $total, 24); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $tid = ''; + if (strpos($id, '@') !== false) { + list($id, $tid) = explode('@', $id); + } + + $isShort = $tid == 67; + $detailPath = $isShort ? '/api/detail?vid=' . $id : '/api/video/detailv2?id=' . $id; + + $detailUrl = $this->HOST . $detailPath; + $data = json_decode($this->fetch($detailUrl, [], $this->HEADERS), true); + $item = $data['data']; + + $playFrom = []; + $playUrls = []; + + if ($isShort) { + // 短剧可能有不同的数据结构,尝试多种方式获取播放列表 + $playlist = []; + + // 尝试从playlist字段获取 + if (isset($item['playlist']) && is_array($item['playlist'])) { + $playlist = $item['playlist']; + } + // 尝试从video_list字段获取 + elseif (isset($item['video_list']) && is_array($item['video_list'])) { + $playlist = $item['video_list']; + } + // 尝试从episodes字段获取 + elseif (isset($item['episodes']) && is_array($item['episodes'])) { + $playlist = $item['episodes']; + } + + if (count($playlist) > 0) { + $playFrom[] = '短剧'; + $urls = []; + foreach ($playlist as $ep) { + // 处理不同的数据结构 + $title = $ep['title'] ?? $ep['episode_title'] ?? ($ep['episode'] ?? '第1集'); + $url = $ep['url'] ?? $ep['video_url'] ?? $ep['play_url'] ?? ''; + + // 过滤无效地址和ftp协议 + if (!empty($url) && stripos($url, 'ftp://') !== 0) { + $urls[] = $title . '$' . $url; + } + } + if (!empty($urls)) { + $playUrls[] = implode('#', $urls); + } + } else { + // 尝试直接从item中获取单个播放地址(针对单集短剧) + $url = $item['url'] ?? $item['video_url'] ?? $item['play_url'] ?? ''; + if (!empty($url) && stripos($url, 'ftp://') !== 0) { + $playFrom[] = '短剧'; + $playUrls[] = '全集$' . $url; + } + } + } else { + if (isset($item['source_list_source'])) { + foreach ($item['source_list_source'] as $src) { + $name = $src['name'] == '常规线路' ? '边下边播线路' : $src['name']; + + $urls = []; + foreach ($src['source_list'] as $ep) { + $url = $ep['url']; + // 过滤ftp协议的地址,只保留http/https协议 + if (stripos($url, 'ftp://') === 0) { + continue; + } + $urls[] = ($ep['source_name'] ?? $ep['weight']) . '$' . $url; + } + if (!empty($urls)) { + $playFrom[] = $name; + $playUrls[] = implode('#', $urls); + } + } + } + + // 尝试从source_list字段获取(备用方案) + if (empty($playUrls) && isset($item['source_list'])) { + foreach ($item['source_list'] as $src) { + $name = $src['name'] ?? '默认线路'; + + $urls = []; + foreach ($src['source'] as $ep) { + $url = $ep['url']; + if (stripos($url, 'ftp://') !== 0) { + $urls[] = ($ep['name'] ?? $ep['title']) . '$' . $url; + } + } + if (!empty($urls)) { + $playFrom[] = $name; + $playUrls[] = implode('#', $urls); + } + } + } + } + + return [ + 'list' => [[ + 'vod_id' => $id, + 'vod_name' => $item['title'], + 'vod_pic' => $this->IMGHOST . ($isShort ? ($item['cover_image'] ?? $item['path']) : ($item['thumbnail'] ?? $item['path'])), + 'vod_year' => $item['year'] ?? '', + 'vod_area' => $item['area'] ?? '', + 'vod_remarks' => $item['update_cycle'] ?? $item['mask'] ?? '', + 'vod_actor' => implode('/', array_column($item['actors'] ?? [], 'name')), + 'vod_director' => implode('/', array_column($item['directors'] ?? [], 'name')), + 'vod_content' => $item['description'] ?? '', + 'vod_play_from' => implode('$$$', $playFrom), + 'vod_play_url' => implode('$$$', $playUrls) + ]] + ]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $searchUrl = $this->HOST . '/api/v2/search/videoV2?key=' . urlencode($key) . '&page=' . $pg; + $data = json_decode($this->fetch($searchUrl, [], $this->HEADERS), true); + $list = []; + $total = 0; + + if (isset($data['data'])) { + if (is_array($data['data']) && count($data['data']) > 0) { + // 对于搜索结果,同样使用较大值确保分页正常 + $total = 1000; + + foreach ($data['data'] as $item) { + // 检查是否为短剧,根据category_id判断 + $isShort = isset($item['category_id']) && $item['category_id'] == 67; + $vodId = $isShort ? ($item['id'] . '@67') : $item['id']; + + $list[] = [ + 'vod_id' => $vodId, + 'vod_name' => $item['title'], + 'vod_pic' => $this->IMGHOST . $item['thumbnail'], + 'vod_remarks' => $item['mask'] . ' ⭐' . $item['score'] + ]; + } + } + } + + return $this->pageResult($list, $pg, $total, 20); + } + + public function playerContent($flag, $id, $vipFlags = []) { + try { + // 优化视频播放,使用更高效的处理方式 + // 检查是否为m3u8格式(流媒体格式) + if (stripos($id, '.m3u8') !== false) { + return [ + 'parse' => 0, + 'url' => $id, + 'header' => [ + 'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', + 'Referer' => $id, + 'Accept' => '*/*', + 'Connection' => 'keep-alive', + 'Origin' => parse_url($id, PHP_URL_SCHEME) . '://' . parse_url($id, PHP_URL_HOST) + ] + ]; + } + + // 检查是否为mp4等直接视频格式 + $videoExtensions = ['mp4', 'flv', 'avi', 'wmv', 'mov', 'webm']; + $path = parse_url($id, PHP_URL_PATH) ?? ''; + $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + + if (in_array($extension, $videoExtensions)) { + return [ + 'parse' => 0, + 'url' => $id, + 'header' => [ + 'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', + 'Referer' => $id, + 'Range' => 'bytes=0-', // 支持断点续传 + 'Accept-Ranges' => 'bytes' + ] + ]; + } + + // 默认返回原始地址 + return [ + 'parse' => 0, + 'url' => $id, + 'header' => [ + 'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', + 'Referer' => $id + ] + ]; + } catch (Exception $e) { + // 发生错误时返回原始地址 + return [ + 'parse' => 0, + 'url' => $id, + 'header' => ['User-Agent' => 'Mozilla/5.0'] + ]; + } + } +} + +(new Spider())->run(); \ No newline at end of file diff --git "a/spider/php/\351\205\267\347\210\261\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" "b/spider/php/\351\205\267\347\210\261\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" new file mode 100644 index 00000000..851099ce --- /dev/null +++ "b/spider/php/\351\205\267\347\210\261\346\274\253\347\224\273 \341\265\210\341\266\273[\347\224\273].php" @@ -0,0 +1,276 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + + public function getName() { + return "酷爱漫画"; + } + + public function init($extend = "") { + // pass + } + + public function isVideoFormat($url) { + return false; + } + + public function manualVideoCheck() { + return false; + } + + private function getHeader() { + return [ + "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.203 Safari/537.36", + "Referer" => "https://www.kuimh.com/" + ]; + } + + public function homeContent($filter) { + $classes = [ + ["type_name" => "国产", "type_id" => "1"], + ["type_name" => "日本", "type_id" => "2"], + ["type_name" => "韩国", "type_id" => "3"], + ["type_name" => "欧美", "type_id" => "5"], + ["type_name" => "其他", "type_id" => "7"], + ["type_name" => "日韩", "type_id" => "8"] + ]; + + $tags = ["全部", "恋爱", "古风", "校园", "奇幻", "大女主", "治愈", "穿越", "励志", "爆笑", "萌系", "玄幻", "日常", "都市", "彩虹", "灵异", "悬疑", "少年"]; + $tagValues = []; + foreach ($tags as $t) { + $tagValues[] = ["n" => $t, "v" => $t]; + } + $filterConfig = [ + "key" => "tag", + "name" => "题材", + "value" => $tagValues + ]; + + $statusConfig = [ + "key" => "end", + "name" => "状态", + "value" => [ + ["n" => "全部", "v" => "-1"], + ["n" => "连载", "v" => "0"], + ["n" => "完结", "v" => "1"] + ] + ]; + + $filters = []; + foreach ($classes as $c) { + $filters[$c['type_id']] = [$filterConfig, $statusConfig]; + } + + return ["class" => $classes, "filters" => $filters]; + } + + public function homeVideoContent() { + return $this->categoryContent("1", 1, [], []); + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $tag = urlencode($extend['tag'] ?? '全部'); + $end = $extend['end'] ?? '-1'; + $url = "https://www.kuimh.com/booklist?tag={$tag}&area={$tid}&end={$end}&page={$pg}"; + + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + $items = $this->pdfa($html, '.mh-item'); + + $videos = []; + foreach ($items as $item) { + $vid = $this->pd($item, 'a&&href'); + $style = $this->pd($item, 'p&&style'); + $cover = ""; + if (preg_match('/url\((.*?)\)/', $style, $matches)) { + $cover = $matches[1]; + } + + // 尝试提取名称,Python逻辑是取第二个a标签,这里简化 + $name = $this->pd($item, 'a:eq(1)&&Text'); + if (!$name) { + $name = $this->pd($item, '.title a&&Text'); + } + if (!$name) { + $name = $this->pd($item, 'a&&title'); + } + if (!$name) { + $name = $this->pd($item, 'Text'); + } + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => trim($name), + "vod_pic" => $cover, + "vod_remarks" => "" + ]; + } + + return [ + "list" => $videos, + "page" => $pg, + "pagecount" => 9999, + "limit" => 30, + "total" => 999999 + ]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + public function detailContent($ids) { + $vid = $ids[0]; + $url = (strpos($vid, 'http') === 0) ? $vid : "https://www.kuimh.com{$vid}"; + + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + + $name = $this->pd($html, '.info h1&&Text'); + $cover = $this->pd($html, '.cover img&&src'); + $desc = $this->pd($html, '.content p&&Text'); + + $chapterList = $this->pdfa($html, '.mCustomScrollBox li a'); + if (empty($chapterList)) { + $chapterList = $this->pdfa($html, '#detail-list-select li a'); + } + + $vodPlayUrlList = []; + foreach ($chapterList as $chapter) { + $chapterName = $this->pd($chapter, 'a&&Text'); + $chapterHref = $this->pd($chapter, 'a&&href'); + + if (!$chapterHref) continue; + + $vodPlayUrlList[] = "{$chapterName}\${$chapterHref}"; + } + + $playUrlStr = implode("#", $vodPlayUrlList); + + return [ + "list" => [[ + "vod_id" => $vid, + "vod_name" => $name, + "vod_pic" => $cover, + "type_name" => "漫画", + "vod_year" => "", + "vod_area" => "", + "vod_remarks" => "", + "vod_actor" => "", + "vod_director" => "", + "vod_content" => $desc, + "vod_play_from" => '阅读', + "vod_play_url" => $playUrlStr + ]] + ]; + } catch (Exception $e) { + return ["list" => []]; + } + } + + public function searchContent($key, $quick = false, $pg = 1) { + $key = urlencode($key); + $url = "https://www.kuimh.com/search?keyword={$key}&page={$pg}"; + + try { + $html = $this->fetch($url, ['headers' => $this->getHeader()]); + $items = $this->pdfa($html, '.mh-item'); + + $videos = []; + foreach ($items as $item) { + $vid = $this->pd($item, 'a&&href'); + $style = $this->pd($item, 'p&&style'); + $cover = ""; + if (preg_match('/url\((.*?)\)/', $style, $matches)) { + $cover = $matches[1]; + } + + $name = $this->pd($item, '.title a&&title'); + if (!$name) $name = $this->pd($item, 'a&&title'); + if (!$name) $name = $this->pd($item, 'Text'); + + $videos[] = [ + "vod_id" => $vid, + "vod_name" => trim($name), + "vod_pic" => $cover, + "vod_remarks" => "" + ]; + } + return ['list' => $videos]; + } catch (Exception $e) { + return ['list' => []]; + } + } + + public function playerContent($flag, $id, $vipFlags = []) { + $url = (strpos($id, 'http') === 0) ? $id : "https://www.kuimh.com{$id}"; + $headers = $this->getHeader(); + $headers['Referer'] = $url; + + try { + $html = $this->fetch($url, ['headers' => $headers]); + + $imageList = []; + + // 1. DOM 解析 + $imgs = $this->pdfa($html, '.comicpage img'); + if (empty($imgs)) { + $imgs = $this->pdfa($html, '.comiclist img'); + } + + foreach ($imgs as $img) { + $src = $this->pd($img, 'data-echo'); + if (!$src) $src = $this->pd($img, 'data-src'); + if (!$src) $src = $this->pd($img, 'data-original'); + if (!$src) $src = $this->pd($img, 'src'); + + if ($src) $imageList[] = $src; + } + + // 2. data-echo 全局查找 + if (empty($imageList)) { + $allLazyImgs = $this->pdfa($html, 'img[data-echo]'); + foreach ($allLazyImgs as $img) { + $src = $this->pd($img, 'data-echo'); + if ($src && !in_array($src, $imageList)) { + $imageList[] = $src; + } + } + } + + // 3. 正则兜底 + if (empty($imageList)) { + if (preg_match_all('/(https?:\/\/[^"\'\\\\]+\.(?:jpg|png|jpeg|webp))/', $html, $matches)) { + foreach ($matches[1] as $m) { + $imageList[] = $m; + } + } + } + + // 4. 过滤与去重 + $uniqueImages = []; + foreach ($imageList as $i) { + if (in_array($i, $uniqueImages)) continue; + if (strpos($i, "grey.gif") !== false) continue; + if (strpos($i, "logo") !== false) continue; + if (strpos($i, "icon") !== false) continue; + if (strpos($i, "tu.petatt.cn") !== false) continue; + + $uniqueImages[] = $i; + } + + $novelData = implode("&&", $uniqueImages); + + return [ + "parse" => 0, + "playUrl" => "", + "url" => "pics://{$novelData}", + "header" => "" + ]; + } catch (Exception $e) { + return ["parse" => 0, "url" => "", "header" => ""]; + } + } +} + +(new Spider())->run(); diff --git "a/spider/php/\351\207\221\347\211\214 \341\265\210\341\266\273.php" "b/spider/php/\351\207\221\347\211\214 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..f8e77111 --- /dev/null +++ "b/spider/php/\351\207\221\347\211\214 \341\265\210\341\266\273.php" @@ -0,0 +1,228 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = 'https://m.jiabaide.cn'; + private $UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'; + + /** + * 核心签名算法:sha1(md5(query_string)) + */ + private function getSignedHeaders($params) { + $t = (string)(time() * 1000); // 毫秒时间戳 + $params['key'] = 'cb808529bae6b6be45ecfab29a4889bc'; + $params['t'] = $t; + + // 构建 QueryString + $query = []; + foreach ($params as $k => $v) { + $query[] = "$k=$v"; + } + $queryStr = implode('&', $query); + + // 签名逻辑:SHA1(MD5(str)) + $sign = sha1(md5($queryStr)); + + return [ + 'User-Agent: ' . $this->UA, + 'Referer: ' . $this->HOST, + 't: ' . $t, + 'sign: ' . $sign + ]; + } + + public function homeContent($filter) { + // 5. 首页 (获取分类与筛选) + $typeUrl = $this->HOST . '/api/mw-movie/anonymous/get/filer/type'; + $typeRes = $this->fetch($typeUrl, [], $this->getSignedHeaders([])); + $typeArr = json_decode($typeRes, true)['data'] ?? []; + + $classes = []; + foreach ($typeArr as $item) { + $classes[] = ['type_id' => (string)$item['typeId'], 'type_name' => $item['typeName']]; + } + + // 获取筛选 + $filterUrl = $this->HOST . '/api/mw-movie/anonymous/v1/get/filer/list'; + $filterRes = $this->fetch($filterUrl, [], $this->getSignedHeaders([])); + $filterData = json_decode($filterRes, true)['data'] ?? []; + + $filters = []; + $nameMap = [ + 'typeList' => ['key' => 'type', 'name' => '类型'], + 'plotList' => ['key' => 'class', 'name' => '剧情'], + 'districtList' => ['key' => 'area', 'name' => '地区'], + 'languageList' => ['key' => 'lang', 'name' => '语言'], + 'yearList' => ['key' => 'year', 'name' => '年份'] + ]; + + foreach ($classes as $cls) { + $tid = $cls['type_id']; + $fRow = []; + foreach ($nameMap as $apiKey => $cfg) { + if (!isset($filterData[$tid][$apiKey])) continue; + $values = [['n' => '全部', 'v' => '']]; + foreach ($filterData[$tid][$apiKey] as $v) { + $values[] = [ + 'n' => $v['itemText'], + 'v' => ($apiKey === 'typeList') ? $v['itemValue'] : $v['itemText'] + ]; + } + $fRow[] = ['key' => $cfg['key'], 'name' => $cfg['name'], 'value' => $values]; + } + // 增加排序 + $fRow[] = [ + 'key' => 'by', 'name' => '排序', + 'value' => [ + ['n' => '最近更新', 'v' => '1'], + ['n' => '添加时间', 'v' => '2'], + ['n' => '人气高低', 'v' => '3'], + ['n' => '评分高低', 'v' => '4'] + ] + ]; + $filters[$tid] = $fRow; + } + + // 首页推荐 + $hotUrl = $this->HOST . '/api/mw-movie/anonymous/home/hotSearch'; + $hotRes = $this->fetch($hotUrl, [], $this->getSignedHeaders([])); + $hotVods = json_decode($hotRes, true)['data'] ?? []; + $list = []; + foreach (array_slice($hotVods, 0, 20) as $it) { + $list[] = [ + 'vod_id' => $it['vodId'], + 'vod_name' => $it['vodName'], + 'vod_pic' => $it['vodPic'], + 'vod_remarks' => $it['vodRemarks'] + ]; + } + + return [ + 'class' => $classes, + 'filters' => $filters, + 'list' => $list + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $params = [ + 'area' => $extend['area'] ?? '', + 'lang' => $extend['lang'] ?? '', + 'pageNum' => $pg, + 'pageSize' => '30', + 'sort' => $extend['by'] ?? '1', + 'sortBy' => '1', + 'type' => $extend['type'] ?? '', + 'type1' => $tid, + 'v_class' => $extend['class'] ?? '', + 'year' => $extend['year'] ?? '', + ]; + + $apiUrl = $this->HOST . '/api/mw-movie/anonymous/video/list?' . http_build_query($params); + $res = $this->fetch($apiUrl, [], $this->getSignedHeaders($params)); + $json = json_decode($res, true); + + $list = []; + if (isset($json['data']['list'])) { + foreach ($json['data']['list'] as $it) { + $list[] = [ + 'vod_id' => $it['vodId'], + 'vod_name' => $it['vodName'], + 'vod_pic' => $it['vodPic'], + 'vod_remarks' => $it['vodRemarks'] . '_' . $it['vodDoubanScore'] + ]; + } + } + + $total = $json['data']['total'] ?? 0; + return $this->pageResult($list, $pg, $total, 30); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $params = ['id' => $id]; + $apiUrl = $this->HOST . '/api/mw-movie/anonymous/video/detail?' . http_build_query($params); + $res = $this->fetch($apiUrl, [], $this->getSignedHeaders($params)); + $json = json_decode($res, true); + $kvod = $json['data'] ?? null; + + if (!$kvod) { + return ['list' => []]; + } + + $episodes = []; + if (!empty($kvod['episodeList'])) { + foreach ($kvod['episodeList'] as $it) { + // 存入格式:名字$ID@NID + $episodes[] = $it['name'] . '$' . $kvod['vodId'] . '@' . $it['nid']; + } + } + + $vod = [ + 'vod_id' => $kvod['vodId'], + 'vod_name' => $kvod['vodName'], + 'vod_pic' => $kvod['vodPic'], + 'type_name' => $kvod['vodClass'], + 'vod_remarks' => $kvod['vodRemarks'], + 'vod_content' => trim(strip_tags($kvod['vodContent'] ?? '')), + 'vod_play_from' => '金牌线路', + 'vod_play_url' => implode('#', $episodes) + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $page = max(1, intval($pg)); + $params = [ + 'keyword' => $key, + 'pageNum' => $pg, + 'pageSize' => '30' + ]; + $apiUrl = $this->HOST . '/api/mw-movie/anonymous/video/searchByWordPageable?' . http_build_query($params); + $res = $this->fetch($apiUrl, [], $this->getSignedHeaders($params)); + $json = json_decode($res, true); + + $list = []; + if (isset($json['data']['list'])) { + foreach ($json['data']['list'] as $it) { + $list[] = [ + 'vod_id' => $it['vodId'], + 'vod_name' => $it['vodName'], + 'vod_pic' => $it['vodPic'], + 'vod_remarks' => $it['vodRemarks'] + ]; + } + } + + $total = $json['data']['total'] ?? 0; + return $this->pageResult($list, $pg, $total, 30); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // 格式: vodId@nid + list($sid, $nid) = explode('@', $id); + $params = [ + 'clientType' => '3', + 'id' => $sid, + 'nid' => $nid + ]; + $apiUrl = $this->HOST . '/api/mw-movie/anonymous/v2/video/episode/url?' . http_build_query($params); + $res = $this->fetch($apiUrl, [], $this->getSignedHeaders($params)); + $json = json_decode($res, true); + + $playUrl = ""; + if (!empty($json['data']['list'])) { + // 取第一个清晰度的 URL + $playUrl = $json['data']['list'][0]['url']; + } + + return [ + 'parse' => 0, + 'url' => $playUrl, + 'header' => ['User-Agent' => $this->UA] + ]; + } +} + +(new Spider())->run(); diff --git "a/spider/php/\351\230\205\350\257\273\345\212\251\346\211\213 \341\265\210\341\266\273[\344\271\246].php" "b/spider/php/\351\230\205\350\257\273\345\212\251\346\211\213 \341\265\210\341\266\273[\344\271\246].php" new file mode 100644 index 00000000..556845d8 --- /dev/null +++ "b/spider/php/\351\230\205\350\257\273\345\212\251\346\211\213 \341\265\210\341\266\273[\344\271\246].php" @@ -0,0 +1,324 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + + private const AES_KEY = '242ccb8230d709e1'; + private const SIGN_KEY = 'd3dGiJc651gSQ8w1'; + private const APP_ID = 'com.kmxs.reader'; + + private const BASE_HEADERS = [ + "app-version" => "51110", + "platform" => "android", + "reg" => "0", + "AUTHORIZATION" => "", + "application-id" => self::APP_ID, + "net-env" => "1", + "channel" => "unknown", + "qm-params" => "" + ]; + + public function init($extend = "") { + parent::init($extend); + } + + public function getName() { + return "阅读助手"; + } + + private function getSign($params) { + ksort($params); + $signStr = ""; + foreach ($params as $k => $v) { + $signStr .= "{$k}={$v}"; + } + $signStr .= self::SIGN_KEY; + return md5($signStr); + } + + private function getHeaders($params) { + $headers = self::BASE_HEADERS; + $headers['sign'] = $this->getSign($params); + return $headers; + } + + private function getApiUrl($path, &$params, $domainType = "bc") { + $baseUrl = ($domainType == "bc") ? "https://api-bc.wtzw.com" : "https://api-ks.wtzw.com"; + if (strpos($path, "search") !== false) { + $baseUrl = "https://api-bc.wtzw.com"; + } + + // PHP headers logic is separate from URL params in fetch + // But the sign is calculated on params. + // And requests in Python sends params in query string. + // So we need to construct URL with query string. + // Also sign must be in headers. + + // Wait, Python code: + // params['sign'] = self.get_sign(params) -> This adds sign to params! + // headers['sign'] = self.get_sign(headers) -> This adds sign to headers (based on headers)! + + // Let's re-read Python code carefully. + /* + def get_sign(self, params): + # sorts params and md5 + + def get_headers(self): + headers = self.BASE_HEADERS.copy() + headers['sign'] = self.get_sign(headers) <-- Sign of HEADERS + return headers + + def get_api_url(self, path, params, domain_type="bc"): + params['sign'] = self.get_sign(params) <-- Sign of PARAMS + ... + return url, params + */ + + // So we have TWO signatures: one in params (signing params) and one in headers (signing headers). + + // Params signing + $params['sign'] = $this->getSign($params); + + // Build query string + $queryString = http_build_query($params); + + return "{$baseUrl}{$path}?{$queryString}"; + } + + private function getRequestHeaders() { + // Headers signing + $headers = self::BASE_HEADERS; + $headers['sign'] = $this->getSign($headers); + $headers['User-Agent'] = "okhttp/3.12.1"; + + // Format for fetch + // fetch expects array Key => Value + return $headers; + } + + private function decryptContent($base64Content) { + try { + $encryptedBytes = base64_decode($base64Content); + if (strlen($encryptedBytes) < 16) { + return "数据长度不足"; + } + + $iv = substr($encryptedBytes, 0, 16); + $ciphertext = substr($encryptedBytes, 16); + + // aes-128-cbc + $decrypted = openssl_decrypt($ciphertext, 'aes-128-cbc', self::AES_KEY, OPENSSL_RAW_DATA, $iv); + + if ($decrypted === false) { + return "解密失败"; + } + + return trim($decrypted); + } catch (Exception $e) { + return "解密错误: " . $e->getMessage(); + } + } + + public function homeContent($filter = []) { + $cats = [ + ["type_name" => "玄幻奇幻", "type_id" => "1|202"], + ["type_name" => "都市人生", "type_id" => "1|203"], + ["type_name" => "武侠仙侠", "type_id" => "1|205"], + ["type_name" => "历史军事", "type_id" => "1|56"], + ["type_name" => "科幻末世", "type_id" => "1|64"], + ["type_name" => "游戏竞技", "type_id" => "1|75"], + ["type_name" => "现代言情", "type_id" => "2|1"], + ["type_name" => "古代言情", "type_id" => "2|2"], + ["type_name" => "幻想言情", "type_id" => "2|4"], + ["type_name" => "婚恋情感", "type_id" => "2|6"], + ["type_name" => "悬疑推理", "type_id" => "3|262"] + ]; + return ['class' => $cats, 'filters' => (object)[]]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $parts = explode("|", $tid); + $gender = $parts[0] ?? "1"; + $catId = $parts[1] ?? "202"; + + $params = [ + 'gender' => $gender, + 'category_id' => $catId, + 'need_filters' => '1', + 'page' => $pg, + 'need_category' => '1' + ]; + + $url = $this->getApiUrl("/api/v4/category/get-list", $params, "bc"); + $headers = $this->getRequestHeaders(); + + try { + $json = $this->fetchJson($url, ['headers' => $headers]); + + $bookList = []; + if (isset($json['data']['books'])) { + $bookList = $json['data']['books']; + } elseif (isset($json['books'])) { + $bookList = $json['books']; + } + + $videos = []; + foreach ($bookList as $book) { + $pic = $book['image_link'] ?? ''; + if (strpos($pic, 'http://') === 0) { + $pic = str_replace('http://', 'https://', $pic); + } + + $videos[] = [ + "vod_id" => (string)($book['id'] ?? ''), + "vod_name" => $book['title'] ?? '', + "vod_pic" => $pic, + "vod_remarks" => $book['author'] ?? '' + ]; + } + + return ['list' => $videos, 'page' => $pg, 'pagecount' => 999, 'limit' => 20, 'total' => 9999]; + + } catch (Exception $e) { + return ['list' => []]; + } + } + + public function detailContent($ids) { + $bid = $ids[0]; + $headers = $this->getRequestHeaders(); + + $detailParams = ['id' => $bid, 'imei_ip' => '2937357107', 'teeny_mode' => '0']; + $detailUrl = $this->getApiUrl("/api/v4/book/detail", $detailParams, "bc"); + + $vod = ["vod_id" => $bid, "vod_name" => "获取中...", "vod_play_from" => "阅读助手"]; + + try { + $json = $this->fetchJson($detailUrl, ['headers' => $headers]); + + if (isset($json['data']['book'])) { + $bookInfo = $json['data']['book']; + $vod["vod_name"] = $bookInfo['title'] ?? ''; + + $pic = $bookInfo['image_link'] ?? ''; + if (strpos($pic, 'http://') === 0) { + $pic = str_replace('http://', 'https://', $pic); + } + $vod["vod_pic"] = $pic; + + $vod["type_name"] = $bookInfo['category_name'] ?? ''; + $vod["vod_remarks"] = ($bookInfo['words_num'] ?? '') . "字"; + $vod["vod_actor"] = $bookInfo['author'] ?? ''; + $vod["vod_content"] = $bookInfo['intro'] ?? ''; + } + + // Get Chapters + $chapterParams = ['id' => $bid]; + $chapterUrl = $this->getApiUrl("/api/v1/chapter/chapter-list", $chapterParams, "ks"); + + $jsonC = $this->fetchJson($chapterUrl, ['headers' => $headers]); + + $lists = []; + if (isset($jsonC['data']['chapter_lists'])) { + $lists = $jsonC['data']['chapter_lists']; + } + + $chapterList = []; + foreach ($lists as $item) { + $cid = (string)$item['id']; + $cname = str_replace(["@@", "$"], ["-", ""], $item['title']); + $urlCode = "{$bid}@@{$cid}@@{$cname}"; + $chapterList[] = "{$cname}\${$urlCode}"; + } + + $vod['vod_play_url'] = implode("#", $chapterList); + return ["list" => [$vod]]; + + } catch (Exception $e) { + $vod["vod_content"] = "Error: " . $e->getMessage(); + return ["list" => [$vod]]; + } + } + + public function searchContent($key, $quick = false, $pg = 1) { + $params = ['gender' => '3', 'imei_ip' => '2937357107', 'page' => $pg, 'wd' => $key]; + $url = $this->getApiUrl("/api/v5/search/words", $params, "bc"); + $headers = $this->getRequestHeaders(); + + try { + $json = $this->fetchJson($url, ['headers' => $headers]); + + $videos = []; + if (isset($json['data']['books'])) { + foreach ($json['data']['books'] as $book) { + $videos[] = [ + "vod_id" => (string)$book['id'], + "vod_name" => $book['original_title'], + "vod_pic" => $book['image_link'], + "vod_remarks" => $book['original_author'] + ]; + } + } + return ['list' => $videos, 'page' => $pg]; + } catch (Exception $e) { + return ['list' => [], 'page' => $pg]; + } + } + + public function playerContent($flag, $id, $vipFlags = []) { + try { + $parts = explode("@@", $id); + $bid = $parts[0]; + $cid = $parts[1]; + $title = isset($parts[2]) ? $parts[2] : ""; + + $params = ['id' => $bid, 'chapterId' => $cid]; + $url = $this->getApiUrl("/api/v1/chapter/content", $params, "ks"); + $headers = $this->getRequestHeaders(); + + $json = $this->fetchJson($url, ['headers' => $headers]); + + $content = ""; + if (isset($json['data']['content'])) { + if (!$title && isset($json['data']['title'])) { + $title = $json['data']['title']; + } + $content = $this->decryptContent($json['data']['content']); + } else { + $msg = $json['msg'] ?? '未知错误'; + $content = "加载失败: {$msg}"; + } + + if (!$title) $title = "章节正文"; + + $resultData = [ + 'title' => $title, + 'content' => $content + ]; + + $ret = json_encode($resultData, JSON_UNESCAPED_UNICODE); + $finalUrl = "novel://{$ret}"; + + return [ + "parse" => 0, + "playUrl" => "", + "url" => $finalUrl, + "header" => "" + ]; + + } catch (Exception $e) { + $errData = [ + 'title' => "错误", + 'content' => "发生异常: " . $e->getMessage() + ]; + return [ + "parse" => 0, + "playUrl" => "", + "url" => "novel://" . json_encode($errData, JSON_UNESCAPED_UNICODE), + "header" => "" + ]; + } + } +} + +(new Spider())->run(); diff --git "a/spider/php/\351\233\266\345\272\246\345\275\261\350\247\206 \341\265\210\341\266\273.php" "b/spider/php/\351\233\266\345\272\246\345\275\261\350\247\206 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..ca733ef6 --- /dev/null +++ "b/spider/php/\351\233\266\345\272\246\345\275\261\350\247\206 \341\265\210\341\266\273.php" @@ -0,0 +1,384 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $host = 'http://ldys.sq1005.top'; + private $publicKey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoYt0BP77U+DM08BiI/QbSRIfxijXo85BTPqIM1Ow8BNwhLETzRIZ+dEwdWDbydG/PspgBAfRpGaYVdJYtvaC2JnoO8+Ik6qMWojfEJxSFLa0Pb0A892tun4gsxoEMjcreZ+YGyaBxAfqX0BSMfdrOgIYaZQjYrw9TRLlUT31QoQIDAQAB\n-----END PUBLIC KEY-----"; + private $privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCquQQ5r6+yJI8CDFkXRp8vUsdD45ov8EP12ooLs56ca2DQXaSNGS9910bAPVA9chkp0mKIvKqjAsHz5Tl9EeNPblarGEeJUIxpxZtiSqNTpvtiD/TjhpzuHYic7RAfQ/h7p/ypE8ymU42pYjsB5t26Mv6XgkLV+jzrSf73HlCuS0iMyLmt6zz3Mw9izM13EpB8iFLtfbbYymycKTx4RAmPQLwhNGex/AlUIYxXP4R2yyaa4W6mEtc6aME2QuzJFxPgP3HJ9NBx/LWVn4skxWjZ7zg+VRQRHnjyVaSLu3Z5gN5ITWCyE32qaHJa6WBahZj5jWhRyAG1bQ+xKJa8lBL5AgMBAAECggEAUwv9SjJ0PSwbhNuM2w23kcWquROWhYtTA91zGY4esehqB/IFgb2mpIh8Gje5OKqwIu/8jpd4SiOlRYdUF8sD0DfUYRZGdj2AkFNX6tBz8tVfo6wvbB6naA1lzzBij1L5JO3qsjS3cJFkb+kg2yP66AC2Z+0tpfk8eRhdtshAZwfcd1DEGt1uAvYL1eaUK9HRvpt9lPeGcHERDl2hBd4uyaF0K1O+zF9y59nYbTySWPxRZq3sFEE85xRMlstD7YZi7W2gKvMFRD4/FKmrZ3m7aKJRITtyKOyyPcYmepNv3Qv7kk59Pg38n2WWQ0Ra/bCH3E48YNCnQvZMpitkTfJhoQKBgQDbnROOYTP8OTJ6f/qhoGjxeO3x1VOaOp8l0x7b0SCfoqNGS0Cyiqj72BmJtPMPqSTjn6MmNzqbg1KOdhXyzNozs+i5ccW1M56j96mr5I/Z0FpE3oyIHNfDDBlf9M8YQqEF9oYxniYYft9oapO7cRQkHER6qpvnHTavwlv4m78CXwKBgQDHAjs2YlpKDdI1lcbZJCc7TwtH+Pd2bUki8YXafWNcPhITQHbOZjr310eK1QJC6GJncjkOqbX7yv3ivvTO35FZTQhuA1xEG1P00FG8bE0tHYPIwQHi9y0eA5cieMdo8E6XYria1mw/3fqSQEsfZyJlR32JQIoGAipM8iO1X2nZpwKBgDkMFIhnt5lNQk+P7wsNIDWZtDWdtJnboHuy29E+Abt2A/O+mI/IdRz2hau/1WO8DFkUnszOi+rZshhPlGP90rCbi1igtTrcrdjp/KkqNjPea5R4OwkgdOu1uOG0NheXNzzVTQaWjk7Opjn5dWa7eP/oV+GFb/oZHJuLYVizHGsBAoGADA7rjZEKDYCm4w5PPSr+oY5ZjaPdQrS+gLqHtMRyN82fBMGcMUdqfUfzEstzVqCEDeaS5HuOBlK3bXzKkppjUTjksN3NQmcxgBz7RuJ9DqXCLXDcb2cwuafYCYOt+YLOEEgwDVm+t2P44dG5e46hO+fICH/7nP+WlpD5buz4GfMCgYB57r3g/6hi9WUDnfc7ZAzWMqR0EhJVYKYy+KFEtdIPzhkkIHq5RASe88E9kzoGoZFdb3tIjvGZWcHerirrqWkMsuQtP/Qi0zjieid5tAPj+r4kbiCVTw0E0jnmPBzGInQi7lpeTTKnG1fbyS5lBS+WmHfIuzpECgCkxhaT+LJJkg==\n-----END PRIVATE KEY-----"; + + private $deviceId = ''; + private $token = ''; + + protected function getHeaders() { + if (empty($this->deviceId)) { + $this->deviceId = $this->generateDid(); + } + if (empty($this->token)) { + $this->token = $this->getToken(); + } + + return [ + 'User-Agent' => 'okhttp/4.12.0', + 'client' => 'app', + 'deviceType' => 'Android', + 'deviceId' => $this->deviceId, + 'token' => $this->token, + 'Content-Type' => 'application/json' + ]; + } + + private function generateDid() { + $hex = '0123456789abcdef'; + $did = ''; + for ($i = 0; $i < 16; $i++) { + $did .= $hex[mt_rand(0, 15)]; + } + return $did; + } + + private function getToken() { + $url = $this->host . '/api/v1/app/user/visitorInfo'; + $headers = [ + 'User-Agent' => 'okhttp/4.12.0', + 'client' => 'app', + 'deviceType' => 'Android', + 'deviceId' => $this->deviceId + ]; + + $jsonStr = $this->fetch($url, [], $headers); + $json = json_decode($jsonStr, true); + + if (isset($json['code']) && $json['code'] === 200 && isset($json['data']['token'])) { + return $json['data']['token']; + } + return ''; + } + + private function rsaEncrypt($data) { + if (openssl_public_encrypt($data, $encrypted, $this->publicKey, OPENSSL_PKCS1_PADDING)) { + return base64_encode($encrypted); + } + return ''; + } + + private function rsaDecrypt($data) { + $decoded = base64_decode($data); + + $keyRes = openssl_pkey_get_private($this->privateKey); + $details = openssl_pkey_get_details($keyRes); + $keySize = ceil($details['bits'] / 8); // 128 for 1024 bit + + $result = ''; + $chunks = str_split($decoded, $keySize); + + foreach ($chunks as $chunk) { + if (openssl_private_decrypt($chunk, $decrypted, $this->privateKey, OPENSSL_PKCS1_PADDING)) { + $result .= $decrypted; + } else { + // error_log("Decrypt failed for chunk"); + } + } + + return $result; + } + + public function homeContent($filter) { + $url = $this->host . '/api/v1/app/screen/screenType'; + $jsonStr = $this->fetch($url, [ + CURLOPT_POST => 1, + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $json = json_decode($jsonStr, true); + $classes = []; + $filterObj = []; + + if (isset($json['data'])) { + foreach ($json['data'] as $mainCate) { + $typeId = (string)$mainCate['id']; + $classes[] = [ + 'type_id' => $typeId, + 'type_name' => $mainCate['name'] + ]; + + $filters = []; + if (isset($mainCate['children'])) { + foreach ($mainCate['children'] as $subCate) { + $filterType = ''; + switch ($subCate['name']) { + case '类型': $filterType = 'type'; break; + case '地区': $filterType = 'area'; break; + case '年份': $filterType = 'year'; break; + } + + if ($filterType) { + $values = [['n' => '全部', 'v' => '']]; + foreach ($subCate['children'] as $item) { + $values[] = ['n' => $item['name'], 'v' => $item['name']]; + } + $filters[] = [ + 'key' => $filterType, + 'name' => $subCate['name'], + 'value' => $values + ]; + } + } + } + + $filters[] = [ + 'key' => 'sort', + 'name' => '排序', + 'value' => [ + ['n' => '最新', 'v' => 'NEWEST'], + ['n' => '人气', 'v' => 'POPULARITY'], + ['n' => '评分', 'v' => 'COLLECT'], + ['n' => '热搜', 'v' => 'HOT'] + ] + ]; + + $filterObj[$typeId] = $filters; + } + } + + return [ + 'class' => $classes, + 'filters' => $filterObj + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $url = $this->host . '/api/v1/app/screen/screenMovie'; + + $condition = [ + 'classify' => $extend['type'] ?? '', + 'region' => $extend['area'] ?? '', + 'sreecnTypeEnum' => $extend['sort'] ?? 'NEWEST', + 'typeId' => $tid, + 'year' => $extend['year'] ?? '' + ]; + + $params = [ + 'condition' => $condition, + 'pageNum' => (int)$pg, + 'pageSize' => 40 + ]; + + $jsonStr = $this->fetch($url, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($params), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $json = json_decode($jsonStr, true); + $videos = []; + + if (isset($json['data']['records'])) { + foreach ($json['data']['records'] as $item) { + $videos[] = [ + 'vod_id' => $item['id'] . '*' . $item['typeId'], + 'vod_name' => $item['name'], + 'vod_pic' => $item['cover'], + 'vod_remarks' => $item['totalEpisode'] ?? '' + ]; + } + } + + $total = $json['data']['total'] ?? 0; + return $this->pageResult($videos, $pg, $total, 40); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $parts = explode('*', $id); + $vodId = (int)$parts[0]; + $typeId = $parts[1] ?? ''; + + // 1. 获取基本详情 + $detailUrl = $this->host . '/api/v1/app/play/movieDesc'; + $detailParams = ['id' => $vodId, 'typeId' => $typeId]; + + $detailRes = $this->fetch($detailUrl, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($detailParams), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $detailJson = json_decode($detailRes, true); + $detailData = $detailJson['data'] ?? []; + + // 2. 获取播放列表 (加密) + $playReqPayload = json_encode([ + 'id' => $vodId, + 'source' => 0, + 'typeId' => $typeId + ]); + + $playParams = ['key' => $this->rsaEncrypt($playReqPayload)]; + + $playDataRes = $this->fetch($this->host . '/api/v1/app/play/movieDetails', [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($playParams), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $playJson = json_decode($playDataRes, true); + $playDataEnc = $playJson['data'] ?? ''; + + $decryptedDataStr = $this->rsaDecrypt($playDataEnc); + + $decryptedData = json_decode($decryptedDataStr, true); + + $shows = []; + $playUrls = []; + + if (isset($decryptedData['moviePlayerList'])) { + foreach ($decryptedData['moviePlayerList'] as $player) { + // 3. 获取具体集数 (加密) + $episodePayload = json_encode([ + 'id' => $vodId, + 'source' => 0, + 'typeId' => $typeId, + 'playerId' => $player['id'] + ]); + + $episodeParams = ['key' => $this->rsaEncrypt($episodePayload)]; + + $episodeRes = $this->fetch($this->host . '/api/v1/app/play/movieDetails', [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($episodeParams), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $episodeJson = json_decode($episodeRes, true); + $episodeDataEnc = $episodeJson['data'] ?? ''; + + $episodeDecStr = $this->rsaDecrypt($episodeDataEnc); + $episodeDecData = json_decode($episodeDecStr, true); + + $urls = []; + if (isset($episodeDecData['episodeList'])) { + foreach ($episodeDecData['episodeList'] as $ep) { + $param = [ + 'id' => $vodId, + 'typeId' => $typeId, + 'playerId' => $player['id'], + 'episodeId' => $ep['id'] + ]; + // 封装参数到URL中 + $urls[] = $ep['episode'] . '$' . json_encode($param); + } + } + + if (!empty($urls)) { + $shows[] = $player['moviePlayerName']; + $playUrls[] = implode('#', $urls); + } + } + } + + $vod = [ + 'vod_id' => $id, + 'vod_name' => $detailData['name'] ?? '', + 'vod_pic' => $detailData['cover'] ?? '', + 'vod_year' => $detailData['year'] ?? '', + 'vod_area' => $detailData['area'] ?? '', + 'vod_remarks' => $detailData['totalEpisode'] ?? '', + 'vod_actor' => $detailData['star'] ?? '', + 'vod_content' => $detailData['introduce'] ?? '', + 'vod_play_from' => implode('$$$', $shows), + 'vod_play_url' => implode('$$$', $playUrls) + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $url = $this->host . '/api/v1/app/search/searchMovie'; + $params = [ + 'condition' => ['value' => $key], + 'pageNum' => (int)$pg, + 'pageSize' => 40 + ]; + + $jsonStr = $this->fetch($url, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($params), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $json = json_decode($jsonStr, true); + $videos = []; + + if (isset($json['data']['records'])) { + foreach ($json['data']['records'] as $item) { + $videos[] = [ + 'vod_id' => $item['id'] . '*' . $item['typeId'], + 'vod_name' => $item['name'], + 'vod_pic' => $item['cover'], + 'vod_remarks' => $item['totalEpisode'] ?? '' + ]; + } + } + + $total = $json['data']['total'] ?? 0; + return $this->pageResult($videos, $pg, $total, 40); + } + + public function playerContent($flag, $id, $vipFlags = []) { + // $id 是 detailContent 中封装的 JSON 参数 + $param = json_decode($id, true); + + $urlPayload = json_encode([ + 'id' => $param['id'], + 'source' => 0, + 'typeId' => $param['typeId'], + 'playerId' => $param['playerId'], + 'episodeId' => $param['episodeId'] + ]); + + $urlParams = ['key' => $this->rsaEncrypt($urlPayload)]; + + $postData = $this->fetch($this->host . '/api/v1/app/play/movieDetails', [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => json_encode($urlParams), + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $json = json_decode($postData, true); + $encryptedUrl = $json['data'] ?? ''; + + $decryptedUrlDataStr = $this->rsaDecrypt($encryptedUrl); + $playerUrlData = json_decode($decryptedUrlDataStr, true); + $playerUrl = $playerUrlData['url'] ?? ''; + + // 最后一步分析 URL + $analysisUrl = $this->host . '/api/v1/app/play/analysisMovieUrl?playerUrl=' . urlencode($playerUrl) . '&playerId=' . $param['playerId']; + + $analysisRes = $this->fetch($analysisUrl, [ + CURLOPT_HTTPHEADER => $this->formatHeaders($this->getHeaders()) + ]); + + $analysisJson = json_decode($analysisRes, true); + $finalUrl = $analysisJson['data'] ?? ''; + + return [ + 'parse' => 0, + 'url' => $finalUrl, + 'header' => [ + 'User-Agent' => 'okhttp/4.12.0' + ] + ]; + } + + // 辅助方法:将关联数组 headers 转换为 curl 需要的格式 + private function formatHeaders($headers) { + $formatted = []; + foreach ($headers as $k => $v) { + $formatted[] = "$k: $v"; + } + return $formatted; + } +} + +// 运行爬虫 +(new Spider())->run(); diff --git "a/spider/php/\351\272\273\351\233\200\350\247\206\351\242\221 \341\265\210\341\266\273.php" "b/spider/php/\351\272\273\351\233\200\350\247\206\351\242\221 \341\265\210\341\266\273.php" new file mode 100644 index 00000000..0d9e9db5 --- /dev/null +++ "b/spider/php/\351\272\273\351\233\200\350\247\206\351\242\221 \341\265\210\341\266\273.php" @@ -0,0 +1,190 @@ +<?php +require_once __DIR__ . '/lib/spider.php'; + +class Spider extends BaseSpider { + private $HOST = 'https://www.mqtv.cc'; + private $KEY = 'Mcxos@mucho!nmme'; + private $UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'; + + /** + * 对应 JS 中的 encodeData 和 decodeData (XOR + Base64) + */ + private function mq_xor_codec($data, $key, $is_decode = false) { + if ($is_decode) { + $data = base64_decode($data); + } else { + $data = json_encode($data, JSON_UNESCAPED_UNICODE); + $data = base64_encode($data); + } + + $res = ''; + $keyLen = strlen($key); + for ($i = 0; $i < strlen($data); $i++) { + $res .= $data[$i] ^ $key[$i % $keyLen]; + } + + if ($is_decode) { + return json_decode(base64_decode($res), true); + } else { + return urlencode(base64_encode($res)); + } + } + + private function getHeaders($referer = '/') { + return [ + 'User-Agent: ' . $this->UA, + 'Referer: ' . $this->HOST . $referer, + 'X-Requested-With: XMLHttpRequest' + ]; + } + + // 获取页面 PageID 并生成 Token + private function getToken($path, $ref = '/') { + $html = $this->fetch($this->HOST . $path, [], $this->getHeaders($ref)); + preg_match("/window\.pageid\s?=\s?'(.*?)';/i", $html, $m); + $pageId = $m[1] ?? ""; + return $this->mq_xor_codec($pageId, $this->KEY); + } + + public function homeContent($filter) { + // 5. 首页 (homeVod) + $token = $this->getToken('/'); + $apiUrl = $this->HOST . "/libs/VodList.api.php?home=index&token=$token"; + $resp = json_decode($this->fetch($apiUrl, [], $this->getHeaders()), true); + $list = []; + if (isset($resp['data']['movie'])) { + foreach ($resp['data']['movie'] as $section) { + foreach ($section['show'] as $v) { + $list[] = [ + 'vod_id' => $v['url'], + 'vod_name' => $v['title'], + 'vod_pic' => $v['img'], + 'vod_remarks' => $v['remark'] + ]; + } + } + } + + return [ + 'class' => [ + ['type_id' => '/type/movie', 'type_name' => '电影'], + ['type_id' => '/type/tv', 'type_name' => '电视剧'], + ['type_id' => '/type/va', 'type_name' => '综艺'], + ['type_id' => '/type/ct', 'type_name' => '动漫'] + ], + 'list' => array_slice($list, 0, 30) + ]; + } + + public function categoryContent($tid, $pg = 1, $filter = [], $extend = []) { + $typeKey = explode('/', trim($tid, '/'))[1] ?? 'movie'; + $token = $this->getToken($tid); + $apiUrl = $this->HOST . "/libs/VodList.api.php?type=$typeKey&rank=rankhot&page=$pg&token=$token"; + + $resp = json_decode($this->fetch($apiUrl, [], $this->getHeaders($tid)), true); + $list = []; + if (isset($resp['data'])) { + foreach ($resp['data'] as $v) { + $list[] = [ + 'vod_id' => $v['url'], + 'vod_name' => $v['title'], + 'vod_pic' => $v['img'], + 'vod_remarks' => $v['remark'] + ]; + } + } + return $this->pageResult($list, $pg); + } + + public function detailContent($ids) { + $id = is_array($ids) ? $ids[0] : $ids; + $pathParts = explode('/', trim($id, '/')); + $realId = end($pathParts); + $token = $this->getToken($id); + + $apiUrl = $this->HOST . "/libs/VodInfo.api.php?type=ct&id=$realId&token=$token"; + $json = json_decode($this->fetch($apiUrl, [], $this->getHeaders($id)), true); + $data = $json['data']; + + // 处理解析线路 + $parsesArr = []; + foreach (($data['playapi'] ?? []) as $p) { + if (isset($p['url'])) { + $parsesArr[] = (strpos($p['url'], '//') === 0) ? "https:" . $p['url'] : $p['url']; + } + } + $parsesStr = implode(',', $parsesArr); + + $playFrom = []; + $playUrls = []; + foreach (($data['playinfo'] ?? []) as $site) { + $playFrom[] = $site['cnsite']; + $urls = []; + foreach ($site['player'] as $ep) { + // 将解析接口封装在 URL 后面,供 play 阶段调用 + $urls[] = $ep['no'] . '$' . $ep['url'] . '@' . $parsesStr; + } + $playUrls[] = implode('#', $urls); + } + + $vod = [ + 'vod_id' => $id, + 'vod_name' => $data['title'], + 'vod_pic' => $data['img'], + 'vod_remarks' => $data['remark'], + 'vod_year' => $data['year'], + 'vod_area' => $data['area'], + 'vod_actor' => $data['actor'], + 'vod_director' => $data['director'], + 'vod_content' => $data['content'] ?? '', + 'vod_play_from' => implode('$$$', $playFrom), + 'vod_play_url' => implode('$$$', $playUrls) + ]; + + return ['list' => [$vod]]; + } + + public function searchContent($key, $quick = false, $pg = 1) { + $path = '/search/' . urlencode($key); + $token = $this->getToken($path); + $apiUrl = $this->HOST . "/libs/VodList.api.php?search=" . urlencode($key) . "&token=$token"; + + $resp = json_decode($this->fetch($apiUrl, [], $this->getHeaders($path)), true); + $data = $this->mq_xor_codec($resp['data'], $this->KEY, true); // 搜索数据需要解密 + + $list = []; + if (isset($data['vod_all'])) { + foreach ($data['vod_all'] as $item) { + foreach ($item['show'] as $v) { + $list[] = [ + 'vod_id' => $v['url'], + 'vod_name' => $v['title'], + 'vod_pic' => $v['img'], + 'vod_remarks' => $v['remark'] + ]; + } + } + } + return $this->pageResult($list, $pg); + } + + public function playerContent($flag, $id, $vipFlags = []) { + $parts = explode('@', $id); + $rawUrl = $parts[0]; + $parses = isset($parts[1]) ? explode(',', $parts[1]) : []; + + // 默认返回第一个解析地址配合嗅探,模拟 JS 中的逻辑 + $finalUrl = $rawUrl; + if (!empty($parses)) { + $finalUrl = $parses[0] . $rawUrl; + } + + return [ + 'parse' => 1, + 'url' => $finalUrl, + 'header' => ['User-Agent' => $this->UA] + ]; + } +} + +(new Spider())->run(); diff --git a/utils/api_helper.js b/utils/api_helper.js index ef142b88..936b85d8 100644 --- a/utils/api_helper.js +++ b/utils/api_helper.js @@ -33,7 +33,7 @@ export function startJsonWatcher(ENGINES, jsonDir) { // 设置新的防抖计时器,避免频繁触发 const timer = setTimeout(() => { - console.log(`${filename}文件已${eventType},即将清除所有模块缓存`); + console.log(`[HotReload] ${filename} changed, clearing cache...`); // 清除drpyS引擎的所有缓存 ENGINES.drpyS.clearAllCache(); // 清理已完成的计时器 @@ -44,7 +44,7 @@ export function startJsonWatcher(ENGINES, jsonDir) { } }); - console.log(`start json file hot reload success,listening path: ${jsonDir}`); + // console.log(`start json file hot reload success,listening path: ${jsonDir}`); } catch (error) { console.error('start json file listening failed with error:', error); } @@ -74,6 +74,12 @@ export function getApiEngine(engines, moduleName, query, options) { moduleDir = options.pyDir; _ext = '.py'; break; + case 'php': + // PHP引擎 - php + apiEngine = engines.php; + moduleDir = options.phpDir; + _ext = '.php'; + break; case 'cat': // CatVod引擎 apiEngine = engines.catvod; diff --git a/utils/binHelper.js b/utils/binHelper.js new file mode 100644 index 00000000..ceecb7e6 --- /dev/null +++ b/utils/binHelper.js @@ -0,0 +1,49 @@ +import fs from 'fs'; +import path from 'path'; + +/** + * 确保文件具有执行权限 (Linux/macOS) + * @param {string} filePath 文件绝对路径 + */ +export function ensureExecutable(filePath) { + if (process.platform === "win32") { + // Windows 不需要 chmod,直接返回 + return; + } + try { + if (!fs.existsSync(filePath)) { + return; + } + const stats = fs.statSync(filePath); + if (!(stats.mode & 0o111)) { + fs.chmodSync(filePath, 0o755); + console.log(`[binHelper] 已为文件 ${filePath} 添加执行权限`); + } + } catch (err) { + console.error(`[binHelper] 无法设置执行权限: ${filePath}`, err.message); + } +} + +/** + * 检查并准备二进制文件(检查存在性 + 赋予权限) + * @param {string} binPath 二进制文件路径或命令 + * @returns {string|null} 如果是现有文件路径返回路径,如果是全局命令返回原命令,如果文件不存在返回 null + */ +export function prepareBinary(binPath) { + if (!binPath) return null; + + // 如果不包含路径分隔符,假定是全局命令(如 'php', 'node'),直接返回 + // 注意:这里简单判断,如果用户写 ./php 或 /usr/bin/php 都会进入下面的 exist 检查 + if (!binPath.includes('/') && !binPath.includes('\\')) { + return binPath; + } + + // 如果是路径,检查是否存在 + if (fs.existsSync(binPath)) { + ensureExecutable(binPath); + return binPath; + } + + // 路径不存在 + return null; +} diff --git a/utils/createAxiosAgent.js b/utils/createAxiosAgent.js index 4c8f5599..ceb8bfdd 100644 --- a/utils/createAxiosAgent.js +++ b/utils/createAxiosAgent.js @@ -39,14 +39,27 @@ export function createAxiosInstance(options = {}) { const httpsAgent = new https.Agent(httpsAgentOptions); - // 配置 axios 使用代理 const _axios = axios.create({ - httpAgent, // 用于 HTTP 请求的代理 - httpsAgent, // 用于 HTTPS 请求的代理 + httpAgent, + httpsAgent, + }); + + _axios.interceptors.request.use(config => { + if (config && config.headers) { + const headers = config.headers; + const keys = Object.keys(headers); + for (const key of keys) { + if (key.toLowerCase() === 'user-agent' && headers[key] === 'RemoveUserAgent') { + delete headers[key]; + break; + } + } + } + return config; }); return _axios; } // 默认导出 -export default createAxiosInstance; \ No newline at end of file +export default createAxiosInstance; diff --git a/utils/esm-register.mjs b/utils/esm-register.mjs index 8dc51f51..d0a46162 100644 --- a/utils/esm-register.mjs +++ b/utils/esm-register.mjs @@ -12,7 +12,7 @@ export async function load(url, context, nextLoad) { const relativeUrl = url.replaceAll('assets://js/lib/', '../catLib/'); const catLibJsPath = path.join(assets_path, relativeUrl); const catLibHref = pathToFileURL(catLibJsPath).href; - console.log(`[assets url]: ${url} [relativeUrl]:${relativeUrl}\n[catLibJsPath]: ${catLibJsPath} [catLibHref]:${catLibHref}`); + // console.log(`[assets url]: ${url} [relativeUrl]:${relativeUrl}\n[catLibJsPath]: ${catLibJsPath} [catLibHref]:${catLibHref}`); url = catLibHref; } // 解决不了CAT_DEBUG=0模式下的相对路径依赖问题 diff --git a/utils/file.js b/utils/file.js index 07da6728..420e67a5 100644 --- a/utils/file.js +++ b/utils/file.js @@ -148,10 +148,22 @@ export function getParsesDict(host) { const jx_conf_text = readFileSync(jx_conf, 'utf-8'); let jx_conf_content = jx_conf_text.trim(); - // 准备模板变量字典 + let hostName = host; + try { + const raw = String(host || ''); + const hasScheme = raw.includes('://'); + const u = new URL(hasScheme ? raw : `http://${raw}`); + const hostname = u.hostname || raw; + const safeHostname = hostname.includes(':') ? `[${hostname}]` : hostname; + hostName = hasScheme ? `${u.protocol}//${safeHostname}` : safeHostname; + } catch (e) { + const raw = String(host || '').replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, '').split('/')[0]; + hostName = raw.startsWith('[') ? raw.split(']')[0] + ']' : raw.split(':')[0]; + } + let var_dict = { host, - hostName: host.split(':').length > 1 ? host.slice(0, host.lastIndexOf(":")) : host + hostName }; // 使用Jinja模板引擎渲染配置内容 @@ -213,4 +225,4 @@ export function executeParse(name, host, url) { } globalThis.pathLib = pathLib; -globalThis.executeParse = executeParse; \ No newline at end of file +globalThis.executeParse = executeParse; diff --git a/utils/pan/baidu.js b/utils/pan/baidu.js index fbaaffa4..1b6287fb 100644 --- a/utils/pan/baidu.js +++ b/utils/pan/baidu.js @@ -42,6 +42,10 @@ class BaiduHandler { this.cleanupInterval = setInterval(() => { this.clearSaveDir(); }, 2 * 60 * 60 * 1000); + // 不阻止进程退出 + if (this.cleanupInterval.unref) { + this.cleanupInterval.unref(); + } } /** diff --git a/utils/phpEnv.js b/utils/phpEnv.js new file mode 100644 index 00000000..31f3d54e --- /dev/null +++ b/utils/phpEnv.js @@ -0,0 +1,33 @@ +import { execFile } from 'child_process'; +import { promisify } from 'util'; +import { prepareBinary } from './binHelper.js'; + +const execFileAsync = promisify(execFile); + +export let isPhpAvailable = false; + +export const checkPhpAvailable = async () => { + let phpPath = process.env.PHP_PATH || 'php'; + + // Check existence and permissions + const validPath = prepareBinary(phpPath); + if (!validPath) { + console.warn(`⚠️ PHP binary not found or invalid: ${phpPath}`); + isPhpAvailable = false; + return false; + } + phpPath = validPath; + + try { + console.log(`[phpEnv] Verifying PHP executable: ${phpPath}`); + await execFileAsync(phpPath, ['-v']); + isPhpAvailable = true; + console.log(`✅ PHP environment check passed (${phpPath}).`); + } catch (e) { + isPhpAvailable = false; + console.warn(`⚠️ PHP environment check failed. PHP features will be disabled.`); + console.warn(`[phpEnv] Error details:`, e.message); + // console.error(e); + } + return isPhpAvailable; +}; diff --git a/utils/pluginManager.js b/utils/pluginManager.js index a7bf059f..5a58d30c 100644 --- a/utils/pluginManager.js +++ b/utils/pluginManager.js @@ -2,6 +2,7 @@ import fs from "fs"; import path from "path"; import {spawn} from "child_process"; import {fileURLToPath, pathToFileURL} from "url"; +import {ensureExecutable} from "./binHelper.js"; // 获取 pluginManager.js 的目录 const __filename = fileURLToPath(import.meta.url); @@ -14,7 +15,7 @@ const exampleConfigPath = path.join(__dirname, "../.plugins.example.js"); // 尝试加载用户配置,如果没有就用 example let plugins = []; try { - console.log(`检查插件配置文件: ${userConfigPath} 是否存在`); + // console.log(`检查插件配置文件: ${userConfigPath} 是否存在`); if (fs.existsSync(userConfigPath)) { plugins = (await import(pathToFileURL(userConfigPath).href)).default; console.log("[pluginManager] 使用用户 .plugins.js 配置"); @@ -54,22 +55,6 @@ function getPluginBinary(rootDir, pluginPath, pluginName) { return path.join(binDir, binaryName); } -function ensureExecutable(filePath) { - if (process.platform === "win32") { - // Windows 不需要 chmod,直接返回 - return; - } - try { - const stats = fs.statSync(filePath); - if (!(stats.mode & 0o111)) { - fs.chmodSync(filePath, 0o755); - console.log(`[pluginManager] 已为插件 ${filePath} 添加执行权限`); - } - } catch (err) { - console.error(`[pluginManager] 无法设置执行权限: ${filePath}`, err.message); - } -} - /** * 启动插件 * @param {Object} plugin 插件配置 diff --git a/utils/proxy-util.js b/utils/proxy-util.js index d4f689ac..01cd3425 100644 --- a/utils/proxy-util.js +++ b/utils/proxy-util.js @@ -70,7 +70,7 @@ export class SmartCacheManager { // 启动定期清理任务 this.startCleanupTimer(); - console.log(`[${this.name}] SmartCacheManager initialized: maxSize=${this.maxSize}, defaultTTL=${this.defaultTTL}ms`); + // console.log(`[${this.name}] SmartCacheManager initialized: maxSize=${this.maxSize}, defaultTTL=${this.defaultTTL}ms`); } /**