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 78c5d859..7bbca429 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,6 @@ dist /scripts/mjs/index.db /scripts/test/rsa-test.json /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 c122d8cf..558b3c0e 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,20 @@ nodejs作为服务端的drpy实现。全面升级异步写法 ### 常用超链接 * [本项目主页-免翻](https://github.com/hjdhnx/drpy-node) -* [接口文档](docs/apidoc.md) | [接口列表如定时任务](docs/apiList.md) | [小猫影视-待对接T4](https://github.com/waifu-project/movie/pull/135) +* ~~[最新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) * [本地配置接口-动态本地](/config?healthy=1&pwd=$pwd) * [本地配置接口-动态外网/局域网](/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) @@ -42,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) @@ -60,32 +66,25 @@ nodejs作为服务端的drpy实现。全面升级异步写法 * [酷9](https://wwbty.lanzouv.com/iGoUV3d3hxuf) * [千寻](https://wwbty.lanzouv.com/iSSN93d3hyzg) +* [皮卡丘](https://github.com/ingriddaleusag-dotcom/PeekPiliRelease) ## 更新记录 -### 20260112 +### 20260131 -更新至V1.3.15 +更新至V1.3.21 -### 20251017 +### 20260127 -更新至V1.3.14 +更新至V1.3.20 -### 20251015 +### 20260125 -更新至V1.3.13 +更新至V1.3.19 -### 20251014 +### 20260118 -更新至V1.3.12 - -### 20251013 - -更新至V1.3.11 - -### 20251012 - -更新至V1.3.10 +更新至V1.3.18 [点此查看完整更新记录](docs/updateRecord.md) @@ -189,6 +188,7 @@ pm2 restart drpys * [源动力-老](https://sourcepower.top/index) * [电竞专业反应测试](https://www.arealme.com/brain-memory-game/zh/) * [桌面启动器](https://wwbty.lanzouv.com/iDZaP3d3i5ud) +* [不知名获取网盘CK工具](http://sspa8.top:8100/pan/admin/index.php) ## AI接入 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/apps/cookie-butler/index.html b/apps/cookie-butler/index.html index 51942ecc..ca9d936d 100644 --- a/apps/cookie-butler/index.html +++ b/apps/cookie-butler/index.html @@ -12,6 +12,7 @@ +
@@ -19,10 +20,13 @@ @@ -93,28 +97,67 @@ const textValue = document.getElementById("cookie-res").value || ''; const active_name = activeLi.textContent.trim(); const active_key = activeLi.getAttribute('data-platform').trim(); - const save_key = active_key === 'ali' ? active_key + '_token' : active_key + '_cookie'; + let save_key = '' + if (/ali|pikpak/.test(active_key)) { + save_key = active_key + '_token' + } else { + save_key = active_key + '_cookie'; + } + console.log(`准备入库cookie:${active_name} ${save_key},值为:${textValue}`); const cookie_auth_code = prompt('cookie入库功能需要管理员授权码,请你正确输入后继续'); if (cookie_auth_code) { - // 使用 axios 发送 POST 请求 - axios.post('/admin/cookie-set', { - cookie_auth_code: cookie_auth_code, - key: save_key, - value: textValue.trim().replaceAll('\n', '') - }) - .then(response => { + if (save_key.includes('pikpak')) { + // 使用 axios 发送 POST 请求 + axios.post('/admin/cookie-set', { + cookie_auth_code: cookie_auth_code, + key: save_key, + value: textValue.split(';')[0].trim().replaceAll('\n', '') + }).then(response => { if (response.data.success) { alert(`Cookie 入库成功:${active_name} (${save_key})`); } else { alert(`入库失败:${response.data.message}`); } - }) - .catch(error => { + }).catch(error => { + console.error('请求失败:', error); + alert(`入库失败,服务器出现问题,请稍后再试。\n${error.response.data.message}`); + }); + axios.post('/admin/cookie-set', { + cookie_auth_code: cookie_auth_code, + key: save_key.replace('token', 'refresh_token'), + value: textValue.split(';')[1].trim().replaceAll('\n', '') + }).then(response => { + if (response.data.success) { + alert(`Cookie 入库成功:${active_name} (${save_key.replace('token', 'refresh_token')})`); + } else { + alert(`入库失败:${response.data.message}`); + } + }).catch(error => { console.error('请求失败:', error); alert(`入库失败,服务器出现问题,请稍后再试。\n${error.response.data.message}`); }); + } else { + // 使用 axios 发送 POST 请求 + axios.post('/admin/cookie-set', { + cookie_auth_code: cookie_auth_code, + key: save_key, + value: textValue.trim().replaceAll('\n', '') + }) + .then(response => { + if (response.data.success) { + alert(`Cookie 入库成功:${active_name} (${save_key})`); + } else { + alert(`入库失败:${response.data.message}`); + } + }) + .catch(error => { + console.error('请求失败:', error); + alert(`入库失败,服务器出现问题,请稍后再试。\n${error.response.data.message}`); + }); + } + } } else { alert('至少选中一个cookie入库项目'); diff --git a/apps/cookie-butler/static/js/cookie.js b/apps/cookie-butler/static/js/cookie.js index bff8f94a..f01be452 100644 --- a/apps/cookie-butler/static/js/cookie.js +++ b/apps/cookie-butler/static/js/cookie.js @@ -20,7 +20,7 @@ function showToast(message, type = 'success') { // 初始化页面 async function initializePage() { // 加载cookie - const platforms = ['ali', 'quark', 'uc', 'uc_token', 'bili', 'baidu']; + const platforms = ['ali', 'quark', 'quark_token', 'uc', 'uc_token', 'bili', 'yun', 'baidu', 'pikpak']; // 绑定按钮事件 platforms.forEach(platform => { diff --git a/apps/cookie-butler/static/js/jsencrypt.min.js b/apps/cookie-butler/static/js/jsencrypt.min.js new file mode 100644 index 00000000..27c29d5e --- /dev/null +++ b/apps/cookie-butler/static/js/jsencrypt.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.JSEncrypt={})}(this,function(t){"use strict";var e="0123456789abcdefghijklmnopqrstuvwxyz";function a(t){return e.charAt(t)}function i(t,e){return t&e}function u(t,e){return t|e}function r(t,e){return t^e}function n(t,e){return t&~e}function s(t){if(0==t)return-1;var e=0;return 0==(65535&t)&&(t>>=16,e+=16),0==(255&t)&&(t>>=8,e+=8),0==(15&t)&&(t>>=4,e+=4),0==(3&t)&&(t>>=2,e+=2),0==(1&t)&&++e,e}function o(t){for(var e=0;0!=t;)t&=t-1,++e;return e}var h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function c(t){var e,i,r="";for(e=0;e+3<=t.length;e+=3)i=parseInt(t.substring(e,e+3),16),r+=h.charAt(i>>6)+h.charAt(63&i);for(e+1==t.length?(i=parseInt(t.substring(e,e+1),16),r+=h.charAt(i<<2)):e+2==t.length&&(i=parseInt(t.substring(e,e+2),16),r+=h.charAt(i>>2)+h.charAt((3&i)<<4));0<(3&r.length);)r+="=";return r}function f(t){var e,i="",r=0,n=0;for(e=0;e>2),n=3&s,r=1):1==r?(i+=a(n<<2|s>>4),n=15&s,r=2):2==r?(i+=a(n),i+=a(s>>2),n=3&s,r=3):(i+=a(n<<2|s>>4),i+=a(15&s),r=0))}return 1==r&&(i+=a(n<<2)),i}var l,p=function(t,e){return(p=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i])})(t,e)};var g,d=function(t){var e;if(void 0===l){var i="0123456789ABCDEF",r=" \f\n\r\t \u2028\u2029";for(l={},e=0;e<16;++e)l[i.charAt(e)]=e;for(i=i.toLowerCase(),e=10;e<16;++e)l[i.charAt(e)]=e;for(e=0;e>16,r[r.length]=n>>8&255,r[r.length]=255&n,s=n=0):n<<=6}}switch(s){case 1:throw new Error("Base64 encoding incomplete: at least 2 bits missing");case 2:r[r.length]=n>>10;break;case 3:r[r.length]=n>>16,r[r.length]=n>>8&255}return r},re:/-----BEGIN [^-]+-----([A-Za-z0-9+\/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/,unarmor:function(t){var e=v.re.exec(t);if(e)if(e[1])t=e[1];else{if(!e[2])throw new Error("RegExp out of sync");t=e[2]}return v.decode(t)}},m=1e13,y=function(){function t(t){this.buf=[+t||0]}return t.prototype.mulAdd=function(t,e){var i,r,n=this.buf,s=n.length;for(i=0;ie&&(t=t.substring(0,e)+b),t}var w,D=function(){function i(t,e){this.hexDigits="0123456789ABCDEF",t instanceof i?(this.enc=t.enc,this.pos=t.pos):(this.enc=t,this.pos=e)}return i.prototype.get=function(t){if(void 0===t&&(t=this.pos++),t>=this.enc.length)throw new Error("Requesting byte offset "+t+" on a stream of length "+this.enc.length);return"string"==typeof this.enc?this.enc.charCodeAt(t):this.enc[t]},i.prototype.hexByte=function(t){return this.hexDigits.charAt(t>>4&15)+this.hexDigits.charAt(15&t)},i.prototype.hexDump=function(t,e,i){for(var r="",n=t;n>u&1?"1":"0";if(s.length>i)return n+E(s,i)}return n+s},i.prototype.parseOctetString=function(t,e,i){if(this.isASCII(t,e))return E(this.parseStringISO(t,e),i);var r=e-t,n="("+r+" byte)\n";(i/=2)i)return E(r,i);n=new y,s=0}}return 0>6,this.tagConstructed=0!=(32&e),this.tagNumber=31&e,31==this.tagNumber){for(var i=new y;e=t.get(),i.mulAdd(128,127&e),128&e;);this.tagNumber=i.simplify()}}return t.prototype.isUniversal=function(){return 0===this.tagClass},t.prototype.isEOC=function(){return 0===this.tagClass&&0===this.tagNumber},t}(),B=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],A=(1<<26)/B[B.length-1],O=function(){function b(t,e,i){null!=t&&("number"==typeof t?this.fromNumber(t,e,i):null==e&&"string"!=typeof t?this.fromString(t,256):this.fromString(t,e))}return b.prototype.toString=function(t){if(this.s<0)return"-"+this.negate().toString(t);var e;if(16==t)e=4;else if(8==t)e=3;else if(2==t)e=1;else if(32==t)e=5;else{if(4!=t)return this.toRadix(t);e=2}var i,r=(1<>h)&&(n=!0,s=a(i));0<=o;)h>(h+=this.DB-e)):(i=this[o]>>(h-=e)&r,h<=0&&(h+=this.DB,--o)),0>24},b.prototype.shortValue=function(){return 0==this.t?this.s:this[0]<<16>>16},b.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this[0]<=0?0:1},b.prototype.toByteArray=function(){var t=this.t,e=[];e[0]=this.s;var i,r=this.DB-t*this.DB%8,n=0;if(0>r)!=(this.s&this.DM)>>r&&(e[n++]=i|this.s<>(r+=this.DB-8)):(i=this[t]>>(r-=8)&255,r<=0&&(r+=this.DB,--t)),0!=(128&i)&&(i|=-256),0==n&&(128&this.s)!=(128&i)&&++n,(0=this.t?0!=this.s:0!=(this[e]&1<>n-a&u:(f=(t[p]&(1<>this.DB+n-a)),h=i;0==(1&f);)f>>=1,--h;if((n-=h)<0&&(n+=this.DB,--p),g)o[f].copyTo(s),g=!1;else{for(;1this.DB?(this[this.t-1]|=(o&(1<>this.DB-s):this[this.t-1]|=o<=this.DB&&(s-=this.DB))}8==i&&0!=(128&+t[0])&&(this.s=-1,0>r|o,o=(this[h]&n)<=this.t)e.t=0;else{var r=t%this.DB,n=this.DB-r,s=(1<>r;for(var o=i+1;o>r;0>=this.DB;if(t.t>=this.DB;r+=this.s}else{for(r+=this.s;i>=this.DB;r-=t.s}e.s=r<0?-1:0,r<-1?e[i++]=this.DV+r:0=e.DV&&(t[i+e.t]-=e.DV,t[i+e.t+1]=1)}0>this.F2:0),l=this.FV/f,p=(1<=i&&(this.dMultiply(r),this.dAddOffset(o,0),o=s=0))}0t&&this.subTo(b.ONE.shiftLeft(t-1),this);else{var r=[],n=7&t;r.length=1+(t>>3),e.nextBytes(r),0>=this.DB;if(t.t>=this.DB;r+=this.s}else{for(r+=this.s;i>=this.DB;r+=t.s}e.s=r<0?-1:0,0=this.DV;)this[e]-=this.DV,++e>=this.t&&(this[this.t++]=0),++this[e]}},b.prototype.multiplyLowerTo=function(t,e,i){var r=Math.min(this.t+t.t,e);for(i.s=0,i.t=r;0>1)&&(t=B.length);for(var n=M(),s=0;st&&n.subTo(b.ONE.shiftLeft(t-1),n),n.isProbablePrime(e)?setTimeout(function(){r()},0):setTimeout(s,0)};setTimeout(s,0)}else{var o=[],h=7&t;o.length=1+(t>>3),e.nextBytes(o),0>15,this.um=(1<>15)*this.mpl&this.um)<<15)&t.DM;for(t[i=e+this.m.t]+=this.m.am(0,r,t,e,0,this.m.t);t[i]>=t.DV;)t[i]-=t.DV,t[++i]++}t.clamp(),t.drShiftTo(this.m.t,t),0<=t.compareTo(this.m)&&t.subTo(this.m,t)},t.prototype.mulTo=function(t,e,i){t.multiplyTo(e,i),this.reduce(i)},t.prototype.sqrTo=function(t,e){t.squareTo(e),this.reduce(e)},t}(),P=function(){function t(t){this.m=t,this.r2=M(),this.q3=M(),O.ONE.dlShiftTo(2*t.t,this.r2),this.mu=this.r2.divide(t)}return t.prototype.convert=function(t){if(t.s<0||t.t>2*this.m.t)return t.mod(this.m);if(t.compareTo(this.m)<0)return t;var e=M();return t.copyTo(e),this.reduce(e),e},t.prototype.revert=function(t){return t},t.prototype.reduce=function(t){for(t.drShiftTo(this.m.t-1,this.r2),t.t>this.m.t+1&&(t.t=this.m.t+1,t.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);t.compareTo(this.r2)<0;)t.dAddOffset(1,this.m.t+1);for(t.subTo(this.r2,t);0<=t.compareTo(this.m);)t.subTo(this.m,t)},t.prototype.mulTo=function(t,e,i){t.multiplyTo(e,i),this.reduce(i)},t.prototype.sqrTo=function(t,e){t.squareTo(e),this.reduce(e)},t}();function M(){return new O(null)}function q(t,e){return new O(t,e)}"Microsoft Internet Explorer"==navigator.appName?(O.prototype.am=function(t,e,i,r,n,s){for(var o=32767&e,h=e>>15;0<=--s;){var a=32767&this[t],u=this[t++]>>15,c=h*a+u*o;n=((a=o*a+((32767&c)<<15)+i[r]+(1073741823&n))>>>30)+(c>>>15)+h*u+(n>>>30),i[r++]=1073741823&a}return n},w=30):"Netscape"!=navigator.appName?(O.prototype.am=function(t,e,i,r,n,s){for(;0<=--s;){var o=e*this[t++]+i[r]+n;n=Math.floor(o/67108864),i[r++]=67108863&o}return n},w=26):(O.prototype.am=function(t,e,i,r,n,s){for(var o=16383&e,h=e>>14;0<=--s;){var a=16383&this[t],u=this[t++]>>14,c=h*a+u*o;n=((a=o*a+((16383&c)<<14)+i[r]+n)>>28)+(c>>14)+h*u,i[r++]=268435455&a}return n},w=28),O.prototype.DB=w,O.prototype.DM=(1<>>16)&&(t=e,i+=16),0!=(e=t>>8)&&(t=e,i+=8),0!=(e=t>>4)&&(t=e,i+=4),0!=(e=t>>2)&&(t=e,i+=2),0!=(e=t>>1)&&(t=e,i+=1),i}O.ZERO=F(0),O.ONE=F(1);var K=function(){function t(){this.i=0,this.j=0,this.S=[]}return t.prototype.init=function(t){var e,i,r;for(e=0;e<256;++e)this.S[e]=e;for(e=i=0;e<256;++e)i=i+this.S[e]+t[e%t.length]&255,r=this.S[e],this.S[e]=this.S[i],this.S[i]=r;this.i=0,this.j=0},t.prototype.next=function(){var t;return this.i=this.i+1&255,this.j=this.j+this.S[this.i]&255,t=this.S[this.i],this.S[this.i]=this.S[this.j],this.S[this.j]=t,this.S[t+this.S[this.i]&255]},t}();var k,_,z=256,Z=null;if(null==Z){Z=[];var G=void(_=0);if(window.crypto&&window.crypto.getRandomValues){var $=new Uint32Array(256);for(window.crypto.getRandomValues($),G=0;G<$.length;++G)Z[_++]=255&$[G]}var Y=function(t){if(this.count=this.count||0,256<=this.count||z<=_)window.removeEventListener?window.removeEventListener("mousemove",Y,!1):window.detachEvent&&window.detachEvent("onmousemove",Y);else try{var e=t.x+t.y;Z[_++]=255&e,this.count+=1}catch(t){}};window.addEventListener?window.addEventListener("mousemove",Y,!1):window.attachEvent&&window.attachEvent("onmousemove",Y)}function J(){if(null==k){for(k=new K;_>6|192):(i[--e]=63&n|128,i[--e]=n>>6&63|128,i[--e]=n>>12|224)}i[--e]=0;for(var s=new X,o=[];2>3);if(null==e)return null;var i=this.doPublic(e);if(null==i)return null;var r=i.toString(16);return 0==(1&r.length)?r:"0"+r},t.prototype.setPrivate=function(t,e,i){null!=t&&null!=e&&0>1;this.e=parseInt(e,16);for(var n=new O(e,16);;){for(;this.p=new O(t-r,1,i),0!=this.p.subtract(O.ONE).gcd(n).compareTo(O.ONE)||!this.p.isProbablePrime(10););for(;this.q=new O(r,1,i),0!=this.q.subtract(O.ONE).gcd(n).compareTo(O.ONE)||!this.q.isProbablePrime(10););if(this.p.compareTo(this.q)<=0){var s=this.p;this.p=this.q,this.q=s}var o=this.p.subtract(O.ONE),h=this.q.subtract(O.ONE),a=o.multiply(h);if(0==a.gcd(n).compareTo(O.ONE)){this.n=this.p.multiply(this.q),this.d=n.modInverse(a),this.dmp1=this.d.mod(o),this.dmq1=this.d.mod(h),this.coeff=this.q.modInverse(this.p);break}}},t.prototype.decrypt=function(t){var e=q(t,16),i=this.doPrivate(e);return null==i?null:function(t,e){var i=t.toByteArray(),r=0;for(;r=i.length)return null;var n="";for(;++r>3)},t.prototype.generateAsync=function(t,e,n){var s=new X,o=t>>1;this.e=parseInt(e,16);var h=new O(e,16),a=this,u=function(){var e=function(){if(a.p.compareTo(a.q)<=0){var t=a.p;a.p=a.q,a.q=t}var e=a.p.subtract(O.ONE),i=a.q.subtract(O.ONE),r=e.multiply(i);0==r.gcd(h).compareTo(O.ONE)?(a.n=a.p.multiply(a.q),a.d=h.modInverse(r),a.dmp1=a.d.mod(e),a.dmq1=a.d.mod(i),a.coeff=a.q.modInverse(a.p),setTimeout(function(){n()},0)):setTimeout(u,0)},i=function(){a.q=M(),a.q.fromNumberAsync(o,1,s,function(){a.q.subtract(O.ONE).gcda(h,function(t){0==t.compareTo(O.ONE)&&a.q.isProbablePrime(10)?setTimeout(e,0):setTimeout(i,0)})})},r=function(){a.p=M(),a.p.fromNumberAsync(t-o,1,s,function(){a.p.subtract(O.ONE).gcda(h,function(t){0==t.compareTo(O.ONE)&&a.p.isProbablePrime(10)?setTimeout(i,0):setTimeout(r,0)})})};setTimeout(r,0)};setTimeout(u,0)},t.prototype.sign=function(t,e,i){var r=function(t,e){if(e=e?t:new Array(e-t.length+1).join("0")+t},this.getString=function(){return this.s},this.setString=function(t){this.hTLV=null,this.isModified=!0,this.s=t,this.hV=stohex(t)},this.setByDateValue=function(t,e,i,r,n,s){var o=new Date(Date.UTC(t,e-1,i,r,n,s,0));this.setByDate(o)},this.getFreshValueHex=function(){return this.hV}},tt.lang.extend(et.asn1.DERAbstractTime,et.asn1.ASN1Object),et.asn1.DERAbstractStructured=function(t){et.asn1.DERAbstractString.superclass.constructor.call(this),this.setByASN1ObjectArray=function(t){this.hTLV=null,this.isModified=!0,this.asn1Array=t},this.appendASN1Object=function(t){this.hTLV=null,this.isModified=!0,this.asn1Array.push(t)},this.asn1Array=new Array,void 0!==t&&void 0!==t.array&&(this.asn1Array=t.array)},tt.lang.extend(et.asn1.DERAbstractStructured,et.asn1.ASN1Object),et.asn1.DERBoolean=function(){et.asn1.DERBoolean.superclass.constructor.call(this),this.hT="01",this.hTLV="0101ff"},tt.lang.extend(et.asn1.DERBoolean,et.asn1.ASN1Object),et.asn1.DERInteger=function(t){et.asn1.DERInteger.superclass.constructor.call(this),this.hT="02",this.setByBigInteger=function(t){this.hTLV=null,this.isModified=!0,this.hV=et.asn1.ASN1Util.bigIntToMinTwosComplementsHex(t)},this.setByInteger=function(t){var e=new O(String(t),10);this.setByBigInteger(e)},this.setValueHex=function(t){this.hV=t},this.getFreshValueHex=function(){return this.hV},void 0!==t&&(void 0!==t.bigint?this.setByBigInteger(t.bigint):void 0!==t.int?this.setByInteger(t.int):"number"==typeof t?this.setByInteger(t):void 0!==t.hex&&this.setValueHex(t.hex))},tt.lang.extend(et.asn1.DERInteger,et.asn1.ASN1Object),et.asn1.DERBitString=function(t){if(void 0!==t&&void 0!==t.obj){var e=et.asn1.ASN1Util.newObject(t.obj);t.hex="00"+e.getEncodedHex()}et.asn1.DERBitString.superclass.constructor.call(this),this.hT="03",this.setHexValueIncludingUnusedBits=function(t){this.hTLV=null,this.isModified=!0,this.hV=t},this.setUnusedBitsAndHexValue=function(t,e){if(t<0||7 { - if (enable_dr2 === '1') { + if (enable_dr2 === '1' || enable_dr2 === '2') { // dr2ApiType=0 使用接口drpy2 dr2ApiType=1 使用壳子内置的drpy2 let api = dr2ApiType ? `assets://js/lib/drpy2.js` : `${requestHost}/public/drpy/drpy2.min.js`; + if (enable_dr2 === '2') { + api = `${requestHost}/public/drpy/drpy2-fast.min.js`; + } let ext = `${requestHost}/js/${file}`; if (pwd) { ext += `?pwd=${pwd}`; @@ -348,31 +353,33 @@ async function generateSiteJSON(options, requestHost, sub, pwd) { ext: ext || "", // 固定为空字符串 }; sites.push(site); - } else if (enable_dr2 === '2') { - // 模式2:只启用T3脚本的T4风格API配置 - const t4site = { - key: fileSite.key, - name: fileSite.name, - type: 4, // 固定值 - api: `${requestHost}/api/${baseName}`, - ...ruleMeta, - ext: "", // 固定为空字符串 - }; - // 添加isdr2参数到API URL - if (pwd) { - t4site.api += `?pwd=${pwd}&do=dr`; - } else { - t4site.api += `?do=dr`; - } - - // 处理传参源的API参数 - if (fileSite.queryStr) { - const separator = t4site.api.includes('?') ? '&' : '?'; - site.api += `${separator}extend=${encodeURIComponent(fileSite.queryStr)}`; - } - - sites.push(t4site); } + // else if (enable_dr2 === '2') { + // + // // 模式2:只启用T3脚本的T4风格API配置 + // const t4site = { + // key: fileSite.key, + // name: fileSite.name, + // type: 4, // 固定值 + // api: `${requestHost}/api/${baseName}`, + // ...ruleMeta, + // ext: "", // 固定为空字符串 + // }; + // // 添加isdr2参数到API URL + // if (pwd) { + // t4site.api += `?pwd=${pwd}&do=dr`; + // } else { + // t4site.api += `?do=dr`; + // } + // + // // 处理传参源的API参数 + // if (fileSite.queryStr) { + // const separator = t4site.api.includes('?') ? '&' : '?'; + // site.api += `${separator}extend=${encodeURIComponent(fileSite.queryStr)}`; + // } + // + // sites.push(t4site); + // } }); }, param: {file, dr2Dir, requestHost, pwd, drpyS, SitesMap}, @@ -412,6 +419,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); @@ -485,6 +497,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') { @@ -515,6 +589,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); @@ -655,78 +734,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/github.js b/controllers/github.js new file mode 100644 index 00000000..b91d4573 --- /dev/null +++ b/controllers/github.js @@ -0,0 +1,75 @@ +import axios from 'axios'; + +/** + * GitHub Release 控制器 + * 用于获取 GitHub 仓库的最新 Release 下载链接 + */ +export default (fastify, options, done) => { + + /** + * 获取最新 Release 下载链接 + * 路径: /gh/release + * 参数: repo (可选,默认 hjdhnx/drpy-node) + */ + fastify.get('/gh/release', async (request, reply) => { + const repo = request.query.repo || 'hjdhnx/drpy-node'; + const proxyPrefix = 'https://github.catvod.com/'; + const apiUrl = `https://api.github.com/repos/${repo}/releases/latest`; + + try { + fastify.log.info(`Fetching release info for ${repo}`); + + const response = await axios.get(apiUrl, { + headers: { + 'User-Agent': 'drpy-node-client', + 'Accept': 'application/vnd.github.v3+json' + } + }); + + const data = response.data; + + if (!data.assets || data.assets.length === 0) { + return reply.status(404).send({ error: 'No assets found in the latest release' }); + } + + // 打印全部文件列表的链接 + fastify.log.info(`Assets for ${repo} ${data.tag_name}:`); + const fileList = data.assets.map(asset => { + fastify.log.info(`- ${asset.name}: ${asset.browser_download_url}`); + return { + name: asset.name, + url: asset.browser_download_url, + proxy_url: proxyPrefix + asset.browser_download_url + }; + }); + + // 优先选择后缀为 .7z 且文件名不包含 green 的文件 + let targetAsset = data.assets.find(asset => asset.name.toLowerCase().endsWith('.7z') && !asset.name.toLowerCase().includes('green')); + + if (!targetAsset) { + fastify.log.warn(`No asset found matching criteria (.7z, no 'green'), falling back to the first asset.`); + targetAsset = data.assets[0]; + } + + const originalUrl = targetAsset.browser_download_url; + const finalUrl = proxyPrefix + originalUrl; + + // 返回这个完整链接 + // 用户要求"返回这个完整链接",这里直接返回字符串 + return reply.send(finalUrl); + + } catch (error) { + fastify.log.error(`Error fetching release for ${repo}: ${error.message}`); + if (error.response) { + fastify.log.error(`GitHub API Status: ${error.response.status}`); + return reply.status(error.response.status).send({ + error: 'GitHub API Error', + message: error.response.data.message + }); + } + return reply.status(500).send({ error: 'Internal Server Error', message: error.message }); + } + }); + + done(); +}; diff --git a/controllers/index.js b/controllers/index.js index 02196ad9..aa6f2853 100644 --- a/controllers/index.js +++ b/controllers/index.js @@ -1,117 +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 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); + 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/other/HtmlParser.cs b/docs/other/HtmlParser.cs new file mode 100644 index 00000000..2e2bc070 --- /dev/null +++ b/docs/other/HtmlParser.cs @@ -0,0 +1,485 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Net; +using System.Net.Http.Headers; +using System.Net.Security; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Security.Cryptography.X509Certificates; +using System.Security.Policy; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Xml.Linq; +using Esprima.Ast; + +using Jint; +using Jint.Native; +using Jint.Runtime; +using Newtonsoft.Json.Linq; +using NSoup.Nodes; +using NSoup; +using NSoup.Select; +using Document = NSoup.Nodes.Document; +using RestSharp; +using System.Web; +using System.Net.Mime; +using Newtonsoft.Json; +using NSoup.Helper; +using System.Text.Encodings.Web; +using System.Buffers.Text; +using System.Text.Json.Nodes; + +namespace Peach.DataAccess +{ + //html解析器 + public class HtmlParser + { + RestClient client; + public HtmlParser() + { + ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; + + var options = new RestClientOptions() + { + RemoteCertificateValidationCallback = (a, c, d, v) => true, + MaxTimeout = 100000, + ThrowOnAnyError = true, //设置不然不会报异常 + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" + }; + client = new RestClient(options); + //client.AddDefaultHeader("Content-Type", "application/json"); + //client.AddDefaultHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"); + } + + /// + /// okhttp封装的html请求,给js调用http请求的 + /// + /// + /// + /// + public object Request(string url, JsValue arguments) + { + Uri uri = new Uri(url); + string Host = uri.Host; + var method = arguments.AsObject()["method"]?.ToString(); + var _headers = arguments.AsObject()["headers"].AsObject(); + var Referer = _headers["Referer"]?.ToString(); + var UserAgent = _headers["User-Agent"]?.ToString(); + var Cookie = _headers["Cookie"]?.ToString(); + var ContentType = _headers["Content-Type"]?.ToString(); + + var Data = arguments.AsObject()["data"]?.ToString(); + var Body = arguments.AsObject()["body"]?.ToString(); + + var Buffer = arguments.AsObject()["buffer"]?.ToString(); + + + + String charset = "utf-8"; + if (ContentType != null && ContentType.Split("charset=").Length > 1) + { + charset = ContentType.Split("charset=")[1]; + } + + var request = new RestRequest(url); + + if (!string.IsNullOrEmpty(Data) && !Data.Equals("undefined")) + { + // 序列化JSON数据 + string post_data = JsonConvert.SerializeObject(Data); + // 将JSON参数添加至请求中 + request.AddParameter("application/json", post_data, ParameterType.RequestBody); + + } + + if (!string.IsNullOrEmpty(Body) && !Body.Equals("undefined")) + { + String[] queryS = Body.Split("&"); + foreach (String query in queryS) + { + //String query = queryS[i]; + int tmp = query.IndexOf("="); + String key; + String value; + if (tmp != -1) + { + key = query.Substring(0, tmp); + value = query[(tmp + 1)..]; + } + else + { + key = query; + value = ""; + } + request.AddParameter(key, value); + } + } + + if (string.IsNullOrEmpty(UserAgent)) + UserAgent = "Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36"; + request.AddHeader("User-Agent", UserAgent); + if (!string.IsNullOrEmpty(Referer)) + request.AddHeader("Referer", Referer); + + if (!string.IsNullOrEmpty(Cookie) && !Cookie.Equals("undefined")) + { + client.AddDefaultHeader("Cookie", Cookie); + } + string rContent = ""; + JsObject header = new (_headers.Engine); + + try + { + var client = new RestClient(url); + + RestResponse? response; + if (method?.ToLower() == "post") + response = client.Post(request); + else + response = client.Get(request); + + //rContent = response.Content; + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + rContent = HttpUtility.UrlDecode(response.RawBytes == null ? Array.Empty() : response.RawBytes, + Encoding.GetEncoding(charset)); + + if (response.Headers != null) + { + foreach (var item in response.Headers) + { + header.Set(item.Name, item.Value == null ? "" : item.Value.ToString()); + } + } + + if (Buffer == "1") + { + return new { headers = header, content = response.RawBytes }; + } + else if (Buffer == "2") + { + return new { headers = header, content = Convert.ToBase64String(Encoding.UTF8.GetBytes(rContent)) }; + } + else + { + return new { headers = header, content = rContent }; + } + } + catch (Exception) + { } + return new { headers = header, content = "" }; + } + + private static readonly Regex p = new ("url\\((.*?)\\)", RegexOptions.Multiline | RegexOptions.Singleline); + private static readonly Regex NOAdd_INDEX = new (":eq|:lt|:gt|:first|:last|^body$|^#"); + private static readonly Regex URLJOIN_ATTR = new ("(url|src|href|-original|-src|-play|-url|style)$|^(data-|url-|src-)", RegexOptions.Multiline | RegexOptions.IgnoreCase); + private static readonly Regex SPECIAL_URL = new ("^(ftp|magnet|thunder|ws):", RegexOptions.Multiline | RegexOptions.IgnoreCase); + private static String pdfh_html = ""; + private static String pdfa_html = ""; + private static Document? pdfh_doc = null; + private static Document? pdfa_doc = null; + + public static string JoinUrl(string parent, string child) + { + if (string.IsNullOrWhiteSpace(parent)) + { + return child; + } + + Uri url; + string q = parent; + try + { + url = new Uri(new Uri(parent), child); + q = url.ToString(); + } + catch (Exception) + { + //e.printStackTrace(); + } + // if (q.Contains("#")) { + // q = q.ReplaceAll("^(.+?)#.*?$", "$1"); + // } + return q; + } + + public class Painfo + { + public string? nparse_rule; + public int nparse_index; + public List? excludes; + } + + private static Painfo GetParseInfo(string nparse) + { + /* + 根据传入的单规则获取 parse规则,索引位置,排除列表 -- 可以用于剔除元素,支持多个,按标签剔除,按id剔除等操作 + :param nparse: + :return:*/ + Painfo painfo = new Painfo(); + //List excludes = new ArrayList<>(); //定义排除列表默认值为空 + //int nparse_index; //定义位置索引默认值为0 + painfo.nparse_rule = nparse; //定义规则默认值为本身 + if (nparse.Contains(":eq")) + { + painfo.nparse_rule = nparse.Split(":")[0]; + string nparse_pos = nparse.Split(":")[1]; + + if (painfo.nparse_rule.Contains("--")) + { + string[] rules = painfo.nparse_rule.Split("--"); + painfo.excludes = rules.ToList();// new(Arrays.asList(rules)); + painfo.excludes.RemoveAt(0); + painfo.nparse_rule = rules[0]; + } + else if (nparse_pos.Contains("--")) + { + string[] rules = nparse_pos.Split("--"); + painfo.excludes = rules.ToList();// new ArrayList<>(Arrays.asList(rules)); + painfo.excludes.RemoveAt(0); + nparse_pos = rules[0]; + } + + try + { + painfo.nparse_index = int.Parse(nparse_pos.Replace("eq(", "").Replace(")", "")); + } + catch (Exception) + { + painfo.nparse_index = 0; + } + } + else + { + if (nparse.Contains("--")) + { + string[] rules = painfo.nparse_rule.Split("--"); + painfo.excludes = rules.ToList();// new ArrayList<>(Arrays.asList(rules)); + painfo.excludes.RemoveAt(0); + painfo.nparse_rule = rules[0]; + } + } + return painfo; + } + + //pdfh + public string ParseDomForUrl(string html, string rule) + { + return ParseDom(html, rule, ""); + + } + //pd + public string ParseDom(string html, string rule, string Add_url) + { + if (string.IsNullOrWhiteSpace(html)) return ""; + if (!pdfh_html.Equals(html)) + { + pdfh_html = html; + pdfh_doc = NSoupClient.Parse(html); + } + Document? doc = pdfh_doc; + //Document doc = NSoupClient.Parse(html); + if (rule.Equals("body&&Text") || rule.Equals("Text")) + return doc.Text(); + else if (rule.Equals("body&&Html") || rule.Equals("Html")) + return doc.Html(); + + string option = ""; + if (rule.Contains("&&")) + { + string[] rs = rule.Split("&&"); + option = rs[rs.Length - 1]; + List excludes = rs.ToList();// new ArrayList<>(Arrays.asList(rs)); + excludes.RemoveAt(rs.Length - 1); + rule = string.Join("&&", excludes);// TextUtils.join("&&", excludes); + } + rule = parseHikerToJq(rule, true); + string[]? parses = rule.Split(" "); + Elements ret = new (); + foreach (string nparse in parses) + { + ret = parseOneRule(doc, nparse, ret); + if (ret.IsEmpty || ret.Count <= 0) return ""; + } + if (string.IsNullOrWhiteSpace(option)) + return ret.OuterHtml(); + if (option.Equals("Text")) + return ret.First.Text(); + else if (option.Equals("Html")) + return ret.Html(); + else //(JSUtils.isNotEmpty(option)) + { + string? result = ret.Attr(option); + if (option.ToLower().Contains("style") && result.Contains("url(")) + { + Match m = p.Match(result); + if (m.Success) + result = m.Groups[1]?.Value; + result = Regex.Replace(result, "^['|\"](.*)['|\"]$", "$1"); + } + if (!string.IsNullOrWhiteSpace(result) && !string.IsNullOrWhiteSpace(Add_url))// (JSUtils.isNotEmpty(result) && JSUtils.isNotEmpty(Add_url)) + { + // 需要自动urljoin的属性 + Match m = URLJOIN_ATTR.Match(option); + Match n = SPECIAL_URL.Match(result); + //if (isUrl(option)) { + if (m.Success && !n.Success) + { + if (result.Contains("http")) + result = result[result.IndexOf("http")..]; + else + result = JoinUrl(Add_url, result); + } + } + return result; + } + + } + //pdfa + public String[] ParseDomForArray(string html, string rule) + { + if (!pdfa_html.Equals(html)) + { + pdfa_html = html; + pdfa_doc = NSoupClient.Parse(html); + } + Document? doc = pdfa_doc; + List? eleHtml = new(); + //Document doc = NSoupClient.Parse(html); + + rule = parseHikerToJq(rule, false); + string[]? parses = rule.Split(" "); + Elements ret = new (); + foreach (var pars in parses) + { + ret = parseOneRule(doc, pars, ret); + if (ret.IsEmpty) return eleHtml.ToArray(); + } + foreach (Element it in ret) + { + eleHtml.Add(it.OuterHtml()); + } + + return eleHtml.ToArray(); + } + //pdfl + public String[] ParseDomForList(string html, string rule, string list_text, string list_url, string urlKey) + { + if (!pdfa_html.Equals(html)) + { + pdfa_html = html; + pdfa_doc = NSoupClient.Parse(html); + } + Document? doc = pdfa_doc; + //Document doc = NSoupClient.Parse(html); + List? new_vod_list = new(); + + rule = parseHikerToJq(rule, false); + string[]? parses = rule.Split(" "); + Elements ret = new (); + + foreach (string pars in parses) + { + ret = parseOneRule(doc, pars, ret); + if (ret.IsEmpty) return new_vod_list.ToArray(); + } + + foreach (Element it in ret) + { + new_vod_list.Add(ParseDom(it.OuterHtml(), list_text, "").Trim() + '$' + ParseDom(it.OuterHtml(), list_url, urlKey)); + } + + return new_vod_list.ToArray(); + } + + + + private string parseHikerToJq(string parse, bool first) + { + /* + 海阔解析表达式转原生表达式,自动补eq,如果传了first就最后一个也取eq(0) + :param parse: + :param first: + :return: + */ + // 不自动加eq下标索引 + if (parse.Contains("&&")) + { + string[]? parses = parse.Split("&&"); //带&&的重新拼接 + List? new_parses = new(); //构造新的解析表达式列表 + for (int i = 0; i < parses.Length; i++) + { + string[]? pss = parses[i].Split(" "); + string? ps = pss[pss.Length - 1]; //如果分割&&后带空格就取最后一个元素 + Match? m = NOAdd_INDEX.Match(ps); // Matcher m = NOAdd_INDEX.matcher(ps); + //if (!isIndex(ps)) { + if (!m.Success) + { + if (!first && i >= parses.Length - 1) + { //不传first且遇到最后一个,不用补eq(0) + new_parses.Add(parses[i]); + } + else + { + new_parses.Add(parses[i] + ":eq(0)"); + } + } + else + { + new_parses.Add(parses[i]); + } + } + parse = string.Join(" ", new_parses);// TextUtils.join(" ", new_parses); + } + else + { + string[]? pss = parse.Split(" "); + string? ps = pss[pss.Length - 1]; //如果分割&&后带空格就取最后一个元素 + //Matcher m = NOAdd_INDEX.matcher(ps); + Match? m = NOAdd_INDEX.Match(ps); + //if (!isIndex(ps) && first) { + if (!m.Success && first) + { + parse += ":eq(0)"; + } + } + return parse; + } + + private Elements parseOneRule(Document doc, string parse, Elements ret) + { + Painfo? info = GetParseInfo(parse); + if (ret.IsEmpty) + { + ret = doc.Select(info.nparse_rule); + } + else + { + ret = ret.Select(info.nparse_rule); + } + if (parse.Contains(":eq")) + { + if (info.nparse_index < 0) + { + ret = ret.Eq(ret.Count + info.nparse_index); + } + else + { + ret = ret.Eq(info.nparse_index); + } + } + + if (info.excludes != null && !ret.IsEmpty) + { + foreach (var exclude in info.excludes) + { + ret.Select(exclude).Remove(); + } + } + return ret; + } + } +} diff --git a/docs/other/HtmlParser.java b/docs/other/HtmlParser.java new file mode 100644 index 00000000..bdad1e83 --- /dev/null +++ b/docs/other/HtmlParser.java @@ -0,0 +1,320 @@ +package com; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class HtmlParser { + private static String pdfh_html = ""; + private static String pdfa_html = ""; + private static final Pattern p = Pattern.compile("url\\((.*?)\\)", Pattern.MULTILINE | Pattern.DOTALL); + private static final Pattern NOADD_INDEX = Pattern + .compile(":eq|:lt|:gt|:first|:last|:not|:even|:odd|:has|:contains|:matches|:empty|^body$|^#"); // 不自动加eq下标索引 + private static final Pattern URLJOIN_ATTR = Pattern.compile("(url|src|href|-original|-src|-play|-url|style)$|^(data-|url-|src-)", + Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // 需要自动urljoin的属性 + private static final Pattern SPECIAL_URL = Pattern.compile("^(ftp|magnet|thunder|ws):", + Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // 过滤特殊链接,不走urlJoin + private static Document pdfh_doc = null; + private static Document pdfa_doc = null; + + public static String join(CharSequence delimiter, @SuppressWarnings("rawtypes") Iterable tokens) { + final Iterator it = tokens.iterator(); + if (!it.hasNext()) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + sb.append(it.next()); + while (it.hasNext()) { + sb.append(delimiter); + sb.append(it.next()); + } + return sb.toString(); + } + + public static String join(CharSequence delimiter, Object[] tokens) { + final int length = tokens.length; + if (length == 0) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + sb.append(tokens[0]); + for (int i = 1; i < length; i++) { + sb.append(delimiter); + sb.append(tokens[i]); + } + return sb.toString(); + } + + public static String joinUrl(String parent, String child) { + if (parent.isEmpty()) { + return child; + } + + URL url; + String q = parent; + try { + url = new URL(new URL(parent), child); + q = url.toExternalForm(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + // if (q.contains("#")) { + // q = q.replaceAll("^(.+?)#.*?$", "$1"); + // } + return q; + } + + public static class Painfo { + public String nparse_rule; + public int nparse_index; + public List excludes; + } + + private static Painfo getParseInfo(String nparse) { + /* + * 根据传入的单规则获取 parse规则,索引位置,排除列表 -- 可以用于剔除元素,支持多个,按标签剔除,按id剔除等操作 :param nparse: + * :return: + */ + Painfo painfo = new Painfo(); + // List excludes = new ArrayList<>(); //定义排除列表默认值为空 + // int nparse_index; //定义位置索引默认值为0 + painfo.nparse_rule = nparse; // 定义规则默认值为本身 + if (nparse.contains(":eq")) { + painfo.nparse_rule = nparse.split(":")[0]; + String nparse_pos = nparse.split(":")[1]; + + if (painfo.nparse_rule.contains("--")) { + String[] rules = painfo.nparse_rule.split("--"); + painfo.excludes = new ArrayList<>(Arrays.asList(rules)); + painfo.excludes.remove(0); + painfo.nparse_rule = rules[0]; + } else if (nparse_pos.contains("--")) { + String[] rules = nparse_pos.split("--"); + painfo.excludes = new ArrayList<>(Arrays.asList(rules)); + painfo.excludes.remove(0); + nparse_pos = rules[0]; + } + + try { + painfo.nparse_index = Integer.parseInt(nparse_pos.replace("eq(", "").replace(")", "")); + } catch (Exception e1) { + painfo.nparse_index = 0; + } + } else { + if (nparse.contains("--")) { + String[] rules = painfo.nparse_rule.split("--"); + painfo.excludes = new ArrayList<>(Arrays.asList(rules)); + painfo.excludes.remove(0); + painfo.nparse_rule = rules[0]; + } + } + return painfo; + } + + private static String parseHikerToJq(String parse, boolean first) { + /* + * 海阔解析表达式转原生表达式,自动补eq,如果传了first就最后一个也取eq(0) :param parse: :param first: + * :return: + */ + // 不自动加eq下标索引 + if (parse.contains("&&")) { + String[] parses = parse.split("&&"); // 带&&的重新拼接 + List new_parses = new ArrayList<>(); // 构造新的解析表达式列表 + for (int i = 0; i < parses.length; i++) { + String[] pss = parses[i].split(" "); + String ps = pss[pss.length - 1]; // 如果分割&&后带空格就取最后一个元素 + Matcher m = NOADD_INDEX.matcher(ps); + if (!m.find()) { + if (!first && i >= parses.length - 1) { // 不传first且遇到最后一个,不用补eq(0) + new_parses.add(parses[i]); + } else { + new_parses.add(parses[i] + ":eq(0)"); + } + } else { + new_parses.add(parses[i]); + } + } + parse = join(" ", new_parses); + } else { + String[] pss = parse.split(" "); + String ps = pss[pss.length - 1]; // 如果分割&&后带空格就取最后一个元素 + Matcher m = NOADD_INDEX.matcher(ps); + if (!m.find() && first) { + parse = parse + ":eq(0)"; + } + } + return parse; + } + + public static String parseDomForUrl(String html, String rule, String add_url) { + if (!pdfh_html.equals(html)) { + pdfh_html = html; + pdfh_doc = Jsoup.parse(html); + } + Document doc = pdfh_doc; + if (rule.equals("body&&Text") || rule.equals("Text")) { + return doc.text(); + } else if (rule.equals("body&&Html") || rule.equals("Html")) { + return doc.html(); + } + String option = ""; + if (rule.contains("&&")) { + String[] rs = rule.split("&&"); + option = rs[rs.length - 1]; + List excludes = new ArrayList<>(Arrays.asList(rs)); + excludes.remove(rs.length - 1); + rule = join("&&", excludes); + } + rule = parseHikerToJq(rule, true); + String[] parses = rule.split(" "); + Elements ret = new Elements(); + for (String nparse : parses) { + ret = parseOneRule(doc, nparse, ret); + if (ret.isEmpty()) { + return ""; + } + } + String result = null; + if (!option.isEmpty()) { + if (option.equals("Text")) { + result = ret.text(); + } else if (option.equals("Html")) { + result = ret.html(); + } else { + String[] options = option.split("[||]"); + for (String opt : options) { + result = ret.attr(opt); + + if (opt.toLowerCase().contains("style") && result.contains("url(")) { + Matcher m = p.matcher(result); + if (m.find()) { + result = m.group(1); + } + // 2023/07/28新增 style取内部链接自动去除首尾单双引号 + result = result.replaceAll("^['|\"](.*)['|\"]$", "$1"); + } + if (!result.isEmpty() && !add_url.isEmpty()) { + // 需要自动urljoin的属性 + Matcher m = URLJOIN_ATTR.matcher(opt); + Matcher n = SPECIAL_URL.matcher(result); + if (m.find() && !n.find()) { + if (result.contains("http")) { + result = result.substring(result.indexOf("http")); + } else { + result = joinUrl(add_url, result); + } + } + } + if (!result.isEmpty()) { + return result; + } + } + } + + } else { + result = ret.outerHtml(); + } + return result; + + } + + public static List parseDomForArray(String html, String rule) { + if (!pdfa_html.equals(html)) { + pdfa_html = html; + pdfa_doc = Jsoup.parse(html); + } + Document doc = pdfa_doc; + rule = parseHikerToJq(rule, false); + String[] parses = rule.split(" "); + Elements ret = new Elements(); + for (String pars : parses) { + ret = parseOneRule(doc, pars, ret); + if (ret.isEmpty()) { + return new ArrayList<>(); + } + } + + List eleHtml = new ArrayList<>(); + for (int i = 0; i < ret.size(); i++) { + Element element1 = ret.get(i); + eleHtml.add(element1.outerHtml()); + } + return eleHtml; + } + + private static Elements parseOneRule(Document doc, String nparse, Elements ret) { + Painfo painfo = getParseInfo(nparse); + if (ret.isEmpty()) { + ret = doc.select(painfo.nparse_rule); + } else { + ret = ret.select(painfo.nparse_rule); + } + + if (nparse.contains(":eq")) { + if (painfo.nparse_index < 0) { + ret = ret.eq(ret.size() + painfo.nparse_index); + } else { + ret = ret.eq(painfo.nparse_index); + } + } + + if (painfo.excludes != null && !ret.isEmpty()) { + ret = ret.clone(); // 克隆一个, 免得直接remove会影响doc的缓存 + for (int i = 0; i < painfo.excludes.size(); i++) { + ret.select(painfo.excludes.get(i)).remove(); + } + } + return ret; + } + + public static List parseDomForList(String html, String p1, String list_text, String list_url, + String add_url) { + if (!pdfa_html.equals(html)) { + pdfa_html = html; + pdfa_doc = Jsoup.parse(html); + } + Document doc = pdfa_doc; + p1 = parseHikerToJq(p1, false); + String[] parses = p1.split(" "); + Elements ret = new Elements(); + for (String pars : parses) { + ret = parseOneRule(doc, pars, ret); + if (ret.isEmpty()) { + return new ArrayList<>(); + } + } + List new_vod_list = new ArrayList<>(); + for (int i = 0; i < ret.size(); i++) { + String it = ret.get(i).outerHtml(); + new_vod_list.add(parseDomForUrl(it, list_text, "").trim() + '$' + parseDomForUrl(it, list_url, add_url)); + } + return new_vod_list; + } + + public static void main(String[] args) { + XLHttpUtils.Request url = new XLHttpUtils.Request().get().url("https://m.yskanba.com/b-ertu.html"); + String string = url.exec().body().string(); + System.out.println(string); + String html = string; + String rule = ".posterPic&&img&&data-original||src"; + String ret = HtmlParser.parseDomForUrl(html, rule, ""); + System.out.println(ret); + rule = ".tabt3&&span:not(:contains(云播tk))"; + List rets = HtmlParser.parseDomForArray(html, rule); + System.out.println(rets); + rule = ".tabt3 span:not(:matches(云播tk))"; + rets = HtmlParser.parseDomForArray(html, rule); + System.out.println(rets); + } +} diff --git a/docs/other/htmlParser.ts b/docs/other/htmlParser.ts new file mode 100644 index 00000000..47c9814f --- /dev/null +++ b/docs/other/htmlParser.ts @@ -0,0 +1,349 @@ +/*! + * @module htmlParser + * @brief T3解析html处理库 + * @version 3.1.0 + * + * @original-author hjdhnx + * @original-source {@link https://github.com/hjdhnx/hipy-server/blob/master/app/t4/base/htmlParser.py | Source on GitHub} + * + * @modified-by HiramWong + * @modification-date 2023-04-09T18:31:59+08:00 + * @modification-description Python转TypeScript, 适用于JavaScript项目 + */ + +import * as cheerio from 'cheerio'; +import jsonpath from 'jsonpath'; +import urlJoin from 'url'; + +const PARSE_CACHE = true; // 解析缓存 +const NOADD_INDEX = ':eq|:lt|:gt|:first|:last|:not|:even|:odd|:has|:contains|:matches|:empty|^body$|^#'; // 不自动加eq下标索引 +const URLJOIN_ATTR = '(url|src|href|-original|-src|-play|-url|style)$|^(data-|url-|src-)'; // 需要自动urljoin的属性 +const SPECIAL_URL = '^(ftp|magnet|thunder|ws):'; // 过滤特殊链接,不走urlJoin + +class Jsoup { + MY_URL: string = ''; + pdfh_html = ''; + pdfa_html = ''; + + pdfh_doc = null; + pdfa_doc: cheerio.Root | null = null; + + // 构造函数 + constructor(MY_URL: string = '') { + this.MY_URL = MY_URL; + } + + // 测试 + test(text: string, string: string): boolean { + const searchObj = new RegExp(text, 'mi').exec(string); + return searchObj ? true : false; + } + + // 包含 + contains(text: string, match: string): boolean { + return text.indexOf(match) !== -1; + } + + /** + * 海阔解析表达式转原生表达式,自动补eq,如果传了first就最后一个也取eq(0) + * @param parse: 解析表达式 + * @param first: 是否第一个 + * @returns {string} + */ + parseHikerToJq(parse: string, first: boolean = false): string { + if (this.contains(parse, '&&')) { + const parses = parse.split('&&'); // 带&&的重新拼接 + let new_parses: string[] = []; // 构造新的解析表达式列表 + for (let i = 0; i < parses.length; i++) { + const ps_list = parses[i].split(' '); + const ps = ps_list[ps_list.length - 1]; // 如果分割&&后带空格就取最后一个元素 + if (!this.test(NOADD_INDEX, ps)) { + if (!first && i >= parses.length - 1) { + // 不传first且遇到最后一个,不用补eq(0) + new_parses.push(parses[i]); + } else { + new_parses.push(`${parses[i]}:eq(0)`); + } + } else { + new_parses.push(parses[i]); + } + } + parse = new_parses.join(' '); + } else { + const ps_list = parse.split(' '); + const ps = ps_list[ps_list.length - 1]; // 如果带空格就取最后一个元素 + if (!this.test(NOADD_INDEX, ps) && first) { + parse = `${parse}:eq(0)`; + } + } + + return parse; + } + + /** + * 根据传入的单规则获取 parse规则, 索引位置,排除列表 -- 可以用于剔除元素,支持多个, 按标签剔除, 按id剔除等操作 + * @param nparse + * @returns {rule: string, index: number, excludes: string[]} + */ + getParseInfo(nparse: string): { nparse_rule: string; nparse_index: number; excludes: string[] } { + let excludes: string[] = []; // 定义排除列表默认值为空 + let nparse_index: number = 0; // 定义位置索引默认值为0 + let nparse_rule: string = nparse; // 定义规则默认值为本身 + + if (this.contains(nparse, ':eq')) { + nparse_rule = nparse.split(':eq')[0]; + let nparse_pos = nparse.split(':eq')[1]; + if (this.contains(nparse_rule, '--')) { + excludes = nparse_rule.split('--').slice(1); + nparse_rule = nparse_rule.split('--')[0]; + } else if (this.contains(nparse_pos, '--')) { + excludes = nparse_pos.split('--').slice(1); + nparse_pos = nparse_pos.split('--')[0]; + } + try { + nparse_index = parseInt(nparse_pos.split('(')[1].split(')')[0]); + } catch { + } + } else if (this.contains(nparse, '--')) { + nparse_rule = nparse.split('--')[0]; + excludes = nparse.split('--').slice(1); + } + + return { nparse_rule, nparse_index, excludes }; + } + + /** + * 解析空格分割后的原生表达式中的一条记录,正确处理eq的索引,返回处理后的ret + * @param doc: cheerio.load() load后的dom对象 + * @param nparse: 解析表达式 + * @param ret: 当前返回值 + * @returns {Cheerio} + */ + parseOneRule(doc, nparse: string, ret) { + const { nparse_rule, nparse_index, excludes } = this.getParseInfo(nparse); + + if (!ret) ret = doc(nparse_rule); + else ret = ret.find(nparse_rule); + + if (this.contains(nparse, ':eq')) ret = ret.eq(nparse_index); + + if (excludes.length > 0 && ret) { + ret = ret.clone(); // 克隆一个,避免直接remove影响原始DOM + // ret = ret.toArray().map(element => doc(element)); + + for (let exclude of excludes) { + ret.find(exclude).remove(); + } + } + + return ret; + } + + /** + * 解析空格分割后的原生表达式,返回处理后的ret + * https://pyquery.readthedocs.io/en/latest/api.html + * @param html + * @param parse + * @returns {Cheerio} + */ + pdfa(html: string, parse: string): string[] { + if (!html || !parse) return []; + parse = this.parseHikerToJq(parse); + + const doc = cheerio.load(html); + if (PARSE_CACHE) { + if (this.pdfa_html !== html) { + this.pdfa_html = html; + this.pdfa_doc = doc; + } + } + + const parses = parse.split(' '); + let ret: cheerio.Cheerio | null = null; + for (const nparse of parses) { + ret = this.parseOneRule(doc, nparse, ret); + if (!ret) return []; + } + + const res: string[] = (ret?.toArray() ?? []).map((item: any) => { + const res_html = `${doc(item)}`; // outerHTML() + // const res_html = doc(item).html(); // innerHTML() + return res_html ? res_html : ''; // 空值检查,将 null 值转换为空字符串 + }); + return res; + } + + pdfl(html: string, parse: string, list_text: string, list_url: string, url_key: string): string[] { + if (!html || !parse) return []; + parse = this.parseHikerToJq(parse, false); + const new_vod_list: any = []; + + const doc = cheerio.load(html); + const parses: string[] = parse.split(' '); + let ret: cheerio.Cheerio | null = null; + for (const pars of parses) { + ret = this.parseOneRule(doc, pars, ret); + if (!ret) return []; + } + + ret!.each((_, element) => { + new_vod_list.push(`${doc(element)}`); // outerHTML() + // new_vod_list.push(doc(element).html()); // innerHTML() + }); + + return new_vod_list; + } + + /** + * 解析空格分割后的原生表达式,返回处理后的ret + * https://pyquery.readthedocs.io/en/latest/api.html + * @param html + * @param parse + * @returns {Cheerio} + */ + pdfh(html: string, parse: string, baseUrl: string = ''): string { + if (!html || !parse) return ''; + + const doc: cheerio.Root = cheerio.load(html); + if (PARSE_CACHE) { + if (this.pdfa_html !== html) { + this.pdfa_html = html; + this.pdfa_doc = doc; + } + } + + if (parse == 'body&&Text' || parse == 'Text') { + //@ts-ignore + return doc.text(); + } else if (parse == 'body&&Html' || parse == 'Html') { + return doc.html(); + } + + let option: string | undefined; + if (this.contains(parse, '&&')) { + const parts: string[] = parse.split('&&'); + option = parts[parts.length - 1]; + parse = parts.slice(0, -1).join('&&'); + } + parse = this.parseHikerToJq(parse, true); + const parses: string[] = parse.split(' '); + + let ret: string | cheerio.Cheerio | null = null; + for (const nparse of parses) { + ret = this.parseOneRule(doc, nparse, ret); + if (!ret) return ''; + } + if (option) { + switch (option) { + case 'Text': + ret = (ret as cheerio.Cheerio)?.text() || ''; + break; + case 'Html': + ret = (ret as cheerio.Cheerio)?.html() || ''; + break; + default: + // 保留原来的ret + let original_ret = (ret as cheerio.Cheerio)?.clone(); + let options = option.split('||'); + let opt_index = 0; + for (let opt of options) { + // console.log(`opt_index:${opt_index},opt:${opt}`); + opt_index += 1; + ret = original_ret?.attr(opt) || ''; + // console.log('ret:', ret); + if (this.contains(opt.toLowerCase(), 'style') && this.contains(ret, 'url(')) { + try { + ret = ret.match(/url\((.*?)\)/)![1]; + // 2023/07/28新增 style取内部链接自动去除首尾单双引号 + ret = ret.replace(/^['"]|['"]$/g, ''); + } catch { + } + } + if (ret && baseUrl) { + const needAdd = this.test(URLJOIN_ATTR, opt) && !this.test(SPECIAL_URL, ret); + if (needAdd) { + if (ret.includes('http')) { + ret = ret.slice(ret.indexOf('http')); + } else { + ret = urlJoin.resolve(baseUrl, ret); + } + } + } + if (ret) { + break; + } + + } + + } + } else { + ret = `${ret}`; + } + + return ret; + } + + pd(html: string, parse: string, baseUrl: string = ''): string { + if (!baseUrl) baseUrl = this.MY_URL; + return this.pdfh(html, parse, baseUrl); + } + + pq(html: string) { + return cheerio.load(html); + } + + pjfh(html: any, parse: string, addUrl = false): string { + if (!html || !parse) return ''; + + try { + html = typeof html === 'string' ? JSON.parse(html) : html; + } catch { + console.log('字符串转json失败'); + return ''; + } + + if (!parse.startsWith('$.')) { + parse = '$.' + parse; + } + + let ret = ''; + const paths = parse.split('||'); + for (const path of paths) { + const queryResult = jsonpath.query(html, path); + if (Array.isArray(queryResult)) ret = queryResult[0] ? `${queryResult[0]}` : ''; + else ret = queryResult ? `${queryResult}` : ''; + + if (addUrl && ret) { + ret = urlJoin.resolve(this.MY_URL, ret); + } + if (ret) break; + } + + return ret; + } + + pj(html: any, parse: string): string { + return this.pjfh(html, parse, true); + } + + pjfa(html: any, parse: string): any[] { + if (!html || !parse) return []; + + try { + html = typeof html === 'string' ? JSON.parse(html) : html; + } catch { + return []; + } + + if (!parse.startsWith('$.')) parse = '$.' + parse; + + const result = jsonpath.query(html, parse); + if (Array.isArray(result) && Array.isArray(result[0]) && result.length === 1) { + return result[0]; // 自动解包 + } + + return result || []; + } +} + +export default Jsoup; diff --git a/docs/ruleDesc.md b/docs/ruleDesc.md index 60bf963e..8f3a7cd8 100644 --- a/docs/ruleDesc.md +++ b/docs/ruleDesc.md @@ -1,4 +1,5 @@ ### 代码格式化压缩成一行教程 + ```text npm install uglify-js -g uglifyjs xx.js -o xx.min.js @@ -11,114 +12,118 @@ $FileDir$ ``` ### 模板规则说明 + 所有相关属性说明 + ```javascript var rule = { - 类型:'影视',//影视|听书|漫画|小说 - title:'',//规则标题,没有实际作用,但是可以作为cms类名称依据 - 编码:'',//不填就默认utf-8 - 搜索编码:'',//不填则不编码,默认都是按utf-8.可优先于全局编码属性.比如网页源码编码是gbk,这里可以指定utf-8搜索独立编码。多数情况这个属性不填或者填写gbk应对特殊的网站搜索 - host:'',//网页的域名根,包含http头如 https://www,baidu.com - hostJs:'print(HOST);let html=request(HOST,{headers:{"User-Agent":PC_UA}});let src = jsp.pdfh(html,"ul&&li&&a&&href");print(src);HOST=src.replace("/index.php","")',//网页域名根动态抓取js代码。通过HOST=赋值 - homeUrl:'/latest/',//网站的首页链接,可以是完整路径或者相对路径,用于分类获取和推荐获取 fyclass是分类标签 fypage是页数 - url:'/fyclass/fypage.html[/fyclass/]',//网站的分类页面链接 - detailUrl:'https://yanetflix.com/voddetail/fyid.html',//非必填,二级详情拼接链接,感觉没啥卵用 - searchUrl:'',//搜索链接 可以是完整路径或者相对路径,用于分类获取和推荐获取 **代表搜索词 fypage代表页数 - searchable:0,//是否启用全局搜索, - quickSearch:0,//是否启用快速搜索, - filterable:0,//是否启用筛选, - filter:{},// 筛选条件字典 + 类型: '影视',//影视|听书|漫画|小说 + title: '',//规则标题,没有实际作用,但是可以作为cms类名称依据 + 编码: '',//不填就默认utf-8 + 搜索编码: '',//不填则不编码,默认都是按utf-8.可优先于全局编码属性.比如网页源码编码是gbk,这里可以指定utf-8搜索独立编码。多数情况这个属性不填或者填写gbk应对特殊的网站搜索 + host: '',//网页的域名根,包含http头如 https://www,baidu.com + hostJs: 'print(HOST);let html=request(HOST,{headers:{"User-Agent":PC_UA}});let src = jsp.pdfh(html,"ul&&li&&a&&href");print(src);HOST=src.replace("/index.php","")',//网页域名根动态抓取js代码。通过HOST=赋值 + homeUrl: '/latest/',//网站的首页链接,可以是完整路径或者相对路径,用于分类获取和推荐获取 fyclass是分类标签 fypage是页数 + url: '/fyclass/fypage.html[/fyclass/]',//网站的分类页面链接 + detailUrl: 'https://yanetflix.com/voddetail/fyid.html',//非必填,二级详情拼接链接,感觉没啥卵用 + searchUrl: '',//搜索链接 可以是完整路径或者相对路径,用于分类获取和推荐获取 **代表搜索词 fypage代表页数 + searchable: 0,//是否启用全局搜索, + quickSearch: 0,//是否启用快速搜索, + filterable: 0,//是否启用筛选, + filter: {},// 筛选条件字典 // 默认筛选条件字典(不同分类可以指定同样筛选参数的不同默认值) - filter_def:{ - douyu:{ - area:'一起看', - other:'..' + filter_def: { + douyu: { + area: '一起看', + other: '..' }, - huya:{ - area:'影音馆', - other:'..' + huya: { + area: '影音馆', + other: '..' } - }, + }, // 筛选网站传参,会自动传到分类链接下(本示例中的url参数)-url里参数为fyfilter,可参考蓝莓影视.js - filter_url:'style={{fl.style}}&zone={{fl.zone}}&year={{fl.year}}&fee={{fl.fee}}&order={{fl.order}}', + filter_url: 'style={{fl.style}}&zone={{fl.zone}}&year={{fl.year}}&fee={{fl.fee}}&order={{fl.order}}', // 注意,由于猫有配置缓存,搜索配置没法热加载,修改了js不需要重启服务器 // 但是需要tv_box进设置里换源使配置重新装载 - headers:{//网站的请求头,完整支持所有的,常带ua和cookies - 'User-Agent':'MOBILE_UA', + headers: {//网站的请求头,完整支持所有的,常带ua和cookies + 'User-Agent': 'MOBILE_UA', "Cookie": "searchneed=ok" }, // 海阔一级列表样式 - hikerListCol:"avatar", + hikerListCol: "avatar", // 海阔推荐列表样式 - hikerClassListCol:"avatar", - timeout:5000,//网站的全局请求超时,默认是3000毫秒 - class_name:'电影&电视剧&动漫&综艺',//静态分类名称拼接 - class_url:'1&2&3&4',//静态分类标识拼接 + hikerClassListCol: "avatar", + timeout: 5000,//网站的全局请求超时,默认是3000毫秒 + class_name: '电影&电视剧&动漫&综艺',//静态分类名称拼接 + class_url: '1&2&3&4',//静态分类标识拼接 //动态分类获取 列表;标题;链接;正则提取 不需要正则的时候后面别加分号 - class_parse:'#side-menu:lt(1) li;a&&Text;a&&href;com/(.*?)/', + class_parse: '#side-menu:lt(1) li;a&&Text;a&&href;com/(.*?)/', // 除开全局过滤之外还需要过滤哪些标题不视为分类 - cate_exclude:'', + cate_exclude: '', // 除开全局动态线路名过滤之外还需要过滤哪些线路名标题不视为线路 - tab_exclude:'', + tab_exclude: '', //移除某个线路及相关的选集|js1 - tab_remove:['tkm3u8'], + tab_remove: ['tkm3u8'], //线路顺序,按里面的顺序优先,没写的依次排后面|js1 - tab_order:['lzm3u8','wjm3u8','1080zyk','zuidam3u8','snm3u8'], + tab_order: ['lzm3u8', 'wjm3u8', '1080zyk', 'zuidam3u8', 'snm3u8'], //线路名替换如:lzm3u8替换为量子资源|js1 - tab_rename:{'lzm3u8':'量子','1080zyk':'1080看','zuidam3u8':'最大资源','kuaikan':'快看', - 'bfzym3u8':'暴风','ffm3u8':'非凡','snm3u8':'索尼','tpm3u8':'淘片','tkm3u8':'天空',}, + tab_rename: { + 'lzm3u8': '量子', '1080zyk': '1080看', 'zuidam3u8': '最大资源', 'kuaikan': '快看', + 'bfzym3u8': '暴风', 'ffm3u8': '非凡', 'snm3u8': '索尼', 'tpm3u8': '淘片', 'tkm3u8': '天空', + }, // 服务器解析播放 - play_parse:true, + play_parse: true, // play_json 传数组或者 类 true/false 比如 0,1 如果不传会内部默认处理 不传和传0可能效果不同 // 效果等同说明: play_json:[{re:'*', json:{jx:0, parse:1}}], 等同于 play_json:0, - play_json:[{ - re:'*', - json:{ - jx:1, - parse:1, + play_json: [{ + re: '*', + json: { + jx: 1, + parse: 1, }, }], //控制不同分类栏目下的总页面,不填就是默认999.哔哩影视大部分分类无法翻页,都需要指定页数为 1 - pagecount:{"1":1,"2":1,"3":1,"4":1,"5":1,"7":1,"时间表":1}, + pagecount: {"1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "7": 1, "时间表": 1}, // 自定义免嗅 - lazy:'', + lazy: '', // 首页推荐显示数量 - limit:6, - double:true,//是否双层列表定位,默认false + limit: 6, + double: true,//是否双层列表定位,默认false // 对图片加了referer验证的有效,海阔专用,普通规则请勿填写此键值 - 图片来源:'@Referer=http://www.jianpianapp.com@User-Agent=jianpian-version350', + 图片来源: '@Referer=http://www.jianpianapp.com@User-Agent=jianpian-version350', // 替换所有图片链接 欲替换文本=>替换为文本 - 图片替换:'https://www.keke6.app/=>https://vres.a357899.cn/', - + 图片替换: 'https://www.keke6.app/=>https://vres.a357899.cn/', + // js写法,仅js模式1有效.可以用于代码动态获取全局cookie之类的 // 可操作变量有 rule_fetch_params,rule,以及基础的网页访问request,post等操作 - 预处理:'rule_fetch_params.headers.Cookie = "xxxx";', + 预处理: 'rule_fetch_params.headers.Cookie = "xxxx";', // 类似海阔一级 列表;标题;图片;描述;链接;详情 其中最后一个参数选填 // 如果是双层定位的话,推荐的第2段分号代码也是第2层定位列表代码 - 推荐:'.col-sm-6;h3&&Text;img&&data-src;.date&&Text;a&&href', + 推荐: '.col-sm-6;h3&&Text;img&&data-src;.date&&Text;a&&href', // 类似海阔一级 列表;标题;图片;描述;链接;详情 其中最后一个参数选填 - 一级:'.col-sm-6;h3&&Text;img&&data-src;.date&&Text;a&&href', + 一级: '.col-sm-6;h3&&Text;img&&data-src;.date&&Text;a&&href', //二级发起访问前进行js处理。解决特殊情况一级给出的链接非二级真实源码而是前端重定向链接的源码 - 二级访问前:'log(MY_URL);let jump=request(MY_URL).match(/href="(.*?)"/)[1];log(jump);MY_URL=urljoin2(MY_URL,jump)', + 二级访问前: 'log(MY_URL);let jump=request(MY_URL).match(/href="(.*?)"/)[1];log(jump);MY_URL=urljoin2(MY_URL,jump)', // 二级可以是*,表示规则无二级,直接拿一级的链接进行嗅探 // 二级 title: 片名;类型 // 二级 desc: 主要信息;年代;地区;演员;导演 // 或者 {title:'',img:'',desc:'',content:'',tabs:'',lists:'',tab_text:'body&&Text',list_text:'body&&Text',list_url:'a&&href'} 同海阔dr二级 - 二级:'*', + 二级: '*', // 搜索可以是*,集成一级,或者跟一级一样的写法 列表;标题;图片;描述;链接;详情 - 搜索:'*', + 搜索: '*', // 本地代理规则,可用于修改m3u8文件文本去广告后返回代理文件地址,也可以代理加密图片 - proxy_rule:`js: + proxy_rule: `js: log(input); input = [200,'text;plain','hello drpy'] `, //是否启用辅助嗅探: 1,0 - sniffer:1, + sniffer: 1, // 辅助嗅探规则 - isVideo:"http((?!http).){26,}\\.(m3u8|mp4|flv|avi|mkv|wmv|mpg|mpeg|mov|ts|3gp|rm|rmvb|asf|m4a|mp3|wma)", + isVideo: "http((?!http).){26,}\\.(m3u8|mp4|flv|avi|mkv|wmv|mpg|mpeg|mov|ts|3gp|rm|rmvb|asf|m4a|mp3|wma)", // 辅助嗅探规则js写法 - isVideo:`js: + isVideo: `js: log(input); if(/m3u8/.test(input)){ input = true @@ -128,28 +133,35 @@ var rule = { `, } ``` + 模板继承写法 + ```javascript -var rule = Object.assign(muban.mxpro,{ -title:'鸭奈飞', -host:'https://yanetflix.com', -url:'/index.php/vod/show/id/fyclass/page/fypage.html', -class_parse:`.navbar-items li:gt(1):lt(6);a&&Text;a&&href;.*/(.*?).html`, +var rule = Object.assign(muban.mxpro, { + title: '鸭奈飞', + host: 'https://yanetflix.com', + url: '/index.php/vod/show/id/fyclass/page/fypage.html', + class_parse: `.navbar-items li:gt(1):lt(6);a&&Text;a&&href;.*/(.*?).html`, }); ``` + 模板继承写法(新) + ```javascript var rule = { -title:'cokemv', -模板:'mxpro', -host:'https://cokemv.me', -class_parse:`.navbar-items li:gt(1):lt(7);a&&Text;a&&href;/(\\d+).html`, + title: 'cokemv', + 模板: 'mxpro', + host: 'https://cokemv.me', + class_parse: `.navbar-items li:gt(1):lt(7);a&&Text;a&&href;/(\\d+).html`, } ``` + 模板继承写法(自动匹配) + ```text 注意事项:自动匹配只支持能从HOST获取分类的cms模板站,采集1,短视2等api的模板无法匹配 ``` + ```javascript var rule = { 模板: '自动', @@ -167,7 +179,8 @@ var rule = { } ``` -源正则写法说明 +源正则写法说明 + ```text 属性class_parse按;分隔后取[3]为分类的正则字符串。 这里的正则跟js的/.*/这种写法相比,由于是字符串,需要实现字符串标准。 @@ -188,12 +201,13 @@ proxy_rule参数input赋值格式为三元素列表[status,content-type,data] 如: [200,'text/plain','hello drpy'] input = [200,'application/vnd.apple.mpegurl',m3u8] rsa加解密说明: + ```js RSA.encode(data, key, option); RSA.decode(data, key, option); ``` -### 其它类型源说明 +### 其它类型源说明 默认drpy源都是为早期的tvbox而生的,因此只支持影视类型的源(听书也当影视用)。 现在已三端支持,拥抱海阔视界和zyplayer生态。 @@ -201,4 +215,11 @@ RSA.decode(data, key, option); 实验特性:支持 类型:'影视',//影视|听书|漫画|小说 影视和听书用法一致不需要调整。 漫画需要在选集播放lazy处理后的url里返回 pics:// 协议,用法同海阔。参考源【第一韩漫】 -小说需要在选集播放lazy处理后的url里返回 novel:// 协议,内容为json文本。如 novel://{"title":"章节名称","content":"章节内容"} +小说需要在选集播放lazy处理后的url里返回 novel:// 协议,内容为json文本。如 novel://{"title":"章节名称","content":" +章节内容"} + +## pd系列网页定位写法参考 + +1. [C#版本代码](./other/htmlParser.cs) +2. [JAVA版本代码](./other/htmlParser.java) +3. [TS版本代码](./other/htmlParser.ts) diff --git a/docs/updateRecord.md b/docs/updateRecord.md index cfc7b43a..6fa7355e 100644 --- a/docs/updateRecord.md +++ b/docs/updateRecord.md @@ -1,5 +1,57 @@ # drpyS更新记录 +### 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 + +1. 合并E佬修改的源,新增&修复源 +2. 新增drpy2-fast壳依赖,需自行适配爱老so文件,取消不可用的dr2的t4模式,改为使用drpy2-fast本地依赖 +3. 规范一些小说源的代码,确保统一返回了类型:'小说' +4. 修改了index.js中的start、stop函数确保有返回值,适配新版zy本地插件 +5. 调整了部分内置解析 + 其它细节自测... + +### 20260115 + +更新至V1.3.17 + +1. 新增一些源 & 修复一些源 + +### 20260113 + +更新至V1.3.16 + +1. 新增全局 `executeParse` 函数,ds/cat源可实现获取本地自建解析链接 +2. 写源说明文档里增加了其它几种语言的 htmlParser实现,可供参考 +3. 更新了部分源 + ### 20260112 更新至V1.3.15 diff --git a/index.js b/index.js index 6b140a7a..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, @@ -250,7 +262,7 @@ const start = async () => { } else { console.log('Not running on Vercel!'); } - + return true; } catch (err) { fastify.log.error(err); process.exit(1); @@ -265,8 +277,10 @@ const stop = async () => { // 停止主服务器 await fastify.server.close(); console.log('🛑 所有服务已优雅停止'); + return true; } catch (err) { fastify.log.error(`停止服务器时发生错误:${err.message}`); + return false; } }; 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 00d03223..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" @@ -23,6 +23,214 @@ "password": "", "会员时长": "", "lazyheader": {} + }, + "仓鼠": { + "muban": "AppQiji", + "host": "https://hk440cms.cs4k.top", + "hosturl": "", + "key": "fL7sY4zN4kB3pG4p", + "iv": "fL7sY4zN4kB3pG4p", + "verify": "true" + }, + "云云": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://staraugust123456.oss-cn-hangzhou.aliyuncs.com/2.txt", + "key": "staraugust123456", + "iv": "staraugust123456", + "verify": "true" + }, + "奇奇": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://yun-1316442804.cos.ap-guangzhou.myqcloud.com/a.txt", + "key": "123456789abcdefg", + "iv": "123456789abcdefg", + "verify": "true" + }, + "鲸鱼": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://jingyu4k-1312635929.cos.ap-nanjing.myqcloud.com/1.json", + "key": "AAdgrdghjfgswerA", + "iv": "AAdgrdghjfgswerA", + "verify": "true" + }, + "咖啡": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://daen-1256234123.cos.ap-shanghai.myqcloud.com/MuQi/mqxh.txt", + "key": "37kj83zs1q16jk6t", + "iv": "37kj83zs1q16jk6t", + "verify": "true" + }, + "小猪": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://tiantangyoulu.oss-cn-beijing.aliyuncs.com/tengxunyun.txt", + "key": "seb5tq9mykp2w9ry", + "iv": "seb5tq9mykp2w9ry", + "verify": "true" + }, + "影视": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://aysappto.oss-cn-chengdu.aliyuncs.com/qj2.txt", + "key": "sada21321sdq231d", + "iv": "sada21321sdq231d", + "verify": "true" + }, + "优兔": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://uututv-1319209748.cos.ap-shanghai.myqcloud.com/uutuv4.txt", + "key": "UrWKPnmQWJA8AQzd", + "iv": "UrWKPnmQWJA8AQzd", + "verify": "true" + }, + "王子": { + "muban": "AppGet", + "host": "https://app.95112475.xyz", + "hosturl": "", + "key": "5a9w6x58dsq6z3a6", + "iv": "5a9w6x58dsq6z3a6", + "verify": "true" + }, + "紫金": { + "muban": "AppGet", + "host": "", + "hosturl": "https://snysw.xyz/mf4kzs327.txt", + "key": "1234567887654321", + "iv": "1234567887654321", + "verify": "true" + }, + "数字": { + "muban": "AppGet", + "host": "http://app1-0-0.87333.cc", + "hosturl": "", + "key": "VwsHxkCViDXEExWa", + "iv": "VwsHxkCViDXEExWa", + "verify": "true" + }, + "火锅": { + "muban": "AppGet", + "host": "https://ios.hgyx.vip", + "hosturl": "", + "key": "062dec75d039980e", + "iv": "062dec75d039980e", + "verify": "true" + }, + "五八": { + "muban": "AppGet", + "host": "https://dy.58ys.vip", + "hosturl": "", + "key": "JEWibY1AgWF0V1xx", + "iv": "JEWibY1AgWF0V1xx", + "verify": "true" + }, + "旗星": { + "muban": "AppGet", + "host": "http://ys.qist.top", + "hosturl": "", + "key": "2SWSPFxugBLPPOKo", + "iv": "2SWSPFxugBLPPOKo", + "verify": "true" + }, + "灵虎": { + "muban": "AppGet", + "host": "", + "hosturl": "https://bind.315999.xyz/89.txt", + "key": "#getapp@TMD@2025", + "iv": "#getapp@TMD@2025", + "verify": "true" + }, + "剧梦": { + "muban": "AppGet", + "host": "https://www.jumengwu.com", + "hosturl": "", + "key": "1f0a873caf2550a5", + "iv": "1f0a873caf2550a5", + "verify": "true" + }, + "火狐": { + "muban": "AppGet", + "host": "http://huohu.yihn.cc", + "hosturl": "", + "key": "huohushipingetap", + "iv": "huohushipingetap", + "verify": "true" + }, + "金牌": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://dtqj.ggtvb.cc/dtjp.txt", + "key": "eecbio48dsq131ee", + "iv": "eecbio48dsq131ee", + "verify": "true" + }, + "爱盈": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://aysappto.oss-cn-chengdu.aliyuncs.com/qj3.txt", + "key": "sda1231sasddad21", + "iv": "sda1231sasddad21", + "verify": "true" + }, + "顾我": { + "muban": "AppQiji", + "host": "http://117.50.204.35:520", + "hosturl": "", + "key": "ca94b06ca3c7d80e", + "iv": "ca94b06ca3c7d80e", + "verify": "true" + }, + "爆炸": { + "muban": "AppQiji", + "host": "", + "hosturl": "https://daen-1256234123.cos.ap-shanghai.myqcloud.com/MuQi/mqxh.txt", + "key": "37kj83zs1q16jk6t", + "iv": "37kj83zs1q16jk6t", + "verify": "true" + }, + "丫丫动漫": { + "muban": "AppGet", + "host": "http://tv.yy-fun.cc", + "hosturl": "", + "key": "qkxnwkfjwpcnwycl", + "iv": "qkxnwkfjwpcnwycl", + "verify": "true" + }, + "番薯动漫": { + "muban": "AppGet", + "host": "https://new.app.bytegooty.com", + "hosturl": "", + "key": "N4yj7l7xKxHF4*gz", + "iv": "N4yj7l7xKxHF4*gz", + "verify": "true" + }, + "咕咕动漫": { + "muban": "AppGet", + "host": "https://www.gugu3.com", + "hosturl": "", + "key": "nKfZ8KX6JTNWRzTD", + "iv": "nKfZ8KX6JTNWRzTD", + "verify": "true" + }, + "元咲动漫": { + "muban": "AppGet", + "host": "http://cic.aicg.fun", + "hosturl": "", + "key": "2c4h36abd96se10u", + "iv": "2c4h36abd96se10u", + "verify": "true" + }, + "方舟动漫": { + "muban": "AppGet", + "host": "https://www.cyfz.vip", + "hosturl": "", + "key": "e72cdfd629e8895d", + "iv": "e72cdfd629e8895d", + "verify": "true" } }, "Appmuou": { @@ -40,14 +248,6 @@ "key1": "", "key2": "", "version": "" - }, - "光映视界": { - "host": "", - "hosturl": "https://ymdtsy.lingutv.cn/shark/api.txt", - "key": "IKXRx4M6cB45SNTs", - "key1": "rectangleadsadxa", - "key2": "aassddwwxxllsx1x", - "version": "2.2.0" } } } \ No newline at end of file diff --git "a/json/TG\351\242\221\351\201\223\351\205\215\347\275\256.json" "b/json/TG\351\242\221\351\201\223\351\205\215\347\275\256.json" index 273f60a0..6cda37ec 100644 --- "a/json/TG\351\242\221\351\201\223\351\205\215\347\275\256.json" +++ "b/json/TG\351\242\221\351\201\223\351\205\215\347\275\256.json" @@ -23,10 +23,6 @@ "type_id": "yunpanuc", "type_name": "UC资源" }, - { - "type_id": "xiangnikanj", - "type_name": "短剧频道" - }, { "type_id": "PanjClub", "type_name": "盘酱酱Club" diff --git "a/jx/JSON\345\220\210\351\233\206.js" "b/jx/JSON\345\220\210\351\233\206.js" index b0f4703d..1965a65e 100644 --- "a/jx/JSON\345\220\210\351\233\206.js" +++ "b/jx/JSON\345\220\210\351\233\206.js" @@ -64,7 +64,7 @@ async function lazy(input, params) { * 包含多个备用解析接口,提高解析成功率 */ let parse_list = [ - "https://zy.qiaoji8.com/gouzi.php?url=", // 主要解析接口 + "https://kalbim.xatut.top/kalbim2025/781718/play/video_player.php?url=", // 主要解析接口 "http://1.94.221.189:88/algorithm.php?url=" // 备用解析接口 ] diff --git a/jx/json1.js b/jx/json1.js index a588aa42..7a0886d5 100644 --- a/jx/json1.js +++ b/jx/json1.js @@ -82,7 +82,7 @@ async function lazy(input, params) { let timeout = 8000; // 设置请求超时时间为8秒 // 调用第三方解析API获取视频直链 - let obj = await requestJson('https://cdnsrc.cdnapi.top/json/?url=' + input, {headers, timeout}); + let obj = await requestJson('https://kalbim.xatut.top/kalbim2025/781718/play/video_player.php?url=' + input, {headers, timeout}); return obj.url // 返回解析后的视频播放链接 } diff --git a/jx/web1.js b/jx/web1.js index e190d820..6c870d83 100644 --- a/jx/web1.js +++ b/jx/web1.js @@ -63,7 +63,7 @@ const jx = { * 添加url属性直接暴露api,不走系统。建议web解析才写这个属性,json解析隐藏起来 * @type {string} */ - url: 'https://bfq.cfwlgzs.cn/player?url=', + url: 'https://www.ckplayer.vip/jiexi/?url=', }; /** 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 e0827c86..6d57ba76 100644 --- a/libs/drpyS.js +++ b/libs/drpyS.js @@ -16,7 +16,7 @@ import {createWebDAVClient} from '../utils/webdav.js'; import {createFTPClient} from '../utils/ftp.js'; import {ENV} from '../utils/env.js'; import {getContentType, getMimeType} from "../utils/mime-type.js"; -import {getParsesDict, getSitesMap, pathLib, es6_extend_code, req_extend_code} from "../utils/file.js"; +import {getParsesDict, getSitesMap, pathLib, executeParse, es6_extend_code, req_extend_code} from "../utils/file.js"; import {getFirstLetter} from "../utils/pinyin-tool.js"; import {reqs} from "../utils/req.js"; import {toBeijingTime} from "../utils/datetime-format.js" @@ -285,6 +285,7 @@ export async function getSandbox(env = {}) { axiosX, URL, pathLib, + executeParse, qs, Buffer, URLSearchParams, 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 0ba76628..9cc52648 100644 --- a/package.js +++ b/package.js @@ -7,7 +7,7 @@ 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_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/奇奇.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 15c80a45..0d84c6ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drpy-node", - "version": "1.3.15", + "version": "1.3.21", "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 3067cbcb..9949858f 100644 --- a/package.py +++ b/package.py @@ -16,8 +16,10 @@ 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/奇奇.js', 'jx/芒果关姐.js', 'data/settings/link_data.json', 'index.json', 'custom.json'] + 'jx/_30wmv.js', 'jx/奇奇.js', 'jx/芒果关姐.js', 'data/settings/link_data.json', 'index.json', + 'custom.json'] def get_script_dir(): @@ -43,13 +45,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: 压缩包的完整路径 @@ -62,7 +65,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, "..")) @@ -106,7 +110,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压缩命令 @@ -114,9 +118,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}") @@ -129,22 +135,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__": @@ -154,7 +161,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/download.html b/public/download.html new file mode 100644 index 00000000..64feb013 --- /dev/null +++ b/public/download.html @@ -0,0 +1,609 @@ + + + + + + 下载 {{projectName}} + + + +
+

{{projectName}} 下载中心

+ +
+ 历史文件管理 +
+ + +
+
+ +
链接已复制到剪贴板
+ +
+ {{downloadItems}} +
+
+ + + + diff --git a/public/drpy/drpy2-fast.min.js b/public/drpy/drpy2-fast.min.js new file mode 100644 index 00000000..8d397161 --- /dev/null +++ b/public/drpy/drpy2-fast.min.js @@ -0,0 +1,102 @@ +import{cheerio,模板}from"../dist/drpy-core-fast.min.js";let vercode=typeof pdfl==="function"?"drpy2.1":"drpy2";const VERSION=vercode+" 3.9.54 20260117";const UpdateInfo=[{date:"20260117",title:"爱佬新So测试版,使用drpy-core-fast.min.js",version:"3.9.54 20260117",msg:` +drpy-core-fast.min.js内置了url和sqlite模块 + `},{date:"20251007",title:"爱佬新So测试版,使用drpy-core-lite.min.js,内置了Buffer库",version:"3.9.53 20251007",msg:` +drpy-core-lite.min.js 内置Buffer库 +gzip和ungzip改为新so的zlib实现 +内置RSA对象加解密效率提升 + + `},{date:"20250801",title:"drpy依赖更新,使用drpy-core-lite.min.js",version:"3.9.52beta3 20250801",msg:` +drpy-core.min.js 更换为更小的drpy-core-lite.min.js + + `},{date:"20250729",title:"drpy更新,所有依赖打包成一个js文件",version:"3.9.52beta2 20250729",msg:` + 1. wasm支持 + 2. 引入 TextEncoder、TextDecoder对象 + 3. 引入 WXXH 加解密库 + 4. 所有依赖打包成一个js + 5. 增加 buildQueryString + + `},{date:"20250728",title:"drpy更新,增加tab_order线路模糊排序,优化解密算法支持文件头",version:"3.9.52beta1 20250728",msg:` + 1. 增加tab_order线路模糊排序 + 2. 优化解密算法支持文件头 + 3. wasm支持 + 4. 增加 removeHeader 函数可用于清除js/py文件的头信息及所有头注释 + 5. 引入 TextEncoder、TextDecoder对象 + 6. 引入 WXXH 加解密库 + `},{date:"20241126",title:"drpy更新,优化去广告算法",version:"3.9.51beta6 20241126",msg:` + 1. 更新龙头大佬提供的去广告算法 + `},{date:"20241104",title:"drpy更新,增加新特性",version:"3.9.51beta5 20241104",msg:` + 1. rule增加 搜索验证标识 属性,可以不定义,默认为 '系统安全验证|请输入验证码' + 2. rule增加 searchNoPage 属性,可以不定义,如果定义 1 将关闭该源的搜索翻页功能,超过1页直接返回空 + `}];function getUpdateInfo(){return UpdateInfo.map(_o=>{_o.msg=_o.msg.trim().split("\n").map(_it=>_it.trim()).join("\n");return _o})}function init_test(){console.log("init_test_start");console.log("当前版本号:"+VERSION);console.log("本地代理地址:"+getProxyUrl());console.log(RKEY);console.log(JSON.stringify(rule));console.log("init_test_end")}function ocr_demo_test(){let img_base64=`iVBORw0KGgoAAAANSUhEUgAAAIAAAAAoBAMAAADEX+97AAAAG1BMVEXz+/4thQTa7N6QwIFFkyNeokKozqDB3b93sWHFR+MEAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABN0lEQVRIie2TQU+DQBCFt9vScvQpxR4xrcSjJCZ67JDGXsX+AdR4B3vpsSYm/m2HXaRLmuySepR3Gdidb/btDAjRq5dT96eCMlfBuzi1QLZUoZy2yz5sOvI+9iomaPEZ6nWnEtxqIyiM1RcAy44GNDhBXUjot/VVNweV1ah68FqWRyjKIOqAcyYF6rGcmpYnHzGt3fycNoMw0d3/THFu7hFSJ/8OXO6iTM8/KSg09obAzIHLO250LgQ0txOZSfgrV4Exdw98uGycJ0ErAeExZGhOmFHV9zHO6qVSj0MpLq7xZON56o++MjlsEgfVhbQWWME+xQX7J4V6zfi9A1Ly9rP1BvEXp+BbVJ/M77n+wfOIDVp51pZ4iBxvmj9AGrtvry6emwfKnVkW+ZRKd5ZNMvob36vXP9YPDmQki8QiCFAAAAAASUVORK5CYII=`;OcrApi.api=OCR_API;let code=OcrApi.classification(img_base64);log("测试验证码图片的ocr识别结果为:"+code)}function rsa_demo_test(){let t1=(new Date).getTime();let pkcs1_public=` +-----BEGIN RSA PUBLIC KEY----- +MEgCQQCrI0pQ/ERRpJ3Ou190XJedFq846nDYP52rOtXyDxlFK5D3p6JJu2RwsKwy +lsQ9xY0xYPpRZUZKMEeR7e9gmRNLAgMBAAE= +-----END RSA PUBLIC KEY----- +`.trim();let pkcs1_public_pem=` +MEgCQQCrI0pQ/ERRpJ3Ou190XJedFq846nDYP52rOtXyDxlFK5D3p6JJu2RwsKwy +lsQ9xY0xYPpRZUZKMEeR7e9gmRNLAgMBAAE= +`.trim();let pkcs8_public=` +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKsjSlD8RFGknc67X3Rcl50WrzjqcNg/ +nas61fIPGUUrkPenokm7ZHCwrDKWxD3FjTFg+lFlRkowR5Ht72CZE0sCAwEAAQ== +-----END PUBLIC KEY-----`.trim();let pkcs8_public_pem=` +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKsjSlD8RFGknc67X3Rcl50WrzjqcNg/ +nas61fIPGUUrkPenokm7ZHCwrDKWxD3FjTFg+lFlRkowR5Ht72CZE0sCAwEAAQ== +`.trim();let pkcs1_private=` +-----BEGIN RSA PRIVATE KEY----- +MIIBOAIBAAJBAKsjSlD8RFGknc67X3Rcl50WrzjqcNg/nas61fIPGUUrkPenokm7 +ZHCwrDKWxD3FjTFg+lFlRkowR5Ht72CZE0sCAwEAAQI/b6OV1z65UokQaMvSeRXt +0Yv6wiYtduQI9qpq5nzy/ytaqsbBfClNTi/HifKPKxlRouWFkc518EQI8LBxoarJ +AiEA4DaONMplV8PQNa3TKn2F+SDEvLOCjdL0kHKdN90Ti28CIQDDZnTBaHgZwZbA +hS7Bbf5yvwjWMhO6Y7l04/Qm7R+35QIgPuQuqXIoUSD080mp1N5WyRW++atksIF+ +5lGv9e6GP/MCICnj8y/rl6Pd7tXDN6zcSeqLrfdNsREKhB3dKOCXgW9JAiAFYtFS +EJNBXVRTK42SNsZ2hJ/9xLwOwnH2epT8Q43s3Q== +-----END RSA PRIVATE KEY----- +`.trim();let pkcs8_private=` +-----BEGIN PRIVATE KEY----- +MIIBUgIBADANBgkqhkiG9w0BAQEFAASCATwwggE4AgEAAkEAqyNKUPxEUaSdzrtf +dFyXnRavOOpw2D+dqzrV8g8ZRSuQ96eiSbtkcLCsMpbEPcWNMWD6UWVGSjBHke3v +YJkTSwIDAQABAj9vo5XXPrlSiRBoy9J5Fe3Ri/rCJi125Aj2qmrmfPL/K1qqxsF8 +KU1OL8eJ8o8rGVGi5YWRznXwRAjwsHGhqskCIQDgNo40ymVXw9A1rdMqfYX5IMS8 +s4KN0vSQcp033ROLbwIhAMNmdMFoeBnBlsCFLsFt/nK/CNYyE7pjuXTj9CbtH7fl +AiA+5C6pcihRIPTzSanU3lbJFb75q2SwgX7mUa/17oY/8wIgKePzL+uXo93u1cM3 +rNxJ6out902xEQqEHd0o4JeBb0kCIAVi0VIQk0FdVFMrjZI2xnaEn/3EvA7CcfZ6 +lPxDjezd +-----END PRIVATE KEY----- +`.trim();let data=` +NodeRsa +这是node-rsa 现在修改集成在drpy里使用`.trim();let encryptedWithPublic=NODERSA.encryptRSAWithPublicKey(data,pkcs1_public,{outputEncoding:"base64",options:{environment:"browser",encryptionScheme:"pkcs1_oaep"}});console.log("公钥加密");console.log(encryptedWithPublic);let decryptedWithPrivate=NODERSA.decryptRSAWithPrivateKey(encryptedWithPublic,pkcs1_private,{options:{environment:"browser",encryptionScheme:"pkcs1_oaep"}});console.log("私钥解密");console.log(decryptedWithPrivate);let pkcs1_sha256_sign=NODERSA.sign("1",pkcs1_private,{outputEncoding:"base64",options:{environment:"browser",encryptionScheme:"pkcs1",signingScheme:"pkcs1-sha256"}});console.log("pkcs1_sha256_sign");console.log(pkcs1_sha256_sign);let pkcs1_sha256_sign_verify=NODERSA.verify("1","Oulx2QrgeipKYBtqEDqFb2s/+ndk2cGQxO4CkhU7iBM1vyNmmvqubpsmeoUuN3waGrYZLknSEdwBkfv0tUMpFQ==",pkcs1_private,{options:{environment:"browser",encryptionScheme:"pkcs1",signingScheme:"pkcs1-sha256"}});console.log("pkcs1_sha256_sign_verify");console.log(pkcs1_sha256_sign_verify);let pkcs1_oaep_sha256=NODERSA.encryptRSAWithPublicKey(data,`-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEA5KOq1gRNyllLNWKQy8sGpZE3Q1ULLSmzZw+eaAhj9lvqn7IsT1du +SYn08FfoOA2qMwtz+1O2l1mgzNoSVCyVpVabnTG+C9XKeZXAnJHd8aYA7l7Sxhdm +kte+iymYZ0ZBPzijo8938iugtVvqi9UgDmnY3u/NlQDqiL5BGqSxSTd/Sgmy3zD8 +PYzEa3wD9vehQ5fZZ45vKIq8GNVh2Z8+IGO85FF1OsN7+b2yGJa/FmDDNn0+HP+m +PfI+kYBqEVpo0Ztbc3UdxgFwGC8O1n8AQyriwHnSOtIiuBH62J/7qyC/3LEAApRb +Dd9YszqzmODjQUddZKHmvc638VW+azc0EwIDAQAB +-----END RSA PUBLIC KEY----- +`,{outputEncoding:"base64",options:{environment:"browser",encryptionScheme:{scheme:"pkcs1_oaep",hash:"sha256"}}});console.log("pkcs1_oaep_sha256");console.log(pkcs1_oaep_sha256);decryptedWithPrivate=NODERSA.decryptRSAWithPrivateKey("kSZesAAyYh2hdsQnYMdGqb6gKAzTauBKouvBzWcc4+F8RvGd0nwO6mVkUMVilPgUuNxjEauHayHiY8gI3Py45UI3+km0rSGyHrS6dHiHgCkMejXHieglYzAB0IxX3Jkm4z/66bdB/D+GFy0oct5fGCMI1UHPjEAYOsazJDa8lBFNbjiWFeb/qiZtIx3vGM7KYPAZzyRf/zPbbQ8zy9xOmRuOl5nnIxgo0Okp3KO/RIPO4GZOSBA8f2lx1UtNwwrXAMpcNavtoqHVcjJ/9lcotXYQFrn5b299pSIRf2gVm8ZJ31SK6Z8cc14nKtvgnmsgClDzIXJ1o1RcDK+knVAySg==",`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA5KOq1gRNyllLNWKQy8sGpZE3Q1ULLSmzZw+eaAhj9lvqn7Is +T1duSYn08FfoOA2qMwtz+1O2l1mgzNoSVCyVpVabnTG+C9XKeZXAnJHd8aYA7l7S +xhdmkte+iymYZ0ZBPzijo8938iugtVvqi9UgDmnY3u/NlQDqiL5BGqSxSTd/Sgmy +3zD8PYzEa3wD9vehQ5fZZ45vKIq8GNVh2Z8+IGO85FF1OsN7+b2yGJa/FmDDNn0+ +HP+mPfI+kYBqEVpo0Ztbc3UdxgFwGC8O1n8AQyriwHnSOtIiuBH62J/7qyC/3LEA +ApRbDd9YszqzmODjQUddZKHmvc638VW+azc0EwIDAQABAoIBADZ/QGgUzInvsLp/ +zO2WbfYm39o/uhNAvk9RbLt1TIZbMFhyOpeKynHi3Swwd9xsfWX/U9zS/lGi/m31 +iKrhmaW4OA1G3vqpMcK7TBbFufYwUEaA+ZJX344euH8pIfdzyneMQ4z3Far2dS7l +QsmjuilVV2kEFadveXewiYoVOWCu00w6bN8wy2SIHlQn+kIL6HQhWz12iKKflIKu +eGRdzLHsKmBt6WbY1Wuhx7HU0fAKdlBDPxCHNlI+kybUYE9o5C2vJiaVM5wqJBgZ +8Dz8kt1QbLJ910JoLXkLVQ8uC8NJKQwFtqQjTGPnEq0+wbgz6Ij599rKZkwW/xq9 +l6KoUiECgYEA6Ah42tVdkNW047f03xVYXFH96RgorHRS36mR8Y+ONUq1fwKidovC +WjwVujt4OPf3l1W6iyn/F6cu/bsmvPrSc3HTN0B1V31QK4OjgetxQ2PSbTldH02J +NPzkt+v+cPxXpx/P5mgt7Weefw5txU547KubGrHUV5rBKFtIx9pj16MCgYEA/EF0 +o19+D24DZAPwlDS5VbEd7FStnwY4oQ5PqbuNOSbSJLMWU0AqzXcRokp8UTyCZ0X3 +ATkS1REq97kShCuR+npTR6a6DlY7sdpPI1SMLNajgB2tkx0EOzX+PfNIbHUd4jpJ +I0ZMAHv/OOtkzQHDaeTWBTrzsWm6/nTiykfduNECgYEA46AMD4HpPECqKAs66e5i +tI6q7JSKskObWVdcmQEfnSAhVOwcvPb2Ptda6UuV8S0xcwDi88rLOUUFUFzc79+P +vTkY38cYVi/VChsluDpk7ptqv0PbGu5Rf+3n4pZdEjI7OvR2W64wAAn67uIUxc7p +yiO/ET0K9rYWb6S9jXGtKMkCgYEA2kPAqoO7zZoBMQ7/oR0lp/HC1HRIbiqx4RlC +8Lgpb+QZPEwA6zPAVVvLVENi4d+bbcRp/xLlKpraNNJcJSSWAMbLPFoU7sbKjA87 +HnTPfRSTEA2d3Ibk3F7Rh8TzS3Ti0JZiJjVzGZAwu41iAMifzwaD8K6boUy80eNN +QH2CaaECgYBUsLYvC/MiYg3w+LGOONuQongoVUXjGqnw2bjVa9RK7lwRdXPUqJ51 +MpVO98IkoLvGSI/0sGNP3GKNhC+eMGjJAVwFyEuOn+JsmMv9Y9uStIVi5tIHIhKw +m7mp8il0kaftHdSxTbspG3tZ2fjIiFIZkLEOmRpd7ogWumgOajzUdA== +-----END RSA PRIVATE KEY-----`,{options:{environment:"browser",encryptionScheme:"pkcs1_oaep"}});console.log("decryptedWithPrivate");console.log(decryptedWithPrivate);(()=>{let key=new NODERSA.NodeRSA({b:1024});key.setOptions({encryptionScheme:"pkcs1"});let text=`你好drpy node-ras`;let encrypted=key.encrypt(text,"base64");console.log("encrypted: ",encrypted);const decrypted=key.decrypt(encrypted,"utf8");console.log("decrypted: ",decrypted)})();let t2=(new Date).getTime();console.log("rsa_demo_test 测试耗时:"+(t2-t1)+"毫秒")}function pre(){if(typeof rule.预处理==="string"&&rule.预处理&&rule.预处理.trim()){let code=rule.预处理.trim();console.log("执行预处理代码:"+code);if(code.startsWith("js:")){code=code.replace("js:","")}try{eval(code)}catch(e){console.log(`预处理执行失败:${e.message}`)}}}let rule={};const MOBILE_UA="Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36";const PC_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36";const UA="Mozilla/5.0";const UC_UA="Mozilla/5.0 (Linux; U; Android 9; zh-CN; MI 9 Build/PKQ1.181121.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.5.5.1035 Mobile Safari/537.36";const IOS_UA="Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1";const RULE_CK="cookie";const CATE_EXCLUDE="首页|留言|APP|下载|资讯|新闻|动态";const TAB_EXCLUDE="猜你|喜欢|下载|剧情|榜|评论";const OCR_RETRY=3;const OCR_API="https://api.nn.ci/ocr/b64/text";if(typeof MY_URL==="undefined"){var MY_URL}var HOST;var RKEY;var fetch;var print;var log;var rule_fetch_params;var fetch_params;var oheaders;var _pdfh;var _pdfa;var _pd;const DOM_CHECK_ATTR=/(url|src|href|-original|-src|-play|-url|style)$/;const SPECIAL_URL=/^(ftp|magnet|thunder|ws):/;const NOADD_INDEX=/:eq|:lt|:gt|:first|:last|^body$|^#/;const URLJOIN_ATTR=/(url|src|href|-original|-src|-play|-url|style)$|^(data-|url-|src-)/;const SELECT_REGEX=/:eq|:lt|:gt|#/g;const SELECT_REGEX_A=/:eq|:lt|:gt/g;const $js={toString(func){let strfun=func.toString();return strfun.replace(/^\(\)(\s+)?=>(\s+)?\{/,"js:").replace(/\}$/,"")}};function window_b64(){let b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";let base64DecodeChars=new Array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);function btoa(str){var out,i,len;var c1,c2,c3;len=str.length;i=0;out="";while(i>2);out+=b64map.charAt((c1&3)<<4);out+="==";break}c2=str.charCodeAt(i++);if(i==len){out+=b64map.charAt(c1>>2);out+=b64map.charAt((c1&3)<<4|(c2&240)>>4);out+=b64map.charAt((c2&15)<<2);out+="=";break}c3=str.charCodeAt(i++);out+=b64map.charAt(c1>>2);out+=b64map.charAt((c1&3)<<4|(c2&240)>>4);out+=b64map.charAt((c2&15)<<2|(c3&192)>>6);out+=b64map.charAt(c3&63)}return out}function atob(str){var c1,c2,c3,c4;var i,len,out;len=str.length;i=0;out="";while(i>4);do{c3=str.charCodeAt(i++)&255;if(c3==61)return out;c3=base64DecodeChars[c3]}while(i>2);do{c4=str.charCodeAt(i++)&255;if(c4==61)return out;c4=base64DecodeChars[c4]}while(ithis.length){return false}else{return this.indexOf(search,start)!==-1}}}if(!Array.prototype.includes){Object.defineProperty(Array.prototype,"includes",{value:function(searchElement,fromIndex){if(this==null){throw new TypeError('"this" is null or not defined')}var o=Object(this);var len=o.length>>>0;if(len===0){return false}var n=fromIndex|0;var k=Math.max(n>=0?n:len-Math.abs(n),0);while(k>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:out+=String.fromCharCode(c);break;case 12:case 13:char2=array[i++];out+=String.fromCharCode((c&31)<<6|char2&63);break;case 14:char2=array[i++];char3=array[i++];out+=String.fromCharCode((c&15)<<12|(char2&63)<<6|(char3&63)<<0);break}}return out}function gzip(str){let arr=zlib.gzip(str);return Buffer.from(arr,"utf-8").toString("base64")}function ungzip(b64Data){const binData=Buffer.from(b64Data,"base64");const data=zlib.ungzip(binData.buffer);return Buffer.from(data,"utf8").toString()}function encodeStr(input,encoding){encoding=encoding||"gbk";if(encoding.startsWith("gb")){input=gbkTool.encode(input)}return input}function decodeStr(input,encoding){encoding=encoding||"gbk";if(encoding.startsWith("gb")){input=gbkTool.decode(input)}return input}function getCryptoJS(){return'console.log("CryptoJS已装载");'}const RSA={cleanPEM:function(pem){pem=pem.replace(/-----BEGIN [A-Z0-9 ]+-----/g,"").replace(/-----END [A-Z0-9 ]+-----/g,"");pem=pem.replace(/\s/g,"");return pem},importPrivateKey:function(pem){const binaryDer=Uint8Array.from(Buffer.from(this.cleanPEM(pem),"base64"));const importedKey=crypto.subtle.importKey("pkcs8",binaryDer,{name:"RSA-PKCS1-v1_5",hash:"SHA-256"},false,["decrypt"]);return importedKey},importPublicKey:function(pem){const binaryDer=Uint8Array.from(Buffer.from(this.cleanPEM(pem),"base64"));const importedKey=crypto.subtle.importKey("spki",binaryDer,{name:"RSA-PKCS1-v1_5",hash:"SHA-256"},false,["encrypt"]);return importedKey},encryptMergedData:function(publicKey,data){const modulusLengthBytes=publicKey.algorithm.modulusLength+7>>3;const segmentLength=modulusLengthBytes-11;const dataBuffer=(new TextEncoder).encode(data);if(dataBuffer.length>segmentLength){const segments=[];for(let i=0;iacc+segment.length,0);let encryptedData=new Uint8Array(totalLength);let offset=0;for(const segment of encryptedSegments){encryptedData.set(segment,offset);offset+=segment.length}return encryptedData.slice(0,offset)}return crypto.subtle.encrypt({name:"RSA-PKCS1-v1_5"},publicKey,dataBuffer)},decryptMergedData:function(privateKey,mergedData){const segmentLength=privateKey.algorithm.modulusLength+7>>3;if(mergedData.length>segmentLength){const segments=[];for(let i=0;i{new_m3u8_body.push(it)})}}new_m3u8_body=new_m3u8_body.join("\n").trim();m3u8_text=[m3u8_start,new_m3u8_body,m3u8_end].join("\n").trim();return m3u8_text}function fixAdM3u8Ai(m3u8_url,headers){let ts=(new Date).getTime();let option=headers?{headers:headers}:{};function b(s1,s2){let i=0;while(iit.startsWith("#")?it:urljoin(m3u8_url,it)).join("\n");m3u8=m3u8.replace(/\n\n/gi,"\n");let last_url=m3u8.split("\n").slice(-1)[0];if(last_url.length<5){last_url=m3u8.split("\n").slice(-2)[0]}if(last_url.includes(".m3u8")&&last_url!==m3u8_url){m3u8_url=urljoin2(m3u8_url,last_url);log("嵌套的m3u8_url:"+m3u8_url);m3u8=request(m3u8_url,option)}let s=m3u8.trim().split("\n").filter(it=>it.trim()).join("\n");let ss=s.split("\n");if(m3u8_url.indexOf("ffzy")>0){let j=0,k1=0,m=0,n=0,t=0;let s2="";for(let i=0;i0){if(maxl>b(firststr,s)+1){if(secondstr.length<5)secondstr=s;kkk2++}else{maxl=b(firststr,s);kkk1++}}kk++;if(kk>=30)break}}if(kkk2>kkk1)firststr=secondstr;let firststrlen=firststr.length;let ml=Math.round(ss.length/2).toString().length;let maxc=0;let laststr=ss.toReversed().find(x=>{if(!x.startsWith("#")){let k=b(reverseString(firststr),reverseString(x));maxl=b(firststr,x);maxc++;if(firststrlen-maxl<=ml+k||maxc>10){return true}}return false});log("最后一条切片:"+laststr);let ad_urls=[];for(let i=0;i=end){return lists}let first=lists[start];let second=lists[end];if(key){try{first=first[key];second=second[key]}catch(e){}}if(option&&typeof option==="function"){try{first=option(first);second=option(second)}catch(e){}}first+="";second+="";if(first.match(/(\d+)/)&&second.match(/(\d+)/)){let num1=Number(first.match(/(\d+)/)[1]);let num2=Number(second.match(/(\d+)/)[1]);if(num1>num2){lists.reverse()}}return lists}let VODS=[];let VOD={};let TABS=[];let LISTS=[];function getQuery(url){try{if(url.indexOf("?")>-1){url=url.slice(url.indexOf("?")+1)}let arr=url.split("#")[0].split("&");const resObj={};arr.forEach(item=>{let arr1=item.split("=");let key=arr1[0];let value=arr1.slice(1).join("=");resObj[key]=value});return resObj}catch(err){log(`getQuery发生错误:${e.message}`);return{}}}function urljoin(fromPath,nowPath){fromPath=fromPath||"";nowPath=nowPath||"";return joinUrl(fromPath,nowPath)}var urljoin2=urljoin;const defaultParser={pdfh:pdfh,pdfa:pdfa,pd:pd};function pdfh2(html,parse){let html2=html;try{if(typeof html!=="string"){html2=html.rr(html.ele).toString()}}catch(e){print(`html对象转文本发生了错误:${e.message}`)}let result=defaultParser.pdfh(html2,parse);let option=parse.includes("&&")?parse.split("&&").slice(-1)[0]:parse.split(" ").slice(-1)[0];if(/style/.test(option.toLowerCase())&&/url\(/.test(result)){try{result=result.match(/url\((.*?)\)/)[1];result=result.replace(/^['|"](.*)['|"]$/,"$1")}catch(e){}}return result}function pdfa2(html,parse){let html2=html;try{if(typeof html!=="string"){html2=html.rr(html.ele).toString()}}catch(e){print(`html对象转文本发生了错误:${e.message}`)}return defaultParser.pdfa(html2,parse)}function pd2(html,parse,uri){let ret=pdfh2(html,parse);if(typeof uri==="undefined"||!uri){uri=""}if(DOM_CHECK_ATTR.test(parse)&&!SPECIAL_URL.test(ret)){if(/http/.test(ret)){ret=ret.slice(ret.indexOf("http"))}else{ret=urljoin(MY_URL,ret)}}return ret}const parseTags={jsp:{pdfh:pdfh2,pdfa:pdfa2,pd:pd2},json:{pdfh(html,parse){if(!parse||!parse.trim()){return""}if(typeof html==="string"){html=JSON.parse(html)}parse=parse.trim();if(!parse.startsWith("$.")){parse="$."+parse}parse=parse.split("||");for(let ps of parse){let ret=cheerio.jp(ps,html);if(Array.isArray(ret)){ret=ret[0]||""}else{ret=ret||""}if(ret&&typeof ret!=="string"){ret=ret.toString()}if(ret){return ret}}return""},pdfa(html,parse){if(!parse||!parse.trim()){return""}if(typeof html==="string"){html=JSON.parse(html)}parse=parse.trim();if(!parse.startsWith("$.")){parse="$."+parse}let ret=cheerio.jp(parse,html);if(Array.isArray(ret)&&Array.isArray(ret[0])&&ret.length===1){return ret[0]||[]}return ret||[]},pd(html,parse){let ret=parseTags.json.pdfh(html,parse);if(ret){return urljoin(MY_URL,ret)}return ret}},jq:{pdfh(html,parse){if(!html||!parse||!parse.trim()){return""}parse=parse.trim();let result=defaultParser.pdfh(html,parse);return result},pdfa(html,parse){if(!html||!parse||!parse.trim()){return[]}parse=parse.trim();let result=defaultParser.pdfa(html,parse);print(`pdfa解析${parse}=>${result.length}`);return result},pd(html,parse,base_url){if(!html||!parse||!parse.trim()){return""}parse=parse.trim();base_url=base_url||MY_URL;return defaultParser.pd(html,parse,base_url)}},getParse(p0){if(p0.startsWith("jsp:")){return this.jsp}else if(p0.startsWith("json:")){return this.json}else if(p0.startsWith("jq:")){return this.jq}else{return this.jq}}};const stringify=JSON.stringify;const jsp=parseTags.jsp;const jq=parseTags.jq;function readFile(filePath){filePath=filePath||"./uri.min.js";var fd=os.open(filePath);var buffer=new ArrayBuffer(1024);var len=os.read(fd,buffer,0,1024);console.log(len);let text=String.fromCharCode.apply(null,new Uint8Array(buffer));console.log(text);return text}function dealJson(html){try{html=html.trim();if(!(html.startsWith("{")&&html.endsWith("}")||html.startsWith("[")&&html.endsWith("]"))){html="{"+html.match(/.*?\{(.*)\}/m)[1]+"}"}}catch(e){}try{html=JSON.parse(html)}catch(e){}return html}var OcrApi={api:OCR_API,classification:function(img){let code="";try{log("通过drpy_ocr验证码接口过验证...");let html="";if(this.api.endsWith("drpy/text")){html=request(this.api,{data:{img:img},headers:{"User-Agent":PC_UA},method:"POST"},true)}else{html=post(this.api,{body:img})}code=html||""}catch(e){log(`OCR识别验证码发生错误:${e.message}`)}return code}};function verifyCode(url){let cnt=0;let host=getHome(url);let cookie="";while(cntit.toLowerCase()==="set-cookie");cookie=setCk?json[setCk].split(";")[0]:""}console.log("cookie:"+cookie);let img=json.body;let code=OcrApi.classification(img);console.log(`第${cnt+1}次验证码识别结果:${code}`);let submit_url=`${host}/index.php/ajax/verify_check?type=search&verify=${code}`;console.log(submit_url);let html=request(submit_url,{headers:{Cookie:cookie},method:"POST"});html=JSON.parse(html);if(html.msg==="ok"){console.log(`第${cnt+1}次验证码提交成功`);return cookie}else if(html.msg!=="ok"&&cnt+1>=OCR_RETRY){cookie=""}}catch(e){console.log(`第${cnt+1}次验证码提交失败:${e.message}`);if(cnt+1>=OCR_RETRY){cookie=""}}cnt+=1}return cookie}function setItem(k,v){local.set(RKEY,k,v);console.log(`规则${RKEY}设置${k} => ${v}`)}function getItem(k,v){return local.get(RKEY,k)||v}function clearItem(k){local.delete(RKEY,k)}function getHome(url){if(!url){return""}let tmp=url.split("//");url=tmp[0]+"//"+tmp[1].split("/")[0];try{url=decodeURIComponent(url)}catch(e){}return url}function buildUrl(url,obj){obj=obj||{};if(url.indexOf("?")<0){url+="?"}let param_list=[];let keys=Object.keys(obj);keys.forEach(it=>{param_list.push(it+"="+obj[it])});let prs=param_list.join("&");if(keys.length>0&&!url.endsWith("?")){url+="&"}url+=prs;return url}function $require(url){eval(request(url))}function keysToLowerCase(obj){return Object.keys(obj).reduce((result,key)=>{const newKey=key.toLowerCase();result[newKey]=obj[key];return result},{})}function buildQueryString(params){const queryArray=[];for(const key in params){if(params.hasOwnProperty(key)){let value=params[key];if(value===undefined||value===null){value=""}else{value=value.toString()}const encodedKey=encodeURIComponent(key);const encodedValue=encodeURIComponent(value);queryArray.push(encodedKey+"="+encodedValue)}}return queryArray.join("&")}function parseQueryString(query){const params={};query.split("&").forEach(function(part){const regex=/^(.*?)=(.*)/;const match=part.match(regex);if(match){const key=decodeURIComponent(match[1]);const value=decodeURIComponent(match[2]);params[key]=value}});return params}function encodeIfContainsSpecialChars(value){const specialChars=":/?#[]@!$'()*+,;=%";if(specialChars.split("").some(char=>value.includes(char))){return encodeURIComponent(value)}return value}function objectToQueryString(obj){const encoded=[];for(let key in obj){if(obj.hasOwnProperty(key)){encoded.push(encodeURIComponent(key)+"="+encodeIfContainsSpecialChars(obj[key]))}}return encoded.join("&")}function request(url,obj,ocr_flag){ocr_flag=ocr_flag||false;if(typeof obj==="undefined"||!obj||obj==={}){if(!fetch_params||!fetch_params.headers){let headers={"User-Agent":MOBILE_UA};if(rule.headers){Object.assign(headers,rule.headers)}if(!fetch_params){fetch_params={}}fetch_params.headers=headers}if(!fetch_params.headers.Referer){fetch_params.headers.Referer=getHome(url)}obj=fetch_params}else{let headers=obj.headers||{};let keys=Object.keys(headers).map(it=>it.toLowerCase());if(!keys.includes("user-agent")){headers["User-Agent"]=MOBILE_UA;if(typeof fetch_params==="object"&&fetch_params&&fetch_params.headers){let fetch_headers=keysToLowerCase(fetch_params.headers);if(fetch_headers["user-agent"]){headers["User-Agent"]=fetch_headers["user-agent"]}}}if(!keys.includes("referer")){headers["Referer"]=getHome(url)}obj.headers=headers}if(rule.encoding&&rule.encoding!=="utf-8"&&!ocr_flag){if(!obj.headers.hasOwnProperty("Content-Type")&&!obj.headers.hasOwnProperty("content-type")){obj.headers["Content-Type"]="text/html; charset="+rule.encoding}}if(typeof obj.body!="undefined"&&obj.body&&typeof obj.body==="string"){if(!obj.headers.hasOwnProperty("Content-Type")&&!obj.headers.hasOwnProperty("content-type")){obj.headers["Content-Type"]="application/x-www-form-urlencoded; charset="+rule.encoding}}else if(typeof obj.body!="undefined"&&obj.body&&typeof obj.body==="object"){obj.data=obj.body;delete obj.body}if(!url){return obj.withHeaders?"{}":""}if(obj.toBase64){obj.buffer=2;delete obj.toBase64}if(obj.redirect===false){obj.redirect=0}if(obj.headers.hasOwnProperty("Content-Type")||obj.headers.hasOwnProperty("content-type")){let _contentType=obj.headers["Content-Type"]||obj.headers["content-type"]||"";if(_contentType.includes("application/x-www-form-urlencoded")){log("custom body is application/x-www-form-urlencoded");if(typeof obj.body=="string"){let temp_obj=parseQueryString(obj.body);console.log(JSON.stringify(temp_obj))}}}console.log(JSON.stringify(obj.headers));console.log("request:"+url+`|method:${obj.method||"GET"}|body:${obj.body||""}`);let res=req(url,obj);let html=res.content||"";if(obj.withHeaders){let htmlWithHeaders=res.headers;htmlWithHeaders.body=html;return JSON.stringify(htmlWithHeaders)}else{return html}}function post(url,obj){obj=obj||{};obj.method="POST";return request(url,obj)}function reqCookie(url,obj,all_cookie){obj=obj||{};obj.withHeaders=true;all_cookie=all_cookie||false;let html=request(url,obj);let json=JSON.parse(html);let setCk=Object.keys(json).find(it=>it.toLowerCase()==="set-cookie");let cookie=setCk?json[setCk]:"";if(Array.isArray(cookie)){cookie=cookie.join(";")}if(!all_cookie){cookie=cookie.split(";")[0]}html=json.body;return{cookie:cookie,html:html}}fetch=request;print=function(data){data=data||"";if(typeof data=="object"&&Object.keys(data).length>0){try{data=JSON.stringify(data);console.log(data)}catch(e){console.log(typeof data+":"+data.length);return}}else if(typeof data=="object"&&Object.keys(data).length<1){console.log("null object")}else{console.log(data)}};log=print;function checkHtml(html,url,obj){if(/\?btwaf=/.test(html)){let btwaf=html.match(/btwaf(.*?)"/)[1];url=url.split("#")[0]+"?btwaf"+btwaf;print("宝塔验证访问链接:"+url);html=request(url,obj)}return html}function getCode(url,obj){let html=request(url,obj);html=checkHtml(html,url,obj);return html}function getHtml(url){let obj={};if(rule.headers){obj.headers=rule.headers}let cookie=getItem(RULE_CK,"");if(cookie){if(obj.headers&&!Object.keys(obj.headers).map(it=>it.toLowerCase()).includes("cookie")){log("历史无cookie,新增过验证后的cookie");obj.headers["Cookie"]=cookie}else if(obj.headers&&obj.headers.cookie&&obj.headers.cookie!==cookie){obj.headers["Cookie"]=cookie;log("历史有小写过期的cookie,更新过验证后的cookie")}else if(obj.headers&&obj.headers.Cookie&&obj.headers.Cookie!==cookie){obj.headers["Cookie"]=cookie;log("历史有大写过期的cookie,更新过验证后的cookie")}else if(!obj.headers){obj.headers={Cookie:cookie};log("历史无headers,更新过验证后的含cookie的headers")}}let html=getCode(url,obj);return html}function homeParse(homeObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let classes=[];if(homeObj.class_name&&homeObj.class_url){let names=homeObj.class_name.split("&");let urls=homeObj.class_url.split("&");let cnt=Math.min(names.length,urls.length);for(let i=0;i0){classes=list}}}catch(e){console.log(e.message)}}else if(p.length>=3&&!is_json){try{let html=homeObj.home_html||getHtml(homeObj.MY_URL);if(html){homeHtmlCache=html;let list=_pdfa(html,p0);if(list&&list.length>0){list.forEach((it,idex)=>{try{let name=_pdfh(it,p[1]);if(homeObj.cate_exclude&&new RegExp(homeObj.cate_exclude).test(name)){return}let url=_pd(it,p[2]);if(p.length>3&&p[3]&&!homeObj.home_html){let exp=new RegExp(p[3]);url=url.match(exp)[1]}classes.push({type_id:url.trim(),type_name:name.trim()})}catch(e){console.log(`分类列表定位第${idex}个元素正常报错:${e.message}`)}})}}}catch(e){console.log(e.message)}}}}classes=classes.filter(it=>!homeObj.cate_exclude||!new RegExp(homeObj.cate_exclude).test(it.type_name));let resp={class:classes};if(homeObj.filter){resp.filters=homeObj.filter}console.log(JSON.stringify(resp));return JSON.stringify(resp)}function getPP(p,pn,pp,ppn){try{let ps=p[pn]==="*"&&pp.length>ppn?pp[ppn]:p[pn];return ps}catch(e){return""}}function homeVodParse(homeVodObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let d=[];MY_URL=homeVodObj.homeUrl;console.log(MY_URL);let t1=(new Date).getTime();let p=homeVodObj.推荐;print("p:"+p);if(p==="*"&&rule.一级){p=rule.一级;homeVodObj.double=false}if(!p||typeof p!=="string"){return"{}"}p=p.trim();let pp=rule.一级?rule.一级.split(";"):[];if(p.startsWith("js:")){const TYPE="home";var input=MY_URL;HOST=rule.host;eval(p.replace("js:",""));d=VODS}else{p=p.split(";");if(!homeVodObj.double&&p.length<5){return"{}"}else if(homeVodObj.double&&p.length<6){return"{}"}let p0=getPP(p,0,pp,0);let _ps=parseTags.getParse(p0);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p0.startsWith("json:");p0=p0.replace(/^(jsp:|json:|jq:)/,"");let html=homeHtmlCache||getHtml(MY_URL);homeHtmlCache=undefined;if(is_json){html=dealJson(html)}try{console.log("double:"+homeVodObj.double);if(homeVodObj.double){let items=_pdfa(html,p0);let p1=getPP(p,1,pp,0);let p2=getPP(p,2,pp,1);let p3=getPP(p,3,pp,2);let p4=getPP(p,4,pp,3);let p5=getPP(p,5,pp,4);let p6=getPP(p,6,pp,5);for(let item of items){let items2=_pdfa(item,p1);for(let item2 of items2){try{let title=_pdfh(item2,p2);let img="";try{img=_pd(item2,p3)}catch(e){}let desc="";try{desc=_pdfh(item2,p4)}catch(e){}let links=[];for(let _p5 of p5.split("+")){let link=!homeVodObj.detailUrl?_pd(item2,_p5,MY_URL):_pdfh(item2,_p5);links.push(link)}let content;if(p.length>6&&p[6]){content=_pdfh(item2,p6)}else{content=""}let vid=links.join("$");if(rule.二级==="*"){vid=vid+"@@"+title+"@@"+img}let vod={vod_name:title,vod_pic:img,vod_remarks:desc,vod_content:content,vod_id:vid};d.push(vod)}catch(e){console.log(`首页列表双层定位处理发生错误:${e.message}`)}}}}else{let items=_pdfa(html,p0);let p1=getPP(p,1,pp,1);let p2=getPP(p,2,pp,2);let p3=getPP(p,3,pp,3);let p4=getPP(p,4,pp,4);let p5=getPP(p,5,pp,5);for(let item of items){try{let title=_pdfh(item,p1);let img="";try{img=_pd(item,p2,MY_URL)}catch(e){}let desc="";try{desc=_pdfh(item,p3)}catch(e){}let links=[];for(let _p5 of p4.split("+")){let link=!homeVodObj.detailUrl?_pd(item,_p5,MY_URL):_pdfh(item,_p5);links.push(link)}let content;if(p.length>5&&p[5]){content=_pdfh(item,p5)}else{content=""}let vid=links.join("$");if(rule.二级==="*"){vid=vid+"@@"+title+"@@"+img}let vod={vod_name:title,vod_pic:img,vod_remarks:desc,vod_content:content,vod_id:vid};d.push(vod)}catch(e){console.log(`首页列表单层定位处理发生错误:${e.message}`)}}}}catch(e){}}let t2=(new Date).getTime();console.log("加载首页推荐耗时:"+(t2-t1)+"毫秒");if(rule.图片替换){if(rule.图片替换.startsWith("js:")){d.forEach(it=>{try{var input=it.vod_pic;eval(rule.图片替换.trim().replace("js:",""));it.vod_pic=input}catch(e){log(`图片:${it.vod_pic}替换错误:${e.message}`)}})}else if(rule.图片替换.includes("=>")){let replace_from=rule.图片替换.split("=>")[0];let replace_to=rule.图片替换.split("=>")[1];d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic.replace(replace_from,replace_to)}})}}if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}if(d.length>0){print(d.slice(0,2))}return JSON.stringify({list:d})}function categoryParse(cateObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let p=cateObj.一级;if(!p||typeof p!=="string"){return"{}"}let d=[];let url=cateObj.url.replaceAll("fyclass",cateObj.tid);if(cateObj.pg===1&&url.includes("[")&&url.includes("]")){url=url.split("[")[1].split("]")[0]}else if(cateObj.pg>1&&url.includes("[")&&url.includes("]")){url=url.split("[")[0]}if(rule.filter_url){if(!/fyfilter/.test(url)){if(!url.endsWith("&")&&!rule.filter_url.startsWith("&")){url+="&"}url+=rule.filter_url}else{url=url.replace("fyfilter",rule.filter_url)}url=url.replaceAll("fyclass",cateObj.tid);let fl=cateObj.filter?cateObj.extend:{};if(rule.filter_def&&typeof rule.filter_def==="object"){try{if(Object.keys(rule.filter_def).length>0&&rule.filter_def.hasOwnProperty(cateObj.tid)){let self_fl_def=rule.filter_def[cateObj.tid];if(self_fl_def&&typeof self_fl_def==="object"){let fl_def=JSON.parse(JSON.stringify(self_fl_def));fl=Object.assign(fl_def,fl)}}}catch(e){print(`合并不同分类对应的默认筛选出错:${e.message}`)}}let new_url;new_url=cheerio.jinja2(url,{fl:fl,fyclass:cateObj.tid});url=new_url}if(/fypage/.test(url)){if(url.includes("(")&&url.includes(")")){let url_rep=url.match(/.*?\((.*)\)/)[1];let cnt_page=url_rep.replaceAll("fypage",cateObj.pg);let cnt_pg=eval(cnt_page);url=url.replaceAll(url_rep,cnt_pg).replaceAll("(","").replaceAll(")","")}else{url=url.replaceAll("fypage",cateObj.pg)}}MY_URL=url;console.log(MY_URL);p=p.trim();const MY_CATE=cateObj.tid;if(p.startsWith("js:")){var MY_FL=cateObj.extend;const TYPE="cate";var input=MY_URL;const MY_PAGE=cateObj.pg;var desc="";eval(p.trim().replace("js:",""));d=VODS}else{p=p.split(";");if(p.length<5){return"{}"}let _ps=parseTags.getParse(p[0]);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p[0].startsWith("json:");p[0]=p[0].replace(/^(jsp:|json:|jq:)/,"");try{let html=getHtml(MY_URL);if(html){if(is_json){html=dealJson(html)}let list=_pdfa(html,p[0]);list.forEach(it=>{let links=p[4].split("+").map(p4=>{return!rule.detailUrl?_pd(it,p4,MY_URL):_pdfh(it,p4)});let link=links.join("$");let vod_id=rule.detailUrl?MY_CATE+"$"+link:link;let vod_name=_pdfh(it,p[1]).replace(/\n|\t/g,"").trim();let vod_pic=_pd(it,p[2],MY_URL);if(rule.二级==="*"){vod_id=vod_id+"@@"+vod_name+"@@"+vod_pic}d.push({vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,vod_remarks:_pdfh(it,p[3]).replace(/\n|\t/g,"").trim()})})}}catch(e){console.log(e.message)}}if(rule.图片替换){if(rule.图片替换.startsWith("js:")){d.forEach(it=>{try{var input=it.vod_pic;eval(rule.图片替换.trim().replace("js:",""));it.vod_pic=input}catch(e){log(`图片:${it.vod_pic}替换错误:${e.message}`)}})}else if(rule.图片替换.includes("=>")){let replace_from=rule.图片替换.split("=>")[0];let replace_to=rule.图片替换.split("=>")[1];d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic.replace(replace_from,replace_to)}})}}if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}if(d.length>0){print(d.slice(0,2))}let pagecount=0;if(rule.pagecount&&typeof rule.pagecount==="object"&&rule.pagecount.hasOwnProperty(MY_CATE)){print(`MY_CATE:${MY_CATE},pagecount:${JSON.stringify(rule.pagecount)}`);pagecount=parseInt(rule.pagecount[MY_CATE])}let nodata={list:[{vod_name:"无数据,防无限请求",vod_id:"no_data",vod_remarks:"不要点,会崩的",vod_pic:"https://ghproxy.net/https://raw.githubusercontent.com/hjdhnx/dr_py/main/404.jpg"}],total:1,pagecount:1,page:1,limit:1};let vod=d.length<1?JSON.stringify(nodata):JSON.stringify({page:parseInt(cateObj.pg),pagecount:pagecount||999,limit:20,total:999,list:d});return vod}function searchParse(searchObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let d=[];if(!searchObj.searchUrl){return"{}"}if(rule.searchNoPage&&Number(searchObj.pg)>1){return"{}"}let p=searchObj.搜索==="*"&&rule.一级?rule.一级:searchObj.搜索;if(!p||typeof p!=="string"){return"{}"}p=p.trim();let pp=rule.一级?rule.一级.split(";"):[];let url=searchObj.searchUrl.replaceAll("**",searchObj.wd);if(searchObj.pg===1&&url.includes("[")&&url.includes("]")&&!url.includes("#")){url=url.split("[")[1].split("]")[0]}else if(searchObj.pg>1&&url.includes("[")&&url.includes("]")&&!url.includes("#")){url=url.split("[")[0]}if(/fypage/.test(url)){if(url.includes("(")&&url.includes(")")){let url_rep=url.match(/.*?\((.*)\)/)[1];let cnt_page=url_rep.replaceAll("fypage",searchObj.pg);let cnt_pg=eval(cnt_page);url=url.replaceAll(url_rep,cnt_pg).replaceAll("(","").replaceAll(")","")}else{url=url.replaceAll("fypage",searchObj.pg)}}MY_URL=url;console.log(MY_URL);if(p.startsWith("js:")){const TYPE="search";const MY_PAGE=searchObj.pg;const KEY=searchObj.wd;var input=MY_URL;var detailUrl=rule.detailUrl||"";eval(p.trim().replace("js:",""));d=VODS}else{p=p.split(";");if(p.length<5){return"{}"}let p0=getPP(p,0,pp,0);let _ps=parseTags.getParse(p0);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p0.startsWith("json:");p0=p0.replace(/^(jsp:|json:|jq:)/,"");try{let req_method=MY_URL.split(";").length>1?MY_URL.split(";")[1].toLowerCase():"get";let html;if(req_method==="post"){let rurls=MY_URL.split(";")[0].split("#");let rurl=rurls[0];let params=rurls.length>1?rurls[1]:"";print(`post=》rurl:${rurl},params:${params}`);let _fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let postData={body:params};Object.assign(_fetch_params,postData);html=post(rurl,_fetch_params)}else if(req_method==="postjson"){let rurls=MY_URL.split(";")[0].split("#");let rurl=rurls[0];let params=rurls.length>1?rurls[1]:"";print(`postjson-》rurl:${rurl},params:${params}`);try{params=JSON.parse(params)}catch(e){params="{}"}let _fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let postData={body:params};Object.assign(_fetch_params,postData);html=post(rurl,_fetch_params)}else{html=getHtml(MY_URL)}if(html){let search_tag=rule.搜索验证标识||"系统安全验证|输入验证码";if(new RegExp(search_tag).test(html)){let cookie=verifyCode(MY_URL);if(cookie){console.log(`本次成功过验证,cookie:${cookie}`);setItem(RULE_CK,cookie)}else{console.log(`本次自动过搜索验证失败,cookie:${cookie}`)}html=getHtml(MY_URL)}if(!html.includes(searchObj.wd)){console.log("搜索结果源码未包含关键字,疑似搜索失败,正为您打印结果源码");console.log(html)}if(is_json){html=dealJson(html)}let list=_pdfa(html,p0);let p1=getPP(p,1,pp,1);let p2=getPP(p,2,pp,2);let p3=getPP(p,3,pp,3);let p4=getPP(p,4,pp,4);let p5=getPP(p,5,pp,5);list.forEach(it=>{let links=p4.split("+").map(_p4=>{return!rule.detailUrl?_pd(it,_p4,MY_URL):_pdfh(it,_p4)});let link=links.join("$");let content;if(p.length>5&&p[5]){content=_pdfh(it,p5)}else{content=""}let vod_id=link;let vod_name=_pdfh(it,p1).replace(/\n|\t/g,"").trim();let vod_pic=_pd(it,p2,MY_URL);if(rule.二级==="*"){vod_id=vod_id+"@@"+vod_name+"@@"+vod_pic}let ob={vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,vod_remarks:_pdfh(it,p3).replace(/\n|\t/g,"").trim(),vod_content:content.replace(/\n|\t/g,"").trim()};d.push(ob)})}}catch(e){print(`搜索发生错误:${e.message}`);return"{}"}}if(rule.图片替换){if(rule.图片替换.startsWith("js:")){d.forEach(it=>{try{var input=it.vod_pic;eval(rule.图片替换.trim().replace("js:",""));it.vod_pic=input}catch(e){log(`图片:${it.vod_pic}替换错误:${e.message}`)}})}else if(rule.图片替换.includes("=>")){let replace_from=rule.图片替换.split("=>")[0];let replace_to=rule.图片替换.split("=>")[1];d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic.replace(replace_from,replace_to)}})}}if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}return JSON.stringify({page:parseInt(searchObj.pg),pagecount:10,limit:20,total:100,list:d})}function detailParse(detailObj){let t1=(new Date).getTime();fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let orId=detailObj.orId;let vod_name="片名";let vod_pic="";let vod_id=orId;if(rule.二级==="*"){let extra=orId.split("@@");vod_name=extra.length>1?extra[1]:vod_name;vod_pic=extra.length>2?extra[2]:vod_pic}let vod={vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,type_name:"类型",vod_year:"年份",vod_area:"地区",vod_remarks:"更新信息",vod_actor:"主演",vod_director:"导演",vod_content:"简介"};let p=detailObj.二级;let url=detailObj.url;let detailUrl=detailObj.detailUrl;let fyclass=detailObj.fyclass;let tab_exclude=detailObj.tab_exclude;let html=detailObj.html||"";MY_URL=url;if(detailObj.二级访问前){try{print(`尝试在二级访问前执行代码:${detailObj.二级访问前}`);eval(detailObj.二级访问前.trim().replace("js:",""))}catch(e){print(`二级访问前执行代码出现错误:${e.message}`)}}if(p==="*"){vod.vod_play_from="道长在线";vod.vod_remarks=detailUrl;vod.vod_actor="没有二级,只有一级链接直接嗅探播放";vod.vod_content=MY_URL;vod.vod_play_url="嗅探播放$"+MY_URL.split("@@")[0]}else if(typeof p==="string"&&p.trim().startsWith("js:")){const TYPE="detail";var input=MY_URL;var play_url="";eval(p.trim().replace("js:",""));vod=VOD;console.log(JSON.stringify(vod))}else if(p&&typeof p==="object"){let tt1=(new Date).getTime();if(!html){html=getHtml(MY_URL)}print(`二级${MY_URL}仅获取源码耗时:${(new Date).getTime()-tt1}毫秒`);let _ps;if(p.is_json){print("二级是json");_ps=parseTags.json;html=dealJson(html)}else if(p.is_jsp){print("二级是jsp");_ps=parseTags.jsp}else if(p.is_jq){print("二级是jq");_ps=parseTags.jq}else{print("二级默认jq");_ps=parseTags.jq}let tt2=(new Date).getTime();print(`二级${MY_URL}获取并装载源码耗时:${tt2-tt1}毫秒`);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;if(p.title){let p1=p.title.split(";");vod.vod_name=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim();let type_name=p1.length>1?_pdfh(html,p1[1]).replace(/\n|\t/g,"").replace(/ /g,"").trim():"";vod.type_name=type_name||vod.type_name}if(p.desc){try{let p1=p.desc.split(";");vod.vod_remarks=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim();vod.vod_year=p1.length>1?_pdfh(html,p1[1]).replace(/\n|\t/g,"").trim():"";vod.vod_area=p1.length>2?_pdfh(html,p1[2]).replace(/\n|\t/g,"").trim():"";vod.vod_actor=p1.length>3?_pdfh(html,p1[3]).replace(/\n|\t/g,"").trim():"";vod.vod_director=p1.length>4?_pdfh(html,p1[4]).replace(/\n|\t/g,"").trim():""}catch(e){}}if(p.content){try{let p1=p.content.split(";");vod.vod_content=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim()}catch(e){}}if(p.img){try{let p1=p.img.split(";");vod.vod_pic=_pd(html,p1[0],MY_URL)}catch(e){}}let vod_play_from="$$$";let playFrom=[];if(p.重定向&&p.重定向.startsWith("js:")){print("开始执行重定向代码:"+p.重定向);html=eval(p.重定向.replace("js:",""))}if(p.tabs){if(p.tabs.startsWith("js:")){print("开始执行tabs代码:"+p.tabs);var input=MY_URL;eval(p.tabs.replace("js:",""));playFrom=TABS}else{let p_tab=p.tabs.split(";")[0];let vHeader=_pdfa(html,p_tab);console.log(vHeader.length);let tab_text=p.tab_text||"body&&Text";let new_map={};for(let v of vHeader){let v_title=_pdfh(v,tab_text).trim();if(!v_title){v_title="线路空"}console.log(v_title);if(tab_exclude&&new RegExp(tab_exclude).test(v_title)){continue}if(!new_map.hasOwnProperty(v_title)){new_map[v_title]=1}else{new_map[v_title]+=1}if(new_map[v_title]>1){v_title+=Number(new_map[v_title]-1)}playFrom.push(v_title)}}console.log(JSON.stringify(playFrom))}else{playFrom=["道长在线"]}vod.vod_play_from=playFrom.join(vod_play_from);let vod_play_url="$$$";let vod_tab_list=[];if(p.lists){if(p.lists.startsWith("js:")){print("开始执行lists代码:"+p.lists);try{var input=MY_URL;var play_url="";eval(p.lists.replace("js:",""));for(let i in LISTS){if(LISTS.hasOwnProperty(i)){try{LISTS[i]=LISTS[i].map(it=>it.split("$").slice(0,2).join("$"))}catch(e){print(`格式化LISTS发生错误:${e.message}`)}}}vod_play_url=LISTS.map(it=>it.join("#")).join(vod_play_url)}catch(e){print(`js执行lists: 发生错误:${e.message}`)}}else{let list_text=p.list_text||"body&&Text";let list_url=p.list_url||"a&&href";let list_url_prefix=p.list_url_prefix||"";let is_tab_js=p.tabs.trim().startsWith("js:");for(let i=0;i1&&!is_tab_js?p.tabs.split(";")[1]:"";let p1=p.lists.replaceAll("#idv",tab_name).replaceAll("#id",i);tab_ext=tab_ext.replaceAll("#idv",tab_name).replaceAll("#id",i);let tabName=tab_ext?_pdfh(html,tab_ext):tab_name;console.log(tabName);let new_vod_list=[];let tt1=(new Date).getTime();if(typeof pdfl==="function"){new_vod_list=pdfl(html,p1,list_text,list_url,MY_URL);if(list_url_prefix){new_vod_list=new_vod_list.map(it=>it.split("$")[0]+"$"+list_url_prefix+it.split("$").slice(1).join("$"))}}else{let vodList=[];try{vodList=_pdfa(html,p1);console.log("len(vodList):"+vodList.length)}catch(e){}for(let i=0;i0){new_vod_list=forceOrder(new_vod_list,"",x=>x.split("$")[0]);console.log(`drpy影响性能代码共计列表数循环次数:${new_vod_list.length},耗时:${(new Date).getTime()-tt1}毫秒`)}let vlist=new_vod_list.join("#");vod_tab_list.push(vlist)}vod_play_url=vod_tab_list.join(vod_play_url)}}vod.vod_play_url=vod_play_url}if(rule.图片替换&&rule.图片替换.includes("=>")){let replace_from=rule.图片替换.split("=>")[0];let replace_to=rule.图片替换.split("=>")[1];vod.vod_pic=vod.vod_pic.replace(replace_from,replace_to)}if(rule.图片来源&&vod.vod_pic&&vod.vod_pic.startsWith("http")){vod.vod_pic=vod.vod_pic+rule.图片来源}if(!vod.vod_id||vod_id.includes("$")&&vod.vod_id!==vod_id){vod.vod_id=vod_id}let t2=(new Date).getTime();console.log(`加载二级界面${MY_URL}耗时:${t2-t1}毫秒`);try{vod=vodDeal(vod)}catch(e){console.log(`vodDeal发生错误:${e.message}`)}return JSON.stringify({list:[vod]})}function get_tab_index(vod){let obj={};vod.vod_play_from.split("$$$").forEach((it,index)=>{obj[it]=index});return obj}function vodDeal(vod){let vod_play_from=vod.vod_play_from.split("$$$");let vod_play_url=vod.vod_play_url.split("$$$");let tab_removed_list=vod_play_from;let tab_ordered_list=vod_play_from;let tab_renamed_list=vod_play_from;let tab_list=vod_play_from;let play_ordered_list=vod_play_url;if(rule.tab_remove&&rule.tab_remove.length>0||rule.tab_order&&rule.tab_order.length>0){let tab_index_dict=get_tab_index(vod);if(rule.tab_remove&&rule.tab_remove.length>0){tab_removed_list=vod_play_from.filter(it=>!rule.tab_remove.includes(it));tab_list=tab_removed_list}if(rule.tab_order&&rule.tab_order.length>0){let tab_order=rule.tab_order;tab_ordered_list=tab_removed_list.sort((a,b)=>{const getOrderIndex=(tabName,orderRules)=>{for(let i=0;ivod_play_url[tab_index_dict[it]])}if(rule.tab_rename&&typeof rule.tab_rename==="object"&Object.keys(rule.tab_rename).length>0){tab_renamed_list=tab_list.map(it=>rule.tab_rename[it]||it);tab_list=tab_renamed_list}vod.vod_play_from=tab_list.join("$$$");vod.vod_play_url=play_ordered_list.join("$$$");return vod}function tellIsJx(url){try{let is_vip=!/\.(m3u8|mp4|m4a)$/.test(url.split("?")[0])&&是否正版(url);return is_vip?1:0}catch(e){return 1}}function playParse(playObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));MY_URL=playObj.url;var MY_FLAG=playObj.flag;if(!/http/.test(MY_URL)){try{MY_URL=base64Decode(MY_URL)}catch(e){}}MY_URL=decodeURIComponent(MY_URL);var input=MY_URL;var flag=MY_FLAG;let common_play={parse:SPECIAL_URL.test(input)||/^(push:)/.test(input)?0:1,url:input,flag:flag,jx:tellIsJx(input)};let lazy_play;if(!rule.play_parse||!rule.lazy){lazy_play=common_play}else if(rule.play_parse&&rule.lazy&&typeof rule.lazy==="string"){try{let lazy_code=rule.lazy.trim();if(lazy_code.startsWith("js:")){lazy_code=lazy_code.replace("js:","").trim()}print("开始执行js免嗅=>"+lazy_code);eval(lazy_code);lazy_play=typeof input==="object"?input:{parse:SPECIAL_URL.test(input)||/^(push:)/.test(input)?0:1,jx:tellIsJx(input),url:input}}catch(e){print(`js免嗅错误:${e.message}`);lazy_play=common_play}}else{lazy_play=common_play}if(Array.isArray(rule.play_json)&&rule.play_json.length>0){let web_url=lazy_play.url;for(let pjson of rule.play_json){if(pjson.re&&(pjson.re==="*"||web_url.match(new RegExp(pjson.re)))){if(pjson.json&&typeof pjson.json==="object"){let base_json=pjson.json;lazy_play=Object.assign(lazy_play,base_json);break}}}}else if(rule.play_json&&!Array.isArray(rule.play_json)){let base_json={jx:1,parse:1};lazy_play=Object.assign(lazy_play,base_json)}else if(!rule.play_json){let base_json={jx:0,parse:1};lazy_play=Object.assign(lazy_play,base_json)}console.log(JSON.stringify(lazy_play));return JSON.stringify(lazy_play)}function proxyParse(proxyObj){var input=proxyObj.params;if(proxyObj.proxy_rule){log("准备执行本地代理规则:\n"+proxyObj.proxy_rule);try{eval(proxyObj.proxy_rule);if(input&&input!==proxyObj.params&&Array.isArray(input)&&input.length>=3){return input}else{return[404,"text/plain","Not Found"]}}catch(e){return[500,"text/plain","代理规则错误:"+e.message]}}else{return[404,"text/plain","Not Found"]}}function isVideoParse(isVideoObj){var input=isVideoObj.url;if(!isVideoObj.t){let re_matcher=new RegExp(isVideoObj.isVideo,"i");return re_matcher.test(input)}else{try{eval(isVideoObj.isVideo);if(typeof input==="boolean"){return input}else{return false}}catch(e){log(`执行嗅探规则发生错误:${e.message}`);return false}}}function removeHeader(content,options={}){const{mode="header-only",fileType}=options;const COMMENT_CONFIG={".js":{start:"/*",end:"*/",regex:/^\s*\/\*([\s\S]*?)\*\/\s*/,headerRegex:/@header\(([\s\S]*?)\)/,topCommentsRegex:/^(\s*(\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)\s*)+/},".py":{start:'"""',end:'"""',regex:/^\s*"""([\s\S]*?)"""\s*/,headerRegex:/@header\(([\s\S]*?)\)/,topCommentsRegex:/^(\s*(#[^\n]*\n|'''[\s\S]*?'''|"""[\s\S]*?""")\s*)+/}};if(!fileType)throw new Error("fileType option is required");const ext=fileType.startsWith(".")?fileType:`.${fileType}`;const config=COMMENT_CONFIG[ext];if(!config)throw new Error(`Unsupported file type: ${ext}`);if(mode==="top-comments"){const match=content.match(config.topCommentsRegex);if(match){return content.substring(match[0].length).trim()}return content.trim()}const match=content.match(config.regex);if(!match)return content.trim();let[fullComment,innerContent]=match;if(config.headerRegex.test(innerContent)){innerContent=innerContent.replace(config.headerRegex,"");const cleanedInner=innerContent.split("\n").filter(line=>line.trim().length>0).join("\n");if(!cleanedInner.trim()){return content.replace(fullComment,"").trim()}else{const newComment=`${config.start}${cleanedInner}${config.end}`;return content.replace(fullComment,newComment).trim()}}return content.trim()}function getOriginalJs(js_code){let current_match=/var rule|function|let |var |const|class Rule|async|this\./;if(current_match.test(js_code)){return js_code}js_code=removeHeader(js_code,{mode:"top-comments",fileType:".js"});let rsa_private_key="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqin/jUpqM6+fgYP/oMqj9zcdHMM0mEZXLeTyixIJWP53lzJV2N2E3OP6BBpUmq2O1a9aLnTIbADBaTulTNiOnVGoNG58umBnupnbmmF8iARbDp2mTzdMMeEgLdrfXS6Y3VvazKYALP8EhEQykQVarexR78vRq7ltY3quXx7cgI0ROfZz5Sw3UOLQJ+VoWmwIxu9AMEZLVzFDQN93hzuzs3tNyHK6xspBGB7zGbwCg+TKi0JeqPDrXxYUpAz1cQ/MO+Da0WgvkXnvrry8NQROHejdLVOAslgr6vYthH9bKbsGyNY3H+P12kcxo9RAcVveONnZbcMyxjtF5dWblaernAgMBAAECggEAGdEHlSEPFmAr5PKqKrtoi6tYDHXdyHKHC5tZy4YV+Pp+a6gxxAiUJejx1hRqBcWSPYeKne35BM9dgn5JofgjI5SKzVsuGL6bxl3ayAOu+xXRHWM9f0t8NHoM5fdd0zC3g88dX3fb01geY2QSVtcxSJpEOpNH3twgZe6naT2pgiq1S4okpkpldJPo5GYWGKMCHSLnKGyhwS76gF8bTPLoay9Jxk70uv6BDUMlA4ICENjmsYtd3oirWwLwYMEJbSFMlyJvB7hjOjR/4RpT4FPnlSsIpuRtkCYXD4jdhxGlvpXREw97UF2wwnEUnfgiZJ2FT/MWmvGGoaV/CfboLsLZuQKBgQDTNZdJrs8dbijynHZuuRwvXvwC03GDpEJO6c1tbZ1s9wjRyOZjBbQFRjDgFeWs9/T1aNBLUrgsQL9c9nzgUziXjr1Nmu52I0Mwxi13Km/q3mT+aQfdgNdu6ojsI5apQQHnN/9yMhF6sNHg63YOpH+b+1bGRCtr1XubuLlumKKscwKBgQDOtQ2lQjMtwsqJmyiyRLiUOChtvQ5XI7B2mhKCGi8kZ+WEAbNQcmThPesVzW+puER6D4Ar4hgsh9gCeuTaOzbRfZ+RLn3Aksu2WJEzfs6UrGvm6DU1INn0z/tPYRAwPX7sxoZZGxqML/z+/yQdf2DREoPdClcDa2Lmf1KpHdB+vQKBgBXFCVHz7a8n4pqXG/HvrIMJdEpKRwH9lUQS/zSPPtGzaLpOzchZFyQQBwuh1imM6Te+VPHeldMh3VeUpGxux39/m+160adlnRBS7O7CdgSsZZZ/dusS06HAFNraFDZf1/VgJTk9BeYygX+AZYu+0tReBKSs9BjKSVJUqPBIVUQXAoGBAJcZ7J6oVMcXxHxwqoAeEhtvLcaCU9BJK36XQ/5M67ceJ72mjJC6/plUbNukMAMNyyi62gO6I9exearecRpB/OGIhjNXm99Ar59dAM9228X8gGfryLFMkWcO/fNZzb6lxXmJ6b2LPY3KqpMwqRLTAU/zy+ax30eFoWdDHYa4X6e1AoGAfa8asVGOJ8GL9dlWufEeFkDEDKO9ww5GdnpN+wqLwePWqeJhWCHad7bge6SnlylJp5aZXl1+YaBTtOskC4Whq9TP2J+dNIgxsaF5EFZQJr8Xv+lY9lu0CruYOh9nTNF9x3nubxJgaSid/7yRPfAGnsJRiknB5bsrCvgsFQFjJVs=";let decode_content="";function aes_decrypt(data){const keyHex="686A64686E780A0A0A0A0A0A0A0A0A0A";const ivHex="647A797964730A0A0A0A0A0A0A0A0A0A";const keyArray=new Uint8Array(Buffer.from(keyHex,"hex"));const ivArray=new Uint8Array(Buffer.from(ivHex,"hex"));const encryptedArray=new Uint8Array(Buffer.from(data,"base64"));const key=crypto.subtle.importKey("raw",keyArray,{name:"AES-CBC"},false,["decrypt"]);try{const decryptedArray=crypto.subtle.decrypt({name:"AES-CBC",iv:ivArray},key,encryptedArray);const decryptedString=(new TextDecoder).decode(decryptedArray);return decryptedString}catch(e){console.error("解密失败:",e);return null}}let error_log=false;function logger(text){if(error_log){log(text)}}let decode_funcs=[text=>{try{return ungzip(text)}catch(e){logger("非gzip加密");return""}},text=>{try{return base64Decode(text)}catch(e){logger("非b64加密");return""}},text=>{try{return aes_decrypt(text)}catch(e){logger("非aes加密");return""}},text=>{try{return RSA.decode(text,rsa_private_key,null)}catch(e){logger("非rsa加密");return""}}];let func_index=0;while(!current_match.test(decode_content)){decode_content=decode_funcs[func_index](js_code);func_index++;if(func_index>=decode_funcs.length){break}}return decode_content}function runMain(main_func_code,arg){let mainFunc=function(){return""};try{eval(main_func_code+"\nmainFunc=main;");return mainFunc(arg)}catch(e){log(`执行main_funct发生了错误:${e.message}`);return""}}function init(ext){console.log("init");rule={};rule_fetch_params={};fetch_params=null;try{let muban=模板.getMubans();if(typeof ext=="object"){rule=ext}else if(typeof ext=="string"){let is_file=ext.startsWith("file://");if(ext.startsWith("http")||is_file){let query=getQuery(ext);if(is_file){ext=ext.split("?")[0]}let js=request(ext,{method:"GET"});if(js){js=getOriginalJs(js);eval("(function(){"+js.replace("var rule","rule")+"})()")}if(query.type==="url"&&query.params){if(is_file&&/^http/.test(query.params)){rule.params=query.params}else{rule.params=urljoin(ext,query.params)}}else if(query.params){rule.params=query.params}}else{ext=getOriginalJs(ext);eval("(function(){"+ext.replace("var rule","rule")+"})()")}}else{console.log(`规则加载失败,不支持的规则类型:${typeof ext}`);return}rule.host=(rule.host||"").rstrip("/");HOST=rule.host;if(rule.hostJs){console.log(`检测到hostJs,准备执行...`);try{eval(rule.hostJs);rule.host=HOST.rstrip("/")}catch(e){console.log(`执行${rule.hostJs}获取host发生错误:${e.message}`)}}if(rule["模板"]==="自动"){try{let host_headers=rule["headers"]||{};let host_html=getCode(HOST,{headers:host_headers});let match_muban="";let muban_keys=Object.keys(muban).filter(it=>!/默认|短视2|采集1/.test(it));for(let muban_key of muban_keys){try{let host_data=JSON.parse(home({},host_html,muban[muban_key].class_parse));if(host_data.class&&host_data.class.length>0){match_muban=muban_key;console.log(`自动匹配模板:【${muban_key}】`);break}}catch(e){console.log(`自动匹配模板:【${muban_key}】错误:${e.message}`)}}if(match_muban){muban["自动"]=muban[match_muban];if(rule["模板修改"]&&rule["模板修改"].startsWith("js:")){eval(rule["模板修改"].replace("js:","").trim())}}else{delete rule["模板"]}}catch(e){delete rule["模板"]}}if(rule.模板&&muban.hasOwnProperty(rule.模板)){print("继承模板:"+rule.模板);rule=Object.assign(muban[rule.模板],rule)}let rule_cate_excludes=(rule.cate_exclude||"").split("|").filter(it=>it.trim());let rule_tab_excludes=(rule.tab_exclude||"").split("|").filter(it=>it.trim());rule_cate_excludes=rule_cate_excludes.concat(CATE_EXCLUDE.split("|").filter(it=>it.trim()));rule_tab_excludes=rule_tab_excludes.concat(TAB_EXCLUDE.split("|").filter(it=>it.trim()));rule.cate_exclude=rule_cate_excludes.join("|");rule.tab_exclude=rule_tab_excludes.join("|");rule.类型=rule.类型||"影视";rule.url=rule.url||"";rule.double=rule.double||false;rule.homeUrl=rule.homeUrl||"";rule.detailUrl=rule.detailUrl||"";rule.searchUrl=rule.searchUrl||"";rule.homeUrl=rule.host&&rule.homeUrl?urljoin(rule.host,rule.homeUrl):rule.homeUrl||rule.host;rule.homeUrl=cheerio.jinja2(rule.homeUrl,{rule:rule});rule.detailUrl=rule.host&&rule.detailUrl?urljoin(rule.host,rule.detailUrl):rule.detailUrl;rule.二级访问前=rule.二级访问前||"";if(rule.url.includes("[")&&rule.url.includes("]")){let u1=rule.url.split("[")[0];let u2=rule.url.split("[")[1].split("]")[0];rule.url=rule.host&&rule.url?urljoin(rule.host,u1)+"["+urljoin(rule.host,u2)+"]":rule.url}else{rule.url=rule.host&&rule.url?urljoin(rule.host,rule.url):rule.url}if(rule.searchUrl.includes("[")&&rule.searchUrl.includes("]")&&!rule.searchUrl.includes("#")){let u1=rule.searchUrl.split("[")[0];let u2=rule.searchUrl.split("[")[1].split("]")[0];rule.searchUrl=rule.host&&rule.searchUrl?urljoin(rule.host,u1)+"["+urljoin(rule.host,u2)+"]":rule.searchUrl}else{rule.searchUrl=rule.host&&rule.searchUrl?urljoin(rule.host,rule.searchUrl):rule.searchUrl}rule.timeout=rule.timeout||5e3;rule.encoding=rule.编码||rule.encoding||"utf-8";rule.search_encoding=rule.搜索编码||rule.search_encoding||"";rule.图片来源=rule.图片来源||"";rule.图片替换=rule.图片替换||"";rule.play_json=rule.hasOwnProperty("play_json")?rule.play_json:[];rule.pagecount=rule.hasOwnProperty("pagecount")?rule.pagecount:{};rule.proxy_rule=rule.hasOwnProperty("proxy_rule")?rule.proxy_rule:"";if(!rule.hasOwnProperty("sniffer")){rule.sniffer=false}rule.sniffer=rule.hasOwnProperty("sniffer")?rule.sniffer:"";rule.sniffer=!!(rule.sniffer&&rule.sniffer!=="0"&&rule.sniffer!=="false");rule.isVideo=rule.hasOwnProperty("isVideo")?rule.isVideo:"";if(rule.sniffer&&!rule.isVideo){rule.isVideo="http((?!http).){12,}?\\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)|http((?!http).)*?video/tos*|http((?!http).)*?obj/tos*"}rule.tab_remove=rule.hasOwnProperty("tab_remove")?rule.tab_remove:[];rule.tab_order=rule.hasOwnProperty("tab_order")?rule.tab_order:[];rule.tab_rename=rule.hasOwnProperty("tab_rename")?rule.tab_rename:{};if(rule.headers&&typeof rule.headers==="object"){try{let header_keys=Object.keys(rule.headers);for(let k of header_keys){if(k.toLowerCase()==="user-agent"){let v=rule.headers[k];console.log(v);if(["MOBILE_UA","PC_UA","UC_UA","IOS_UA","UA"].includes(v)){rule.headers[k]=eval(v)}}else if(k.toLowerCase()==="cookie"){let v=rule.headers[k];if(v&&v.startsWith("http")){console.log(v);try{v=fetch(v);console.log(v);rule.headers[k]=v}catch(e){console.log(`从${v}获取cookie发生错误:${e.message}`)}}}}}catch(e){console.log(`处理headers发生错误:${e.message}`)}}else{rule.headers={}}oheaders=deepCopy(rule.headers);rule_fetch_params={headers:rule.headers,timeout:rule.timeout,encoding:rule.encoding};RKEY=typeof key!=="undefined"&&key?key:"drpy_"+(rule.title||rule.host);pre();init_test()}catch(e){console.log(`init_test发生错误:${e.message}`);throw e}}let homeHtmlCache=undefined;function home(filter,home_html,class_parse){console.log("home");home_html=home_html||"";class_parse=class_parse||"";if(typeof rule.filter==="string"&&rule.filter.trim().length>0){try{let filter_json=ungzip(rule.filter.trim());rule.filter=JSON.parse(filter_json)}catch(e){rule.filter={}}}let homeObj={filter:rule.filter||false,MY_URL:rule.homeUrl,class_name:rule.class_name||"",class_url:rule.class_url||"",class_parse:class_parse||rule.class_parse||"",cate_exclude:rule.cate_exclude,home_html:home_html};return homeParse(homeObj)}function homeVod(params){console.log("homeVod");let homeVodObj={"推荐":rule.推荐,double:rule.double,homeUrl:rule.homeUrl,detailUrl:rule.detailUrl};return homeVodParse(homeVodObj)}function category(tid,pg,filter,extend){let cateObj={url:rule.url,"一级":rule.一级,tid:tid,pg:parseInt(pg),filter:filter,extend:extend};return categoryParse(cateObj)}function detail(vod_url){let orId=vod_url;let fyclass="";log("orId:"+orId);if(vod_url.indexOf("$")>-1){let tmp=vod_url.split("$");fyclass=tmp[0];vod_url=tmp[1]}let detailUrl=vod_url.split("@@")[0];let url;if(!detailUrl.startsWith("http")&&!detailUrl.includes("/")){url=rule.detailUrl.replaceAll("fyid",detailUrl).replaceAll("fyclass",fyclass)}else if(detailUrl.includes("/")){url=urljoin(rule.homeUrl,detailUrl)}else{url=detailUrl}let detailObj={orId:orId,url:url,"二级":rule.二级,"二级访问前":rule.二级访问前,detailUrl:detailUrl,fyclass:fyclass,tab_exclude:rule.tab_exclude};return detailParse(detailObj)}function play(flag,id,flags){let playObj={url:id,flag:flag,flags:flags};return playParse(playObj)}function search(wd,quick,pg){if(rule.search_encoding){if(rule.search_encoding.toLowerCase()!=="utf-8"){wd=encodeStr(wd,rule.search_encoding)}}else if(rule.encoding&&rule.encoding.toLowerCase()!=="utf-8"){wd=encodeStr(wd,rule.encoding)}let searchObj={searchUrl:rule.searchUrl,"搜索":rule.搜索,wd:wd,pg:pg||1,quick:quick};return searchParse(searchObj)}function proxy(params){if(rule.proxy_rule&&rule.proxy_rule.trim()){rule.proxy_rule=rule.proxy_rule.trim()}if(rule.proxy_rule.startsWith("js:")){rule.proxy_rule=rule.proxy_rule.replace("js:","")}let proxyObj={params:params,proxy_rule:rule.proxy_rule};return proxyParse(proxyObj)}function sniffer(){let enable_sniffer=rule.sniffer||false;if(enable_sniffer){log("开始执行辅助嗅探代理规则...")}return enable_sniffer}function isVideo(url){let t=0;let is_video;if(rule.isVideo&&rule.isVideo.trim()){is_video=rule.isVideo.trim()}if(is_video.startsWith("js:")){is_video=is_video.replace("js:","");t=1}let isVideoObj={url:url,isVideo:is_video,t:t};let result=isVideoParse(isVideoObj);if(result){log("成功执行辅助嗅探规则并检测到视频地址:\n"+rule.isVideo)}return result}function getRule(key){return key?rule[key]||"":rule}function deepCopy(_obj){return JSON.parse(JSON.stringify(_obj))}function matchesAll(str,pattern,flatten){if(!pattern.global){pattern=new RegExp(pattern.source,"g"+(pattern.ignoreCase?"i":"")+(pattern.multiline?"m":""))}var matches=[];var match;while((match=pattern.exec(str))!==null){matches.push(match)}return flatten?matches.flat():matches}function stringUtils(){Object.defineProperties(String.prototype,{replaceX:{value:function(regex,replacement){let matches=matchesAll(this,regex,true);if(matches&&matches.length>1){const hasCaptureGroup=/\$\d/.test(replacement);if(hasCaptureGroup){return this.replace(regex,m=>m.replace(regex,replacement))}else{return this.replace(regex,(m,p1)=>m.replace(p1,replacement))}}return this.replace(regex,replacement)},configurable:true,enumerable:false,writable:true},parseX:{get:function(){try{return JSON.parse(this)}catch(e){console.log(e.message);return this.startsWith("[")?[]:{}}},configurable:true,enumerable:false}})}function cut(text,start,end,method,All){let result="";let c=(t,s,e)=>{let result="";let rs=[];let results=[];try{let lr=new RegExp(String.raw`${s}`.toString());let rr=new RegExp(String.raw`${e}`.toString());const segments=t.split(lr);if(segments.length<2)return"";let cutSegments=segments.slice(1).map(segment=>{let splitSegment=segment.split(rr);return splitSegment.length<2?undefined:splitSegment[0]+e}).filter(f=>f);if(All){return`[${cutSegments.join(",")}]`}else{return cutSegments[0]}}catch(e){console.log(`Error cutting text:${e.message}`)}return result};result=c(text,start,end);stringUtils();if(method&&typeof method==="function"){result=method(result)}return result}function DRPY(){return{runMain:runMain,getRule:getRule,init:init,home:home,homeVod:homeVod,category:category,detail:detail,play:play,search:search,proxy:proxy,sniffer:sniffer,isVideo:isVideo,fixAdM3u8Ai:fixAdM3u8Ai}}export default{runMain:runMain,getRule:getRule,init:init,home:home,homeVod:homeVod,category:category,detail:detail,play:play,search:search,proxy:proxy,sniffer:sniffer,isVideo:isVideo,fixAdM3u8Ai:fixAdM3u8Ai,DRPY:DRPY}; \ No newline at end of file diff --git a/public/index.html b/public/index.html index ec8fb831..8e5f11e2 100644 --- a/public/index.html +++ b/public/index.html @@ -14,15 +14,20 @@

drpyS(drpy-node)

常用超链接

AI接入

    diff --git a/public/sub/order_common.example.html b/public/sub/order_common.example.html index 33cf163c..788cdb87 100644 --- a/public/sub/order_common.example.html +++ b/public/sub/order_common.example.html @@ -1,16 +1,23 @@ +豆瓣 设置中心 番茄小说 -大象影视 腾云驾雾 央视大全 +短剧聚合 IPTV [优] [盘] +[磁] [漫] +[短] [官] [听] +[书] +[画] +[M] [搜] DS cat +DR2 hipy 推送 diff --git a/spider/.DS_Store b/spider/.DS_Store deleted file mode 100644 index 76d54f9f..00000000 Binary files a/spider/.DS_Store and /dev/null differ diff --git a/spider/catvod/TuneHub[B].js b/spider/catvod/TuneHub[B].js index 743cacc7..b591eb88 100644 --- a/spider/catvod/TuneHub[B].js +++ b/spider/catvod/TuneHub[B].js @@ -1,9 +1,9 @@ /** - title: "TuneHub", - more: { - sourceTag: "音乐", - errorPlayNext: true - } + title: "TuneHub", + more: { + sourceTag: "音乐", + errorPlayNext: true + } @header({ searchable: 1, filterable: 1, @@ -13,4 +13,4 @@ }) */ -49C57C16005D76F2E1EAE4A87E44399FD8FA9EB639A8DB67273AAE010E4A267D74CBCCE33F3FD3E55732E58E8E4F75F5D5FE3E1E9A49C6F1DAC30B212673A40F68344F727EC7AA38F51D2F038EE87D9CB7EA39DDDACF0B1B321F37AD955B9BEE3C414AD8C39BB1809D16BA2890C82375D9B6581AC8150290986CDDA413FBE5134C029A46C70DC7B9022818D3EEBD37587914D13143162B5644F387B783B7F0CE2A574258097A4A43575D031EAD5CA64EA935BBAA7FF5F16D996DAE87AFE5F16260247A4F9A7F4DC6023DF3A3C51B8DE81823B0760A93960FB89B758DD661BA936C2C202629EFDFE47AAD723128868B3492C12666440DF75DF487D870AEF263FAEA5EC956C82987F0AA8CB2F9B9841DA480F318EC4CACC79259521381F9E405EF8E78C6CD2FA5BBBD216541A5F6FDEB8ECDFDE8D28FFA7AA4032D42DD5E2D1D8C8D91D3A8A44F9BC1D52A853C3AB61AB8C4C17B3F6D341A8974E43EE26C75EB5D43E03A35B30FCCD3746DF7BA68336FA799674CBE5B28D98814D03A5452444EF27FE2DB4919550FCCAB1AF2BE6458A82D76A3F61D6D0E40651F562EBAB2FFEE8FD3C3EB8CCD277B3B91AC8F3AF9073480F1A818F7C29CA39D70D593E863BE4E8A1FAC9EC181656AFDE79F4F893F71F01B0001ECC092C557B589D4583EA7113981ADB82A99CB86F46711A5643535501166A765D5B9C5C36207249BB1546088A9D60BE5CF1E9D10BB39B52449CC3A3227E67A80BFF960E08F02157255D1C257F6AB6EFE76CD7FC6CFC06EB80BB01EA5EDCFF70CD37728555D6372A26C8E4DE1E677FC71C57D39AB7B32CFF1497DA645AE38CB2277895B6CFC0799A15CE98AAF389D9472413AD97024C3E0FB7145FF57CA3747FE8B8086F864FDA33CDE307484775BC68681A5D24009478FB62C9FF18AAEB13E2647485F9BE7D2EBD4049B683A69E13A87AB96A9E7C8BB913F3D581270503A76258D6429B04D6FBD4CE3D36289CA9DDAC9D913C3B69E770D8ADDBFE8430CEA197B18C4A670DB864C801E96AC6D5D3EE4CB0011E4C0867855C187F108C69E9A618976198B9B038E6A5E768E41BDC6666C0DC47AF520A49559F85290CCA5CD367B18A58EA94089C8E4A4F99FD4FB43C8F9BCEBE40895150BC3A68A1936153529F80FAEA92E524ADB5ADED9EFDF7B80B4F475629E975218C71145CFCCF8320A68C3CD28E9CBC9308947F99C4F1707AF3961E564DD706B8BB57336C8A4B565F20AE677D3A389F3B8BDB567196C08BEBEB745D28A0067138EE1D06F855EBAC29651E9A85310371309FD00E70F50DD2E9492CE5CCB0686D830CB38591388C708EAEC826A9DF82D2DD36FEAA0AEE97F5B0FC580D1357A59C4A0448BD6AE22987529748710D30AFB68EE801588C96F3B9FE27E3757AF53A2FC545BF8DA08D9C7E21CA0B1FD2B46DE51A420EBBC94E8B41B35D3BF6873173DB295CB8E55631CBC19467CA95FBDB7D2D3DE8E8123BE4291C0F575A0E06D3D3AFD2D03E25A5C24E3D3B34D5E91CD61F2775875143E30FA87BE75C04E681E6A3B84963A1FB3D5AE485A4935300621DB8D61723C6B1A80608E86F16C116F4FF35BE86415F33BB7BC8E1324EC1E59F4CAE2928ADDE1854BA70EE2A4984936A6995001E84D1D920384971D07530F01FBF2A4D26380EB253FC6F7A992BD3D48403B6953EA415A49F93FB010F13D4B47802EEDB671849762199A5D994B07F53F71C17E9C31A42E7F301C361F341DAA15ACEAEB4497ED00D6A2BD4F92C79C210665AD075847D4C75F83AE4948635EC4C610E130C783F694D881B3B3B272897EFB077AA57F7C5C002EA8BDE6F283C366234114A10663AB292E8D63096C1A71D0C434AFBA029B3E8A61A7DCAF489A64A028A67F118B09A17C949ADB008E0F9613E5FD608120C0772372BA291642A9572F4FBDCDB61FFBC9ACABC72CF59B9EC963ED270BF971FCB4DCF6220C8DE7CBD8C23D9EFF7372CCCDB4B2FDA1E3FE6BD3D2107B2918D22EDA43C758DC642C7965D25A7994EEE159F7B4200759F7B19DD7B35062D9D0FD0F73DFE53778B6108367152C4F3EA86849584E998159761A28DC3E66AC8280E188A0E85E19BFDB607368D60829AFB6E0781A825A22E0F207EEF4D2DECB8BE17016E2558C121BA6490FC683236004373EF6FD292F9CF15CFADBE447560A6CEBBADD4EC55763EC33CA81B30F4D73AC75D4F4EB07DB0841AB45822781558366755C68A2FCB09415BC7A413881F5086628EC1982C0BEA1E5391F9E6090B345F5AF07339F1A292D21FB8E69DB0AFA8ADFDC38FD15B7EDD7CA1F515308E2E675246A567C651A659C86E30BFE86CEB3A24C92DDD7D5F3979318DE93C8E44293D23A8EC05F6D60CD9D975C06256A835208F24F4C375DA2ED5AD081E9A07AD4321E84AF45EF35F36673CB0028FD6133E4AEF95CB87CCCCB4C5F3EE04572B661E3C0018061001B7FBC247D17BCAD70DF4EF35C6E4B394AF95CB39D66051661422614D7A99141B3740FFFEA8730C56C7F108CD3B878EB0FF1F55F56D73F11A63B6CE749FF06C7034F549B84732B8B3134EA475A41F5A7754B346870AEF263FAEA5EC956C82987F0AA8CB43579764937CAE17DCE483DA217E771D719855AA51C29AE5A175E5351AF41252CF56C6926C382D9B49A1894B2533B80D274B8A20D46310EA441D91533196B69991233E4A34E1DBF4020037E8B02D753FA5EE1FBAC2A654815A9A1FAD37AA5CA1F30E367F33D164D0E242317673D7EE1DDFA555E09D754BDE842F6DB40489D56AAEC6D21915A4CE8100AF34573622FC7FB249737B79B9AF95F8F7658ECC9557D2EE26C75EB5D43E03A35B30FCCD3746DF7BA68336FA799674CBE5B28D98814D03199575D9C26FCB81FB4ACF769CFCCAF2C04D827BB19526148E8104D1EFB0AD8C54C8442A9A662CF7F8D18865FAD41D45CDCA40C1C374BEDC05620CB49BAA2D42CF16F6D1B74D958410A5F7F8AEE94B83F80D3430FEA113409750F51069E8D44551EBF01B1D82EBB4A4317F8C91A3E3B5E1E47234CD0347715E59B0A72C448AF4E1998110B4F9EA8A8C04034615F7130FAB7E142587D9311D38901B027C4C377468735EBF1F7DB23E863731D5762B07FD6A82C5284D560840668C9C7AD2AF7BE418C54EBDE0EEBEAD6C535387DE337587DD51D88AD1A5E55CD1EE982BB2BF32F4F99E66EFB9BB03C20525F02A80B6E53DB0C9C7805D5CBA9C0938BFADBCE33ED1C883A5CDEE61AB126785C0E66F86D729E52F757226B556C1BCDF5D8C58533298F99E66EFB9BB03C20525F02A80B6E53D7D329F9FFEAF8C7455FF740B0489EEE29ACA6FE48942A0F154191389E89C3007322CEB0F9373DC5BD557761D514DF02E02785758DCFDEDCF76FA89C8EF99CBB6F9BE0DAC3324FD9BF9DB237F10A262F8D4A4F0F5D7897132DB49ACF8C8F0EDDDB70BD592C49109C2EF67F58F3CEA90B15F0BD1854A3E2C7C3E49B87BED667858895D25474D432C71F43ABDA35D49BBA1C4C8AA25C64986F24E1F4F115AE0798405D97B8CBE8601F7E807F397873AE298B0DB9FFFFFA964D1A2B08876D8675BBFA4AED02703CE4DB76B6ED1D1C4698421EC740A91C9F8B233161A844B7BB124FAEB73FE92507AA634CF849B6B901DD80357DBA01D7DAB4FD672E9C65A0A0CC20E04D9E5E155CA122E5EE1A7122716A60073DF85BE18B2AF8DCC4EBFFE54C994B81B57ADD276E3CFBCA7D7D475DD7F945B7668949D94E20A43D6765AB9F27E54BEAC60A2737A05B0B6EE142560A658499833F06ABB1F01952DF6EC4F84991610C97C8E7B5CA94BC7984516929B83BDFA2F7AE9F02766268FF65DEF711B4046EDCEBD85F3D8925D65F41C7AF698045DE0238F758C1D4782E4B3DFF5AAD9E8324B1EBD85F3D8925D65F41C7AF698045DE023FEF9996E1E374075521867B07DB4022E4683922888F1DC7FA4DB62FA818D68399A518D7123E213C146D6F04622293C2514D3F20E65E5F3E6531DB37FB34A3E87BBB3D2EAC134D41AE28D13BBCABA6BEC89D97DB7F2FFF9576B8E33E6139362BC7F161CCBC3C95A9588D897601F8CFA27D009312225A18B1367B9FA1809424339A0E1A5EBDA119000532C88FFC76E732984F6172EFEA0C23CD55BAD77E95F390A782447CC26A17C85F03BE6F422D83993E7BE02FAB2E777F41363A1E9865632D81335D51F95C9C468D7C97A97F09F6D3A48A373392A7ADB704286C05BEA36E05AFB4FDA2584952E42366DDA60406B2188920838652F856098D16305706AFDF92A5EAAAB3E0DB0006054F0257C266B4EB1EDDA0BEB1F166115A892B1DAA17994CD67C44EF28B5314742AA388809C7A8DE858EAD2849C7F798547AD7764492D79D9C995806F34319ED84EF8141D17FA2CBC368B4E396E562BC15182A8E78F98F07AD6620B0F073005FEE992FBAA0067C455D233EB1981F6E0E52F95408A5A4D2ED4CB389996C1D2DD305817DFB84F11E0C768C95857428D5B1B1826B66092E7DB2F2E815CEB57006FBEBF3E30C622D5F1BE97BF3A8FF4B6DDFC04F44E3F130433B46A18A4265396A0D420417410C43D2AE1ACD6689F3851EFFAD5BE131D2CE6903A361C47AC69B40CABE96F85689B9409D6DEBBE7F694DA1EB70248F358AC450CE245864718700BA0E27AF2BA58CE9E282F5A5CF165F7096E8F8A1D98105839F62F38B3E969CF3056F585D4263A6193BFC230E18E940C8D7A00EDAC3B979F103385AD540C0C8CB909640C5AA050F368F74782ACC117F067E07BCB552465AC803AC2D0CE07E3597E2D9FDD49B4EF56DF1E3257F1D626E38B28D3D7F0F177FC7EBD9E582F813E21A94C4F464FA8647121838ACBEDC3E995CE97991DBC795B08A9D1C1F2F27416C06CDA975EDC546662C84C4EBD85F3D8925D65F41C7AF698045DE023E1531A0664F4C3C50A3208F3B4C8DF66C7724F96FE405DD5FF307F7F4C6FEDC8E8A31D5115510860B2697BA28F7044D241D3488128DD38CD5EEB5D0F975754BE369B0DF709CB84595B49BB2C64A23C9625F2231FD201B600F1DF1869E324DEC644C13BED311020D41230320C4A1107563766DBACA37669C9DDB211773C796138493D57010018948AAFD6C7210DBFE2AE26F64D64ADE840600D4901ED33AC4274767ACFB56CD4E7961A9B794DE8D9A003D01C73BC9959B38D842E52D59AC4C9D2CEC5539F6597DD4E522023235E29C43DADA96868AECD13A0191AB14A764ABF04491CCB54647ACE030655A24142C7F513085BECD5CA7E6E0C7D2CB7A862FDEF4E17AA1222674E203A979B9A6AEBA57DB37BFF688F8F7C00E8C2B70A7F19C0C034F3EE04572B661E3C0018061001B7FBC286DD1E2473104AA1F7265E8C1BEDF987F4A6CDE98BE4D895512D8D9243684437FE2F78484B789497BC33E3573544674730A36CEB1788695FB03DA5AA6A927B46AE521C3DA316E8F9561EF788F9368022C57BC830678E2A46A5BBC704E0A26995611F022348B5E4A73937FB7539F3E423C715E5F3928BD6B16B964F1E5C6CB928069F6E3F2F2BCEDD1BDE2E8CA6FDB9F16614AB7EABCC8EE9A6B23B67CDCAD6170EDF34E2BE785B14D8D5F548C00CAC0EA7BDB0518ABBFD3962DD4D6D7F41363F7D4379E61AD5F564ED126F9C9A5A56FB52A140CEBBD57D33C5A7CF27CFD7C9788A0464F01132C443F9A0E8FE0026D69875640616D909285F08A8B73EF6D3E9EAB546094F8DE14CD216A1F140EA8EFC5C2749B35D4E1576416D3B2FEA03B91A96193868837C950A97CAAB318998E38B894340B0BC4DF6C6F2CE95B6A41DD1B3377D95C1B31E6F4F2172F98307065127D91CB74738844DF6BB483699F2C3039CAAC44C344217F02E6EB880F0E53510CA3A5D02E97CC13A6F1E28F012995EF4EFC375BD1026F72503D1CD12FC4B96199E641C55B18C1E2FDD95FFB51ED7C48513E7BD85F3D8925D65F41C7AF698045DE023E1531A0664F4C3C50A3208F3B4C8DF66C7724F96FE405DD5FF307F7F4C6FEDC8BD85F3D8925D65F41C7AF698045DE023033EF147E8CC4ECF5175E02999C57F126CDE4EFF7DD5CEBAC895CF0DFAA11D2BBD85F3D8925D65F41C7AF698045DE023BB44251B0C00A9A22B0E8E0E277F5C15C5C419FC58FC3D1BC71562A0C99F23CC885151B1E57A72EE78FA0D4AA997D1B4A4112EB0951790B21F54B674BE8DC80EF43A55DF44E39A6C0090B31D37860D370BD3399CBB4A41210FB4C866D4AFA6422A354721FD9159245C0B46817BF14D6ADFD3880F2C6A8A3E63F5727FD47458C8A5605999E929D2DF7C88976CECBAA1CCC83522F2EB50019B05A50C143535CD7863C193884E9EE283AFCF32FC5083FC36A07AC03A968F1961A96B208131C4EA356882AD8B25CFAC8B56559F23FF374E0A31935E9B78B5F9BC7F31DE2827D85CB6C136259ECF5AFD7C75D0CDB36047850914B8921BB3279574048BF18A0C304562500C0A475C7225718DD6BBC502340C0F269C46F43A9015448B40A0ACEF01A0CDEF6DB5AA3EFCADCC10C3E748FE6CB42FF5F91B43C841F8CB487882A1430FDE0DDEC533BF43E27D623E82433584561FEE98C27B2609590DDE914C292E348E73AECAF5BF3C5EBD5D2CB8E76E58DADAF609FBE4228AA5CA5A97B9B43D632AD5AD2DD48EFE744EE8E5BFCB1D3126050D6EEC6CADB048E038FFE7D86B980F8805244558502C074542EEBAEA0641F9744CE066CD32D453CF2AE4B73CE8CF582C0D3B6F29E5B1DF4C911023DB6380F82DD3AAC96C433B93D5AC0143BFE6AF86A5C5AC88DE831C958AC0938ABDFC6B71EBB1A9530E8DF72B594AF325F85367E2B4BD406D89C5FA39F18617DEC9FA3FCAD57A504D668D218E2ECB89174D3C071460DE1DF04D07FC887C0F29367A798DF4AD42D487CF356F6F39BC48309E7E1F56714AB01A3A80C37FF7091ADE278C2846C17479FFBD1ABE92DFA5ACF5FEEE6EA64AF4A530E3A50AFF852AA07E1BA58AF0B440341C41D3488128DD38CD5EEB5D0F975754BEFEF9996E1E374075521867B07DB4022E4683922888F1DC7FA4DB62FA818D68399A518D7123E213C146D6F04622293C257A8644A9594A0F9BD4CC49CE2D88C5724FECAC743A96B6BCA33973BFAAD4C51CCAAA981579429DF919F62D34C03AB8E5E010E4A267D74CBCCE33F3FD3E55732EC8B2E55151A84A2704DB1572F9CF566A283B832DB04C7AFA5CAD1A4F44669DE6819692FFC5ED90F9E8BDC9EE606662E740CE72EFE40D771AEB865FFD75B234FE235D021507D4345F69F5B21CBB6257A2FDE51DBDE75A898A4F68819272A9E19E735623245E772D2DD9D27E9D1B55D1E3E427092FB61A26F910EF99D8B603D1BA5A2EDAA44E5CE1FFE643D4B66718E89DBA5F5CE59F4EFF8DACE14E053E629F5AA22421304102D2A0EA75FA03B754A2753E1B08F2B029B4D2651A187987E07950BE6DFB40937BF2C395D9188506B5D6C247D557A9A6DF1281C69DA22E83CE5364418B5CB4F21293D9CA1113802B8BEF181BB609263D29F9EAA0236DEDAC2C5562DAEAF19CF7DD37F782373EF8CBA7D72610CA46C1C65110781F268320F3C04B669A518D7123E213C146D6F04622293C25C6735C2A1F1DBA42B20C67ADEB5A0AACE26D858680A609E7A55387A24224D3BC53323C7AD61404FE7E052ECC85027C74BA4CC745758EB4512606482D35B29F8615FF3D4E1621C5828B5576CD31E6B9F99A518D7123E213C146D6F04622293C257DD76F840D8C554D8E44D21F8107E270B5D150911A9407602CBDD7C273934AA5E0D4B13229504799E1E37C07E091CF97589BFDD8A725FC6A44910CBDC1E75ECFC08C5627EB33B0CBE720324A56018E86ED4CFD53C7BC9EC9D891C66893426E1E9D6F5C8B8B75436FCEF24FC1747EB26E0CDC4ABAF45DDD56D10484FEE3B853679A518D7123E213C146D6F04622293C251607F9E555C25D256A43085B836BD51B78C52A28DF6F8BFE04B737C16E6C16DA4D1E45B053B9EDAFE7832A1610679E17B6E051807152E42C20EB2B4ECC67D9DD5DD48058B23FE47FA06819AD6D85D76C07E2FE400AB7B48C75188D4F752E0020F69B971AED8465E7125507A93A074A47D71E5976478A9BB8D89929BC2243EBE5FF056848FB06CB86684BFAFA8FC24D04D95016BF8DCA06656750A232ABF134EC9A75A56B2C446EB576FE31A9E3F0DD68759F7B19DD7B35062D9D0FD0F73DFE530C8106D66988FD29175B3BC4A469694ECEBA25D2BC00AAF3DCC48C89B9039A9D2D60807CEE5AF946E5967AA29463F87FFBAF14CA67338761A37B2949ABFAF36CBFF3D6F75DE16766D42B4CE7E0DC283EE88758653DFDBC0B6640CD551F04A385B879CB680AEA6727E872F66DA8FF4A93A7F983C9E18C611FA16E5507D227551EED743A6FBBF28BBFCE21792F93D4B95139B738204F01E303FAE6CCCDFEA07540E2D3FE0607604EB7DE21D4A5A88E9207392CAB7D45E61B17E89AA9BD5D57B655F25B3DD95391E0C28A6D6D41264BDE4ABE4AAA7D4A2CA42B3592AE59DBBDDAE27B2061ADC85D726BF7CD1B09814B1A5DC146E7B67E3204A55A1D40AEADDFF4EDBC05125616F339AB94C8D1372D8B721CE77FD07B2E160C53ECD39D83363D1CCCF56B8A269526556814F8BF95E78F06902F5B874F5D0869BD9A7E44A7F28757828B88FEB2AA3F0015D7B131D6D43C026F36001A934D056C98474C520C53AD9D17AE583CA808D25D7310CD40B4A7E40FB7AACFFA83C0B7B228B2F0652E9C7951873F02E8EA0C6EE6E1406E237332565A82F5652FA564676FA890A1541FFE4AE533EA7A8357A625FBD5E1CE4C10C735B490A06AEA75AE0DA30A97B4AF9D08B3DD1F3C506877BC7877BCDE197486C670A2B433C1304DC36A84A30EE3FE9F638204CEBE17461E78246FA12BA9DBAB14912DAABA5F5CE59F4EFF8DACE14E053E629F5AA22421304102D2A0EA75FA03B754A275C42E38504E589559471F71487450AFEE2DD4B2CAD697D15F5E32F4A8511F4C0010B29C34B5E1976823A40BF754AEB1AF554A01083F5FB0080F6C0BAE6AA623FF4725A5053197DC200C1DE33C6EEE4F57C7D60EAFBACB7EA053F4204C7A8CEC8B3D89BE58B5BCFD901C21A135DC438F5DBD8D15B1D3A6B881523A24D4780C0F72C44C344217F02E6EB880F0E53510CA3A5D02E97CC13A6F1E28F012995EF4EFC3B2B6254FE3A4CFE08048C0974D64BC4BBD85F3D8925D65F41C7AF698045DE023D33105FAFC6BE8261F5588BB41223BF8680ADDCE5587F4E0EB8B46E8971757F97E0FBE7F14FAAB29B0E4F58E8D9B0EA7832676F9F67EDC553BE422E3F8138D01D9FA2147BFE93D7FFC63645F6007078F90ACCDD16D8BCBE9B817AE1B9483942D7655802374BCA624FC653F6EC8676E47B33829047F9990CD40D520E75D276407D2F4C738077C5B09461C44B1AF7457A4B0E558AB9BC06C4697357264DB4F602CBA048DC12078AF524440D15D8CF698C406EF1D7962E51B6194FDD606DA0183E5F1027CF587BF194CB0FECB49E633DF8C8760C892E16016A68D429E55EA84208577020362D9E67CDEDA168C07F396D32B8DED79147A96E01280ECA0B6A404539CDBB36FB4ED62ED022B7E2F4A7B5D4347C2F853A12D94E0F53076625961D9734AAD1842957C04FB648185ACF4D9628B309E1E5D18D4C30CD7BA7BCB85565322E74016C3556977DEF81D1105744037FEE77C8E7B5CA94BC7984516929B83BDFA2F7AE9F02766268FF65DEF711B4046EDCE61534E072766133D47B1F0A4A6D9A9BA64092BF37808E3449E2E97B7F4C62329B3325CC279DCBD5B8BF4BEFDAFDDEBE01BB609263D29F9EAA0236DEDAC2C5562DAEAF19CF7DD37F782373EF8CBA7D726DCEB59175C30A0A181A3B1C4EF29D1080F4FAEA12F44695D4151246AACB095CFC363BDF2AEB5939EB50063ED5573AA2F71CC7D530A61A82DD3F25CD0623F5389457EDAF473E1CCE71C61F3F67945AF6B283AC2609B7B0E0FBB343CBCCFCA28727B22B9C003B72606805EE3D3916245AAE26D858680A609E7A55387A24224D3BCF45C14DC8F69566378E8CCE2EA53971967F70DF82FB035CC3D1AFFD4DEE1C85B0168570FE09419856DDEF0C4E19CBC0E759F7B19DD7B35062D9D0FD0F73DFE53156E887E4C6464BB8BA4BDD7973507EDE5A1A7364175A73DD18C458479A783CB4E39FBD97AF03D83A136C986A51D845C518EF55E0BF7B4B135BE863E04D329646780C2ECD947EC5B17986548714E1C6EBD85F3D8925D65F41C7AF698045DE0234997842DDEE0E53DD6C45BD674C1662C1BB4F8B05D6A2B25200A708BFC4BC9ACB6E087FABF24E38C355D8EB672A9100A61B0921AF3851F75DEA886E76A0879EB2B39FA9B88A8BDE32EF32CA9B87F75D13B5BE1B9ACB9A143CAEE59AA915EBD329A518D7123E213C146D6F04622293C25289D0E81B737979D426C970DCB0C4734170841D0928C823F9968A8748A0CA2FBA58F316B60CC4894564FEA18F65C99CDAF75059998BF393FD67A6048ECA298BDD486216F0D429348B803B35B3224EEF79867468E727ED4DC4B0F11FE6DBB14147129C156947A2E998726760C00DDADF985D86ED80035EB990FEF7FCFF297917E52FCB67594B459539A5AFCF4983385418F1D08DF53DED13B5044A8C03F391916B61AA6BEB701F598C70A1838A0C1C45F910B8061AFC275F67E8B88846993D7AFAF5294B9E2A38B0F7CE70C08A7435E011F422F7179CD548A59C109FDF92E57B4151FA82F1A4AF90D72736CEE2B434908119EAC9EA1BA62C6A1952B2736645981522A8418E529A82D99EC74C3CC1861268716D175F85F295B4C5D1FF78E05C91EED2213BFA616F8686BA41C16D2E4615C92FF0D57A8D09C00DE38283DB56CA7A54305FF6351601C0F30F4D0349618729A6DD572195648282BE5E9F5D459A626CB98C38490A74BD2821C96B97EE255DBE2826DB546A2BDA73F9C0AADCEC3E00B7C42F8448845108746A9F33B55EEDD62733985E34033CDE9AB53630E914A7259A5ACCD569AB22809DFB0286C3F1D0401BBFA6834E1FA4A795A80DBF097CCFE54E29A539383BDD37D314561A169EDE5CD30C52051BE3FB4B3FB9B9E61E72E84BD6C02238F0B5E3C65B197F55C26390E268DD5CFDF59EB38663EAB94B3FFBA1C1DFBCDF4BD99F055108BD23EFEDCA93FCC6D28B2CE2B6C369AA808E85C0BD0878EE9A50C32F0E35C178388367E675F6A80A24F3DA0BA80DF935535C5F1E12B01B90B61F578FAACA2BAB8C9F3772E087648C2CCA8609062038BE2ADF104EBBFB248FDA2E9C782F030D4D4C6C4C62928A410FDFA53CAB030400CE7D3E2B89704E9B6A5BD4FD3DEBE8DBD5705267D3FA8545DE66CCCEAE9112A214C1DEC90B554CCAC785E0312A1252FB9180D9A74C5ECAF92D17ECD7E107C12E70FC080A2C5C731102FFB312C8C6BE32DBC66DF36BF38790E741E50AA9B34AE9FC54C1AEB08C85B8A04AF11D3D43EF8618AE582295FA3BD81748CD1FDECF06A4B755697349AE9390B9A29D50A0EA0B7CF4276FF728138D7DE8EE337859CFCCE31593B67048CD2382988A132A47082827EC2C400236A0566A873C65D99CB0E64DA88BDB8A9E4013BD650AD884AF953350B129A52E0B6F3001B0C8762A850C5AC89735FFA4749FF5B4EA31FE9562DB91E0AC445FCD26E32A76EB11978783635B1368F191AD1BD3CF86E2FD5B57F6EC7B19B20906A868AA12114904AF627B4C24254491EE1E995081E79E6FC6D1603187F47419CBAB88C11C55DE3B5B23B8C482F684EFBE72A4DD0E7A74BA31738E6C3FBD414C08D058E779AD07DB9F98B7BBA979909B6EE8EBA25BABDECF7A6276B998D13C61CFFE8C02D4807FF355A0422E59E4A423872A315B720579962D11C64B465275B6798E3ABD847C52A94A81F310CA3688DFC9B63C5235ED8AC143C8AE78ABB546E07D4FA40546DDB5A091D804A6B0CF9F462A20BCEABC87F63747685D9C6240B074355ACF602992B4C5F0A0ED6A5746F76326D60160983F46F0BD3399CBB4A41210FB4C866D4AFA642EF961BBC5E5671DF3E2DE80E5D12CCA6D5C810D10830B95279C2237DDE4B0EF018A4FE028C8E10B6682DA1D3962D2ED30FD08AFE8E7E53B9FD3ED852CF9118442B7CA2CED841BFFC7C380E0F793FD2504165527C89BCF6E870C7169FFE6C451B0751178874F843FBB4FC057021B4DD84E8F8DC4CFB9D44E59E996E1D213BEBD80506424759E54DD4BA6F728330D9BD37EFE18BD34A34408A4057BDF48736109A01498DBFA676613FC0BE57673876878F7D6A978705E981705C8C06D911854FA79EB2769DE38E7C9EC44999F40A2F0E499ED1253C318D61803783D9F218E2D2B0A2C19E0E8D1CA37DF5122BB611533B83249599A7D676232CFD1827052463F790439F697E14115AF8BB28ECDD3E5DEC9AB2B1C519B7B7CBDE6E71A2E97E2EA19B7BB29A27B7015CC8AFDBC5269890BD23C464C6715E330C7695231BB73286F86DFEF9996E1E374075521867B07DB4022E91FBA4916E44BDC76F57AF0DD359420317110B47E64FDAA99F48CD63122EB10412461023EA3AEA5E293C3FDAC72622F91F9F69ADBC86AF71C45014CBDE8267B7BF768CFAFAA782C801672D9BEA620CD31470AE8DE70DF0C5930A0FE69346A64F5A6704E80B58B28D270215F7D4966EDBBD85F3D8925D65F41C7AF698045DE023941F6A1A1CFF0199A7D59A2C5E10CD863BDDAB65591812566A6E59962BF444A9BD2302D583393896F6563E8EE25D3FF422F499814A59E5BF5E4E824D14FFB8E06CAEAB99C61FD0DD9C3FDA5E5D5C7A6541D3488128DD38CD5EEB5D0F975754BE4C4162A42F604DB252DED62324713B21F9524CE2B1ABA2BC9884484E0A84D242CFE51180EDAB3EEE9B8D5B48E24A22DABD85F3D8925D65F41C7AF698045DE0233A4DD4EA09FD063DF2183D099EEF292C41D3488128DD38CD5EEB5D0F975754BECF896CE447FBCA40A2B448E6368CA0AEB9204E722D69FA901329E8557611DC9A360932A431FFF809C938B39FC061E1C5D3899DA134082DF39BFADD04C11F8505AF75059998BF393FD67A6048ECA298BDD486216F0D429348B803B35B3224EEF79867468E727ED4DC4B0F11FE6DBB14148A1771EEFBEEE909A0DFABCC647AC020E34AEA4D85725F7144F41B2A2A1B39C49ACB906ACEBA8F3865EDAC45B976392CEEFA22E7C1A36830A77F25DAAF01C4F3EFE18BD34A34408A4057BDF48736109A01498DBFA676613FC0BE57673876878FEA8667AC8FC4C178B67F0E1EFC5E9F754B798C11EEC63A4633FCF017FCFDA0E5E02E5314EA0EED92B79FB7EBB9E42CBDC7CA572E9CD371EB2D26406D38E84895354CB8FD5FE9EC11DDF4A8877D9A9EA835A93C86B3AA7F86DC16EACD64EFD0A6721F5F5734EA5EBBDF2E59211A1AE390A1B7BFE9E65EF313A027D21D0D7A5248E0F3F91CE85ACDB29EFB20ED5730884B767ACFB56CD4E7961A9B794DE8D9A003DE7B01946C783B7754F84D0B8BDB6AFC7CD8EDFEC276205E199C18B0A527D8E99652F25F773FE75283DDEE229E5A1D8D0548A65E3FF8822387585C7AE2FB9ADB973184627D5898A10345DC161118B1911621F0D62ED4541175D35916907D0E183BF4C9DC33B5DCACAE9EDCA97C4FA54C197D44F48BDA5B61D7AB26679D53878968008B26779086937779560E95DEB13CC293470256BC736B46D72B1BAAD0EA134A509BF7B4124B2D8236D755DC39869CB5E5DEDBB14409F9914670B22307564FC4FE8BBBC65D11DC8DE9062673001D63A0DD338357AFEB044A24051782891B93869A5729959B055EED453F2BFBE82589FABD7CCBBDA72E1F2373FB24AFB792903A814433B290CD32424C9B871A6B1D2714D6E7FBC7CAD12595D1A86E06D924B0C16DEF82412FCA1FC9F250076E2259BED71B9A6D8A5C11F20D7428CB65DB8CF16A62586343898045DE023288CEEEF3C9ADE197835237C23130CBAE9F66F2CC9C2E3E4E15871E22EC2123CBD85F3D8925D65F41C7AF698045DE0238608632AE37689204C71CD8035DFB78F585E562D8827DDBCA13FA8B2B87E80AD094DF3C72E5C03916681C066F12145B679F1E5C55432B8AF2DB2369EBDE6601ABD85F3D8925D65F41C7AF698045DE023DC08C3C37E76D836FF328381F4B5A2E5761905AED4CFA256A99857AEB8EAF2E0BD60BFCA19B5B8C7FB0E0E0DD77A9E18BD85F3D8925D65F41C7AF698045DE0231677ECB898B405635530D0F02309DEEAC68F61E1A9AEB4431DEFBE22E32BF3B1BD85F3D8925D65F41C7AF698045DE023760BFEBD041BD83A573E494A0E7B857E9EE372F823A14B3A2526A14CCA952EB1024106687299409AFB1B7644FC67784F8960D206FD9CC48BF2A6D4AD386F0FC3BD85F3D8925D65F41C7AF698045DE023A5426D8847CCA7224FAF3C1EB2B7E97014F3C34A0EDD728AD3AF6564A5BCBD47BD85F3D8925D65F41C7AF698045DE023D374249DE91A7A199D5EAA13FC9BAA81AC9530E1A04F40B1E22CE2B653911DAE60F96F5CDA3A6358FB7D295DD8F2A0C8986265C54F4F37F307F1A5E2B3774B677017735379BC355C218215E37C6B27949E311978DAC1665B8D5DE94806C1CDC5DB00E1C19BF21B1EAE681AD2BE3E6BF4F8AB6F3275F0F334C06879DEC1DCDA04759F7B19DD7B35062D9D0FD0F73DFE53DC565A831AB75A896A6AF13D723D5D208653D0ABE44AA5EB979F0B3F8852C2E0BD85F3D8925D65F41C7AF698045DE023B4DEBAB9F106AB9F72FCFD2F75802472E8DF348A048C4C9A2A89743D98CDD550A97A3046E31A63677F701D4C12682671BD85F3D8925D65F41C7AF698045DE02328B2CE2B6C369AA808E85C0BD0878EE9E56FA9532B393417B1C28197EF8CE2DDACCC7EA9326BB13E84C2213CB195319F53AC67B4AF04E32675FD526A4FB9B5E0B0684C40E2B3EB76EC56AA7EEEDF8B0C8E0937963DF293B6B167F628F9EEFA407B9D0F092807159843163EE1CA439B2FE2036FE5929D4D9682012C414C78ACF335849D86FF31BDA855E3AE05F74C44E34171690B530C8394EE450C405737E519B4FC024F9BBD3EE74972579BD17CDDDE759F7B19DD7B35062D9D0FD0F73DFE53024106687299409AFB1B7644FC67784F681DED9DA336EE010E691C10B546A4A69EBD9B3D211A5B6E5A32FE7EB2F2D4BF8A43090D1C42571C272CDF7C8218DFDF32A5D5F35BDCB11C2885E5D3C5E4AEC79BBEC3A17513DFA999DB7BDF1D5D1C3A404FAFED1AD507F84DD0BFAD71803F714D110644BC3A1C19A9029E8CD072D375DB7C9AAC1D910A11710C149BD9442E42F679DABEB700EE8DA9C55EE59C0BD11EF24AEB2DB573CD57E202912F272D31C2A4CB7A83D4D0918273667155B772B1B7F9C7A930FFE44CA1D511935B2E4EA6FC53036DB581FEC0BD4B8FD18E97F03ECAC992F29212F0015EC004720164456226E8BE0EBD2A5A7427AD2D7E2831A97E22759F7B19DD7B35062D9D0FD0F73DFE5371889DEA8057BB633748593F66E935AFB6343D3E7B97673604BE36BA2514946A57327449DDB8CA356102F13145C96C43BBEA305B4873467150E91D2FE99EF600F6471EEF504AB6EB077ABCD60DEA4FAD222964804D8F397C88BB3CE203C3FC50DAA28221C7309A32767C8AF7A3F6BA51F13EBECB51F56840397A7C9A50EC1D0EF9EF056F5E03A30E8DE717C644B373320BE2EFCA599C22BDD3F615DD1AD1FEC945FDB9F48E8FE4EFE454B767E821565E23724AD3F764858AFBEE79EC225F51F648A305ED92AC2A5DD137EA1D3A71F004D21FB8E69DB0AFA8ADFDC38FD15B7EDDCFE7435550063B4EEA01961219DB08B4D76152F106A659232CC1A6056CF73E03395889BA799A45BA4423BE3CD263554F2BF9F13C0928AB30D1DA6D5024D4E4784B3F93C134E2D25B791526200914A1A5D8EBE951DDE59FAD584E388B1E755051A224C1C7965B75570FC34EEF7E4796873B91A800397F9637BD38E4D20F45B7CCC6C632AB9FA884D9043639DFB99494C7AC76BE8C479DB11E92327CE95C8ACD13960ED28A9C5C871E074ED2E6DAC9A3F1A8CBEE60571255D72C86719F4F6EE4FB8BE64AB8EC7D7C09BF4D5A5F1C8DA9EF7173C5B91D776E975237E1D7B88C7A0558D76BDC1D3D5D76F0DBD65F7BE108351C98CC79C8AF50A0DF2238A338F80FA6421D1C6D372B97A0610859AE220A2149AFCF6458F67F5C7538077DF91A9BB08D85CB4B641E0F2CADD953EA3B2AEDC5FDBD85F3D8925D65F41C7AF698045DE0230CBF636D894226D96BB15AEC8D8581D241D3488128DD38CD5EEB5D0F975754BEF30E367F33D164D0E242317673D7EE1DED8B924C1C7ACDFE5047E7FD40BAD6C8759F7B19DD7B35062D9D0FD0F73DFE535C89EA76B151569E06AA62A383E1E794693D3AB25234B6FD3C25A85888D2285544C13BED311020D41230320C4A110756FA1B63E371AC3D46BE6380A6FC6E95AEB07C08443927543C3055265213C8523BC6DDFBCAAF7B1BAF7AFDAE23D9882086203D0B9C81FAB4EC272DC4F9EC3AA56F0EC9BFE808C9B1232ABF8E0F74FB4976DAC111B23670A043C101908F4E7C7530E081A657771B8F46FFDA056529DBFA1FDA9C43E69CEBE71071C4952357E8DEFC4496495CC0935CA4856EDEA2C77657DF7BDE514B76AC3A5B01EBCFB6533C0466476165ADB065C504C27A8AA26688CB04C577A4AC3B53C59BBBC1B505412686AA69F496286F6A3CE9B8CBF9B0F1A12B971AE6F65CEE1FF87F5A66DC7807EC371824B9573A6182E5D55BA47463283B1FCA7C99633638E03A0D086FD4D1A2FC87AD84799D786AC319CA21E30369BFB0F4B9FB7DA6E4789ACF239456B494536B115EDFC2803AF64639368DB0D5ABC8EC760E476165ADB065C504C27A8AA26688CB04C577A4AC3B53C59BBBC1B505412686AA69F496286F6A3CE9B8CBF9B0F1A12B971AE6F65CEE1FF87F5A66DC7807EC3718D2E1BA0DB6CB447B7CD0672287A43E1B57DB829D748C06C0F437D2B9A293F0AACCA82E3C3D3002DE250BD5A35953B888A84E82E82BE4DC8E22EF93AB5DF8FDDAAF8D73399F37851C3252C8B6061D7E667F0806D16F859423F7433A7F98EE82AB2E5432CE4427FA70ED00FB883E4C184655ACE5B78881613F26F32C35935B79C0BD85F3D8925D65F41C7AF698045DE0237419C09F9676AA43D79028C0DEC9AD0742EC7FCC9795942CDC86FE20471B1DBECB1B7B1F92EA800C713649217D4F317E2461DE92D29D99C2EAFD19A2E9529013C992F29212F0015EC004720164456226BB0E0BE6D697E7908775917F6C3B5FDEBD85F3D8925D65F41C7AF698045DE023657E973D8722D710BCCE37CDECFD53EC59505CD6B969079E1E39C0064DA05DABB8323A4CB163F6A6DF0D90AC76B41256F453E2823D3B4A8F23645DF724A39B1682879F6C8855D6B6452C76F8185D0599FEAE325EE7F5B46C944128DC6BDA672E8429294B5DC011B4CC7574ABF543072258B3CA7A042F273F427243679A3FCDE953DECD86FE6F70B57324D48DE02BDE6D68C7C675A93927B1D850069FB130B55FD6CCCB7DE7247E665054746453CD56938AC06546BE2FA76A366FB7E5AB85754BAD0544787E385F280DE1A10860D7EC57D4CCD396FA6242BBED714331553A5961CC8CE56FC74D972FC9C0E63E139DE643D3D648DC7C9FB81C1DAED9962A849A9C4A8886F5EE70A7B4C2E4CC1083DF36AFC5A0A14FAC3C226D217D24016C20D462DAF6B8C577A1254EF99B491B1FC9CD28367D08DD38F9759EB0025B1D8807160CB9511ED601AAE8BC200CD0D593EBE83F47DE4527A4397B662DE665C4870EAA01EAB13B939C94C1D39790D6744737D1FB6EE4297A6D67B30F79EC5FBE71D50C55DF74E78765B6311AA4C6A1662B1E42988CC101E8D11925202B1AF5AC871D777756FE261B1170C22F2B3405962A0C29A26BE03EC83881FC122E4FD8C50022894FBB0CE2E5E99B1AD8B3567BC32575D3D82E8F3F32AA0C840644CA5F782903F713CAA0679031DA54FF85F24AAD26EDB5A5907633855F4FAB6FAF9C3401B4716E7C84848D026628B0186902E484EC4C8DEEC0EE221B6CD13F94F616E9F586CC0F8E90117FB8FD1C52A8E4514A63E55311C9CAB37D0B523CC14BAC61076E12702C18C9FF2A87767201525161948249861B9A5380A699251028668E5767EC92363C2081F2DD53EAEB9E13B32A37912EF3968499C4ECAD594E6B1D3A2894415844E2F34608FFDA7C44F24757104F87143482C6759F7B19DD7B35062D9D0FD0F73DFE53B58E1F57E04FDD99A8277ECD7A7D9EEFDB71C800DC06BA6B8908B170CB5D5086BD85F3D8925D65F41C7AF698045DE023457BA1CBC1E07ED5E4DC22D16D24BEFA9E76B607D6A24243EB0C3D2C2AED1FF241D3488128DD38CD5EEB5D0F975754BEF15EBD1B624A75CB701A85BDB56FED4DC7D60EAFBACB7EA053F4204C7A8CEC8B2404D73C9A8A4238EA0A0469FFEB1ABD5CC0EA7E984FB6B6097034F5ABF49305BD85F3D8925D65F41C7AF698045DE02371BE420F4351456D183F5778DA9D5286A6C291038E30E1D72EC2B46B20D1D26EF72C0C1992F409EADDF0CF22D29FEF3559C2654E9E9606156D4A8AE60F576C960F507C77CB239DC882AFAA744FCEC38F99CE97F431E94D6ADB5ED6706DE7AE078EDDA4642326B8ED0405E378D800C0521D9BA3B2450F59BA0D4C93B044443156BD85F3D8925D65F41C7AF698045DE023731C037AAB932BDD08621CB8E083C41F400245F2958607818CF3F04F9B918316F7227844DDF94D8452F901A3D39E2E2ECEB7A0FBDDE3517AD09F7F4D7682A38EBD85F3D8925D65F41C7AF698045DE0237A8644A9594A0F9BD4CC49CE2D88C572D3CDBAAB53717C5E34AB42CEF95908E7BD85F3D8925D65F41C7AF698045DE0235AD504FB238FF216D8FC787220B130D207927D287418A9E61C900241AA66388ABD85F3D8925D65F41C7AF698045DE0238E2717DD847F5A5928BA2090E83F09B192B1F4120B5CDEE818337F06A72B3E91A23FDD80EA7A59FC2C21AF3BA8634514308E1B16488D3DA40AE33702E789F3EC7017735379BC355C218215E37C6B2794D1EBE11F548D44077CC2C5CBE0C78EC999FB6508398D43498FDE8A7B86E1A5336C5CC08BF62E5E45D1668BE4472ADDD4BD85F3D8925D65F41C7AF698045DE02307C52FDE9214241B5CD6D7520E7CD64333BA36091DD6950ECCEF0C7218496808BB2058EB72A17509690DBC5F93CF8FD5AF084CC108488E8A04298B1D7D7623F6BD85F3D8925D65F41C7AF698045DE02333C4C97C4E7DF8C73E05681CAE93A112D63096C1A71D0C434AFBA029B3E8A61ABD85F3D8925D65F41C7AF698045DE023EDEE89C5C7B475E37E6E2EE0ACDB2D8C1F831B543D81701882F78ABDBF742E6FB7EAD883F2EAEE3005E364A772C0CFD9507D5E403CD27A7807128F934F3DCEF3FDA019F6C565A99A043BB093AD34EE8985CFF78963BB1B3C5098BF1A93DD1C41BD85F3D8925D65F41C7AF698045DE0236EFC4A24C399EC087CE89BA138D0860D531B95C868AE2425A777CB68F73DD308A900C576F5ECD4777EA03269138E99D56D8AF454716E2A218094DD5E7C7B335ABD85F3D8925D65F41C7AF698045DE02347B29D92E0021D01AE2BBCE9603DFE3EBBEA305B4873467150E91D2FE99EF600B201070A90A36E97ABA2959C665C1C4AE60448634AA731659BA49C8BE563007BBD85F3D8925D65F41C7AF698045DE023BB44251B0C00A9A22B0E8E0E277F5C150B9E32AB7E484F5CB1CFA54AEF901771BD85F3D8925D65F41C7AF698045DE023C2C749692E9963851F071F6BBA1962AAF6887DC5BA394DB9123145773A02D605BD85F3D8925D65F41C7AF698045DE023E481DFC1997544BBB6A7576C104345A12DAC53A86647442FEF898FDC18E45497D521248C4167DED76F8BB089093E6453B1AB022404FCAA7C8A30B2B4DE446606BD85F3D8925D65F41C7AF698045DE023731C037AAB932BDD08621CB8E083C41F400245F2958607818CF3F04F9B918316F92E9F0C3CB68301EABCF963EDA46EE48990B4C3C6FEDA59669663A701B15E970F14184C0EC1FC9D45DF4452FFE1789B6112F02CC76698AAB05636E0DF529C309F5F8E22730E0275D86262EC265DF75DDC68703F4A1FB97CF47225A98BEDC0F9582F813E21A94C4F464FA8647121838A5D24967791F6BC8B38BE61B855ED009569B689C183824F549881CA937ACCE88331268EB46AC963703107B93E5474E1BD0210BF5152D303BC0DD13B2706537AB3288FFC70BE7422F1E4CB469CE4352893DC16DB766B8465FECD103E351D7CA1D39B2CECA91272509C45C0E81C5B8C376372BF8133791DD7EDA704362449E21909FA7A88D706E0300A66B5C50C51DF2A9BB33B38250BA7A4F19BF08153B5B05DE8C56CC1540BAA4076ACC53CEEFF757DD24EC00CCB74897B1195765BE8F0744D27E9A06B1605945808BCAF15C34E9552F875ACC2B4C817C302DB7BC5ECEC9501A4AF084CC108488E8A04298B1D7D7623F6F77615C815EF15999BCD7377087ABFEDB08FD5C7BFD2BEBD6DE392CAD75D5454F91B24C233DA85558A5DBC06A31EDEBDE51CFFB1DA1320F0F0096B6081E76FA81DC6C729A70A659CFD95EE60C4D02724E1A29EE055EACE1B9A37E104048C4213371F64C719EF1168DF6183FDE0F20F0B2F74155861B799E71AF03CFDB7287F93FDFA3C57C440EDC3E9D1D6B86B800310D324C796943943621D0C2EDB3C654A2F6554F02300DC660638B80081FABFB72565035A6E8A8D69B9B892FB491F74F5F826C58893F1ADAF23CDC390C51CE7AA1E25BDD6AF9D4AA6E2A47DD9219764CCA16AD76C9679F454D1A1622BAA86E5A038D47C27B3431A1FB928AC56C8F88D5DC3E6BE6560A2B524CE52B8930FA12DFBE4B3B2C4AC300D6C24F09B40DBEBCDABE3A17CB9CAC0265A4D43B5C670331DD381EDF98A462C2E02F59B51BD15E2D91F7B1B2DAE93BE374D0309AEC6AF508A4A065C7A3F7D14E508876ECC06D7A68A34F5548C0FE2102C814400EDE64B7F9659E3FF3DA315CD3D8A9A3BCC451A582028EEE34F334FD3EFA774FC97C178509CAAD38FBEA760DB663E53A947AAE9A457B2120319E9F3D85DD98E77D8C660707E96C6CF10BC3DA96524B8D770B3EA373CAD258B24E5886ED9DFDFD2D3820EF3A31D8C6A51C985FE627D9FEA1BBF6548ADB5A97EE83CFA9CDDAAEE250BBA76D6D3E21E903DFD8BCECECAD077C1BF7F73449403DCBD1604C9870947FA5D5A176169CE9C7476B348E6BAFA431FD72724EF1150B031EAD5CA64EA935BBAA7FF5F16D996DA5456D29D9121E3C904BC1D296F106F24AFCF6458F67F5C7538077DF91A9BB08D6D463648A4A1EBBA7C8925A1C36BECF664A603DA159CC8232A4F4ED7F0D5020A7A8232CCEEAC73048024F926277CD8E2847F1A6F4C1ED12AD01EEE00C49FC2F7B1B481D6498FDDCF85A19F9B954F5C225A064A159E5F07938C59598A4DEF868EECCC3391D502F21DBC1D578E9968FC6572DC548862FAF1FBB4FCC022930BE351F5CD945D20E33CAAD72DD7BEA60830214E4D00CF7BFD26D8E5F640F41B211695F7C9F78B4DA8ED5F92F9BE45020D0B3737FC54B92D8A0897283EB15D3BD5BEDB8FB39E72BF4FC690F60B843DB3CEF5BCB846E094C24593101477EE3DC253D935227D470C0275DCDAA536D82225919E0EF25BB4ACB0C655AAFD35EC4E81DF94437AB695DA3248FDE53B389A0959A5271F39B738204F01E303FAE6CCCDFEA0754063C509AB8A32D83E01363F9F354D36EE9BBBC7943CF61D113DC9DBDC4AC95B2542F52F3113A3E11ADEFF4C9A26EA14B3317BEDEC200AC6A044B9005D9DB2688C64A3877621A031FDD5D66779E956FAD25637D089B4CAE6666F16CF9EC8D0B00DE1E3391D0978C75EBFC2F684D0C5224650A546B0F2D01C02E8832520E441AFB92AF5C7FD5CC65016AA8FED447EDD70C631D8F2AF46ECCA3401875F133679CF94E3F47098A978E4DED047535A267071D5CB1B7B1F92EA800C713649217D4F317E7BD8448B6835D6ECB0F6C6B7C5A25EC4DE66B76B5CEA65D91A8BF9A957D005DC58DD661BA936C2C202629EFDFE47AAD7E27AE871DC506D0FD2EC0645277EDD0CD5555C8DBCAE02B2D00F31DEBB24D8FFF953797E67AF2D9D6F82EE355D5568E2BECAA9FCE153FBCA63796AE684F4CE43066F9E480BBDCA1B255AC2A8A7DBD2E4B05E94951DDF21D3DFAD8C976DC43FF4F90A76BF2E9A6A1E07C8DCD2D5CDAA22EDC920DD1415604C47D7C6855E3D8BDB5A3BF83117313B9CCC0A64E0A1388A8D1685637B8CA991A412315940079E167353F31B3BF394597A2754A17B9F79796739B738204F01E303FAE6CCCDFEA07540CE49683653466D771EE37E772A3E91451525F505EFA825C12AAF5588231BF96809F79A76AF1E0C186BC35A2D3A182AC504FE7AD798EC2B86E356293F50DB0BF0293FD44A581075EB2DCADE1245542ED43A6F04FBC222EA14CBF07ADB1B83D7464E05EC557877887B17C519122C7A5A0D2513E3650EAB6658FB9878F9078D66628CE0562BB9A8226112F84B7CB8A94AF6FF485E769F7AE4DF199136A87CFF064F10534B3D5B7965C1113230433D7FC469AC7AD31CFAA8BBA9872B6B3CAEF4B178E769BE1058F29500DD0D8E79A4937340C6C632AB9FA884D9043639DFB99494C79E76DACF4DCCDA25931FEDE2DB964CF1740AA49DE0126098ACC43877F0D843D1165077DF8798580D3C8AEB1C83AF154BA6CCFCFDB805368E09B5443BB02486F8AE0C8310DC4D990EFCF6B12DC8451DD9BF10C2CA22DF96859C7BE3C7D885386CA8122E06E74C15B81E0C6C508A26CFAA8AD89B8B47DE0E0A27E61277B1E25F0ED66DE000534FDF2E5E8A054DD6AD783D7139058D747394FBD578E389377F4E9D899B2B07BD964CE6EAD55095971E0651851B931106CBB2C85290EFA9FC65DBFF2320C143A4D5C5822EF92AA89635FB450B4F1ADA4A7FC6AFB6F6B0BDB4DD0308F90B1EAD040E5C8B9E2BCF7B93DC4DE09020A7DA04A1F1BD7F2590C73FFE385298B77851062FF7FAB906F40CB60865D0D5BA58F0B3E5BB7FF655F5C9B50A0C866377ED8205D3E0BBD7D6ABA87D6AEB9AB5B23B8C482F684EFBE72A4DD0E7A74BA983332CF6DC9E35A7CE697D223134E764A3877621A031FDD5D66779E956FAD25637D089B4CAE6666F16CF9EC8D0B00DE1E3391D0978C75EBFC2F684D0C522461375A54C1D9EABCA85A0AE26D5A0AB5BFA7A88D706E0300A66B5C50C51DF2A9B408A4EC230AA0968CE5F4D689FF80E64AA8E6F81CE43760AF675B88F3C71EF40A99A3294DAFFB6BA8AAAFED71D6926A860962C4746E61DDB895A30B5615DD7C0279E5D19BAA6003EA65B21255A914EFC11EAF1AFFA25518BF79558F09CD6F55B0D83987A102361FE52A3753C173DBD78E041BB7CDE4F03823D5FE6605B1DA4BC7AB6C63F3FB048B6F9893F0BA5E4F0FF6D19D037C68BFC72602B2201279FD471F43E3500ADBE2464B5604E1ABC1C7635B01813CA41590158148414B2F8B1E4E9B8D675ABC9E2E1B54AB98EA46557A76204B046B578027E36FED32CBE0E7A8AA6221B4520461EEC3754A9042BB3041B069DEAB002583B00F05282422F4CC077300044D4D197D7DE79964B3BDB616635B6409F84C013F39A4C9960D68CC250D734523307DD2D1607A380424E47B4719D68D59F58C0DA3995E356D51ACA07DE88F5EB801C39880C07B906FA79C344B1634C983F453ACD4A7AF2FDBE2EC6F5204B8AAD0C553AA2F2F9DFC5157314C48852AF65C22B7AE7C6BF2ABF1B1A101C6D51C44DC97EB404C1A7DB1771C22386224B6196031A63264E63FFA9BEB65DA0D651FB1606BF3C0C5213A7E108344F412F4321269C46F43A9015448B40A0ACEF01A0CD060E2A2A970F1C4195ABD8887C6A3996C69D18847B52A52D4313131F9FD45A462E08E8D5A469BF2BF41AD323ACFF62F1738709A6DF227482590182BAF6AF0FEFBCBB8412125A448D84AC10FCA2B122C1C8979B815ECAA6B9A67D55773FB6D38239EC2A8729A43F15A9E21CC0D27743685C17F3F70614387D987088345F8411062A31967755B924CEC6D5CE1277EB38679AF8AB14E40178DC26A11FBDA5D2CC1FBD85F3D8925D65F41C7AF698045DE023931A78F0E31D1AADE5C9A2F67FF80F9BCB1B7B1F92EA800C713649217D4F317E872A2AABAC168D94FB990090AD8944DBC73D68048590E547A2DFFB262E8FF66BC992F29212F0015EC0047201644562267B783B7F0CE2A574258097A4A43575D08DD78FFFCD1EF10EB50AB7FF8C871A5EAE4157C35108E4712B285B44265092C1BD85F3D8925D65F41C7AF698045DE023AE418E1866E3C704469A1497FAACC04880D2A9E2C03358D7744DECC217C7DB591A5F6FDEB8ECDFDE8D28FFA7AA4032D48EFA8FB37536B500E19B703E0FF54ACCF2EBAA13C4EF5566616C6029FF671C8D9E8BA151FF293C863DA15F91E60CB6E57B8759144BB847E7AF6BEE0821F45563960ED28A9C5C871E074ED2E6DAC9A3F17C61A3F8BC4D8EE6A063BD7D50B7B248B6343D3E7B97673604BE36BA2514946AB05E94951DDF21D3DFAD8C976DC43FF4DDDDDBFAFC1A5B06C81376AD32BD4E7164C6680C875294B6DB0171DBA525096EB7F2A5BE3188BB2C548CE7DE5D9582479AF8AB14E40178DC26A11FBDA5D2CC1FBD85F3D8925D65F41C7AF698045DE023931A78F0E31D1AADE5C9A2F67FF80F9BCB1B7B1F92EA800C713649217D4F317E872A2AABAC168D94FB990090AD8944DBC73D68048590E547A2DFFB262E8FF66BC992F29212F0015EC0047201644562267B783B7F0CE2A574258097A4A43575D08DD78FFFCD1EF10EB50AB7FF8C871A5EAE4157C35108E4712B285B44265092C1BD85F3D8925D65F41C7AF698045DE0234275C5F2D8E4A0730C04190836651190A35617E938A4937414E013201A0068AE1A5F6FDEB8ECDFDE8D28FFA7AA4032D4D470F24B81FBC5E80FC3440AD1D09692960ED28A9C5C871E074ED2E6DAC9A3F1B4218DE3ABB5A2B2243DEF9C3253E12A7E230B6CA9093101768C651B95928DBCBD85F3D8925D65F41C7AF698045DE023E3DFC3795682788D80C7821AC3F21F04A5EE1FBAC2A654815A9A1FAD37AA5CA149659AA7C12B243F1F4132BD7AF1FB34775DC4F361034EFF791874F5AF05CE75C490BBFB69A97E8F0ED644685168694A10FB665A2CDE93163E6108553A931CD69A70F6B1C2538EE232AE5DB1095FB3251A35540C69A83B21BD1809E51EB6DC6024993A05CF866B25DF57DC770554C33B27D1410EF0F091E41E57EAC5FBCE09822813F2E20F19CD10D45E6955DBC483D6D4BEFE477C9F9457AF1F5681559C0C4199E0F2FD11EEFEDE456ADA7C081E4862E7EF1E7936B727CAD8622D2A1DCD4E4EB1B05651EF4C7B8CC769CE1ED0558439DCBB6E04316E4B11E55D91E39C20E9316402B99AF222D8358C157769A34CD06961D401F059B7E1FA37CECBBE807C8771B1712F44878A89BC5634DC8617C0BCFCFB34A4E9C0473917833D20CE43FDD1A585EB485E2F9ED4CAC7B64A10481B7EAA3A2B2D1F699DCC15E2E1237E83821D723766DBACA37669C9DDB211773C796138199B231E12DD6EE9A27298EB4817AB0C48BBE8C9E276FA9620CB804D6E3C869231833DC70EB3C4A34AD9B1087590ED2C88842952B47AB27A42C223F162630FFA855048E09A50EF76A317BA78C47643FF8B4B3E4316C9262C1A52E5DEC0D6AB4C621DCE0C89FFC95A5ABF77269D82C06BA32010F28840706D80E448FCBDBFE3241DB527128FA1C7AA36D95C093CB9C928BE348830813177413A4BF9E6C1F055805C5B4623B6A9D281CB8AC736BBE784B867977269A62F83C698861BEB8211995A826E2B9CB27FEB1EB18732F13392187C4F2BA835217ECAF1E7709B74F5A694FEC3BAC37F0DD2F92673CB4720F2BEC17F04463F7FF14771573CF74030158E45CF1C0D7C4D0C921C1F77DC462708141A2630B2E34D2748597E6A6D7EEDF61E36F242F6D0EE18114EC1EC0E0D68F136AA18AE9E7917696DC41A0B58DE11225EB88B5DD639F02C36C4E807E3B1C6576F40EC0A1A549172BFE88A373C063B7B6387CAE9950CC1E3F071D1293A4647A8B03392A709068CA110B8D9E9504069C572464232D3F336721D31A3E002FD03E029EF70A85509BBF01398803B36EDA687D2005E9D86BE0C6AB9323068BD44DB5CB174AC049A2B439168E80E9A8186CF54A2A29ACF8DA9853F68C302EC0E721EE74F981B2C57F1FE016F326465819CB095FA3A4E2D921930477E4766350AF8492C5918CA01AB7CD408726B48FC8D2D01F61C35FB582F813E21A94C4F464FA8647121838A10C8AF5B08D833BB6F0F6D92B7ADBC7CFC91D2C1248F2F1A257912D631CB2E893BE9FABBF170B53DD1CA5ADE81B2BEB2414562E593683EDCACA9AD34DC4C9CC7BA67799E6658F78A57BA9823F78438B6F2E412EF8F8EFCC01C25ADC166E6E4535B75A70CFAEE3767551AE63DDF61FA3CCE9A71F381F5432168F86140750DE0262A282ADC67D9DA4D42292C5B03BDD0969DA877750AD6465328F07568368D6320CDB97CA80B763D17AE4B4BA26F765AD017987E7F1681F0570D1885D1B1247DC5BADBB59F582C4EAA34DCDD7C2CA6288D21B584287E808995661491E44B46408C118E77C07AD477F8979D40382F1AE0C1451D020157299356C8F2F6A8711C3D4DAFEEEE0ED28C521ED683ADDB303C4766980DF45226652F96909C4B758432A3317A869B402C94EEBE51F4BD8C1BA3EEF6D0AFE9E747994CDA68E0D96869BA3D388C2F3D83D7BD1A3725A9A00281B60F7B9AF8AB14E40178DC26A11FBDA5D2CC1FCB1B7B1F92EA800C713649217D4F317EF470C298FCCD413864004714241CB3F7EA5C63B57192F63959EE1225134DC25369D07092AB41EBE4E365A3A22EDC2343FA20DCF993F4B2A01874901FDA1E94FB7C8829A72049C2926100642F03B06371D3102223C9C2A17EBB617E997F60E3121750E8801B67D29ADC81CB86B6C925047FA221B685953167A5610CB2E2C17E5D1A5F6FDEB8ECDFDE8D28FFA7AA4032D485D1F79E08F5852454F6A905945DA324CB1B7B1F92EA800C713649217D4F317EEF5320088E15FE08D9288020B9497F4A0D4D9B56E081C6DCD205737803EE07FE9C230D997FA678B5241577ED601DD857FDCC3ADE31BE956502ED15E892BE6DC2B01813CA41590158148414B2F8B1E4E925F081622DCAA176B556DB01C24B503681F9D4F6DD2E461B9D2D0062052F31CBA6C2ABE8613CFC66873CA00D6ABC373B5E7B48413928A658CD6B55126C751F0D68612E1B7DFFEED920D21208A564A8F58EE6EFBD4DA1D5F6595231F86F7530AE11268657C45E7954248098A313D398077DF0189FE06D65F74C0F3189D9A9AFD2BAC8CEE68E0189E5D791F90B472FDA79C1FA19CEA2A5C2DDB1E5659DC81C6209EDD0C2FD3B72763AD3B232E21CA9173A34C568BB69FDE829D4E8E67F68F40671616010BA23E25F0776F37A6D55A5AB6AF56D73F11A63B6CE749FF06C7034F549C47BA9EEB60991471B50BF41CB21EB9FF2C2ED8DA2968D9457A7F8465BB7B93C7D92158495DEC56045E6E66116B89A97EDE4B8B59376425431AAA4E185C61BDB14C1067A63B97503917F66248CADF876D62EDFF4097E54FB3B06526B1026B9A598E65933FA9482A2D78E15B75D824BE477D5B867C9D9917E3EB1AED55F16A8C30EDFE1C818A5CFD5FB50B1101820408F0F41C85479CAA593A2C99DCE5F5D3031391046AAEA136FA2DCED2C8B1A37954DA1C2B8935F66E288B9565C16E522A55B7B08C4FE02684C7674D6FEE2F6CC9D0E45AB9038F605ED5D7E8D48564147B211E436A1C0161A03647D5347CB2AF8483F44C3049544AFC6528DF904577790EA0CA91A375F048D636EC65097CA3A0A5E0DC9C807513EF4843238B00F3ADC3C4B356BA7CBF2AA1C2A0645F448F5DD59E8C905DFED180FACDAF58EF98C13FD7B13E41CE8F9B15EA83959E47564B9EBCADEACEAB0703C051CC3AB3C82573C4550315977D0ACDA15424FE4A70643C2D2FB71E589512B725F5FECDA537441823A043CF97D892EF7D93E5B6AD0516E253971C44F8F57211A3EAB0993A396D35E48F0C8C530A7BC98F8BA996CD9104F27F77FBE469C93589CB327C8A91353B4D58C23A6E500B8921BFBDD96ACC2EF83115B908A79A0AEED4F658496FC58B23CFB37C037F7708DF8A75E83AABA451BA81B9C4C879491BAA00695307521ECC350CB613B3FB3EE65579A31CE4933ACEF0A2AA4F2B5B18700DF3B5A7CEB49620D8980FC6E87CC2C0F62DEC7BA2390D182C363B7705F20575DCE8BE076E8FC370FBD27BCABA911547D625E17A95C49B18F50F5F4CF898A7C965A5667116D82A7A6BA995BF28912F20A2AA4AD643535DDCF1A7D2C5D8A651314151839258454DE0C090D0FC39829CBC4BF341A68422C45EFB9341A55DDE2A2AE27388031F222C3B5B4A150073F2891823AEDA85183A377F41B2CE771A0575F2697C8E41791A6CEF8710997CD12F6EEF2221E3EF620057E561D492A2C3ACCDC17646A23A57C0908CCBEDE03E75BDB0E617A768ED2A0D0606B20806E888A072D64C61F14D3723B576A5914726FBA14C33498F5A533799F31531C04408900022BB39F43902613488A4A6CA90685E41CBACF34A5179CF78824273CC25674F53E8CD0452745EB8F61218216D47E070D7D514691488D4B78F38E77939BF428D679C2D9126485B9E3E2D3476EBDC8F4938A2DBF844BA74574D11C0702405E64F605F2D2ABF08039740D6BB63D1A5E7B14BC928CFE05CB2B10CA22AEE3CD5BE398C165827BDAD3C06147ECA0208E7E13D8D1D72E36F24A01F3C80BA9771725EA3F0C9BB5CCCDD152ECAE9E0FF4267BB737144DDBE6EB336F86C0AFD43E36C871D76A8CC95124FB0D3A4045DC549BF3DC455088DB60449608E1E350E88D7C077A44AC432429DC9E073225624D3C0F98EDA881CEEC74A98E6034E6EEFA4A0F2BE4B2B2441429022BA321918DC48361CC0765A4FABD160F915E51E4719EB162607AA2FAA7F15ADC317788BD42034F424C7F704E103A030FADA8103093C6601121DB695C8C466C0D87A2E0679C42F6130AC399F5C3B295BA99FE4FC244E35D3632BDA32B5F28399BC82001A0A765AB80F2D97CC73EA97342E165F9CA8FD2F1AF7473EAA0811972A3894595047B883EE8E9F8129F2B3B86D54455DCC3F75683166AC65417598B33DB9BC823C63EC7F42009E2E3449ED1794D4B22CBB62322C19785F841C3514D68D259308E114C46AB03F133BA8C2F4F2F92D6AE6DFA7EE1327DB2F0611675CB4EE87E569BA9BA3E8716E5DD35BAF205E259CEB7E48D7911545FB07FB516A0A8F4426F9C1BC6E1BF04B73CB6854381E9B295A848A9B6194A060980504AF0736C905E57A321918DC48361CC0765A4FABD160F915E51E4719EB162607AA2FAA7F15ADC317788BD42034F424C7F704E103A030FADA8103093C6601121DB695C8C466C0D878538EBB5B42CFB7ABD1154B3329A077DFE4FC244E35D3632BDA32B5F28399BC894900AC757E781176C8C0A386A06A290211BB944F4CDFD88F7E7D2C35EA97B25C0B0537C72BE7972E0100D90C36CCB4344CE5A0D0DDD48CF43E64B4F63E2447E30611C4F031FCC3AFB317528C2A5E26171006C412E79536D51719722A20FFE96BC8FC855A00280CFCA1C8B5CD9B023D0E76A28997505C3FD8C9AFD76BA39AB9FE6F24DBD6829E1C5CFA50BA6BB9CCA3833C418C279DB0605509D6F9ACE2F2AF0AA5ECDEBC506BCE63329F259207528CDF90F5499F69D9D66C0805335CDEAACA2EBC1EB4A90E0BF496B292EC1EE88E9042A24ED26639D055802C1ADAA59A1FFE8C9C01F8167DA2C1AE3EEBA76320008AC11E360926272F7B3E27F28E70D514056F7578144087C22B53C8CDCE243381B1BE8C09183DEEF9B234127FB199000910D1806CBE9D6F462BEEB364B76FE7315BBB491F67B2B1978D4C8CC56A164F3C0289A32DE32F9BC101D0F4EB1947E460343FDB5422D6E3A313A35D0B69571601231CE56C362D1072E02ED94F6EAC2ED2B6E68D05D5707190156C2697F028F666566F9E5077959C4D5D28F10B651DF4349C6E0A32DB4F3F49066DB861C7EFDA04CA1B726F41B60AC0BB00004F402928A56664568108A6F33B0F2EC45F2065E25D0D37F939D4BB229816F764F616F769A93011F13D397154ACD28AB3C1E4DBEB53FFB367282F8A8E8EC0EB0FC5541BA6654D10688D7CF1F0F8A7A7FD5A3B2EC00D1E320FCFA8E24E22E6D77A377784D3F7F1366555794E094CE0988F38A507323F765967BED47C4804EA126CF6EC461B5E2B1B5EF618C07F8A48E93C52136ED5E938073A684312444B1479817D3462ADEB9BF91298B09407C859D2BA103B9A0CED034B6252D660BBF34A717D2B5F87FF0998BA321918DC48361CC0765A4FABD160F919D14E4385C82D1F33D710179DD7BAE4C8364C47B59AB25C59510B524969D82C139740D6BB63D1A5E7B14BC928CFE05CB2B10CA22AEE3CD5BE398C165827BDAD3D83D27C28200F64944AEB94E54F87929720163BCB6C98F1340C451E426DBF78C05C26179D001D10C257F832186E8C44752970ECE2DBE37F54481D6CFFB61AE816802E3DC6D137114FF746B4CA8A9CDBD3EA5956C241E54C46AEDE6411DB8F07EF1A33D5CBFBB17BB13A35CA639F00E061ACFCD8933BC830C9800D174C1243E88768AA4F6501BA7916DC5424F64E5DE2542F37EA44A2F863A84CD9A4B448302F3DFB5CF8C9AFF45EA01E238D93C365893DD9566836D55D3E0012D333123FD18A0B918B4990E0A4E8C7BBB504F9D4103EF2108E5E5C7CB0FD726B4A902FA3347FD2CCA84574C0FDA505A323FF363DC713744681AB37EF466D2C4A22523645E6B9286C40F8116D652C04F06C2A76F392017394BCA7E99985B461F7A531B9B6AB0C3150E535D863E86E7A4B3C10ECACA83DAE5DA015C2FBAD4685F870C7DDBB48ED5756617000DA4F82A8BA9857F3AD312540AF8630D2664FF095137CA3BD8317C4F9F9CB4E8AE101B19F6E9F43B68717932654EE7106DC4A9DDCE765B96DC85D5E65854970FB1EFF5C0EB23175BC01991DDCD40F2C6FB7F5DE5198369C67DB53417F54B2A96481BD4F1799234C301C3D4408D446FCB75EFA24BEFF5476CEB929C9142DA10C4E1B4B7A51CCB0C0A65892EA68EC9AA6A4127611058F53029BF61CC208E3C87606EE5C4D98D026D890FC6940634978B8C8BCE60EA67A8301C1D2BE1A1632776B60F90338E60FDB2A93D2BF1BF3F493953EADBE1C7F6E8898F35EAFACD6011980CBB006B3FE3238D8962CDDC91BF803DCA36B6C8C5896BE6F8D70CD52F362E7B8946543580F5A8E2C06CBE295628BA9BF1B36D5D0095F841743C04403F59650FF162383245760F289D28AFA2149DACD17BF6CC722815A9CE5426CF99F532CEEE2CDB227B1052A12BF244A6A07FE84576F34DC76C92DB09B66E13073E3D96F18632CE9B2B724EF416E293EFA48A98D865F7BE412B2872C5B3BCE204380740452C01EC1628073865DCDBF39FD620FDFA3C57C440EDC3E9D1D6B86B8003105EB89F671B49C1244E22CF24D164A6525FC819555EF3BC679D92030B3702887EE6546EE3ED0C8243BC3BC1114ED10EAEB9511ED601AAE8BC200CD0D593EBE83F4F0F58EA98B96164B11B52BBA8FACE12124F44D490DCF990C249862E8A3DD40CD0E2A9B7AB120FCA572F12237147EE2AA513DCE4B67AE75B3BC45ABADFF30178F8E3BCD972EC976782F2111D41C9760AAEE3E16277735FFA6F8AE3F90B76505DD307A324386E7FA152BB40872459AF31AAB6767B2A453771A250E07F3A7F8B7F677FBFDA7FB4EBC3B368D482939D9E0A8D5EC29228EBD9460C390C78231D65CECBD494C4656D3360DE5223C4F867CADAE24CDB1C8F8EE371F8077F59AEBC2CB1FEF7FF69B1640087ED587183879D9DE58D777E7404B9EE04312BFC2FE020C9F201339C340AFF4247A142E7EAD96EE605E82B34056F6C25DC8092F1936308B4C20C9C5E1C9ADFB0E0B760A5F9F33C3C6E6D80AB0D778A3565FA9221C10A277FC72E929DF009706F57F5C95FB0D2CC06BF602C71E3136F2F78C9F13AD65CF257A07490B016EF5D8FD16C261DD7B8A216726004469B2416F8305B5BC50EB133092C4044C916228B2210CC6F76CC19A2A4B381808E9257BAA851C2F22CA5FAC357A1CA771EFDC6C8194A52A3E6BD0D46CBB15103505E9E7D1ED48A15BA49B58018E8953645D7AB3C32CE9E7E1BA9B0973C21B98ADA16CACAD1DEA5E5A9496BFB8296F39B827C4A9FD200D06AD5B88D968EA0DD5A5AA4417C3F9DE7365DDCDF78B390CAFAC76D2E430D04F75E12A9DEE6361BBD3AA01C1B6F5AB3B21F1ADC278EDB16D8AA22668770FB3CE11268FB455C4766083E4FDB928727EBA53AB83F361A793C245C4175122CCA4505DAD812FDCA4A59A89F0D568498C36731D675FC358118CFCA6B759D6827581CED4C460F452229323F159A8AA089BDD3F079BACB2223D89652B834B17CB35C6E9CC613BA12CED786ECFECDFDB3D7B7B43E43394DDDDEEF9D1F36962D906F797C39829E1594814F7D261569F57EF18BEE88EA6CE6A55728C1209C441927F0C455DF3735A933A4A7A334227B7AE9DF76A7A003233EC5F5CB7405CCDC836B63AD602EFD2B8010CCAF1CE32DB3A2C9EDB39ECC6AA953D1019A7A017AE6C3E0A2C0F60857ECC181F1C215E4085373F397D6CC6DD460478D10C24E3EC1EBE2E66D523D10E7FC208168745447D64135DC38B79EC5743F8C65E1A484198DC2E25D387168C7482FA30CF9903456A300AAF3202A3A400DF54467D8B4404A406C2DFBE259C470848C320B2364B0F1882172DB641EE72EC525AF890F8B3D71C8C3798802785DF5F53088062678FF2BDD846435019E1C25D03865629C9ECC7EDEDA7A725FCF5D4B2CAC8E72F21862647DB17154DB596D9C4A620F48806BECFAACA252642A0E4638E57049786DD1E1C2C972EC784D9C11F4A9604421C66F1BF0B33DEB1DC1CC2FCEFC78492C5A85473EC1EBE2E66D523D10E7FC208168745434DD533CD4BB98E231B38A293BDAD174528AFC6A1221AA43887392C5C3F71FF356A300AAF3202A3A400DF54467D8B440458FC3D9D21EB5E6F4ECEEECC7AE3001F1882172DB641EE72EC525AF890F8B3D71C8C3798802785DF5F53088062678FF192E94F9950A846C3ED933BA3B2DE586C4679F5CEBF7ACAF95F91F1AF96A3DF508942640014C29CBB10DBC3E9646C698B259B49D420724A0198B58C282EEB5C3E749A27F9DDADCD9FFA11756B022079EC74D0F4B30533A0AEFACB8621269F120E061EFF5A0C3CF101A620F6159C6C0FD990ACF69E9BB6886CA89B53ED138E3B55BEECDEC298F4B44FCE1667E638BACE079918963A07718986E9B9914A6BBD09BC74D0F4B30533A0AEFACB8621269F12012F7D8712F52B9AFBE60BB6450EC8565E58C863FFFB6D2D12E07A075739D25DCB32467CE8142713BE6672E87B184F33CB4144E020FA1888262F841162EAAEF2B43579764937CAE17DCE483DA217E771DE0B7FB5FC86DAE66C6F84C7E07E68A45DE3DF701B7105875ADDE96249FA611A80EE750FD99467410A4EE5FE39D2F63C7BF7A3AE2BA0282940EEC048A7D81F9DEC74D0F4B30533A0AEFACB8621269F120525246DC973F774043252AD3CF52393DED94183B8E9D15E82E432F47A5F37C5E8130C86DB98F1150366BDCD7CE1AAA39B4144E020FA1888262F841162EAAEF2B43579764937CAE17DCE483DA217E771DE0B7FB5FC86DAE66C6F84C7E07E68A454AF9AC0B93E7ACF58D2B126275A55D430EE750FD99467410A4EE5FE39D2F63C741BC58552F3DFCB86293F894968D87B0C74D0F4B30533A0AEFACB8621269F120525246DC973F774043252AD3CF52393DED94183B8E9D15E82E432F47A5F37C5E8130C86DB98F1150366BDCD7CE1AAA39B4144E020FA1888262F841162EAAEF2B43579764937CAE17DCE483DA217E771DE0B7FB5FC86DAE66C6F84C7E07E68A458CC128452EB84C107F66F8B4B2F9C2800EE750FD99467410A4EE5FE39D2F63C7D369B09A6A5FE25D142224123E5602FE0273085D89F573D015D49F69C9DC381F09B34BF70A70380C8AA9159A40DDEC150F61E82292C8D7E88C58AB000C7B9E56C0DC4D885F18564DB87B2EE9DA8136391FF0E07819AC249B4A861C162F6ED8EB979926DFE9E2C0F894094E088103F641DA08528F71C2204D0C78233B0509F5992753ADA7FD1E96820C3C514BE1C4E99CF43B1834765CB869FDC46644D2C70D48A69DA4DCAC498D4395E8DD905231C8ECAF78562BDCC96064AC17E2ED262B914CF763A31F00698D23769CEDB9A43F744AB134A56226AC016AEE4F69AB3414D2CBBF4010E90FF7211B2C1AEC547ECFF2837EDEDA7A725FCF5D4B2CAC8E72F21862647DB17154DB596D9C4A620F48806BECFAACA252642A0E4638E57049786DD1E1201878C150CEDDFD34875BA3E892955D55820FBBF010FC7332136E4D269DB90F128D4398ABDD3D275A21AFE067C2C20E21B27A167C5B0764CE0673A69793A1C110062503532F2818EF8BEE4ABF1CF5E9610210AADBDCA6D6EE25683478D5BA77C89A2888FF7E5D1E5D17B950572DDEB41F36DB0278BEDB21FB29D99F5113ADCE71A858DCFCFF218A957B96027914ECCF10DB840A370D66D5301C905F2F6F5160643AC4A12D925D9DBFB7460088BA1968389DA6D169143BD0763BEAB73979C0C2319C9CB8A7340F9C7E24C65CEDBA15B23ED9C38FD9F649522C59A508ACEA371E11C088FC291FF08890648C32199B4AA167BB8F83418592041FFD8F6B6883D6C7B56E795EFBEAD0CBFEE7B60C70BFDBBE79C437697C9D353751364C5322188B2FD7059D0BEB29AA376AD833A9BEF0414D1C99D09B5427E93DD533CA25A931AAED027E94D9B8C9FDB94973EB5F098809BAB8803D5DDC35819D9635646F4A17BE32D724B40706EA41778AC28B1C2B567A3DA7354CB618C0BCDED08E1DEDCF2D49DD2155749630086DCC07888254B6BCA4C701498DBFA676613FC0BE57673876878F3E754FEB7E849160F84682DA29B7A24E5D24967791F6BC8B38BE61B855ED0095839DB31136A9401B6D8899B7B0F0253D2C3B4C4818D1C121C3B7911AD15ECE100DD1459A4D14660220932F5B6BEF9FD24F72A9B2C59B567A63B5A389B4FAB8A959EBAE04752298F612544A65A625A19A838B4FA99429B61D783EFFD9554B25FC3BF24ED8D3CBC357CC0D166906F08DF5500B958536BA496B23B5B7CFDA98E7025DF3212B1FFF842FDEC8E37082DA51E3BA2390D182C363B7705F20575DCE8BE05799F45CE113BFF3EBA5BB46B537838C0FE739C3C77ED252E97AFD0BB238A57A518C44660F16E12916CF0ACFBC51B86EAE3776EEEC83BC1BB7615AFED1925ED77BFA9540BD5BD51F8F34F54D099B1C74653F03F7A9F854AB0DEBF8553839CB59D5D334FAD0EE28AC877D0F7E5A59DA2DA6E328493916C52FC66D14FAAE4FFD09BABF1F6A8417284D947D0C8E80910558569B3576F59417DE611BBBDA0F26913FC6C7F595170F495FEF4593D69AE29E439F7BEA86761617CA1ADC2B7616C69FADBAC7D97E830A83020F6B07D467DB8E819C62530F180C022C087E616EFDF73B380DD5035FF873C69F3D6CE063A371361DCC1CD88E80BD22DF2195E58C5E4898A645DE0913EF32AC535BB2EF04025747F900B54FF246CF62831915CC19228A403E82A485F892DF24683E149D178F64C125850C9B9FB34D740B346A21DCC0C37AB64D288ED9AFC9DA79119017C42D23CFD5BACAA437CF50CDABCA5592B561EF445579327768125D5A8A6F29529AD88AB1401388A5D46F14F76D940AE69F1C9F743290CCB73F0B161A354368C24CFD48B1C471B310CF48E8AE3ECE878CF84C4371551621C2A3B0CE923A90C301B6F30CB7C36FECE520535F83E27EE8D9ED58155EAC22DE132EEB452DBF1BB5FB68AEC43DD55069A54371F960902CA4362DA662095E7C09A25B16881ABD799CFEDDDEB5C1C684E61ADC0437AD806A6B3B4E6E9F86C2847F1A6F4C1ED12AD01EEE00C49FC2F77E34D0561CE49FD3C254D650918F3541E373F0BF2AE07BC2F3EE193AC49749AD641D340EFBBA6F97988190F0EC0DC08FD7A17C0CDF8C1C5ACFCF568F75B53592B7171CEAAC6786F0E2531AF2E633B4B7D22D3AD0F8C320FC9F2DFCFF10A27AA22BD997C6EDB44FFA62823BB94CD03F59DB56A7158788441E7C892C9F019F960D0E7AB0FB6E3CF24DD277C8E8A1B58DB0706E4FB5CFB6C7EE605D1295DE401BF0F16D1724DCF710876E645C599BCB3A87D30625D156156F294009D6C932C0A035D1DF271A933B5B03FF46F6D3FDF2E78CA11CC8138FFEC139A9876743A674208121CBA7883E2333027A8346DC0228D4582C90C49F9F60103DEDD1A527509255E3476BEF0215A10209EA8F638E1093219648073CF5923A10CAEF423847BCD0CE232E7EEFDC21E6D5713649B6088F901FDD9CB5051099FA8E523987483B806F12A92257CEE95904BD92987BB9F634CFF34E7FB1654B9CB0804D4D88F5F78385EE2C8454EA74EAF2049D430ECF6D15BBA04E4E290DC94379005A5F9952259095BD042E76B84CD806343CC01D2DEDB64410F933B34C1F0B6652EBDAF5C27902D95F574E06B27A83D2E9A88AA3FACD1A3F9289A52077CBF61ACEA49D1DB0CBB70618FBBA8F1E783CBEE58EE7B055FA8456FE0D351955535167C34E3B8C9EFD3C63C2AA79970E59277173775C5EB91A101C70076ECEEC4E539F4D39254AA988B3FA27AB10D4075DCAA263583553D7689DF98E366D5B23B2E1C78EBA5EDCA2E3E213FA371B743623A9895C7484EAA1AE84D91F42005BFB62A8E50BA38BBB382A71FC92FB89F08A2C9FB2FDC5D524509D25CA5A93D189D25AFC410BD515D4159783DF3561762A78B8CE862F9A50E1C53E719CC8C53E01041B4EE1D679C20A9488CAF5BCC7C9BD66413D60C6148A199580687DBD9C55ACE5B78881613F26F32C35935B79C036074B93412A1C4A4C57C36A7A14FEF4BA2390D182C363B7705F20575DCE8BE034027B1E25B8346F93197AD0CC50E9E25B32BEE69C3A2C943C19DA82B8B10785D7AC923486F3429C2F2D0DB312FB54A3268E2D3294F116FCEC32CA91BA72B264D3CE8D4787F753B3508954FE655695BFF16D1724DCF710876E645C599BCB3A877F903C41232128523411D5D1379C90C4EAB3E63A7F616C5AC139A58E03832C1366296E63851C32D5B122EEAED63BE4FAC68B17639DB5CAE60D1D7F4ED9C7B8E147D557A9A6DF1281C69DA22E83CE53644FAA16406316B3E1601C9B6BB0F2879A790B7D66CD43E9836177156E3DAADE5FC2827178C479F9AC3433FFF0ACC4BC90D542E9A09E53FC91CD50827C9979F66F0F4EEAC47F341B2CA6932CEDC9B28687ED386876F68938D9E4A099B51FA590F587987D99E053DD1F5D9BCA540C69D866CE2CD2DACFD1E729081867579194B64783BEB0909DD513322CCC610435B4169499C48745A2F15A6FFCA8BED684EE6F316F6B85C29999349FF8A6B22511E2DFF20D5E7BD5D1407688361BB0C87BA33278546C523057CEB78BD007805E3B4EF7DDCF4D1D099B6E639C26E538BB91BFAEAE99518D7785C9458A5C4361EF6C4877DA81F42F00A98B1DAD20DE71610CD0544B702C9C9C62AC34B7481CA047784192F882DDFBAEA6DC3C41F738865E579AA23D0F27352E455475D74173E68980595E7FE5ED277FEE6546B82128F80FD183BF13CFD64DD5958F99ECD37834B60C7F5ABCF081F6AC15C1904894940B2CB8229552FD0D2C80EAE0231A72ECDEAC5FE3AA0D419D66FC1DCFBE5E54F69EB085B8D75F763094CBBEFAA935FDE5CDCB7F52383DF31DF10B000CBC5525AC72C017F7137EE09639E76C63246EE6241A6E845E8F352C646920B9F8B8B802E11951095D00764EB952F669F103C5427AD8177E19E09BA1932EB87CD09C6FCA87350DEE0610ECC9D6AF220D8705FFA18686293E051062306940390C50760767B18B42AEECA807A06E66F0B3FE2CBF3C1B86552AB11D6B5C8410663B541B5FEC8742BD25B6E5E63695772626790AB6D2CA6AEB862B6B9A3E530FC34582735D33DE122E11CAFD6A7F8F295711996E218C7CE487D18F5DB118FEF2B63BA4A73A4441F368266B46232F893D4A761ADB5FF895A8A897B1CBAAD208F1D7F89C3CD05330F8B32EBEE85B17AA71936512777A52873AA66BD82DA648518EC47A8A20E716C271A8D372E69DD04F50CC9F7573211C7CDC468473ED9B19DF7FEF2671D11FC4AC6106E8DCA20025B1D1A90ED311C1954F5EF63ED47CE573A7699771813F84D1FF2B246D5A73E6B129AAEA20DF3EA80FE0AEFD5015B4003E5318EB46F1F3A3103A02F95A07589B73F0DE0A9F19CB90402F4CCDF2566BB809AA50E39C06532377541BF1D06CE1AB14053B5731BD3374660356BD428F77F23AB1D1773D0D254E206220861DE1E61B49235F378401F9F05CB776CE7C5FD477C6E0FFD69D4DEF00AA6EDDB67462EFFE39A043BFABCBC6421836207B9A7E2BF4A583FC9FD8627B3500DB4051BE4232B1E09C08A8CFDCF6C767FAE774CA531FD7A2FC7179A881037096729FA2A078A560AFFE54568B25224078B378FC555A56375B8A6C204BCDEDA5F3FC5B37DE1F51D277C0856FCD800F66B51BCD6ADCDCC335C3181A766FA83307C8D6A5AFB91748C395BF2D0B1A56408099B3B150912B524FFAACA252642A0E4638E57049786DD1E1C9BF1F11C20204E9AE636F0128B22923C6F49BB88FB34155651EA046C7CF5530B33ADA80A14351DA4CD9D17148BCC1545E393CFF648B6F4B84380D0824D3B41245CCFCBE8F7890DBAE4F419E56506DDFB7F3F063E51D316B84CB3A382458C2A9AFCF6458F67F5C7538077DF91A9BB08D85CB4B641E0F2CADD953EA3B2AEDC5FDBD85F3D8925D65F41C7AF698045DE02391233E4A34E1DBF4020037E8B02D753F941C15E70EFB40F910ADD901ABE334B79A0CA3C28DFB3FEE5802EA78DCC5B2309B8332F088805CCCB013B39AEA9CD400533B896DC6E8C9C4DDBEE1C15557FF553E80A7A51B5348001B6E07A36C3818EAA20128EA0C69928A4A17277944EBC2F7AE0A76EB11E6187657115F0295C70A1E5A46DCB16D5903BD04D071AD5B9A7E125A46DCB16D5903BD04D071AD5B9A7E120458C0B3945CF462B298A4AB8A7D4BF09034AFC7921493F6DD0481C00F980286601E478F4E2479204C5F17337567B2AAC9E4C3A7A7BF448D855E354F8EE6443A98E0EBE060E7EE1218786A10EB31E5D302DC0B3D38E79185141251C1C8DF289FF4606C49F808BC063BA23B507A56621E539638627CDC6A397B76F12A70A889AF07909E2C65A4943C9FBC0D55F81A24B7D57ECD956705A7BD044D116FE8DAF36A23E08087C0A96BA03A3E27631AD192B2F8B7DB714C4389284C546850D06E61806BDAB443FE16A886735FAAE55E52F4CAABCB1EF4249DBEF799F1AFD5D1E27F64269C46F43A9015448B40A0ACEF01A0CD060E2A2A970F1C4195ABD8887C6A3996C69D18847B52A52D4313131F9FD45A46A90E9D9AFBDC3F51385141C11DCF0F463D5DC2ED6CF93ACA79906ECC2FD16B21243C6DEB8C7813548ADE1542523DD1ADBD85F3D8925D65F41C7AF698045DE023AC76BE8C479DB11E92327CE95C8ACD13BD85F3D8925D65F41C7AF698045DE0234596838E9EB787D7BB592A0C6D3B9CB2E02483B2E4E9CC5B2A489C5372348880D0502B32493605A5825140310E8F1062AF39349201A1F651B97A05DC163D5B1E3AB5321B9743ABA45BDA10E4E03376BBD6D643EDCB46D17B8084F845FA2D3837EAC101CC3071AE824E3A8EBE5C2083CD3F2995155B43271E489A7CCFF0609DC48AC0962067916437304258E0EB39BE10BD85F3D8925D65F41C7AF698045DE023471FC11F929D5AC57EB4826B3A1D6FFEFA7A88D706E0300A66B5C50C51DF2A9BBA470AB2401512D23E0C74B5B8489DAFF0760854224DC80EF1369D5D401A5797AFCF6458F67F5C7538077DF91A9BB08DE5FD3356933AE604FB81F68008A25111A6BF0B55A1A09747474890A7BA50C037E30D079BA25FC0FFC628D3FA8A3AD251A4112EB0951790B21F54B674BE8DC80EF43A55DF44E39A6C0090B31D37860D3707E22CDEBB0DD2BC96A1CBF5C1C2836A68D30F1175F65D96F95009BE6D9E3A8AA3E85F8E49AFF22E39A6C2C68B41E714A543908282D501EEF96AFFC35A614F6EFD9EE808F0C401B1F9CD4D9ABB107836F2B813D347BD4A19D47030D81C9328209A501F38441FB29D1287B928B0ADE1DC0F031324DE5EBCAADDDA72F93E306D5E9A5A814634C6E78C379647469BBB5D1AF9BB6D30336378B3700E3354CADBB9761021B3ECCEF8E4B435F349E574AA35ABDDFA5B29D67AFBF3EC71FC77F0F98406DED3B7F9F0C48E6C2B6711069516D66B22EEEBBB2D15D7C72D38EBA85CBF1FEB9401D2ACAFD6353A509E3BC7E156232E92A03FD2074E62BDF47F28168797634782F7F916161BA32194AAF7D5D8B524BA7D7B81E34FC3D95957D0B7338E82C824B796CD6DB91CA7C3B0BC3CB03B46565F57346B148C8BC1C3FF82E73A8A0E3BEA81FA0866BE45F65995D3325173761D2C631C1F392E8C0C2375CA3CEE91C7335AAC9BAAF463F25BE80AB2FEC4F7661D7CAAAD14AF3F6DB2C4A53BAFEAFE9F86B3A3A847DFBF4A315052A79132DD5D77EBC6027F586A833226D440F1F2681F102B8991E6251F0D856E31D282DBFCABBD57C33220C9A6D2264AE5AF0452F316AA7EC9FF2A87767201525161948249861B9A5380A699251028668E5767EC92363C20D4D2A4B9B37B0A603B4BC03643EF69A0274B8A20D46310EA441D91533196B699BDB0DB462A3121256871242602C0A460A5EE1FBAC2A654815A9A1FAD37AA5CA15DE6423DC852BC35F4B52A1C50A380E40D5089E7D4B4BE114A7BC76870333FA29F1FBF9C2668BF0C1CD09586A2A39F6341D3488128DD38CD5EEB5D0F975754BEEF7C2BCEB87181F1337D86F7A35F9C8A8960D206FD9CC48BF2A6D4AD386F0FC3BD85F3D8925D65F41C7AF698045DE02380CA9968BEC8C3806BE83F9FEC0AE17E2043BC47185872D09852C85B98FC678BBD85F3D8925D65F41C7AF698045DE023EB4FBE05DB26F2D700F3D49B201941251FAA39C59D6014979B1B79CC70C525059E47FF03DB6ED5D45448EED8C47A9922701CCFB2F3B633B7495E3650998C626541D3488128DD38CD5EEB5D0F975754BEC0F5AEA89C5456E8C8F1A95CEC674E8AA82201B75D451F7C2BC03DAD6A9576B4F2A237635ADB337EE4D4D594B41882BA7A0B11D6B701901978806C304FAFBA64A544C2D5A67E8669D652FEEDC83BF5FCEAFB7305EC214AF84A3393DE6E4A766ABD85F3D8925D65F41C7AF698045DE023466443ADE24BCCF6E56A13E01F712352BD85F3D8925D65F41C7AF698045DE02363EE647AA2F1F371068F64BD02E5AFDEE9A06B1605945808BCAF15C34E9552F8BD85F3D8925D65F41C7AF698045DE023C84E6D724637155406C9A08884420A3C41D3488128DD38CD5EEB5D0F975754BE44CE5A0D0DDD48CF43E64B4F63E2447E7DCD32895D220B01C35FBCA04EEE978FBD85F3D8925D65F41C7AFF \ No newline at end of file +D193A38548D8F92C9F96EDF387782AAE1F1110DB8308D0A5F393E5617413046E0E474BDB9E0711DBD2BFED41AD3AC655F2F53AA59FC8E6FA4EA591CD227B6CD0D71B9B65B10B73A9D0D6E086340447750DA4F2968523C6B02BB55EEFC4FFF5408D87F914981BEE9BD2AEC641055993DE620B2BF0817E2733928DB1EDE5E7CF63435C79AD230B4E1813AF9D7CB2BEC613912F5ADE2C60F9E884C02890B9958C80817D822D9C45B93B4B0832C644D05DD71F96A8A410C597C8FAF996F8913F24F15456466153AF6B13F4E4B81BF05B82981DD8E042E9F3DAFD83F8F306B30C1C1C0F2F01D253C84C61AE55ACC2257911208A112BDB557CFC2DBF59989DFF0D8A68991117D439AE45F1F7ADEE8833A4F945B65611F3AFF39DBACD4B92028D4E1AF4C0617D430E0359294DB1F1B62B76AC9A49281E02B26CF3186748157229813BC741830685BEA080685A43F36331BCDCF684E4226E6F2B08055A0D2EC61F49994748D5D2D4B00842B91A19BE778D79C0A090CB4DAEE4FADE4C04F6C8D9346D8BADDF15ED581079771760B968F91C738A3500965729FC0DF2946C4770AD8BAB260A58100BB9BD1EF8B050A89100E7F7A32C55671405DF8B9DB5EEDA3034A263776E511748D54DDDB9841F6E64E5E6FF27B903A986641147E095DFB97B940C99241F88C4DCB9AD672345F801B53139B63B62F62C7B03B5905F3C3DB6C3BCE5240AE258CF9FF39C6B27F1D7995074C20CBD56EA63DE875CBD4115B82C268829785CE345CF898FD5BFD8DC54FF25FDCFF3FDD9A4DA3F67FB8FC2E9C3EF90D906714378CD99C6888ED392B948A76D000ECB2ACD53D081A2D13C12BD71FEA42DE43A8CF16AF620FE8AF4AC4732052E228D2F534AE167300C73A33A596E57C419F188FF226B84E77E6B76A7068C9B6F73A68E5602B2FDBFCD4DE6755044B9AE2ACFC42A7358F9FD6D62DBB8AB398519018083E7FEBE8D4582ADD05A5D1AC8862606C83ABF9F65FB92D273BE906805A8FBF2ED37E058752AD4548D8129A38962384E1E48D05C28C59970E3C6A2CC8EDDB89C37B29D1B1484E4BC7A7DFAF62C6BCAE2BDFA54D9A486E6998A52E2F375AA68E6405CC81DD23D3D261C9A09E9D5CE8D982FF355F52C6C234DE463A485977F0CEB2E62E91F8AC51025D1339D4DAB430FAA7FC98C9CDD8E8A824580EFB801F7D17C025B824F4C1C74EDBDEE483B974DE6126F0BA5D57CC6C84DD13B9DCC5E826246C82D1B0426A4F5E7B8D0E8BA19C517EDB8AD46D59724947B2C36799A714A5C2A141A6C0DA4AC060E4F9F980E86316217493E25971CF03B475530285CCD48718709D701DB78266D2E6108C21CCFD2F32ED4DD001B6ACBF566403C140176417305556D5B2A4A710124E0118A4ED8C9F9C73CFD75E60FDF9BD79089C00F652F8A7A082000EC7D3D1E30C28CB9AD20F76AFCC2B042C1E24E9C5084FF75098C7461ED0DB0526E9279FE1FA66292309B039F2489E67F124390C1AB4A1FFF4756E70B769543AEA5C52296BFC3625F6896C3B6D1C1D7FEBAB5B630FDEE3421ADCEFA139B85DED5A461AE81ACFC4860ECA8DDCEB6A1EB65D7595F034E2FA5351EE6FB7066288B555584FF4B5260EBCB13302EA7965D2EB631F733F48B3D182B584209705F64793493F2837ABD0B88F096C8367610DD7E6EDF6D16519A4CB9B2FD37763CB38ECCB2C0C7894FFFF0E6A23C4D7EA159B5207F4BA7B37ADE268A17CA94D8F6DD470538BFD91CCB9397804F1F0569EB927277A516BBD0DA42132D0263CD4EF2EE8981E02BAADE5AAE50F006838B4CC54559251BB70E64F69A6D30180AC10ED08F8B26E6BB56C16FC7A59D6C7D14B82FCE923440096B3F12E3D45D8937AAE0694F20F0458D942D7438B5AE3546E2CB3931953F8DAB4E034F01C8A201EE0184AE45C53BC38F49FE1290E359EF7A34E2E5D61743F242B7EF0B104D867EC04A06EB41C1DBD36C5ECE3B90C32426DE619A2D87EA177268DFBD31EB47F189BB43B73CD6F3568AAB971607CE51F4881910EFAA8EBF92850C6DF6880E8832F3109DA2ED05FA2C13516FB4A2AA62AB605909BA1176F94471BB383DEC8AD233FA3CB9F82C97EE9B5B54192E294D1F9FDFDB091C856FDD01FCF2A818FAC7D52B55600739AA6055B6F3EE000DED0758C476584AF677DB62AF5965CBE70C4414AA4EF1424F3C1BBD6D3B2D73B219C7B59015B96017FA7AF924D7933D27DA6B397390FD868A074E23C7A0E5CB5463800B0DDA41D39017FA4A5FF28F6B76CBBFFFBF9D575F4E5BC929B26CADC11ECA05D55674853E0E21D833D5A4FCCB0F5E30BEC17EE79F35E7D90BD1A42864BB18B0397B0CAE69B1FA8F6CB5C2648FA0052C87525E7C88E07A2A3E43DAC2741C9DCB02729017DFCC4CB3C4E67E934026A89F53711EC58B68E5A1237EA07B58277550E2D8300FEEEE977300E1F4FE1EE026AF966317F9D77B76D9927C1492046329D031341D1038C81D2DAF991AF3315C3ECB5C896B6A2618CC4AFCA990D993137ADF85AD5526CEA27DCEF0E9A472D0A5A48C8A4C6741B9A95CFA7762E36F5016315D4B60540C80CA604C652982A05887527F086A7D6E964F5F98533C5234C8216BB12C9F29D50703DA92DB0A95FC5A3A4F70456F5260608132CBC509F5D616C7EC18F86754B626D605915DEF700ADC5A58415CB096EA33239BE3DDCCD4AFE385E8839AAB2277D8D563F1C9BE7470D052B932672C7D9733AC71024F7EA7C198C2AFA7158A206F94A3F5F22EE405C947FBB4467978A5584DF6625E1F611025284FF53CEB89D95CA59F5EDE0D106867D925C4E074F13E7F976BB43EA47C7A82D2AD05FB53FCCA6D3F4535442B6500ADE46821165120A81A056FEA4A391DEA6C076C902C1988BA409BF2527D79622D9E4B265344A54C7C93C9751CD95610C3DDD187763ED3FA4C4C4C18CDA08A6131CF69D4786C09825DFF311E06F76A808401D626C96ED4BAB1992EA405804D89AAEBD00786B32A39FFF6F6658D97DE30E7B5FB56E072FA274DEEFF9FDB6F90FE62FA5606E44750D55340515F0E4CCD985AD1477653AB8989C48FC1124B1F230A7843AAA1C8D7D7E4C71D332600F326044916F1CC40A73F06BAD4909F5B3BF3BC11687643C799402A858719CBDCF511DD52C957E58F89288042565D3EBB6BC2A7DB3D654F84416C5DF679D360E717915F3B4620AE8DC7C29577EC6AE0C04387096050E8B26442BC7EE05F5C1BEB8743A66C61D4105611CC980CFC597F9067AB1EB04917D15AC84B5E2D70F64F0E4AA345C85586535FF84670E1D44CD202F3464BD4266085206648876CF9D979AB0AB4FC95304B5458E496CA73C57A6769B1293FF5E60FDF9BD79089C00F652F8A7A0820042864BB18B0397B0CAE69B1FA8F6CB5C2648FA0052C87525E7C88E07A2A3E43DAC2741C9DCB02729017DFCC4CB3C4E67936D7AA851D75A98689178574036C731EA5D57ED4ADDA3080E758672BF8F2B5E9C9EEDBB2F9B08ACE2C154EDAAF9CAD3FE849DA9F49D3B8F50DADAD7260CE3BA5E60FDF9BD79089C00F652F8A7A08200634733CD567C2ABAB043D9A1729107FB8935A16A033A14C8E04ACF9272B1AE6B37C5741CA612C33DA393783E4019C46B40AEEF9AEC04BAC299C4FC3A4947F26272A1A6CEE1C46D639D60277B049369F722B6F5E2AC4E5393B68B20D94E75D388F4E2984C4DC4CEBF32A18256EBFCF5C2D2E92FD912A8F3C39D08700993BF9E1D2C348CBA1750950E441148EC41C37F93CB012CAB6D01B9FD3B7F6C991CEBB6AAE501041A8AD33FA7CA35232B94F70498CFCB53FA60A6C101767C533D3AAC0536BE1EEAEF63821FF8291A922F5F26C898B4EC36457D57755DBD7405C4ADEB3E8204F231FEAF0292463C894287E70A3BE7793C611388FFECAE622911352C3E9081118BF90EDF23D6C2C359BAA9976F50AD5070086E71CC4CF1788201F6E860DB5C6239C4C1565C6D78322E39B47F343B747D6D9B298EE9AA994B8E2FC7D0EC1645CE5AC55518D77645C46E732980A1FCADF3337E03DF4870370D7DEBE565CAA7DEB6BE97B7BD4571551B4B086C9C45B6E329F4205DF90033998315782F60E597F53DF5AAC2985CB4918C4CBD6C1AB57C50B5573567F87BAAA30BC760868E2DECC9FBEABC41EAC388AD2008F2BA2ACEA54118522A6C218FD653B789E55917205311BE517FC801A96E01B1E67CE7D68A175376C519F411F4A47462A03B303181518A599C4767759A482B188B489653BEDE7C6B990F26EBEFCFE167C33AE939C86B225A98615B1333CE760A7F7E64821F66EDE231623DF0184FCC268F962841CC52B2D0131D3E1FE308C6BADD37F2444FD1E5F7FC4A197C48ECA4B0CE0C7CF65A8879C6E98FC1C9E37F0C07F5082DA7D2C890E799EDB5B183225F2ADF4E380181A01C1DB78266D2E6108C21CCFD2F32ED4DD001B6ACBF566403C140176417305556D5B2A4A710124E0118A4ED8C9F9C73CFD7E3A5E28DE83BDA4F14C81430F79B59149A0A0C83C85091908664F09C116AC7054A3327B8D730A860EE00669E91A3CD70CBFC52FE0C3B992EAC898730FB4AA0A84E133F4283120D04882910CBABA9EF3290551D6E3F753872BE03EB7836AEFB8CEC0DCD45DF51BBA74261EC14CC7D643C1136ABBAF748A67E699A298B1C3F1B9371946E13F86EDE3A87343EC488D9B0500F02011315076DC447038B3040DBA1AE6628A0B4B7CC814A824A1AF2AB772FA6CE15C6F2D26FFF0535EE089F724F9AAECB006E5B24AF6FD86A3F4522FB271E037A07D7F7E121FC28B848B89AE43E5955DBFDAB87BCF2CFF2398A444A21F9CBCE96962D776A37C4217055444A504527647E2DAA9D372F35C2D5E728D286C5D2EF4F182DC7044EEE9955D98EAC48BA40253E197EA19B6892EA8E465EA6B9450D25678A24F4D7A194207BA44F4558CD9C6F81DD23D3D261C9A09E9D5CE8D982FF350C3A785C3EFA8B2DF89E9676FEAD3ABAF5ED22F9CA95974ABE455AF670D371F9708570D605A8EF1D772A46D4A4598DE7A340E3CD1C6181494E4CA2FFED2FDE9085FA7AAD08E9E53712F530223A6EE675970191E16D0257681A605B5FEFDD2377F3CE496718E17E87B97820B63F3B344AF5ED22F9CA95974ABE455AF670D371F9A2D02EE765B7A1A4479788CD01D29C650EED3DC0DD2F5D0BBE37409DA166DE04FECEBFBD086ECA394FEEB9CE7706D086C680FD4585E0B6880AB0D43DB7E4E6A32C6BD8746385F0EB59A306823A2B6238F124390C1AB4A1FFF4756E70B769543A6428CE59AC277202272C9BE75B3BE48770040556E3A898209377EC61C11BB200C93D3D60FA4434E3D331192D4DA0A66DB6E79A697C7AF5EA38BCE079F8943C3B8ECCAE67F78FAC53DB3885440D407B970D02AD51E6F313BCF8B31CB7212C69540D3093DD8A63CCD8C6B905CB24445ECE23E9E41C1FF16C43979FB270C125A7EB2F793B057C27CB8634EE5CAD4AF262A1B8A2542C6FC3F1BD8ABB57185A128C31F5A03E5C0649B0FC00907BB251E1930DC7A0074337884245C90EFE9EEB3DE43FFFF6A6F25C1582093BA39B3EB0015C25C89E6DFB0DCB101FFEC422041F7855281B646FF5AA7FA152B71E9891B9ECE45C4E7D818C639F34FD15C9161FE715A9D680B3433DF504FEFBCFC45B4A48FF7A66B0F8050668D56ECF0E020C98B5134636864D05F9831BFDBA7ACEA3568FFE9D9AFEC8397AE08A666AF7EBE42AB82DF7CEEA4A96E1D6808CF330D0E87A7E325001173A7560DACBA924630C093DF7464496A01C2CC880231759906C3296BBABE5CA609631B87FBBD24534C5FE5C9C8E92DD46B74999C4AC99B8AE9E36BDA3E94C9FF638EF4C11A4FDE3C0B42CD6029B5DEF5C2A0315EA04748D2F31E9E5A08D2757054B90B5BFD0F7FB2489267D070C05C6BD50057E660A48B5549BBF626A61B675C874914C34E4A36642288E13042644087064073CFAC019229F32D7D0146424C15BB62C4A29115B20900BDEA4D62F92CFEB77D6755374B91B0DF2BC67735308F8C362B1B527D184B01AA8B6AE082AF056F4ADB5E9BB3B4EEC31676BE5D0D2458B71CE2AD4C96DC0DCB2DC80954DF29615F5D3B1424A59765480E7C1BC1FC9B5E5FA4EAF3ECEA4AFAD6833E6C23AEEB1FADDF06EAB3A65C21AFE1866F810E0D2F27359947C1ED278670F6760D1866256848EFC42084AA7B08E66474DE9B4AF608D790525C171097EC284EBECBBEF903B973F28128F57F7FA5EAB3FEE39F39F73E70AB8DB7AF6DF76555F2C08DF2DFB0BEF91B7AAEF075D62923156F06A6CC7A412DCAF299CCD09E7A4A73F265FDD820278B29EA55D04FEF3ADBF550634240524BFDBFDAB87BCF2CFF2398A444A21F9CBCE96962D776A37C4217055444A50452764020A51494EDEE4A776B476D9896791E5DD25F0387B4325179A59211977731481B0CFD3B95FEB136482280562CD92EE23E515D557739091007B7DF36F65BC0A814174381C9B70CA4F3719B40BF206BCF36784620B572B0073519EC00D70DF194A1611552E481D8CB8984A9C47CE1D021BB2F393716300687442355012A701D45085586535FF84670E1D44CD202F3464BD0A8D509DD2E810D74E1B9345B354B05C80080BCCCD51912C805438511671665C42864BB18B0397B0CAE69B1FA8F6CB5CA4836D147E5126D5965FEC07515F182FF379671AE23451BF61CFEEA21B6392BD01C49FDBD8D14C9E72242920DD048A65A72F2509A1E0F41AF23375B8DF35CE0C26C79627CFD589744DDD73A245A9C58285586535FF84670E1D44CD202F3464BD8A9D29DCD61AAEA6D78D18BCB9F05DDE977D3D797CF70326472577F7EB3020C169617B352639D23CB5C282376B4C6596A861BCA09DDE17E70C65F1284CA8E31C50EECA7862BF57DE5BB290F5266ADAF6403F1674010C7BCCA7F777E9BDE20B2CB5BB3E933D1A1AC3F70DDF972F74ACCB0B04E696DC5600C17633770D5E71D13647FF067A9E03DDFFBC5868F5579C0A4AE07D75831050E7C8D135178BE956E0B99AF8E855305BF4F336A6A49A2E06950BA2678BBADB09C1BF8440BEF4AB3D1D9108E835360E1B82850E73495FA25D2125EC4AD08CCE216021C2BA4B29587111D8649C8FDE586840F41DEA617BA590E8B6B0426A4F5E7B8D0E8BA19C517EDB8AD46D59724947B2C36799A714A5C2A141A644029EB7781821F523BB59D7D41DEFF880C8F3838E91A0DC50A3168C176F1970678A24F4D7A194207BA44F4558CD9C6F81DD23D3D261C9A09E9D5CE8D982FF356D3813B751632A4B4956CAF7375AE5DF8328A0601622AA04EC100A2F09DBFB8D9BECBF9BD01EC0120DA0F7E37F0CCFBD751C139FF5259912824D8A282BE31DE0CF018B0CDDE558235C20DBCADEDA2438F124390C1AB4A1FFF4756E70B769543A2316DF5737BF2370F256BA6EDD59FC2484BE22468CB6F54C73E31EF2AEAD37B7EBC269B87C4B29DDFE15A8021B40CA6CFD8696FE14D53C65C9EFE55A92C60790BBAD8AC51822652E1050D3854196218E4909E1392DE7F4D9972BD1DCD6F33077A8028065D19B24D5FBE981C7E87DDC2EC53EB66AC6DF2B95A8CE4849F2D0CB339A09E9763A5BAAF434B5B4E8EC7CB4315E60FDF9BD79089C00F652F8A7A082006988D54D60DC5755E18A175C2848A25480FE2B0C6C50A151B4F8991A3A367FBB018B1C2FE08D0C4BF5F8951D959222A4E3CEFBA19ADBF7645FCA7E4ED70B142C5E60FDF9BD79089C00F652F8A7A082006ADC768EA156F676D11E5189993223D5F124390C1AB4A1FFF4756E70B769543AA516F1279B3C6749A2DDBE8F4922041DECF6BCFF3D3DA17D486BC282BCAB7EF666321F0797E36FB66608941333FDDC8CB8A2542C6FC3F1BD8ABB57185A128C31DF19D34700013DECFF16F142C375AF7BC95CB9F7F4E9701594A423A9D38621398BF9D02B01BA3375E690FEB09F72413350C7CA82A4C0E5AED13385BEC1ADBDCD351D88503C3EBB1F9653D1759A3D11B4B208F5FF806C0EBAE6CE5335EF4214EBB949A1D3D5BC93A366610C9A4FAFC5E955FEC35C0B559DBF099FD661D71023D159E3DA82EF132F37836D32282327188DEE5BD186D267A239C8EE787362B342F3B413B48614460C638522E10816FCE998C1BE220DC9913E2FA0D2F110579281867474092436222E5E462F8FEBF66EA6DB75FCC314772016976ED2CA93C89ED8426EA41B1880EE6D52E228A043D06CBE8BF5E736A0908A70C0EC5307BAF29F1AFC55D416DE91C587AF637A765110C1E81216F67F798FB10A3F18541BD7662016AD9EC15949CCE011BEA096A0B07F43DEAC4ED8C144435E4A728DC9487ECDF7E3572FA940DFB2764CA21CA92F52CABEAF51705CD043BE6E3F66115174800941FADBD5248AE47B066019799DFD59BE2B4ADF7894C456E1BA72A1136B1244031902367D4AA0F64EAD1D09FDAD07D171695E134B0738B6E266F5E5DB3F32B6BD3B4D59070283FC81847B9685A7B54F84CD7D4F7EF034B5A85422779EDE6FFAD03267510C24EC34FE5E1A507B337A59D9AF0B24B6FDEB0D74B8938CFA433512E2212C8C376A7438A4756FCC5BE7FC4F984698CC5CD53BAC4FBB626005FC86D7B9753427C0DA4AC060E4F9F980E86316217493E2DA4BFD967F36546735F1BECEB7C32DF6BC5E5935111C7964F18BC66FDB3CFA077C3A5E028A6B4788087A65DCEB44D778AFD02F12B58A4433D96D275E174847A00DFF8EE2C91097D80E5C230140AC819F95204AA40B905808D19C9669C10B474DC3AB8FEB00A39C43B7781C898FBF2CDA5E60FDF9BD79089C00F652F8A7A082003B9F566B437FF05B8DD7BE10F4A3FD0EA9F1EC748B198A27AAFC300122315024394AFB6452C53741AC6DC335BB8C135601653A47ED22B71781A9EB0F29C39D12094DEC1C39AFBE67BF2A30A8F70912BB81DD23D3D261C9A09E9D5CE8D982FF35212AA754B0C1F6480265181CAC6753306D99C8E1DBA5BFDFA7D7E07C2E27A3506FA66667982377484BE74248CCE7BBFE5E60FDF9BD79089C00F652F8A7A08200CDB769BFE04398F4F5EC2670FAE5E7EA81DD23D3D261C9A09E9D5CE8D982FF358FA6954D2DCD680DEFA8BF92E9695D6B007138E9DA8EC5BA7E04F19A78D4DB8AC65E6CFBFBCD7679CB1B6BE361783DC6B59BB79970E8283220BEE52C7B85E71B40AEEF9AEC04BAC299C4FC3A4947F262B61C0828B301FFE9B2F151147DF6BD8D813BDA2540B0A72E8603D8CE46E5AD7EEE7989AC1FBFE07E5E534FDB421114814D6DCE8F0DC3DEE1BAA31C9E0C8FBA2D18C2ADE2A29987079051993012EE803F44BCE9036813249B47E406BDFF2534AA5E288F54FBC3B8DF5A0E147D24647300B69492BE8B1E202D491C309F0415683F38E7431BA0F0B85379B778C9E97D596C5866136A2E066358777397E0D2087FD1CC761F250D711F03BC998DB710E5A8AA2AA85DBC5720156A7E5DFD17B40CC776CDFC0F94A0B2EE8980FDFE9DBEA499E5DA4A82EF17718D3BD5167BB6FF923ADCCD69F1BBA51FF77209740546F05A25347182D5A778B203B2A834D686E7B6477CB6601938C3CDE68953EF2293034A27AA5360BCB2624F13F17227AC556C43BC6066FE78B5476FB0EB9AD211E68534592B36D00D4E21DC101E6EDC0F048CCAB870101A6781AEA94FBC1679E537FA0C0B81202F4C589D4596B217FFFF262113716AF32033F10485FDC7F8712120DAAF5954DA7E8447EDC01C6B65A9000B69FFA035EAC85B48690A0E1B8ECED1776D8504941FC7BEAA88D846221AA2572489932BDB20CFE381C28FF609594C2235143F2EEA0E70B0DC77FEE7B4EF73A3E03312F86CFAB94331FE3B4C9316DC71FE4771CF46D33929E04D1098E9171565354ECDD59D21670ECAF6204AFAC53FDE5C1131A6364E873614FB73443911E10CD10B3B137F66CE08DB61B587A6AEA950AEB6D0E0DBED20ECD63DBD67A59F149DB463FE0C346C02CBCC344545C288388E730BDDD99AF481C560DA10BBF45F73831873EB54FE4CE80E6A3D128AF7484704F4259DD19C1E18E287649A5C534FFCC6FAA2BC28016B578296D4DF43939E256B0F403E5CE32683F4FC1C7B8A183C10A48DC716EE010ABCCA084C17D2A0B6FB92B9BF1BCFEE05F42B178D683892D25E0C5F7B9D7B33CF0990F1AEF6E1A8B6A88E5EE3D60C640A702580A2CB0CF2E58407E75EFC60F3A04ACF5A3F0F2CEC9C861D2C655F574FD8FCDD884D3BA8D9B5CEC2B415F9E662C34AD197E340E4E09AB39D0408DD8E74AF47C863F4A60049BC4D340F73299EAED075AC504C8D427437D85AC65AF66130D6437F50ED71105F6B9FE83F4B9E8F79DE35E98E1CDABC51D1B4CF82EA868595ECE41A4057F97FEEC4CD7CCBAD687774F37656BA0F329BAF085604D3A57485D92AA85DBC5720156A7E5DFD17B40CC776CDFC0F94A0B2EE8980FDFE9DBEA499E5DA4A82EF17718D3BD5167BB6FF923ADCCD69F1BBA51FF77209740546F05A25347182D5A778B203B2A834D686E7B6477CB6601938C3CDE68953EF2293034A27AAE8A250ECAEE0FF7720241CB3F0EB517DC41A67E3DFD7642A5582249D5F64C0691B4E641AD3B63942780CE3A7632DC2A33D1063B5D9B3EDCC84D19647BE6865CFD4C09F6A695161FBD411EE77FA76DCB14F2F13A329BD0B610401667B6C8F373467FFF6C10089FD8B12104B87C0C82964B80DB71640A03159743F0FAFC547143EB83D0F760B5206366C469386A8898D780CE8F5B86EEE86E9CCC2E02CE71ED83A4DAED88BFBD660CEFD7B798D45DCB9D8CE42B8475FFFCFB6000F3741B0D2AC6FC5EF1A57BB14E0776A75E38D28F7266754E54CF54CA0EA46F18883C94F2317531DB78266D2E6108C21CCFD2F32ED4DD0B6DC986C7E93F40E3DB4551AB007565100F12F71798680E28589EB37BB188303D3E0647E68100211B446328BC7985D2170F65D6D19BB7B9411FF44380344ACE580C285B3493533C162023A9BDE601720633FE17B71CBADC7C67443F7C83B9CA9298DDB22144B084CDD036C0E76A01A8F56FD693FF7B7CFA86C91088582C2755A02426F3D58192EC537DAEC22FE288AA7A097A96B0AE7A6A7C1FF384438C0110D3B838DBADBDACB3188DE93566A0B078DF3B743AC972822AC35F8CEC940BD10BCA93D4FCB23A12FEEBFA2C681BA837FC6FDCE92B2638579E533EB5F73561ACC3662112FAEF62EF715008958144E59A7C530FA3A1CB4B173B47BAACBD8C4156D408E65A298095CCB277AD6AC1F16783C4F50BDF2E189F2496AC18EC81AD1BB3FD86F8C1DF4E7C62FEDC2A48CCB0DC7B3AEBB9ACAC2FC1BBD929F9E68F2BDE666DBCE14ABA6BB56E348F18015F9B1202CDF22BE0812BA474D00829FC1F402E52B128C84BE00165F4969120E84F843764062986049D05463B28507DA78F542893C2E82E326695F5AAF6EC00A274AD2C9B7E38BF9D02B01BA3375E690FEB09F72413350C7CA82A4C0E5AED13385BEC1ADBDCDEDECFEBD83C084CFE757B08ECEFE8396C576FEB54EC70BC14B02FBD3630E4C2B46486FD23E2CD97660082C078974891FAF47C863F4A60049BC4D340F73299EAED075AC504C8D427437D85AC65AF66130D4C09F6A695161FBD411EE77FA76DCB14D6AE1E74D0794A0D79FE755F030F94FB6A2A5FE9C608BBD5CE5143B75C02A50C4695A4D07F8D181246C63682B71883B777AD36AB1614A093B4FC9DDAB0C014DD9C4BD8231D3DF5496E30A6DBAC58092A5402FE6A5C84F8D8BFC0B199068E11773A26304168A1D8A6FFB6C7CACD6C4449861D11F6E4579F4BC4C8278F9161E4B78369447B5821765A24DA6A511D8B1543C17C3174588E1E2B2C97D7A15EF6626B82415828F646D119527D975018F013E58552ACBF408B6E99E240F5D421F53F782CBF8C82DFBB9351572CF2007C8E9A5FB0941570E1A0EF038FBF31D024DA176DD00CEEB6658A33335AD615C06107754304117AE21E05EAF9DE48C458F92FEDB48B1A9D291BE70D99A9B21B00AEC2396026881F83D58AFBCB775B0DB0CE32F885D18237BF3392F65838123E8EBA0B8CFC25381C5D4F99D5787E60C62125D71B463709362C65B87BA8905153CB331787D76A6DE7B2E8CF29C0AE8B9FB6F0B1459742FFA76624348B9AB81EC40AD35D89EA0FEE48A071786325592C66122BA1121813BEFF20267609FF3CE16D6513F70E3EE5B95CB9ABE57F26E7883F348CE10BC67223B71687F2D33077D5BC8CD70BE378783DA098F445DDD0321CCF51FCC075E2F2E9A243EA04DA2D3DE44B27545E22FF372DEF10CF1A16877290FA6310FD88BB63636CDE3DFABE4EDCE4CF427DE6A67BBABED9D8E2BF531D264B65610B12BEE6893845881B1A45A98727B8CA448497E602BE8B0BBECEEC170921171DCD99E48A3D0DCAB45CB551188A65E1BCA844D7BFAB1E675BA01222CE85902DAF8D1175FA04ACF5A3F0F2CEC9C861D2C655F574F2AF9219CD0ABF76FD469296F02281E351D8C6076B6027A6D6974E674B3D2D62374BC8544F02BD61A724216D48CEBA378448DFA150281ADC0FD0DBB2C63C0410376A85C9F75A0E85B8CB4A3228B45F77DB9D123C5C52C44E7019119E8BDA684392E706160A1D63FEA60F0E5F02E047B03E352E99B59E8F2C336B1106E3EDCE1DA5D616C7EC18F86754B626D605915DEF7500965729FC0DF2946C4770AD8BAB260A58100BB9BD1EF8B050A89100E7F7A323AB780946F1DFEC7B2FBBD947589EBB56254F38E2A2710B3C1EC3291FEEAEB59E939EFCDAAEA02F5A145CA46DD41E5CE85FCAB762A6F3A6892450B38DFF5F30E0538F1E3A5C117C930DEF91AC2C8902DE6E1B4D730EDC273E1CAE690009E0B5326D4B0EE0C52D9078BFCF78520BF575C81DD23D3D261C9A09E9D5CE8D982FF35F63AE92926BE3C45B9CA5CC2DE8AA3A37679CB0029B892023A8E42E311ACBAC93C00EF308030F96A5E997B2E39574501927200D120D4536BBC979BA03157B0C9CF0990F1AEF6E1A8B6A88E5EE3D60C642C348CBA1750950E441148EC41C37F93114DB5B299AAAF3F7458B8AFCE2E4EB90E70B0DC77FEE7B4EF73A3E03312F86C2D5BF1D722DF17E9B9BBC102C631117DDBBFE4B3AFAAEA680324691221FCCA02FAB94331FE3B4C9316DC71FE4771CF46815088963EB2AAF4765AB1A114FA5E226EC14E00171258D01C1B704CDEB646DA58375B4147F095214948E0D548828C765E60FDF9BD79089C00F652F8A7A0820002AA9A2DFA66C69106593E1D284F365D736CB1CF5BC6B67ABBA027F3B0CDF445BC7E0093A440655014C985B30D3AC69A4E1526404858C1A0737CC865A4CB4ACE0CB9AE351F7F92B4C228BBC65470FF24C0C393EB8E79484F8F71190DDA71F1045CA1C2C5BDBB5516F7D2D43B400AD92274BC8544F02BD61A724216D48CEBA378A83439CDB2ED39BE750EE48AC55B22555132B4ED790ECA41F2A58BA26493060966F4F148DF74473B43481B948F13208B89C6BDF2A32C74FCC48B448EC7CF83AB91324BB16EEC2F7700455F010100D243C041509468786DD0599BE3F2285E3C2954E54CF54CA0EA46F18883C94F231753ACA54C091B6B412E006651608C16AACF1202DB75B7C88F7DB2737FC4E16B076433426B12373E1AEF4C3033F10680E44DDC3548054E537D40D7AA20DB0BA9C182C7632B3F85A3367E093106C987C1072BF6E166C9D4634983F2A0F6A100F227D8B7F9A7F13E0CFE11FCC8324FA781791CB6E496C8FF5DDF1E8EC95B95569AE9B46F74FABFA2943C5FB197FA58A8B5F0FEEE6CBF015C6ECC5EF85A6DA3294674D1C73323E669BEFD72AA28F36758FF587824EC4537B294F585FA601B4E2D0AEFD267270AA0899517D5552337100B98F6D3EC4A839252B6CC4F099B2EBC6CB52F4133588E0472ACE53FBF83080151E11B954A574EC9B5839310851C3E1E698D6506BEDFADF030298C14FF1CFD97FE57E3F418E5C2E60FB2CE36BC93945C71EDD76B5C41D44093FDD03A3D870A98975872568BCF686D2F73C15590836806109FAF277C3A5E028A6B4788087A65DCEB44D778485C236128BF62334E57365D9074F9AAC16F8DDE83A9DEAC108D9402DB6418C119646A1F77BC87696A406D5904E1733D48381CF0B9BCB058F77884A98BE9292B5AD5F29BE845F7940D28036BC87D3AD1A16148E17977A07A81D78C705F7FBE008EF99BDB795F61BA33DC8BBCBE33EB8154CBFB4908FF071B78C82BE8EE0D599905696A4CC9672B07BFA0DB800A08C204A059D5C7FF972B0DFCC79431C424DE75AEF43C78B5F62B1D23B3C8AAA65021C24266085206648876CF9D979AB0AB4FC97D79EC1B4D102638932383E7E33A1B6677AA7A622C17AA6CB2A0957A299EB0359F65026B66F5C75FA0661F5D722249155B3221E72A4F28B81657D14976BC364B9E7F68817B241C44C6224C79F48AA0C94F91726C621F9FB992AC6E141417FDA65E881D09CEE9CAAFE847FA8C6088B8C84BEA70E221FF155A1EEE1C4FABD7B72CE3C237D551003313507BFF87AF1B8C2B53CC016475C8EBF7CC577AB7C5D534AE9FA817848E34FBFF7BCF8721079C45B3684B4B3E7425471D40A6DB939A0FEAFD6E34FA0B5DFF4BC85F5158D2C4231698E7A718DC92120282102A4F7AE2F0EFAFF04A4887296AE40C1EBE573845E80C178BCB9BCE4EAA3D7F83C37B07F2A2AE8CE2BA4651B2E354BC910E6B691572D345A615064E990E94DD3E3EF77C2D6C705AC37D22DC22F93A5722FD2E2B68D5D7302327993F03713133828494E5BD5213FD240A10C188EFBB5148F2191F4D2821F57E8FEE768B9D6C85099F670F3469D458B867C0F687C248940D62838ED67816F354E3FA230C573003C20896F9962DE6DA771EEA875C1390AD2B267FB3BCDCEC4B3B2FF3660FCDD8CF864FAE2DAC777B41D5FDCAF76B0B976B349A0EA2414425DA670E9C90A88BB2EB2A8DAB17C5210FEC080B63933CE1FAE14619A001F07FBC6C4266085206648876CF9D979AB0AB4FC97BAEDA401D8483C23090724F44375FF2E9B68C519C871848860646D24051382A1E02BE10F9A43436B7C3E61FE731A5C95399DDAB701BCC9AB4B3D8780C5D4F9526D4B0EE0C52D9078BFCF78520BF575C81DD23D3D261C9A09E9D5CE8D982FF35F63AE92926BE3C45B9CA5CC2DE8AA3A37679CB0029B892023A8E42E311ACBAC93C00EF308030F96A5E997B2E39574501927200D120D4536BBC979BA03157B0C9CF0990F1AEF6E1A8B6A88E5EE3D60C642C348CBA1750950E441148EC41C37F93114DB5B299AAAF3F7458B8AFCE2E4EB90E70B0DC77FEE7B4EF73A3E03312F86C2D5BF1D722DF17E9B9BBC102C631117D936E52A7F8E05239FEC06204B23E57EC5E60FDF9BD79089C00F652F8A7A082008D263FD0905D117CF337B0F3F0C07D88106A84728A154272EDF05A8723705F88E597FA500C500DF8D62FC8C640A4C9CDADFD7B078EB07B639EF0D7C2EBC90DAF3D382BE0A5255E567DCC7D1480CF982D50EECA7862BF57DE5BB290F5266ADAF6C8A9912D2992B0753FAC12A9093807876356ECA2514CF8DBEF20DB9B4D90659039C4D8A207DF3B4C94346973B08B39D0EF4B8C2048E203E8EF3D2E621CC2061127F160BB30A57CA295A7B90073AB1BE47D36089A5918A5350B1697F8582FD985D5F4D5D307980DF1F185EB582BD53B7C64644992D2369E1A7BC078145538991BF77029DADE05C7E2C0530AF850EBB0D6ED012693A344D3913E69C65C6774CEC6CCFF414E2968931B58D26E224E83C1C40D9104751CFC9BCE42EECE5BB6599B5A70F89AE40E3FDA57E3C75F7DD337DD7D73431539360D4BBF8704E8DF04A7D8CFE1AED0B2B3343BDD239478089B36B7A59166675C81C6D8A68460A021D5CECDCA03723DDFDBD95974DDDCDEC54D85AE6338523D57F738842208C5AAA68D31639825AA96342A80730D7C2E34137E508952880FE5211D37B88E856585338D3BA273136DD9BDD1422B3CD30034C82A884CFDF49232A2C7EF59B770FE1382DA5D5B5E05E5123CF751F6F0B392DE0BB7023706E28E4E15743EA7E43E76B6B5D667D1BA767E5D7A8C2F6E436E5BC27155EFA59689DE0014723D2282A3A10759748C202B25F7E2241CC663787B934242CC3C28E2B82DE8FB1C82F951964F3304EA095E2EF52FB4CC333D5BA9F88FE97C58751029923B73104AD8B4B02AA55DB1FE4C5380D76DB4DBE91E4750D9AFE4673F0FCA59971F7CE476CF09A95D0C4D322BAD412A2C5DF54B6B0D71B9B65B10B73A9D0D6E0863404477C22E660017A1DF0DB6CA7E046D054C75D02D113925412FA434A12F35BBBE5712D43F70F6B23C727E038DFF7A47552A82A31BB649113A8E66155662EB8B16F261D10658420BE4CFFED4776CFF97A0A448271130E8C69D9BC4F9E8DC1B2402E117F44BDF8F1C667AC460AB6C16F26E306909D0D7CE08846740C043624F54334630A9D52331F1A536A062C842922145D4BC2A92EB8BD8004CC1697FE9F3E35E8FCD29D0AF0684BE5F48B26AB39F8DB0469162531C670B1D8B5CEE36DBE6E28087142D83368EBC243AF99F3EE2FDE2618EF4F8B7FCBF77370DA895FA5FA8C53B99BC02CDC471B5AE7D16AE6CD18012DEE0A92814D1EC03D582759980D7C093D5FC45509CC76CD6D7829F7880BF31D09D6427C967556E56AC0535F29DC21A5FB0257B5A65FFCB36635B73C2B377AF4A4D3CDCF27215BA09258DF21B9EB0812AAE7646D0FE892594629A4E0F704B28044A94A423021A67CFA0F4714F77C524CC872DE3AC927FCA79D4DC8FF12FD8BFF70D42EFD6F471B73116ABC48F40D87D7DA0143CBE8256A281DEBBA09C0C8C4A5E546DFC13CD796E71290FE3116E7FA6B0643B5B51D799E00829BA5D95C9598E9DF66525D9FD5A64CA63FC6B792E11F252E3F74FD63CF26E8E7D666887FCEB183F92C04356FA4EA6142BA2633CB1C5B6C5418C39F8338B3F17C41D66B13133B63AD907FB98FB72A789D7C81237AB8C450832E7FA16FAFDC9678D6A2868DB17E8FB930EC6DD1A07080F2A235C72FC03BA9ACCD885832904E755684605B2F5C96A3DAC48C8E42E74F5E6DDBCB0F6029FD6488D777DD3CCF16DF3AA3A1CDF3FE1B3DBF441FBD146096DB74A2D72E4FC855C46C0FEF75A461AE81ACFC4860ECA8DDCEB6A1EB64A7B548BC3F1A193B0E0AC29D3CBBBF18035BED3CC476BB5D8809888F8ED011B82E8C304F526C0CF1A6E78151D2A211275AA0F07FDB04E3E6E04530416D447FF34187DA0BD2937AC050179E66DE42054BFB45B73C7A709E25D571096B1E35992551A4B179C3554A8D1F074468E060FA74CE97C8F02AB0690BDC131438354EEEAD54A76EA875A3460F8C8B1D4DE37A77DD95B4EC79D3345A2A7B3A34D7ADBC5F994664AFE3D28ADF5520C61940692E2FFC766D986C3199B638235425EB952EE2BFB530B7E4E3216F2552389A91BD39028FB40A9F72598A38AD70EB0B74429EB527D8C81D4AC6C7793C0516D79C6A90BF575032724F77C94222C3B18311491BEE7C497A2B23522C2C1C4A6135083B535964791FA6A45C83F53863CBB7D981B449D0386C7C9FCD25A24DC22333434F888579A72C3659CE2744CFB7F0B12D00A4F09C766D986C3199B638235425EB952EE2BFB530B7E4E3216F2552389A91BD39028FB40A9F72598A38AD70EB0B74429EB52E3CC207AD79CA08BD37F90D65EDF043B7064073CFAC019229F32D7D0146424C17AC09CB5FC3573E6449619BE516008C8716B9FA49227B74A5033EF12005B762508DC31678149791DC5941A192E65BC7F2102F1199E81855B42CF40E2BE18BC9A7D7A2AC5CF4EC44D812411DFFD54126DD2651333D2F20740CBE7B27D081D0531A63C6E8E889A6D76871860AE5A5F5B6661A076B70FCA3B2BDB161BA2A620A2FA85586535FF84670E1D44CD202F3464BD941AD7A126EC4ADE32C183FF69C8A875960D86DFC174C9995894285A81F74F58A29828AA6F86DE4F979227499E8DBE0F65284815A21B1DF887DEB8A08CD8C83F4236B1565ECB20FCA1CA43BA5186FB0BBF503FAE034EA48B99CD3C84E7ADD7485D6BFF44F8F43FC6D44DAC3F7128157688DB263941D98358633FC409F0B4790FD825C65855F3AA4DB8638E8E94461090024032E39EC9D2545D5F674DC4DCAD492919EC0DF4C27B8787915F23B8A46DA0438008060E89454DDF616B96F5BE93933997B7A83454C46400B730229F81FAAFADCC83E784FC891BBA2AA1EFC9ECCB2575B6EF078502B0E9C47DEDBE6713370AB2478DEDB9B093EA3B4565296F835935AF6D4B96C833372195F47E5BB062CC35AED154BC722519815E7F2C9731646750BC183E2C0821191C0D251A9B885E18A949805102E977CF21FF9E5F29BD230D15DB2BA6FD7635EF748C0783678BA57431DD57245B14C503FBEF9CC63027B70948212844643D9653AE84A8436F5B6DBAA61DB59E6F2412345F6C1167EBECCA66CBD77F98C14E6BA24C9053090BEFECEC0356EF575F3F297085369779C621EB5152C37898516280DC2888DE16B3D346461FA14BF615EAB90A5A48D5244F71A534CC2D2D29D54A12F562CE1FC82B31764792A518BB73BF49D97D33A5992D4E03586E1CFFDAFC87B68B2D42BDE6D96ECEFCA0631F733F48B3D182B584209705F647938E6967F5AF954A758DD27343C2CDCDBF73B4661CD8F8FFA8BF5E1B9390F40EFD5A81423E42338C590635DD8374F3F42F54971E5D18CDB2688E482B4F15EAFAA8F5B923A1D15362320C442332FD2A095842E57AAD4C11C5D878369F780667341850DACA74BF09D829953ED62690885D695EEC6A3A79BA5B1136812DAEC950E5271AE0049A168FAA9925107AF223FD90E0596902EC86671162050A53BF68FE657469654437E1C58962ACA7EEAE220D1DB14266085206648876CF9D979AB0AB4FC962D98F56BFC3DE64F58C257B905FBBC7BE24791AFEEF8BDBD87B9F09E374DC49B804DDBB893880643C565599C9462781AA3D214892AFE766EB1F65EE8A052B5830FA3A1CB4B173B47BAACBD8C4156D402152ACB360E685B1909C02FEFE8F69965E60FDF9BD79089C00F652F8A7A08200916936174C238C71AE01403EFDAE5F12B9A334409BB5C17D65516F31D67C0E4A5E60FDF9BD79089C00F652F8A7A08200347A1D545154A734A09C61BCFE5BD9E543D4BB7D4DD64D2C0FB55FE78DE5A0DBF63FD0F20EF70DC79CF52901AA00173F01A38291D41299BBD19877D5582EEC7D5E60FDF9BD79089C00F652F8A7A08200B027986D13E77C6FA4A84EF67A4A1EF17856B48714199327A7CCA8A78D728FC7FE7CDC630FDC4FA4C1CAA1132270BE8E8C83AFDC34C8F8CA76722C49C102C4B36D0A58DDA26C3C807F18E8DF5744DA715E60FDF9BD79089C00F652F8A7A0820066F486EFE4F8BD13894E2F39E94233B999A89890646765CCE732503A403F83E8706D61F4560D7052C1E69832E5D7182DE1A39831A0BB08608BC47AAC8EC4DC625E60FDF9BD79089C00F652F8A7A08200D62E42233A1442D5770E83AB1253F1A8854E4D59673CEA69FD7CE1DDE6182BC87D304E10E2C06782C6938E2913A545E75E60FDF9BD79089C00F652F8A7A08200944DBA2EBBF50A3D1ED49E95D266A767AB95028396E9CC8E167D17CAC462DD313EDC6F3ACFC1D0C1FEA3F1D2D18AB8B7FD8696FE14D53C65C9EFE55A92C607903916BAF47DD8F733F1EA95F5FE850F4ECB1B9700B3300FCEE2491B90DCCF2168584B79232C90C181A3A66EBF53F467FBFB6578DEAA3D0576724D5D44FDEC180B5E60FDF9BD79089C00F652F8A7A08200AF4A463E84E48411449918C67B7BA9F3E98D4959826CB5FBF27B17FC8F298C3883295FC3F3F4AADB14BE7AAA86460852F124390C1AB4A1FFF4756E70B769543A2AF6E613B486A80A7BDAFCEDA44CDDB725341EC3BCB55968BD3D491220E588F25E60FDF9BD79089C00F652F8A7A082003AE56C0EFD5FB6521566107342CF303B9AD210B129B078CB67D72E7FEF11DEFEFE849DA9F49D3B8F50DADAD7260CE3BA5E60FDF9BD79089C00F652F8A7A082004F91726C621F9FB992AC6E141417FDA6A2D02EE765B7A1A4479788CD01D29C65A40C80537867337D0594A5DDE86777965E60FDF9BD79089C00F652F8A7A08200B70BC25FFA1E3614A6898B5CC1D413CB89753D750BFE6796FF2645E2751190CAD0E476671AD8C487168FE08E6963FE600C05CD9A6E89077BD3529EAC8211A138232CBDB7621D775B518F25A113FF7B59111C85701AB0CA46B62D9876D5B55DEA196F58E0992E8C3BA67FCB0C19FAB0FCC8EBF32D81F86167D255044228A61AB8FD8696FE14D53C65C9EFE55A92C6079085EA4B528C834F9353EB219213C32989C5910D566E6A32EFC7696E5DAF9D36745E60FDF9BD79089C00F652F8A7A0820038864BB66DB4C5C71CDB8C698B09298F319EC514CF153CC20892EFCFA9802F96E3A5E28DE83BDA4F14C81430F79B59145E60FDF9BD79089C00F652F8A7A082002C577FF3EC72E117DDF6DE38864287125AD5F29BE845F7940D28036BC87D3AD100B568C4FFD55BBC2D3A232656EC1FF4AEE1E224B268F9CA34E8BFA6D1A56BBA5E60FDF9BD79089C00F652F8A7A082002F7DE05A277F40D47B6A7A59051E491C38682CF8F894FF74C34923628A1B3154DF696E343438503C0ACF3D8101E95DFF80D03BC6923A6DFFBA96DA1E7A88E34B706D61F4560D7052C1E69832E5D7182DE1A39831A0BB08608BC47AAC8EC4DC625E60FDF9BD79089C00F652F8A7A082001D762F57F5B89C51DE8F7A7528AC5F0E1BBBFC8F2CF80EDAD255C22B740D9D6E38430D132D2584D2C7384FF243DE645AEEDF1B836CE4E171CA74D06A9A7BF1134266085206648876CF9D979AB0AB4FC9BAFC520B9D9AAEB2AEB513662DDC630E72275D472A7726D52AA106B8A488ACB3C4695A4D07F8D181246C63682B71883B29CE91BDC716A04A29046917E1FB2B794F662ED65151A693673D6E42281CB2D03E3130F8B8B1D343103C18EC66EE51E1F27894945E15647785CFA2B8E77C65E91F33D39281500676C242E4D7B78C214442920A048F3CB50724F3887E1D9F6A8235E50B515124774BF58CA2F5A5547D8442920A048F3CB50724F3887E1D9F6A82004770E28D65469093CF8F1C3BDA04F9D02D113925412FA434A12F35BBBE5712E7B1591772B86890550FB3F627B6EEC0F124390C1AB4A1FFF4756E70B769543AB06DCA51F2699FBE8FAA4C56D8FD9E8B9AD210B129B078CB67D72E7FEF11DEFEC8356C8A8F80D223480EF7B6D263BF2A5A8350C12EA88182736A432CE0EC3E626700013898990C953B6F8D314DC6C9EE8AC9B20A8FE9335AB3060AD065FE8F50153678E60D2EB666CE9A8ACCB50F8A727F9847AC4EC02C36235967F1A985CD5D5EE9803EBD7FE7EF339A982869029179EF47B74A3A628A22477E7F1B171CC8016BFF9337870F54380E77A967368974A17EA4728DEC7C5A910201B41567498F67F1BCAF46837EF3032876B744A04C4C7D5158CA7C5D55F17A2A4270E0E3BA356AAB7533E90C1B53B984D52C44D2F81FD0C4DD3467407B6BBE42EA079F25C9F956B2CCA870907B9D8EE5B34975B7E614F212B6FAF0C07F24549F70B93EBEDC0C99BA539E6B52ACE5A58692CB9AEF03A3DC37DF2A4E1ECC468B9B0AA28AACCEB803811C718BBFEDD4D607CD40CDF5E63F197E966EE61982C137EAC56CAB5F72CE9535EB7D078C7AF08BF317B10B440284F0AF0AE332E7BEC3192CF309C77B93458944A8945CE61C7A5ED5AEEB723E074DFAF4A96DB9F79F963E0B453E84F26EA4FFE140C402AF8A4B068E76390E2D9DFB89AF8C0C96ACEBB844334ECFAF6890DA232FE33F9434C00D271E453FDF8D323C5C714742EE7BE2299F36EB9795A38C89B5338DCFA180C55704DE3939F2622A2CAB5E60FDF9BD79089C00F652F8A7A082005BAAD6C18FD6510B4003D426C2CEA4415E60FDF9BD79089C00F652F8A7A0820098D66C77BE30F9D747FA4A8994EE8E91F5AE53BAE478FEFEBF97C577F718D3C46B0F2FD9E15F603E23954A5D30025AD69574904D7C263243FCF0E37051675BD0B6CA6934987282661AE5ADA8F71D786394F245366F689DC57C1AFE5696AE11AC84D44F5D5FEFC5A537EA69DBCE2A9A913B66881EBE71CD4B56D6C3CF20A2CA48B00A7E8DBBB8D3EDFFE2BA40FFC455168426A5BF96A379E0E15366AA92AAB34355872A15B108CB86E979E980F5D219496766DFE33D997C1749CF3E08FF045132EF3E1FBF56F521E5775398FFF4CAF1784FC936B988130818CDF0F6CC859C8A6294FB380809DE2A050A8F839BBF4BDA1F9730D85C4FBE95D57CA374DCB69A4FCEEB28D51E26D801D2F2959E88A26EE4CAF7A59472E258840E202546079771EEBA4E3928A3157C0EDD85607FCCB7EAFD00DFF92CD6661EB8211A450F6845D6E914C18B81526E08CD3EE90AA054466099A1F0A271A47103E0B645568CA606B3ADCF18391FA810BEC4C3671D4040E78E9E0ED5874096405AE843E52B72031AE66E39992B34F41013CD2BDA83195639EFFC0FC99B255FA8B22853A8F2F7201F90783F32B66292506124E9A272F3D9D12E42EC8C50992B36C9450C09E3702AA6CE86C7E9958E5ED53AB5D58DB22D4BE35561FFB6DEC4086F904C21FE3A74A2E59755EB0E5E83C9F1E64A3A32D4C6428BA2FFD1EF6AACB450D20789C0737D47D990899498FB72A789D7C81237AB8C450832E7FA6DADC736F99CB4AD82EC4D9E3205244446FB3D94946C71E8FB0C46748F19442A33B3B961567BCF752B8DA450844CAF110D71B9B65B10B73A9D0D6E0863404477DEF3878BAC3B241F74AEA73B9995AF0C416477FD905FA2CC57D7F4CC329A3031B2A8ACBC82811A38809140A8DF3B6B3A1E4750D9AFE4673F0FCA59971F7CE476A80BD16D50DB3F04112538BEE6BD1C5F0D71B9B65B10B73A9D0D6E0863404477DAD5BD590F520F65F7B0AB0F4805495F7599BCE49FE0D5D07C875DC3E4993301D7332809A05125DBF799487885A16E547A93A03B58D2EB745B6F0F1A69F698267E2FB3241B983857852785940165B33393A116392487B725944E5A4D225C48F8A1C03B32C6E1441E3750D991F9FB8C0E1463BE1B4342BBEB3541811A6AC1F53CFC199E9690C3284165601A8DC1030A1CE575A04502533F601900934DBD2382B07BA99543D782600DD65C244A534973B97908BC1AE96366DF60DAF1E62505BAB7FB88AE4078C22CC8EA76ADC1F3B0AEF0DC844CBDBCE61DC8FBBEB6E0AB6748AC10EBD6F04D3A22ED7737ABCA2C0F4A008B780C96228218F7A95878CE7E9FEAFF7064073CFAC019229F32D7D0146424C17AC09CB5FC3573E6449619BE516008C8716B9FA49227B74A5033EF12005B7625FD513D85798D13C9147B890F4885025B74764E783A13878DA8030E324D562C0E1CAD4C6DA6C63F463E1CFD00AC052DC760C937CA1F0E188EB39805A5F49CCC9ACD613E72274C91050E1275F18252910D5A4683470C799C21ED97C1E5589BF6DC1F2E9D8729E06F79DB2A0E4C84471D51DD7ED0E3B0FDC6E6CBD16066425A5407ACEDC76CD580D8411A0E75B96D02FBA0B882F43E0205E104E406BC207C5EEBE4DC45222586F018ED903D9EDE00706D94EEA64FC9040AD0BC312FB589FAD2DAD25EAAF8134C9655D890DDC632F20449C3BD3A350D2E93E273C409B99B216D0F8A7D90E0E66D433E244970C001F7D638726A50517EE17879A2F02F8879FC0CA264BC1D064ED48BD1DFB244AA34CCCB26FAE69FEE46506EC7EADEA5609B67D02C013FBB1CF135A814CF4ED6A0B25CC67F355DFC43BBF3E4000C12732DDB7807CD3E16D236FF1BA6EBB63AEA81822CC6071FA341234B9611A60EFF6149DA8275E822F81796DCC30643F93B357A308BBEBB3B39077FC5543AE442FF4363AC5C8D3C95A6602FC9CA4D37624510E1E112A4278A4180CAE2FE04CABB593EB39D972FA981FA3D8FF0CF46E11753B53CB263B674955EE9803EBD7FE7EF339A982869029179D5768BB7925E2E4461F44744C609D898FFCF63241054C2F170B777A06BFF8B0F1ACB355F2E6F8FE25CAF6C89EB4E232FB628F747D3532D69BD7337FDF0A36DFA7C24787ADCB7570B8BB4E79E3B0163EE025E29E24F72408A36862F581A1F7E2153797BE59CB698E6E808614C3F08C6B07E28593BB7345D779FE72B7C8368A1FE4549D544826121B8530EC46CC038A85424ED3E3E2F5F92316BFEFC7D51AF3474915A1A5195C5C578FE43B05C5F711392118E0715FC74D72113A1397399D11D4FF9D1B70E089FF91685FBC044365DEECDE35AC7619A109FA5F175439D902D6E5E5BF1D28A96827D3CB6AEC6FCC6F31AAC88677E9DCF3233F6AD1DB812982171EAB9568F3F4C642D26E0C3FD12ADFD9BD944A2372E1EC573C3C623D7D453C5AA768A8977E4B10E764E3DA7BE6DA48800891ACB355F2E6F8FE25CAF6C89EB4E232F1C391391B81B824267D675978E92BCDDA9F6C86046BEC4697F56E511C9F52987EAC88656849FF89E8499AF02B02120F54DA55C1ADA30E6D85A1ECF2C4B7AE008FB6AD60AF1841F862D07B3558DCE2D6A89B4960C577B6A120227D9C38FB82CF13FDFE86751B209DE05545BC2019BDBB20D125D1332DDD77E98322B42E743BDC7BC1FE61C7F3BA6871C0F498EA562243E7F80BBDDEE7681FCBF2FB31186BDB8C5DED1F85A7ED25EEFF0F033568188462F5B54743D45D07B8FE228A1A5BFF31670621A3BFED98D872836C2BD79E66CECB09FFD9BFCA7769F2EB7C660CED04317F85280729F6678FAB19B5FEF07D9553430871641CF1856A64BB16A6B9BF9F4D9E8FE88D3C50A7B10B9B76EF1E6A7CDCD239544AD6ABD92A91DDC846324EA81202593E5464CF37780D22AD8134A1C9165AC8F4A338F6D3F80076333DF7DF043DEF0497D3464F8868AFA69B52506319A96FFE4AD5446D7C941596B28C7147412703B66639C78179D261EAD9C16D0BD0F5CCD18391FA810BEC4C3671D4040E78E9E0ECFF950BC259099D7EC3B6D372674D8CBB11AABA31816F45804AA1F3FEA83CBFDFFC79054456F23A824B1B331B5797F86394A8D1B8D49ADBA7B99DA4C98BC0A8D75842E2A34EC077E10BF07F59EE57ADA42920A048F3CB50724F3887E1D9F6A827FA38BA0DB25ED5C55520ED7897BF281760AE3EAD73336013DF41289137683B2BC73E4C102303F0B8D9A91A1CD9B69F909DAC673FF888F3F57B08A3B10552DA00D71B9B65B10B73A9D0D6E0863404477C278C1A488CBAA6078F9E1E855C1D9E25B40433F69FB5A32B1764CC2F629074DAE9A9782596E893C77F512B44BF6EE7442920A048F3CB50724F3887E1D9F6A8270A8DF7CDA3F319875FAA1000DFA6F1B68364BDC5C8F5681456C24FFB7EECF44A41769B14124565166FFF4D223FDA14D8806707F3D474FD04055F1F7264F11FC852459F768D18935D7F133989B222A756E69491FB68FDF0D24611EC91FBC446665534BCEF47B43FF40075C47EC623B311E4750D9AFE4673F0FCA59971F7CE476A80BD16D50DB3F04112538BEE6BD1C5F5E60FDF9BD79089C00F652F8A7A08200F11ADB84810E827BB26BC78ADFEE8786AB1726001DEE4AFAE5E30458A8141491F3187CF63A45794509406245E49949495D040ABC57DCE155FC83595B13D86408F626D991F02DCA7AC9395C39F186EB6F1C998FE80AC5AA48A28EA58A99C8AE97F63D1011FB7014E9936096D5A40F7609DB30E0AA1864E520D112DA84023B14AC4DF991EE711AAC2EC90213D8339C70251A7B1EE7E35D5A27BE75D07E285D16CA75A9505B77C92D51DC6FAAC5F091344BD9FA0900D9A61E069EF30A3BF748B0965FCFA3BCF1DDC642C9B7564CCBD76663B6DEC4086F904C21FE3A74A2E59755EB25DCCB8ED4DDA1E58DB864D397758CBD5E254E273DA3D41612CC378BD0DFDA85F9CB3753D744C48126D331137589696B9574904D7C263243FCF0E37051675BD058DBABF65D79D12D2563D17625AC3CE2665B412F827DF6C42B77E9FC6217255DC5AEB180C2001D8C7BCB45F8A93643F53461C305D795EF4A190E400DB833FE767EB6A6185014CBA7E25203A3E121432BBD2D15688B183DD944EDB309EDD9AE37E280FA0223D49D31AC7F5E1F2509DE7F7275C0A097E6C1B68C91C605CFE765DBAE819D580F476BB28EC9587AE514591A1E5131BDF39CCC06EE29E3EFD6538885ADD06C19979BE17C7498BB7D2E6B4821C278C1A488CBAA6078F9E1E855C1D9E20D71B9B65B10B73A9D0D6E0863404477DAD5BD590F520F65F7B0AB0F4805495FE989E3BEFC26CF9E49F9AD2B29CBF809F10A2F8817BC1437DDD85A5F05FF398A52CE9F9322EE61CF2E4C076938D968A582E8C304F526C0CF1A6E78151D2A211275AA0F07FDB04E3E6E04530416D447FF34187DA0BD2937AC050179E66DE42054AD0E57597834E57EBEB42F05FBAFFE2018A2ADCB775E6BC8F5E8231E1AB2BE2516FDD0C2D67427FDF59059324455D63244D3BC5A3389A59061A7A7F6D6A51520E92F35C599656ABF843016A0BC5E646CC497A2B23522C2C1C4A6135083B53596EA660DEFFA0A7A84113071E6D55052D391087650A97621AD797C3E850BABB10B03E3828567A19D25308B9F5BF680B8B74BEACD41D43224452AC82EC6474C515F179AD82E785FB816CC990DC6D75D6EBEA948FDBFE35B05BED32A1478A2F506C6F3CAB0195F4D503329FB9C0B36E5D100BAF71924DDBABF5B8D2983ADA6F9D584A72CA386EB2C0D11848AE87F22435FA4DA0FC862B7C695D5935E5477D71FE1281DA1E08DB2ABC658BC7D1B964F2E6052521D57562439558F0A395457BBC57618968C56BFE36D8111FA2A1BE93BB218C1C36F4726C722B81F0D79125AFF9A88A6EE752EF7DD05313840D0238A30D624B2C8A9912D2992B0753FAC12A909380787CB9E1DFD42A50C539464FE469934C0F6AB6D59118D3F8CAC2A2414D044563844CFC1BEFECDC72D2FEEAA33EB485A75EF3281BE729508130428BAFA851983B5FC77BC77925088CA7F025A6B0C5849196DD2EB553B05EEE97C600EA4FDCA6BE2E1CDCD3E39F425B9FF429A748F301D66679C262E94E17E4F0357B8C2BCE76AD6BB30753D545C43D98E9CF7C4CCFE7905C1C3CE7CA658F5EB96A756300A357198A2BAA89F065C8D300B4AA1FC8D468DED1860D209C7E3F1692F2E9DAA7E65B35EE86394A13B8B95818594FF15011A936133556DDA583754EB8E3DA1131584C8358292E09755EACD4D0EE6FE9AB5BE65755C9A2C091628A0DFFEB31CA1930C3743B287423901F1FA1BF0BC968A80461F42B68B940F46A399B6FABAF52A051F9D980411B7904E5A3E5C47CE3F7A88FAAF04A77E3ECB33200E6F05F73FFD732D91C81F98610B1365F1C518D2F9612423F34D780543174D3FA1E39CA5C6D33552EB69E8288968D0685E5E278A78BE31BA8491B3F8BF7471A9D995081296D63EA84B6811759379B895C35EF7839E55B6B9C094A5E6B508C0F73D5770079E509179E3B06C0BA108C4821A1837495347B978490A2950D8679F9D2E5F28E4AFCC8890588CE3EEFDA064B1DFD5FAD10B1F9729EEE6698BF9D02B01BA3375E690FEB09F7241339ADC83B09C6BAF0A8EBE47EEB8696A7F7B9464CC17358384A378C9033D4CE1BD37B2B07CCD53076BB3D85E6066BC33A98EC1760121363E97A92C8223E9022B5F1841602D3419F64538C6E1456F6AE20CDBC9ECE4BA939B1994E9FE0A97016A6C4F4ADB860A92E78E8202B8A2B45F5E2D569E5082A683DEE79B4A53FC62C644D376971508F07D2EA3E4EF3478DF05D1E9893293C4641A048032A25AE0DBFF6AE710F0C5EF6250FDC82491EADEB3B416009508963EBBCCDE37D5F94D9516A83432DB2C5FE193B133FF7F1B6B40170B58E61062D7066AFAF43DA6CFF8D895D741DBCADBF52197AC2BD7CDD1FDEC3888E78AC5366747CF801ED5596D75D5392DA22375A9505B77C92D51DC6FAAC5F091344BE86E5D112279083A4A7DCAB20B1A7F1BCBCE2BCDC60FA1876477DF7D9ADE1C70B346EDB179022EB626B0D9968E6B67E19219552F52AA5EDE723D85BDF6D91D8259B3B759EACDB3CC4EE9F5A10F6C46E960BCB35EB12650A3ECD8C3F1BEC25852DEAEAACEEC5201ED45A68AEAB29A178D1B68A77358A5282714B3ADC25BEBFE16B9A445B9DECAE497F0EDAF61B556C3826DE9920F743FFA38F845645D9699CCF357517FF4D1DE9CC603B49736EC3B528EFCF38E82E3DCC60BEC159AE2C97B0C2C278A3D45A7CEBB3409CC8CBF11C4B8791ACB355F2E6F8FE25CAF6C89EB4E232F36A7A4A09FC675DCF40F65097705BB4539C4F190446076ACFD60EE773F5CFFCCADD06C19979BE17C7498BB7D2E6B48214A847FA43786AD2E9315D900BE7D0EF90D71B9B65B10B73A9D0D6E086340447727ACCF4853592A8EA9FEE17766D2D52F7E05C638A25D13F7DB462EA10ED45687244C49A282115BB647636B6B919FA909A0BC8E1E7C8735CD078610ED1F8193B6B5C787A2ACDAD3B62DC556017234EC75C489EEB86D54859A17DC86F1317E9B96A624CA1FBE149935D635F062DA0C936A2BA532967EB940BD9BD9F4D61F5A7294E511748D54DDDB9841F6E64E5E6FF27BC741AD3879CF9BDF7BE9314DECF7860B7FFC724208D20B18BFB4F34A7336C25A4C58FDF700232005025FB0DFEFCDD70905B632345DA245A861A7CE002B07B303A6719C60136B12068FBA8D5E868712C3775BCB7C0812C65E4F31D3AB6B81534BC943477121FDB4B01F74391F73FF140B9FCD184037F56BEB14E2F81E37EF9B940BFFC16052EDE32750F7E81D94950032534D28060C691BA851DCB7E727388CEEF4186191D467124A4DB0B2E03849DB9450782D1756CC3B9340DF667DC52C468989827A88E9D6766FCC0C47EDB9812AF75D511E3D9E82C2B14C4BFAF10418A692C9484D7B71CF38960B51D1FF4D1AAA1CC82B0D07134BE91704B9C3786BA575986450CFD03134DBC0F7D747D7423700E8F921E4C9EBFAC2402EBEC8C6F999C51AA341234B9611A60EFF6149DA8275E822F81796DCC30643F93B357A308BBEBB3BF30E921A95029BF8AFB794075653A37E8BF9D02B01BA3375E690FEB09F72413395CF21F72AAD34EFE157BCB2D07EA44031314698F4C2730A8CAF9A8B8DBEF42549C2AD57CE3B634EA18273C573DD0DA34A1EFDEF0EF70871680E716C2BDA0AD139D2B6DF65665C8B731F5D544207968586B67928CE069F20F82B29226A4E1E775126F707BA1E884CC09614C72AF99FA5FFFE478CFE94C52206DFE59FB90DCB640D71B9B65B10B73A9D0D6E086340447760E5366C8A3CC58B37E15E3073EBD5A0156B6AC753142566D2B0454992A3E2C7A41769B14124565166FFF4D223FDA14D5454DE9EF80AD2C3B9FA565BA91230D6B9A445B9DECAE497F0EDAF61B556C382724011FD4B21AEE00AA0F55DC11A29471B0015BF0533DC62E42F00F8526E5B8EFED2319F593666FD5821C7DF0054B661FDE9C2C680DB7A6C20E2A256D9DD6C9C1ACB355F2E6F8FE25CAF6C89EB4E232FD636795E0CADFB13DE6DD5BB32C13670D11ADDF96A4C9D136A3CF44E8968309D8C7878614FDB3215CC53D40E54049D0D21F64BFBC36ED23F463095F4D152AE559EFE74973EA5457A26D40D87BA0496961286216F59339381A59B32F8299EF6A93912F5ADE2C60F9E884C02890B9958C80817D822D9C45B93B4B0832C644D05DD720520BC351A22CDC73359AB3A9B8566B1D1E524D3F7A0025E9BC0E6FDFA448D922B78F2BD55319A6D58764AE398C84DA715057188D54F858B0FE27FCF255939C0EAB3649A6764D1276E569E5E71B2AC5ED235562BD46D94F8D0F8350DFBB981185F382B5EB8B4BECF4927ABBC16855368BF2C2686DFC7C49FBF50ED9B48AADD0B11EA4D8B5F0A1268F7188B509AB8DDA3AC87379BF134CF59841E03D7EF1DA7AFA852DC4999800E78A5A8CEBE6A5A2F5399DDAB701BCC9AB4B3D8780C5D4F95698BA6A01AD04F1143CDB0B17B71EEE1D4054D328DF0BF7F81FAE372337A42BA55BB43F7CBE82D2B47E21034CE6CD03653A3B1AE6CDDF7C31BFBAAC3113B956B2C47B30EE85BC778089E177AEA0DF0F6C26C846FF187D7BB0A733104D7137118DB4ED69497F10032ED381129B7A138EC81A842E9FF91850A8ED2766A68F65AE01D3EA730626C5C4E95D1CA07682E52B61CAD4C6DA6C63F463E1CFD00AC052DC7160F5615AF5C5BE395E74C82B2773BB17C912C9487CC73CA934C6D3063EDF3462B746626DA7216A32067632E297F3DB06CD7A0071D5B6915B78F5D10C5C475B312C35E7BA546252030E173B7242717BB0B06E4E741E18676E1038A77994A0B45902AF66323336026A0FBADBB407A671927DDEC49B0C3B1F641A6B4D08F8414D02BA21DA43D910524AA88AE7B47B9C5E02C226498F1ED7C2AFAD7989EC4983B33D71BA686A6ED86B98A8C22C1FF3B946D49202737FE6799D0B798DF6014347DD4CEE0985DB5920223EBFBBE79109EBA6A1DA7DFABBE8EBF361122299F1127B35806C940679B8F31E87073B3DB44E801689DCE391AE7F767BC04033A9A7A0E7B10DA7D1EB39E3E6DF15370CB56A18A109AB6C86FC9BD48FFF88EA2B8E6C32FB754184A9ABDE61F9932BDA04358FC37231CF360B2311C22253D78FB0B0EC4ED0A97A15E9E17BE557357D8BAF0DCCE9D1BB42FDC8E7B0F3B8FAA164F095942287163A628A90D10C588156EC752BC31DCA6AE3AA172AAEA09BA838ED3CF4AEFCA78AA92538227A671A1EDB062B31847C2786FF9DCF97B394DE7AD5753E04B83C8A0C7C7883E98DA520307FB59BED3E62EAD4BD33D61716E63CE1F58D2439D588C6963A3B7DBA201AC8527AE025DE54818ED4292E2A877869F32CECE9311AF4E80AB3E84A8E15CEAD204665337D7DD5FD42DA97D18D906F9B75EE5FF5ED4560FA0AC0068A505D4F1A3270B4EA716D49A5F0913245978A39A1765B3D296DD425C138B1E7BF5E7ACFF8FA57571618DD2ECD955F36DE3931FA9A4EA10EB4E490362E2905E9FB4BA4BA328DFB27DFE90814B13256690B62CDD232B04DC476CCF26582F40E7C30E2425E781C912F32FD10D1B1DC67720E160CF40576BE8BD9548A0926B2E922DB49C46550127F1EF38DD43ED893B8A82A0A27C6B84310CC8B6BE62ECBE07E0B03095669C6A29CBC06C77A6D2741EAAC217AE5DC3512261229A7675376393E4944DBA2EBBF50A3D1ED49E95D266A76769213A8E0187F13E1E68DDE775BE022063E9D28531960CAA3A42848479361335A36BFD9B1E31E040477F831945D380EB7A9E6A1D37740EEEC6BBEA78E0FB6E7413AE2AF23953E996F7F3968C8994D0795E60FDF9BD79089C00F652F8A7A082001F9D9E1C718CD4EBF04A7A6AAD40A84C220F1193058B89792FE515B60885F4A989D0EAAB995F49C705EB4568A3515CAE50EECA7862BF57DE5BB290F5266ADAF620624B48B4F02DA65998DC01E7C8B19EB246CBE2B39A726B68FA9D18F9FDCE81D446205C9743DD69AB3613C8A5CCB18FDA05B8CEE8B7D81B9ACF15A2470FD9A8A87A7E9DEEFF4FF29E542FCA575B05B5D3EBB881FCCAE04171132FA7A678F56E62F598B75CB4B038446F79FF50B97C83E6A8EC7245CA5FC2DB9CCE780F1DAFB942072E99A175EB3B77DA2E09390EACFCED8B18FD682CCD38A2E38863A4634C844736D3613246CC6E7CCE0CC07D05BBE02EAAFAA2204285EB1D3A061D1E93CB8533E9CDC1A4F8BAF11D3C6BDFE89DA48376A9A3D16D082D038B5B70B346C0822F93E5464CF37780D22AD8134A1C9165AC8F4A338F6D3F80076333DF7DF043DEF004D46A8ADD10BE658F9BBA4257BF32C0A9204DAD80C2B38EEF63A42129C557CE92C3C7668A7EE101150E9DE9335673BEA39CDE89907BA478D032C7B392357EE74BBFA6E71F285EBBEF7DD5938342ADFE86E5DF932C1B0462B5A588C35C7C6A14D859660D2629396EF409B1B3EFEB1957E456D3AD82366E5FFEAA9059BD2279871CFA5B461BC18777ACDB71F9CA5BE0D79C3AA9E295E2CD60C9C1A33B1796271AE09F0B1EAE9F0A6EA8FCED201D2B024BE3FD9D52B4B7C870CFEB1CF3FB9E2BC6BDEEF881603BDABCE9D31C4F9E3E1D3141FD50BDAC4C891A8008D64CCFF206720FB48D4D891FF655159BE6187F357E7F794E810C81FDF60B69BE3156EFDF90C166E76C544EFFB0956D537F174FE7AC11FBE4A49FCF5FF42726259A7A0E692BD347440677291FA9F1EF9436F6F615630EEFA86EB6F11D6D74B07CD117401474DFBD877BABAB3112E0E6331882A1B9B6C1C77A29750E30349DA225A784FC9B6073727C23C16ABE2C524603BA2CFAE9F16BF6D92B008BA8831782D9893612D2D0B38A901A6D4A7FAAE3F99181BF27511FA611285A9DD8635204F28C712D08CF45DAEA8D2951879553E1B1571D19995B657AC3A4EE9588FC06839D451FA315C113CB78FF576A45DC8A13BE145BC452E6A42BF68007A9468E5730527FE677C32799A312872EDAC1B872365C009A72AB0DB4CD261F0ACC50F8C730197383F44097189CA5A1E488604D529262006F3E92991251C06B8D640C47105C1C28E54B36F7DEAB0DFB5DEF026C3A1035DF6A667B8A1DDEC09EF2ECDCA4AAF4A787028CAA462D0B8C36450E10D370E0EA9632EB57B41D35A5B8CF74F3C0F888FEB231E1FD25634B6FAB9B78461C07B2EDC7B0AC6E0596F34A6B8DF77C686B99F73899E6175899E70A41E48B2DB15ABA07E8AD548C3D9F793B968AC39A9CB173423C4FDDF47570908D72135EC67FF0C025A2C2760E0FB31C8A29DB6D79AFBB9EDC32C8E3548181E685586535FF84670E1D44CD202F3464BD880DF723E8B6773B9EC29617C52F1C3844C0FE7630976B1529AE41441A8F8BBC9183B1AFF817EBF79E29745002788E872F22D98BCF10E4205FDD23FB8B4D07C7B6BF6E4EB39286390BBA78AA519995C0E51BA66D6B3057AD5F70D3FB09EB111D178F57B3B35C967E88F8128242546004233972EB9F7B3817F2B07BDA5A6E9D54CFFE83A94339EEB1E3EA834F4682EAA15125A0E9F00D88610BCA6C37DE8FD81F60445DFF7C004FA55A22D82AE4EE4D7925E5953C2FC831887F33DD308CC86B2934B53674502DBD1764734933B0E54BAD11910BAD52D57B7682688C03FFC928249437FBC9894D74B9BB556A33A2F5F9FEFF8D987AC1167768C538CAE2B7BF6B9A703E3AC7DB2671EC1224560A3302CB6E26F7175A1F5FA8FC6C43C45670D78FAA64F174593763FEA5EAB4E53E523B30A72152ACB360E685B1909C02FEFE8F699672E707AA0A5A1833FCE59899F358615199FB2EE19C5478AC6B7B4F32D6312CF1BDC12F0913367CDB233931C1408A73BB6854627BD622085FC9E230C58F24533A562815C5D3B29969DA6B19F4401619C7FACDA64641B740FC3959102F8D5FA38137E83DD6E14074485F2F4BE13E2FF274583AAAA0E3F73C2048381834DEA19D6A4C9C0D12696130D17D4A7987C8B6740A065055B4D707F3F9B2269B0BCE3D20DB88B7E5E2FB1BA0FF41F100FAE35FAE5D2C9893FB773922C4543E94EF33EAE432BDB53D43D69DE8A9C2BED249F6BA469EC46E5545B1CB64F43CE77FB458BF4A310C28660DD531370710F4932C23DDF5F28A85CFF245D274F39550DCA1333A6B609C870E814799A738657D2DC7031B7C2B16312576E7B14DE88F101FAAB272508CC6CDAEF0BBD1B8C8EFE86D0192932C9F21ED793A9EDEF199280CD73C24BDB7CC592C03B2B49415984F84AF0AFCEEBFA9211A1DE277729D30D067A5AD1E7380703A772D35B5BC72456DA149CB95CDFB4ABD3BA1658741FC15B1F183F12792C861AA0C1FD8D3C56B972B04CD6864DC6663B1849467D991D079C37360B4D76DDE14E8EF65565DC72B0CE99E3000ACBB4C6674009E466044C33CD3CDA9EBC5D14D0288B7E5E2FB1BA0FF41F100FAE35FAE5D2C9893FB773922C4543E94EF33EAE432BDB53D43D69DE8A9C2BED249F6BA469EC46E5545B1CB64F43CE77FB458BF4A31A54884EDA0C0944A56CB153BE08AF53F936E9D1AF033CBE78CF44049CCC3DBB4E2D83735C8CEC939D2606592F955A872F7FA70469E1525A02CC85D40CB2227B6B53136A68C2F8B380325B9EAC9FF8487D950634E568679E4D2AA3A1E2D4CDB2B6B48008EFBDC7F6B9D54569C4E3B356C056D71F0E9476B59F91CF87ABE7B1CB902C376CC876DD6D6C87281A5352C9AF616D3E550B0FFA1CF75D4F9E7CCE882A43851D77E1DAF045496A519D6055393F8656A0225D3DD5C88A8DB81C3ADCDD0FA9F114ACD9956FD059963D04A0F80B406E66A3073F7BFAAAB757175A1AA4AE5825C01C0F7DFCAB1E7E983561E3C02B11B1E646D26028EE4A809EEBB6D8D00304B3D9505C31C6B1507B9DFDAC12B6B48B1492CE56C92E7CD00CD009239407F48BE20FCC596E07449223352CC149467E9DBFD89AD65185F0600A5C3B2EE1DCE0C434F39ECB9E8891C4E57A78B3CDB50EF3FD882AE1C734956EA016FC65CE252769208C2B02101299882A042281B5F3CF6B350F486822949579C7EA6B1F74315348777618AA400B077BEF0AFF12A4993585B71F90849530CC39DF061B159F4FFDA068602D2FB585EA1AEBF72140957988672C8A9596401F947F7AAC66A90D4D129D1C8ED07EAB0D58D7B3B3FC42A6AB2038E39ED3BB5325493E56B2B8AB2EC3C04617917AFEB9127C2395F4DCE61E71592538BD78BE82AAF0A26877EA79A07E85755C4E3C60BC9C6BB2CD59B0CE0076C0AEAEBF5CF8744B084EB6E970764C9911BBEF6B488424CEB22E22995D5940A8FE269718EDD9774AFFB6A0027F008267AEED10D60FEBC76EF14658AF318746FD885EC6F446E252328C08B8890A4AD8157FD8DB752B15CB0B5B0B6A26084D266B18AB67A2631E9E1662706DE5FF1C080CE451076925FA90DADD72942180F19BCA16AA9A0010C26EF99E8B9718F90210DA5D6B74EB44645F6A92D9C89A1D6DD7BCE13D92152ACB360E685B1909C02FEFE8F699672E707AA0A5A1833FCE59899F358615199FB2EE19C5478AC6B7B4F32D6312CF1A77660EC219CB6A463C6D997533AF56D6854627BD622085FC9E230C58F24533AA5681701A12F4B75DF79614A18F208D8304466B3E3AD1631BA46A5B10BC23E9C13EDF0EEC008012F7F96B9999D36BDCB0D741C580C00DB08FD52B10E7DA8206F2398CCE0429C47357EE98F435058B9ECBA307AEF6B9CAA576F1891363E8EC5A98FE6C01F0500E19F8CA3A5A90B392FE381BFE0078D30EB2AFB6FFB311099788F81CAA53998C2CFAF63F6241725D8C8A9AB9D74CC665C417D44FF092F6334BDA0C7291974FE719DBE616DC854213BE5C759A892D4A84AEC3128FA725AD215610F7A52CCC0E4FD13AFB50A14A9B23E6F538B7C7E273A2439C81C1C6799529227388B249EB9C964E7D675C711598CF364275347CACC707814DD4B8B96CC915F7934ADC823A7BEAF4AE2E10083CA50760987D75E3B7AFB5A85526E058250FFA0B0F5BA9467CA90AF80FC08696361D9BDA2B09BAD96FC1912C71D3C45660F02C9DF993388813A9CF4FB56B5B6AB7218E6C95B14257FBCA7F53CEFB0A118042E6C063AAA574A79BBD4E86EA828F6D459364B6B69A2AC85716D0C2F3391B22BED7A30390D2F559DCC8AFB26202ABD6007A655B059178B69DBAA5CF662C419C135FED092043B7F4C878EB5096CE6029A2BFB075E01C550F5581DC44DB6F438AF50FE79E8D05B493054553E86AC4B3F20F5DC1C8CF86BF831FB8C0BDEC227550D784602100BF1E29AC17BE19827F0D2ADF75E1BD46A779CF034B00870E6E1B91276846852C3C60890389716933EA0D0F0B393D27206DBB6C2CB58EDFAA27D6E67BF7EE04D54A262C87F3348B0E79050554F2386146A5584AE0D25FDE17C4EF2AE25FD47C6559707DD18A2301C4492C5D099EE4A3D01505BD1C26A857F135462950A86B95E203C67AE7862A54C55AA44EFAF3106BF6F11D613A93BAAAA39F83C5A3380AFB6CF5EFAEADF3E007993E1552A47A507EBBDB2C0E4D3465CD9835C4EBCA65A437BFF1658DEC6ED5890B61B37658F717030C454341789689767B0A641DF07FA1088D1DEEE25B3916E75486218EC13D0652C18D205B69F6CD4801EB699B3FFDE9B467AC09CB5FC3573E6449619BE516008C85961EF15FDEB06FC5A2E9048EC4D0AAE443D48421EADCF682DB8C3C982FA01652649241952D6B366562F7905CF89E3F31F2BE14E1E1A97CCDA8720120C05FB8FF66E9709E36ACC9E0115559B55DB54B2B1A598697D534106E608760996EB71431C87C27FC78594CF6332885E11F489933A2FEA2718E24E913DA4063D0C7770F73EBDAF4454EE3FAFC36EADAFC9B639C219D685A588398AD9171E3900C59B8636B24A7D5621565FD81EBF4D558AB79D3B242F07D52EA2E5D4DB5E6E1CB0E555C65F073735040B2AAAAE01BB20813D81613882736CD227C1BC2ADEB387F7BDD938180985AEDDB94ADEDD6D80509850B06ED1ABC55318078936F734E0739C5D126110D55F672CD4406AB1E8B0CAF75A198A3F102024F7F0FAF8E83840E82B40C23E15C3E7BBE90799D946802AFDDEBDFD76B4FCD3F0B2B08C1D689D9CB877BBB3BFB4910A9B3F87D148A940F4F69AD509E758171CA9AB1511A719D32D0DDF46114353684C4290A999809D8E2E9B490335E8C4C566463F9F957F3E807452433608A748894BB349806A5D52B086105A9978A1BF4AE393ACAD454C3A3C50F6A0C9C4F8BBF98AEFD648D114827BFF6C1ABAD9B6FD5068CF1CFF688F01D04E2C84CFF3A3DFD7417F3321DDDA43C4E618D9266F385ADE27CD213571C100763F9829367B19EC9090D02DDB462416E4BE0C511E8E28277516463BBE0EFC6DD7BB1B34E01D9FCA9E1AE5C12987FDB72D5326B49D72D2B0D333B758402962277D6DD0AB9A00FA3A0A0B0D5906F0ECCF3C1B49296E695D2D7BAAE8AE1D5D6C19060C0D1ED06F6ED5BA4EDCA9AEAB5B74E28FB797DA9AC6BC580B946572A14877EFCDF155C8672139A1762EE612C804D54FC83FDA1D98047618DE293ED3373F55EAAB6B39853C5E642273454C174E8296F5CD7C69BB2545820C78A70BE4E27A375882F05C473E008A1E76D5C22578AD6166B6452195B29AACE2E3828F05860836204222D3D3E2AAD59DF19AF793A2AD7151E038EB8E234FAD7E78F62CD4412D44C92D978900583FDF495FAACFC8CD66491B6CD2B8FD6037AC42210471AFB554225E508660D46A651DE239150D8D7161A8198CEE2C439141F74D87DFE052C6938980F0E2547ADA486FD2C39926110B6F96AA81856829F084A4AA4419185AFE81AC61D7A4C2EB08507E28593BB7345D779FE72B7C8368A1FE134207A8DFF70A58B2645A1C5A04B839027F79B81AD5822C81DE9C1492AF6A5A99C6246203DA5E9739848509F1CC448075A8D820716CB2DAF2CFCC9288C27C26D554BFBFA23316DE9F20F95C01187B638A5E086036C981976DCA778C974939D5218D905A7BF6B7EF35F891F45F6B5A967EFA91D293DD49E9E1D7450722F594692DCAF8A732EDF309884B6DE87B5FA3FCF74D87DFE052C6938980F0E2547ADA4867FA973DCE6822ED57403DCC25CE89D52686D7F5184D9D4FEC417B4594D5972F7E28593BB7345D779FE72B7C8368A1FEA69748AED23148AF13321250E8B21FD9027F79B81AD5822C81DE9C1492AF6A5A99C6246203DA5E9739848509F1CC4480E2EE15D4802F9425B28DC2B88A09201B33ADFA5F8EF7619F32CC0B2E1C84BDBFDD5DA623D60BFE1EE7AF878B79BBCDBCEBB5C1A69D903F6DA5D89287D88700B08C3DDE8C86C211A776F2779D056BE964209554C9BB2F65F9F582FCAE6743112844E02BAE9FDEBDDA79A49451174B82D2E2CA184FA69E014972FCB221980BE3A2E9F64A221463704C4F8C816E4BDC88211E6E737E029658B989FA40FB5B04231E39EEF0539B2B820BF332FBC22C9DBA80F32DA57492F5DBDA7E203FCE8005E46EC6A19E8B04538B99EB11E932578BCDCA68A095CFA8D01C36E2858BD601EBDCE59966B4AEE1FEC7FA38A18E4C8526B651E5A6CE3D2A232778E08210386DE5446C9BF27A9E62485755783838E81E8D3E2C262E407AFEF5609F406C6D2AB41E8E644E4A99BA0BA74BAAAAC6D1D836DF8B37662D6D29BD84EDD5B660C52DC94945F6FA7A19D0C8811B0882A3675A2913A7D9E408F7FB5E7EF5667F94D928D932197F7C9346B2DA1B8CF76C6F1BCEBB60427B3B4DBD2E8280D6774A0150EBA566F38DDD6357436E7D69B285EAAE0197C509ADE5A6CE3D2A232778E08210386DE5446C9BF27A9E62485755783838E81E8D3E2C262E407AFEF5609F406C6D2AB41E8E649FBEC5446C6BBB8FEBA04E3F014FE13B662D6D29BD84EDD5B660C52DC94945F6F4A8C37D97032DAA44DCFAC7FB2FC97AE408F7FB5E7EF5667F94D928D932197F7C9346B2DA1B8CF76C6F1BCEBB60427B3B4DBD2E8280D6774A0150EBA566F38DDD6357436E7D69B285EAAE0197C509ADE5A6CE3D2A232778E08210386DE5446C9BF27A9E62485755783838E81E8D3E2C262E407AFEF5609F406C6D2AB41E8E6465E580C53A246E98A7361EB4E83DE9B5662D6D29BD84EDD5B660C52DC94945F69134AF3A4D94BC06805632D6AD1BCD4B98D7AA96B23DF79DDCEAD3A9F786D7C8AF68EAFFBAB437658BF4DA274D6BA1A1E6E42161B5F27645B7E78770FFCC726AF41E70577A77FFEE25141233155CC0941D655193D0E73FFD5F76AA7D7FA757573CF0BB7A6931618524DBAA353143C3AE8B708625E303D488C839C75ACE73FEE4539749A9E7623D67B284B75C536B7B66350147B6177E836960743B9EF8EAF7B277F202B1C5EC39224F8CE1C6E3D92328EBB5C1A69D903F6DA5D89287D88700B0FDB199AD08B26058EB277A16EA86041A09E1DC1A2E89534C2AC41D17D552CB1A80CA849D7EFE23C60A820F5DDD19A8751050D6B582EC5CEAD818F1E89DF9D87E8968189CC494DC9E02CBD1FEC9B7EFB6D6F471B73116ABC48F40D87D7DA0143CBE8256A281DEBBA09C0C8C4A5E546DFC0C7D05DAD886935E8080006F4929EEDE6D84BDB256666A87780007F8557804F9C5995697933DC702F83E9CD9FD4CE77285B82F55A5783E098726296F13272D6BE2A909CB41315317E0B68CAB469E0084A1C14874321846970A47A1DD14356AFE897954B2DC1A8493719322D58541B44B3F3505D4541F98B7E841CFC8F2114BC0520A1F667A531084E0C785B7C5C985A4866F95BCE7C8169B8AC2D08A3135CBA60ADE52DF0C714F2E56AAE8188934C0B318D089527621F11054CC5674ABE8F1E5AE6BC6DD008790DFE374CED9AB4A39C0EC5CADB836DBBC6A0CF0BC5E22F0D92EF114E6F0907945C4E684D8425EDA693EB7038D63AA21E1D55F3321DC1CCB54A5A93B257C78DC5B50DAB7DBD8B2AFBFF7BF45E7138FACA3F9EE9FD2CB446569C459E3DA82EF132F37836D32282327188D61837EA701A086E68B54504B6B9219E538E7431BA0F0B85379B778C9E97D596C1BAD000CC3581663937AEC417799BD222CB605A276A14DED7B7A4F57BCA5D538E19B2CA1C6F9561AA1862D30F3BBCF9ED9F5661C226D9D2838C831493EB568BD410EF6A579E9D2B6C53B8D72C1E89ECCF6B4C22ABD61C1FD4166325473C077226CF0AD18FC177A95E056AF7AD30D2FF07CF7FA4FC2799A4B0F16711537CDA51801F457B866D6851B1F95F82CD92D7B59D84EC11A6FBF9BF31CC719507E4B7412AED6D6BF0D9132BBCCFBDDDA4D333323C7883E98DA520307FB59BED3E62EAD4B15689E00C56317CFDA0B91936CFC7A87FF95B3E1FB740E639BEA12DCFDA24EE74E0589F828CC587C6C396CFAE38C9C7912872EDAC1B872365C009A72AB0DB4CD34657B0A3AFBE31CCA99E1EDFD1F567FB2AA773D0039184632CAE32BFA178E446F4530D9B6506FDDBD7E0D0C9A99BB654EB5F151558A6A322FBBAFC7B1D9688FBD983E2DF567930E6DCB47A0C4859A7C72411C634645F1F79E2B0A720D7E5B2EDD6AFA7885680FE43CC9B92DE78C909887E2AC0739A2857CB01B3105F88594CB2E130E625D3DCB6EA87FF486E514B77D5743DE2FD7E67C60060691EF57F87EC5C9401095EB359D52E644CC268E5D156D42307EB4B055702C3BF0BDE33C3B92BCC14581190510E97255A604700D0C72D60953180F2E1C653E8E084091C72A7B2C9FE52A76713BE11AB2E748B05A2B54CA8F9D031320F978E744C9FC16E3D397F093D5015996370D0B6BAD6A13B49C35A6643B152CE29A27691EEF5A918F97CBF8F9D5AAC77535BC55716E509AC698B9E2DB3825A20B3AD0DB0B20BFBD030BA183B651537D04E877D3EA85E0CBF45A0BBF59BFDE01FE51136FDA8C73D039A4FEF648CF604D3B596C2D7FADC67DCF950F4F5C077920AFF9DF7CBCB8D8D9C72856238028F83D1EA67405E689B377F067897E16744EC2DB544DD86B502CF55044166F489CD9F34F10A849518DAEE8B4ACE87A48381CF0B9BCB058F77884A98BE9292B1E21D376886D3E90C183CDE57094590A2C438AD410B7BFA7500690BBDB73C254CBCE2BCDC60FA1876477DF7D9ADE1C70BF35A1A51C35C0FA9A9E962CCC247994E0E474BDB9E0711DBD2BFED41AD3AC652A51BF159A728876F89B2524CECF9B1A908EA76CF52654293FF9B6A84523E7887B32E4E9C45CC12DBD09C235F68F71A0A3D480305069A194741B073AD04B2F4B30DEA7752C64E2E514D6334F12EEBD983920FCF2EC469BF966DFB0E99C43C848CBC686282D7473E9ECCEA37C685D602067FEA229112D1C53A5E108FD4073F39016325F3F6A049AD82984F4AEAC51B6981C34A1C33496ED4305FE760972AF9CDD5D374B2CBBB1BF4CB51422C2EC56890AF5ED22F9CA95974ABE455AF670D371F92AB7DB3A47FC4864AA04A3D72A30695C64490C4E7419B1A620095A88655E4C78588BFA29B3DA20215A90BC3690856A1CAC4E12A6640B45CDEA7A4488DF5D924549D7ECFF8F4AB05C1C97571F83FF773F70ED7B49641B4F56FB8EB60A271896FD15CAEE19F89DB12684ABDC7A721CAE575F147F9A3E1C7E45C5FF7ABF6C25675BDEB839CCD2B9DB51A26D85608D3281B0DBF0D5814FFF5C9C7021A5FFBBAAE68CC5F6E80D815976DEBB74D704804D923C063F9E30321E70E90BF3E5B7C07D0E723160380D5E6EAEEED9D4A549AE684C9C9F2AD2B15B12DE74996072C803AC48DF8FA2C1FD5093E65F1FB4BBB6BEBCEBAF2F1ACA0BD4F6238EE59BDA4D6A95AE9B9B149575B18E61F0BF94412D49778174492757BD36286D71827E83793E118A310D3D51BE57F59579B084D3CC6FA4AFF0E4FF202E08CA57C0AA876CC569A95C532CC849745355216B485E4ED90E4431DA79C09D2C2E967AD674C6940598A762E50682F6EFB130D2D66278FF8B357933009025742F2CA9A175299E6127752695E4D7888E8951B433F0F863E127678A51D6ACA2BFB8E1707F4D1612C35E160172C681B24ECEC50AC5C69C019564FB4B8BC0A39809A80015AE4F87F1201A5BED1E189EC78C2535360F0F9F0643CBA3563CB01F0960B05A26D5E70384D904D294764DC96CAD4D4D5F6408AABE5F2D98877735B4E899F51188A977A2A0579B1DB0681557D337ED6E13F42A9D4EFAFF6D60BCF794684542300970A295BB50DC52A04773F0438C9DA351A50F7C8480857E34424BF91B03C2F4B86E0DCFA64A908D26AE646CF0AD18FC177A95E056AF7AD30D2FF09B4CB58DE74B3D0764E574FA1DFE6BF0CA69ABD09F0E51ADFC3A0C0C831BB74686A0C66F10E4D23258EE266CE009073084E4EBBCCE322377406E056C12C6E567A18299A780C0709825D878A65F065DA825556DA8EC11CC6C7477B421DD3F4BA8CA43220143D5B3E478384FED669161DB3A394BB33823110D20387313DC05B13E8F651AC3A11F6EEBEB16DCBF962C010C142085EF529093304121FF976A6ADC71EEDF1B836CE4E171CA74D06A9A7BF113CFE5C1C0B4394A3F95BFD5155DD0254A1D3D1BB3B787513641F75BC03FA53A127B5A18B6CDE1A90C42AC63A2242B3BF4FD87C016102BFB33F4B384AC1435D1A250983E6835A9562A55266AD700D4DF3784E4EBBCCE322377406E056C12C6E567A18299A780C0709825D878A65F065DA825556DA8EC11CC6C7477B421DD3F4BA8CA43220143D5B3E478384FED669161DB3A394BB33823110D20387313DC05B13E8F651AC3A11F6EEBEB16DCBF962C010C3912F5ADE2C60F9E884C02890B9958C80817D822D9C45B93B4B0832C644D05DDC2A53CFF82D46390445BD507F709679D45892692C65DA7BF395EC7780604903773889371BC483F652F10A22BBC0D99190F12D10097E78397869459904B301F625ECB61555702DB37F96E6A98ED2A39367AAD775E650B279D7A78A2416550AA0CC689A9EA24C4CE181AC698F9E3D5C1626FD4E38FFF45A0E5D02787D3768E9F01444824405B0E37444095010B5B05DAE8191E3BE64BEE55D7FBA600DCE013F160363D057251FB6DDF5EED7445430940ADBA4DA1A835ADFD183B460E2EFE1FCF1437070AA87685D7DE7A4D801C3E7AD260B92A3BF02C42331681A1649CF1F424963B5930A91374F0712943657DED8B88D41003C3ADC4D47EDA6D4C81A925EFD39A652F0132558D66A04513A6F848A31292E41665CC36E5D5C049B2C687612B872FFFAEFDA052918638B1169DFB5067841C68CB5235B9058E6DB6375C5E5EB5F8E7A341EB59F13457ED7AADB50890ADBF568523B36905140FFEFEB3B1BB627251A35400C556D590809E1DC376BFB2E70AC3878235719E283E4F998DF7987FA6EB620C0027B839BCEA2E67A0FB7BC4674E744F5B173AEB4D454FD026F763741EEBE54DB5E2FC8FBCDA997C3F5BB93BDB201CB87BE285945126DC3B3761A092B09864FB8AA755EEC56D2CBA58CB30F082A86709A5DAE13C9057BDFD2EA77460A47B08271B2CA885F7A8109AED0273E91A0994DECCFD46149F6E96793C5FA24F3286CC8ABC66EE6FF7C06FA89834897743A78A9B2A13C1B59EF002C3F317BCEDED1111033444245316941F3CA82DE63E6509E4C50B1B4D6D9A10FADF2E1E33F607425A916376A37B594615CAC19B27FC29C8D1D3697C8973077B68CBAC5962828E822F64747CF05D960DD68E0D8E58873C2D0EE94CF799EC311311106CBDC8D12D1944359DD32C2B936D74D9BA0AC2D63E812A1AF5146352E3B42BF63F1C4720AE5B638EF99BDB795F61BA33DC8BBCBE33EB812D35126755BA23D8E6E3FE54C87A29B24E86FDC21B68FBE93073FCC1B8912E3F46556EFB22D271DDBB7A890CFF0D8AF94A5D9B8AB01BDB2AA0336AEFE8603C930FC2BF9757041EB19D25D1A9AAA24F6148915242866CD64B0CA9854A72E2F85A5D7595F034E2FA5351EE6FB7066288B5F104FFC21F5707D57326D2EEDA5A581783733E5573676BE027EB10F240A405A470A2CD69DA3CB401D655F8630057D3F9211F1358B910750EA40787D382A6FA3383AA3E454CE9B7CFA35E138BBE17D9DEDF243CDA5B81F8EC76F1E06AF381EDDF33DDD7C11368E00226295BB6674FD5D885E46595DC10056784CE6E52843CEC62661516C10693D6857CABF9BDFE3979EC85E0358B5544B055D63EC2E2CE691E304BBFA6E71F285EBBEF7DD5938342ADFE5E60FDF9BD79089C00F652F8A7A0820099944766620F43E24C7CDF0E6B3A9BD757ECA767F83965272848ACA250E6E6DBF47F198C1E38A95CC946154F0895A47B2CB14DE8F5C7F8F73C9817B571BC053F2C9D7E4D9C91BC221608DE4405D271F195FE1F4392999D66F9B0C2489FF2CDE0B47CF09FE775DD06959376365C1DEA7EF124390C1AB4A1FFF4756E70B769543A56FA4EA6142BA2633CB1C5B6C5418C39F8338B3F17C41D66B13133B63AD907FB42920A048F3CB50724F3887E1D9F6A822459535575D8847FE91C9482BB2571C45E60FDF9BD79089C00F652F8A7A08200D7306E1A5D137DBDBCBC65E9C4296C1437E75FAAA0D6C21F054F129464330F059ED2D755C80D9D275E7E1A8926A72DD6E08499552B33D333725D435F45BFEE2F0C0319174F8CF4489051ECE3608862C987858E86D5027E94A5A727388E47399FFD02336299F8898758A77334A6BAD97C6930EF724EB1EA3E2E1A473710054C3A7A3ED97E24D7FE1699A6C774A6F902E463B0E4C93DE3ED6237B75B49EA417D0C9A4DA3F67FB8FC2E9C3EF90D906714370BB0F64717BA060346BCF83A61F158F052D702AFF4F3E50B85126E4E937E985C5AD48B41E3C6C3384B29093E89E951492A9EB9C3F1C66DA8BFE7946BA9462F4A531D18215C7E5E68A5296A9888F0ADC7D47942F9F19EB4A90878C5B32DCCA3DFAB6732A07C6EAF38239B24F1A94608AE641E844B489BD3421C261AB7B9ACE01802715DC836537F1163C3B50261D2F95F18E21937A53F7FB1BA102CE4B60E99EC4AC5691ECE57700073D01215732A1DFAEC4A839252B6CC4F099B2EBC6CB52F413DFC59733196E9292B98593E595A8080642C1397333A6A72521CBD55A167C2AF875B8DA24F44C6C62CF4BE436CEBAD05E1211392D812A1643A3F1B62A4367872288A0B53618449C6C398DB6A29607975D0FE892594629A4E0F704B28044A94A423021A67CFA0F4714F77C524CC872DE38C50992B36C9450C09E3702AA6CE86C7AD7E78F62CD4412D44C92D978900583F3D0FA3FA9B280CA8872E58DCFC0D8A50E29386D5C8E28D854389DF46B5EF0332B5740CD79EC81D0408C046778048DC2D38FBA52E008ACD6C6A1A218D5BC64FD0EAA82FE8B9262CC211772A65708CA82BDBFD4D356ECFEFB2C5DAEFF17C138103B777100662AAF54181938CEFA105A97555D0929EC9E35EE580A2985315B9414E4A1EFDEF0EF70871680E716C2BDA0AD1E01329303675DD375342154202EAB0835E60FDF9BD79089C00F652F8A7A082000E1D920DBCAC2D3797C823E2D9752FCCF124390C1AB4A1FFF4756E70B769543AB6CA6934987282661AE5ADA8F71D78638C658D68215FB7C145EA1AE4A4D1BAFCC6262A6ADCE2E2D7DCC651F99ED8F5D88B721B0A7D09F5DB16CA9F9C305FD3D04A7B548BC3F1A193B0E0AC29D3CBBBF14A7B548BC3F1A193B0E0AC29D3CBBBF15EE9803EBD7FE7EF339A9828690291793FEE933D87DCC7C2C0B5D8FC391E66514F05C79CBDBE6E43CC6A933DE6E71C7D59CC6D6A30DE3CD64C982FA9BAED986C8CC1EBD889D805DE85197B1FAB829B539DB1F77BD75BB7563C3D4A92D203991A491B73A8E2AFDB9F897497112D8167379DDDF14B42E8EC7EA441AB144AC3B33D32778F1791BAE64BE34C4AED463C8B505FBF6AF568E20F6F2BB7F20CF76F82D3CD8AF469A72E690FD385CA898AF3D006F2BC3441B8E6C543F04FC776B7159C3EC1BA9DA7949527990056BA367776A23DFEF93924CBB922A2DE93C2B88805412829D0AF0684BE5F48B26AB39F8DB046915B54743D45D07B8FE228A1A5BFF3167095D9F5CDE8058E79FEA503DCBD715DF7D6C572CAB9A9643247F4C348F046EFEEB4AA96EC22D16BD5BDD5B94B43BC711B9AF0412F50D5D51A95BFF2B364363092D4878F7CAA096958953427DF62AE49EA75B4C6A0E30906843FDAD3725D1295868CB7BDFA49CEF53C10DE5E2D717ECC0690BDB19A50C4D5855681B29099750E036465966E6787857DCBA418805D5E394956A3E57B9693C125129E78C2CEA1E1022A765C117CE4C50A81FD411F16A024B71F2E9D8729E06F79DB2A0E4C84471D5169654437E1C58962ACA7EEAE220D1DB14266085206648876CF9D979AB0AB4FC985E9F20FA0A1B3992FE0B96254F3B04C81DD23D3D261C9A09E9D5CE8D982FF3504A9156E9989AC6C5EF325854FE97C2FA9F1EC748B198A27AAFC300122315024857EDD154AA69A8A1B0EC5F4EBBECA22FC1FCC52E5AEA5B40E7BD6524184C9D8F214A465BBB51DA2524A14EB8319C8F90308AA4FA6D0CC632D85F09AC9139EE7AD0B486FEF8F79F144FB2B1AB45CB2FCF6B488424CEB22E22995D5940A8FE2695E60FDF9BD79089C00F652F8A7A082009E08C7F3004159721CBECD1391B189B18535D3ECFE76B71581CFD36116F69AE95E60FDF9BD79089C00F652F8A7A082009053BF3ECF8DABA2B664AA8F1A724CA25D114EF47BC52C5E24B87C65E9F54AA036FA000988C12BD1BB8AB11B5A12C0940CDC12FE46D4455FD4254F3BDCB81F7F5E60FDF9BD79089C00F652F8A7A08200CD7C418F25983FC0911A243705860A783FF5B3DD682F9791C2896D832B2CD91B43007E36C8E9C92792796EB7BD6940A83D18F83B1BDFC7AEFDF506268212020591B72350E45E64F96C882E65C728D29A81DD23D3D261C9A09E9D5CE8D982FF357D10AAB0AB0342F6B39D5A2BBE60695DA50579A219B6906CB461A0DCDFB1CCE2292DDCEC03EDA0EE704AD3D813DF2B2536652B5AFFB4E85C33E5427075E94DF5F124390C1AB4A1FFF4756E70B769543AEE9A1B5370818786124F6832761FC26C04D3B8426D0E53A5935BD606C37A5EF0A5F9236859A40F7825916FCED1FBC2C581DD23D3D261C9A09E9D5CE8D982FF35A69F7CBDA0C3E8DA2696AD6E6A932B8A6EC14E00171258D01C1B704CDEB646DA6B310611A575EBD9B1D696BA95955152BEFA489F6717746E78D951A02B0017E695204AA40B905808D19C9669C10B474DC72AD57118A3FE6AF29764F410A3D943DB1016043BAE3C120F6B263FA12C330C7069FF062C520DD732B8FFCD8933AFDCB88BCE8869DDF5159F92D67058A582A72B9B148E4B775AEFF316BDA34C06499AC21AA16552CFED8DAEC6B2FE6A89BB09348F64B8E792674BC538C4030CFF4EC603180D60AC6D2687FB6576DDAB6813D475D24BC619D6CDA5BA3F2D8D4A7B08EC10238F28DFD9F365088F967363690A00FD8696FE14D53C65C9EFE55A92C60790D61BAC7247E2662D770870600AA382EABDE5D8DCDF501F470B2D99A093E865F626E9279FE1FA66292309B039F2489E67F124390C1AB4A1FFF4756E70B769543AF6B488424CEB22E22995D5940A8FE2695E60FDF9BD79089C00F652F8A7A082007EB429CDC69284326413D037BA0108D5F124390C1AB4A1FFF4756E70B769543AF342A63F79D5F59CFA514E7DEF3ADF38995B2711DA8DC749D4174A231009FE365DDBDC080B8EB834BE23577DA750C6E0F532842304A11E0FE16C19588E5B14B15E60FDF9BD79089C00F652F8A7A08200AF4A463E84E48411449918C67B7BA9F3013617FA333999664F82C5FD74A749989CC5841AA9421B316BFF5E16C0071AD9846F9A2C3EFCF19DE5DACD396ACB99ED5E60FDF9BD79089C00F652F8A7A08200B21FDA68506DFBD72F9A4D7BD9CB907E3909D0E2793CBBC0209059D1CC435C9D1093D85B2E85FE2EB569022C231F4A515711195B5803FA447ABFD35AD3D0DC215E60FDF9BD79089C00F652F8A7A0820073516D40ECC872E864FE7F081EA8EF50E06C4E1354FDAF4F30A5045E7BDCA44D5E60FDF9BD79089C00F652F8A7A0820018C7594DA46BAFAC6CE3AE35B656EDCA81B06F12819F4BF66DFB84CD5DC211A95E60FDF9BD79089C00F652F8A7A08200453644983C4B10318BBE34527A92EF2E3E3130F8B8B1D343103C18EC66EE51E19EAF7D87501ED3531BD62812E43367A9DAFBEE28D1BFFEA00F8C32919F1906ED5E60FDF9BD79089C00F652F8A7A082002678D6D437A6A894F44DAE4BDDD4A2EA9E2CECD26789011A807B2968FDB35AA426E9279FE1FA66292309B039F2489E67F124390C1AB4A1FFF4756E70B769543ABE669352DF8BA54E5E1656F1643A1B753E6A6DA2C51E93AE0F39D0D2545026F01C14034081E13C9E159C6F8983C895E042920A048F3CB50724F3887E1D9F6A829BF27A9E62485755783838E81E8D3E2CE8D49F6522C7B9D298CE19DAAB3EB498D9F5661C226D9D2838C831493EB568BDF \ No newline at end of file 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\227\267\345\221\234\345\212\250\346\274\253[\346\274\253].js" "b/spider/catvod/\345\227\267\345\221\234\345\212\250\346\274\253[\346\274\253].js" new file mode 100644 index 00000000..9f9ded18 --- /dev/null +++ "b/spider/catvod/\345\227\267\345\221\234\345\212\250\346\274\253[\346\274\253].js" @@ -0,0 +1,321 @@ +/* +title: '嗷呜动漫', author: '小可乐/v6.1.1' +说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下 +"ext": { + "host": "xxxx", //站点网址 + "timeout": 6000 //请求超时,单位毫秒 +} +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '嗷呜动漫[漫]', + lang: 'cat' +}) +*/ +import {Crypto} from 'assets://js/lib/cat.js'; + +const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'; +const DefHeader = {'User-Agent': MOBILE_UA}; +var HOST; +var KParams = { + headers: {'User-Agent': MOBILE_UA}, + timeout: 5000 +}; + +async function init(cfg) { + try { + HOST = (cfg.ext?.host?.trim() || 'https://www.aowu.tv').replace(/\/$/, ''); + KParams.headers['Referer'] = HOST; + let parseTimeout = parseInt(cfg.ext?.timeout?.trim(), 10); + KParams.timeout = parseTimeout > 0 ? parseTimeout : 5000; + } catch (e) { + console.error('初始化参数失败:', e.message); + } +} + +async function home(filter) { + try { + let kclassName = '新番$20&番剧$21&剧场$22'; + let classes = kclassName.split('&').map(item => { + let [cName, cId] = item.split('$'); + return {type_name: cName, type_id: cId}; + }); + let filters = {}; + try { + const nameObj = { class: 'class,剧情', year: 'year,年份', by: 'by,排序' }; + const flValues = { class: ['搞笑','恋爱','校园','后宫','治愈','日常','原创','战斗','百合','BL','卖肉','漫画改','游戏改','异世界','泡面番','轻小说改','OVA','OAD','京阿尼','芳文社','A-1Pictures','CloverWorks','J.C.STAFF','动画工房','SUNRISE','Production.I.G','MADHouse','BONES','P.A.WORKS','SHAFT','MAPPA','ufotable','TRIGGER','WITSTUDIO'], year: ['2026','2025','2024','2023','2022','2021','2020','2019','2018','2017','2016','2015','2014','2013','2012','2011','2010','2009','2008','2007','2006','2005','2004','2003','2002','2001','2000','1999','1998','1997','1996','1995','1994','1993','1992','1991','1990'], by: ['按最新,time', '按最热,hits', '按评分,score'] }; + for (let item of classes) { + filters[item.type_id] = Object.entries(nameObj).map(([nObjk, nObjv]) => { + let [kkey, kname] = nObjv.split(','); + let fvalue = flValues[nObjk] || []; + if (item.type_id === '20' && nObjk === 'year') {fvalue = fvalue.slice(0, 2);} + let kvalue = fvalue.map(it => { + let [n, v] = [it, it]; + if (nObjk === 'by') {[n, v] = it.split(',');} + return {n: n, v: v}; + }); + if (nObjk !== 'by') {kvalue.unshift({n: '全部', v: ''});} + return {key: kkey, name: kname, value: kvalue}; + }).filter(flt => flt.key && flt.value.length > 1); + } + } catch (e) { + filters = {}; + } + return JSON.stringify({class: classes, filters: filters}); + } catch (e) { + console.error('获取分类失败:', e.message); + return JSON.stringify({class: [], filters: {}}); + } +} + +async function homeVod() { + try { + let homeUrl = HOST; + let resHtml = await request(homeUrl); + let VODS = getVodList(resHtml, true); + return JSON.stringify({list: VODS}); + } catch (e) { + console.error('推荐页获取失败:', e.message); + return JSON.stringify({list: []}); + } +} + +async function category(tid, pg, filter, extend) { + try { + pg = parseInt(pg, 10); + pg = pg > 0 ? pg : 1; + let cateBody = `type=${tid}&class=${extend?.class ?? ''}&year=${extend?.year ?? ''}&by=${extend?.by ?? ''}&page=${pg}`; + let cateUrl = `${HOST}/index.php/ds_api/vod`; + let resObj = safeParseJSON(await request(cateUrl, { + headers: {...KParams.headers, 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, + method: 'POST', + body: cateBody + })); + if (!resObj) {throw new Error('源码对象为空');} + let VODS = []; + let listArr = Array.isArray(resObj.list) ? resObj.list : []; + for (let it of listArr) { + let kname = it.vod_name || '名称'; + let kpic = it.vod_pic || '图片'; + let kremarks = `${it.vod_remarks || '状态'}|${it.vod_douban_score || '无评分'}`; + let kyear = extend?.year || ''; + let kid = it.url ?? 'Id'; + VODS.push({ + vod_name: kname, + vod_pic: kpic, + vod_remarks: kremarks, + vod_year: kyear, + vod_id: `${kid}@${kname}@${kpic}@${kremarks}` + }); + } + let {pagecount=1000, limit=30, total=30000} = resObj; + return JSON.stringify({list: VODS, page: pg, pagecount: pagecount, limit: 30, total: total}); + } catch (e) { + console.error('类别页获取失败:', e.message); + return JSON.stringify({list: [], page: 1, pagecount: 0, limit: 30, total: 0}); + } +} + +async function search(wd, quick, pg) { + try { + pg = parseInt(pg, 10); + pg = pg > 0 ? pg : 1; + let searchUrl = `${HOST}/search/${wd}----------${pg}---.html`; + let resHtml = await request(searchUrl); + let VODS = getVodList(resHtml); + return JSON.stringify({list: VODS, page: pg, pagecount: 10, limit: 30, total: 300}); + } catch (e) { + console.error('搜索页获取失败:', e.message); + return JSON.stringify({list: [], page: 1, pagecount: 0, limit: 30, total: 0}); + } +} + +function getVodList(khtml, rec = false) { + try { + if (!khtml) {throw new Error('源码为空');} + let kvods = []; + let selector = rec ? '.public-list-box' : '.search-list'; + let listArr = pdfa(khtml, selector); + for (let it of listArr) { + let kname = cutStr(it, 'alt="', '"', '名称'); + let kpic = cutStr(it, 'data-src="', '"', '图片'); + let kremarks = rec ? `${cutStr(it, 'public-prt£>', '<', '类型')}|${cutStr(it, 'ft2">', '<', '状态')}` : cutStr(it, 'this-wap">', '', '状态'); + let kid = cutStr(it, 'href="', '"', 'Id'); + kvods.push({ + vod_name: kname, + vod_pic: kpic, + vod_remarks: kremarks, + vod_id: `${kid}@${kname}@${kpic}@${kremarks}` + }); + } + return kvods; + } catch (e) { + console.error(`生成视频列表失败:`, e.message); + return []; + } +} + +async function detail(ids) { + try { + let [id, kname, kpic, kremarks] = ids.split('@'); + let detailUrl = !/^http/.test(id) ? `${HOST}${id}` : id; + let resHtml = await request(detailUrl); + if (!resHtml) {throw new Error('源码为空');} + let intros = cutStr(resHtml, 'search-show', '
', '', false); + let ktabs = pdfa(resHtml, '.anthology-tab&&a').map((it,idx) => cutStr(it, '', '<', `线路${idx+1}`)); + let kurls = pdfa(resHtml, '.anthology-list-play').map(item => { + return pdfa(item, 'a').map(it => { return `${cutStr(it, '>', '<', 'noEpi')}$${cutStr(it, 'href="', '"', 'noUrl')}` }).join('#'); + }); + let VOD = { + vod_id: detailUrl, + vod_name: kname, + vod_pic: kpic, + type_name: cutStr(intros, '类型:', '', '类型'), + vod_remarks: `${cutStr(intros, '状态:', '', '状态')}|${cutStr(intros, '更新:', '', '更新')}`, + vod_year: cutStr(intros, '年份:', '', '1000'), + vod_area: cutStr(intros, '地区:', '', '地区'), + vod_lang: cutStr(intros, '语言:', '', '语言'), + vod_director: cutStr(intros, '导演:', '', '').replace(/,$/, '') || '导演', + vod_actor: cutStr(intros, '主演:', '', '').replace(/,$/, '') || '主演', + vod_content: cutStr(intros, '简介:', '', '') || kname, + vod_play_from: ktabs.join('$$$'), + vod_play_url: kurls.join('$$$') + }; + return JSON.stringify({list: [VOD]}); + } catch (e) { + console.error('详情页获取失败:', e.message); + return JSON.stringify({list: []}); + } +} + +async function play(flag, ids, flags) { + try { + let playUrl = !/^http/.test(ids) ? `${HOST}${ids}` : ids; + let kp = 0, kurl = ''; + let resHtml = await request(playUrl); + let codeObj = safeParseJSON(cutStr(resHtml, 'var player_£=', '<', '', false)); + let jurl = codeObj?.url ?? ''; + jurl = safeUrlDecode(safeB64Decode(jurl)); + if (jurl) { + jurl = `${HOST}/player/?url=${jurl}&next=`; + resHtml = await request(jurl); + let encryptedUrl = cutStr(resHtml, 'const encryptedUrl = "', '"', ''); + let sessionKey = cutStr(resHtml, 'const sessionKey = "', '"', ''); + kurl = urlAesDecrypt(encryptedUrl, sessionKey); + } + if (!/^http/.test(kurl)) { + kurl = playUrl; + kp = 1; + } + return JSON.stringify({jx: 0, parse: kp, url: kurl, header: DefHeader}); + } catch (e) { + console.error('播放失败:', e.message); + return JSON.stringify({jx: 0, parse: 0, url: '', header: {}}); + } +} + +function urlAesDecrypt(ciphertext, key) { + try { + const rawData = Crypto.enc.Base64.parse(ciphertext); + const keyWordArr = Crypto.enc.Utf8.parse(key); + const ivWordArr = Crypto.lib.WordArray.create(rawData.words.slice(0, 4)); + const encrypted = Crypto.lib.WordArray.create(rawData.words.slice(4)); + const decrypted = Crypto.AES.decrypt( { ciphertext: encrypted }, keyWordArr, + { + iv: ivWordArr, + mode: Crypto.mode.CBC, + padding: Crypto.pad.Pkcs7 + } + ); + return decrypted.toString(Crypto.enc.Utf8); + } catch (e) { + return ''; + } +} + +function safeB64Decode(b64Str) { + try {return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(b64Str));} catch (e) {return '';} +} + +function safeUrlDecode(urlStr) { + try {return decodeURIComponent(urlStr);} catch (e) {return '';} +} + +function safeParseJSON(jStr) { + try {return JSON.parse(jStr);} catch (e) {return null;} +} + +function cutStr(str, prefix = '', suffix = '', defaultVal = 'cutFaile', clean = true, i = 1, all = false) { + try { + if (typeof str !== 'string' || !str) {throw new Error('被截取对象需为非空字符串');} + const cleanStr = cs => String(cs).replace(/<[^>]*?>/g, ' ').replace(/( |\u00A0|\s)+/g, ' ').trim().replace(/\s+/g, ' '); + const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&'); + let pre = esc(prefix).replace(/£/g, '[^]*?'), end = esc(suffix); + let regex = new RegExp(`${pre ? pre : '^'}([^]*?)${end ? end : '$'}`, 'g'); + let matchIterator = str.matchAll(regex); + if (all) { + let matchArr = [...matchIterator]; + return matchArr.length ? matchArr.map(it => { + const val = it[1] ?? defaultVal; + return clean && val !== defaultVal ? cleanStr(val) : val; + }) : [defaultVal]; + } + i = parseInt(i, 10); + if (isNaN(i) || i < 1) {throw new Error('序号必须为正整数');} + let tgIdx = i - 1,matchIdx = 0; + for (const match of matchIterator) { + if (matchIdx++ === tgIdx) { + const result = match[1] ?? defaultVal; + return clean && result !== defaultVal ? cleanStr(result) : result; + } + } + return defaultVal; + } catch (e) { + console.error(`字符串截取失败:`, e.message); + return all ? ['cutErr'] : 'cutErr'; + } +} + +async function request(reqUrl, options = {}) { + try { + if (typeof reqUrl !== 'string' || !reqUrl.trim()) { throw new Error('reqUrl需为字符串且非空'); } + if (typeof options !== 'object' || Array.isArray(options) || options === null) { throw new Error('options类型需为非null对象'); } + options.method = options.method?.toUpperCase() || 'GET'; + if (['GET', 'HEAD'].includes(options.method)) { + delete options.body; + delete options.data; + delete options.postType; + } + let {headers, timeout, buffer, ...restOpts} = options; + const optObj = { + headers: (typeof headers === 'object' && !Array.isArray(headers) && headers) ? headers : KParams.headers, + timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout, + buffer: buffer ?? 0, + ...restOpts + }; + const res = await req(reqUrl, optObj); + if (options.withHeaders) { + const resHeaders = typeof res.headers === 'object' && !Array.isArray(res.headers) && res.headers ? res.headers : {}; + const resWithHeaders = { ...resHeaders, body: res?.content ?? '' }; + return JSON.stringify(resWithHeaders); + } + return res?.content ?? ''; + } catch (e) { + console.error(`${reqUrl}→请求失败:`, e.message); + return options?.withHeaders ? JSON.stringify({ body: '' }) : ''; + } +} + +export function __jsEvalReturn() { + return { + init, + home, + homeVod, + category, + search, + detail, + play, + proxy: null + }; +} \ 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/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" "b/spider/catvod/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" new file mode 100644 index 00000000..1afdc504 --- /dev/null +++ "b/spider/catvod/\347\225\252\350\214\204\346\274\253\347\224\273[\347\224\273].js" @@ -0,0 +1,252 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 0, + title: '番茄漫画', + '类型': '漫画', + lang: 'ds' +}) +*/ +import cheerio from 'assets://js/lib/cheerio.min.js'; + +let HOST = 'https://qkfqapi.vv9v.cn'; +let UA = { + "User-Agent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36" +}; + +async function request(url, obj) { + if (!obj) { + obj = { + headers: UA, + timeout: 5000 + } + } + const response = await req(url, obj); + return response.content; +} + +function init(cfg) { + const ext = cfg.ext; + console.log('番茄漫画源初始化'); + console.log('初始化完成'); +} + +async function home(filter) { + try { + let html = await request('https://qkfqapi.vv9v.cn/api/discover/style?tab=漫画&source_type=男频'); + let json = JSON.parse(html); + let data = json.data; + let d = []; + data.forEach((it) => { + if (it.url && it.url.trim() !== '') { + d.push({ + type_name: it.title, + type_id: it.url, + }); + } + }); + return JSON.stringify({ + 'class': d + }); + } catch (error) { + console.log('home函数错误:', error); + } +} + + +async function homeVod(params) { + try { + let url = HOST + '/api/discover?tab=漫画&type=7&gender=2&genre_type=110&page=1'; + let html = await request(url); + let json = JSON.parse(html); + + if (json && json.data) { + let data = json.data.data || json.data; + let d = []; + + data.forEach((item) => { + if (item && item.book_name) { + d.push({ + vod_name: item.book_name, + vod_id: item.book_id || item.id, + vod_pic: item.thumb_url || item.cover, + vod_remarks: item.author || item.category || '', + vod_content: item.abstract || item.description || '' + }); + } + }); + + return JSON.stringify({ + list: d + }); + } + } catch (error) { + console.log('首页推荐请求错误:', error); + } +} + +async function category(tid, pg, filter, extend) { + try { + let url = tid; + let html = await request(url); + let json = JSON.parse(html); + + if (json && json.data) { + let data = json.data.data || json.data; + let d = []; + + data.forEach((item) => { + if (item && item.book_name) { + d.push({ + vod_name: item.book_name, + vod_id: item.book_id || item.id, + vod_pic: item.thumb_url || item.cover, + vod_remarks: item.author || item.category || '', + vod_content: item.abstract || item.description || '' + }); + } + }); + + if (d.length > 0) { + return JSON.stringify({ + list: d, + page: pg, + pagecount: 999, + limit: 20, + total: 999 + }); + } + } + } catch (error) { + console.log('分类请求错误:', error); + } +} + +async function detail(vod_url) { + try { + let detailUrl = HOST + '/api/detail?book_id=' + vod_url; + let json = JSON.parse(await request(detailUrl)); + + if (json?.data?.data) { + let data = json.data.data; + let vod = { + vod_name: data.book_name || '', + vod_id: vod_url, + type_name: data.category || '', + vod_pic: data.thumb_url || '', + vod_content: data.abstract || '', + vod_remarks: data.sub_info || '', + vod_director: data.author || '', + vod_play_from: '番茄漫画', + vod_play_url: '' + }; + + let chapterUrl = HOST + '/api/book?book_id=' + vod_url; + let chapterJson = JSON.parse(await request(chapterUrl)); + if (chapterJson?.data?.data) { + let bookInfo = chapterJson.data.data; + let list = bookInfo.chapterListWithVolume.flat(); + + let urls = []; + list.forEach((it) => { + if (it && it.title && it.itemId) { + urls.push(it.title + '$' + it.itemId + '@' + it.title); + } + }); + vod.vod_play_url = urls.join('#'); + } + + return JSON.stringify({list: [vod]}); + } + } catch (error) { + console.log('详情请求错误:', error); + } +} + +async function play(flag, id, flags) { + try { + let itemId = id; + let title = ''; + + if (id.includes('@')) { + let parts = id.split('@'); + itemId = parts[0]; + title = parts[1] || ''; + } + + let url = HOST + '/api/content?tab=漫画&item_id=' + itemId + '&show_html=0'; + let html = await request(url); + let json = JSON.parse(html); + let images = json.data.images; + images = pdfa(images, 'img'); + let pics = []; + images.forEach((img) => { + let pic = pdfh(img, 'img&&src'); + pics.push(pic); + }); + + if (pics.length > 0) { + return JSON.stringify({ + parse: 0, + url: 'pics://' + pics.join('&&') + }); + } + } catch (error) { + console.log('播放 请求错误:', error); + } +} + +async function search(wd, quick) { + try { + let searchUrl = HOST + '/api/search?key=' + encodeURIComponent(wd) + '&tab_type=8&offset=0'; + let html = await request(searchUrl); + let json = JSON.parse(html); + + if (json && json.data) { + let searchTabs = json.data.search_tabs || []; + let bookList = []; + + if (searchTabs.length > 3 && searchTabs[3].data) { + bookList = searchTabs[3].data; + } else if (json.data.data) { + bookList = json.data.data; + } + + let d = []; + bookList.forEach((item) => { + let book = item.book_data ? item.book_data[0] : item; + if (book && book.book_name) { + d.push({ + vod_name: book.book_name, + vod_id: book.book_id, + vod_pic: book.thumb_url || '', + vod_remarks: book.author || '', + vod_content: book.book_abstract || book.abstract || '' + }); + } + }); + + return JSON.stringify({ + list: d + }); + } + } catch (error) { + console.log('搜索请求错误:', error); + } +} + +function proxy(params) { + return [200, 'text/plain;charset=utf-8', '番茄漫画源代理测试', null]; +} + +export default { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + search: search, + proxy: proxy, +} \ 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 c2dd7151..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 @@ }) */ -3F92FF3221E86493981E0E9FA6A410616FE9D82B7088F2405DA1FF5E3DA4CF8EBC6622329A4F10547545E251D92CE0208E523028B3BE3D239A96123EEDC4AC8ED7C157CC80EC2E3A181A70E880FB204560BCB2D2BE90489208C325AAE1E36B143C547422345F38259B04D9FF32C086990A8C724956A95ACCC895AD6894F7F7112A7E9CE8A8865653E53462717C9ABAEEBB19D2C9D2D0D9E5F3FE5895B8542AEB9BD17047A1E995F537259F7A9DB3C24CBEDA4885E0438DB43CA61B8C2421F38F36532D52140E33DEA1A93658A3C8FDDA21505C74ABDADA5522FBF056F2B094156D04E6DB178D2389C73E0CDCC98636B87231801E9077C2EC540EEC7472B916E932BB22AB80711AE3E9B91B6602B6ED1BED66C4BB9AD16275AD6B3824215FB382F46169632BE5FC3B13B1D35E21F267BDCC78910C0F11955410B88413962BB54BD5C448F8CE50D01B0D46AF57C760D50BA8876955C5D9D3AF363B784529F73C30752A7F66D4BEDDC2D2B3117066170EE700F9062AA94277D4702B4E6CFAB7A89C6C7EBAF559FC2E1E3710FD5ED233289EAEB65FE6A5CEF6D46ABB2F20016055F6D89339E9707A5E1E3FE9A36A3B089BF3CAF0F5F1A6D33AC33F4E6877D8CCF7AE980A19A1E08DDD378013FC35798FCD7DCE3DC83225D624A2874768A0EC501831F1C781CF038D479E37EFE0CA02D3E2074C548C9D8FF175386AF082C58B0CCB4831AFDB93EBD59FF7D436D0BC5ADD37CD92792C47813E5DAA3B0E62CA83CA58AEF5C92816347276C75BDD5B07E649721870D4539235E39920DBE530FDC64B41E475EA35864DB72EC24382DF62D273D1CEBBDE06795C8A5C89F0582273A1C8B8B47A83F8BBAFAA20BFEE19C30D313678C1581F6553C85DE830BB5AF252D889EDF0084D5C5EEF3D0A381FF503E7CD6C0F75D5A23E149E6DAFA28BA04B7233280F6B997F7EF6FAFB334D8A5A1C1918A7DCEF34CA042D8046BAAC172F66FEFED1E533E5A12EC42C8243872CBCB7F6B628442542A9BAF5DC3FFAB465D883D0C95DD6CB0CC3B26C105988590566E2BCF10811CFC77F44B57E8871A71C17E7CA73B36BC4028D3A898888E4AC7CE3F1277CCDDEAAFC8A9D1F74C41FA851377408C8F549997353945A39590AF9E98D0740B1C9E63606AF240A8F0C0F951099CE4133E4B94D8E944652D320A1C632AF79F60B70ADF596AFDF6DD63B1B1BD088773731FC1148A6104FA9B6E64296DB07150D4106C823DFFA03826F49918B176A0BD22C7708B2E8C576E928711FBA0C4EF7D94AE56C3B930CC698436827B6273EFC1E92DB4035DEC1A1EC10412D8DE59E689070E6B2922D4C78883C584028D06876144A2F745D8DFBDE8A5AF44B9FC85B9491BAC7801D89B8020A428E54A4363BE83AF2C57A59984ABEEA1E554B2AAA2F1C09AE71730F8DD70E423C4B430F17FA34E0EC58BDA6C851CA30F5BE1EDCD212E2ED16418F322D928E464F1A0F758D72C5876E9A2F688D66FBF3BCF4CB2C21C478D11FA55F0C5BB055A00C22A7062F402AA49AC1BCA796ECCE2F12D6B715FC0C4863F5907A3DEC94354432F71B5820C5F726E2A17181F2D60222DB4FC8BF2098E2ADC23017AB7B0AE746FDC279B8A0267BD93BC3B03C6BA50E4DC82A968E5000B21B7CD9F6C0039CAC264BD8BB51F2B219659A3E06785B539BB96AEC0312B69EA13C2804687BD263486A09C5A1B87F394A2D687C4CBFB39C19999A6E43052FCCD3A88BCDC1556EC6C87AC1E323CD8F3055BA13D583DEE043F40DDECAED3CDCE364AD55132E5AA0DE957C34BE6C320C9B8826091EB849E74EEC8B5C7BD369A59601F67F2A4965DDBED6B7E7AFCDD6B4F102A2B410C29A7395952988542EF5D973B77CC76F2B03B2063F369DF61C12D8515F0B6ECFB5E0E284DB2C93AA4B7DB1830A89B9B1F04F9A7CDAF3E2933B7672A26716B96CB4F24B3122837116881C614077A4D79237C9394D5040D05544A3A6E7C17C3798CE94293872FB388AF0AF7C23D6CC66F0E5E1C17BDA4FE8A2623CACF86312C660EF1DC293C3B2ABE77E750ECB413BB726FAAB0FB16B75A8562EFB13CF8B31DB167C480F7370354A0E42DA6645071DC1126D8464039DC90B489A7935CB262F26CAD439901535969B28E7516C04CD4DD03E2EE41F83C3C8E7A53606293F8FDAB2BD922A4C521A4F8A9A3AC773A1FB865AFD55C79A874ACE0B911E9E4E6C8BE677252AB1C6C5C412E3BC69235CAE75B5F45B7A589C8CE9674EA65E243C16E6013D9CF3CF8685A88905382E8F4D9CA6F53F18822EEFF2F19C357C9764A25DC479E528997FA11890BC3CC3FC351FA98D29B019A1EC69F221CFAEF8F9DF90E6E0D3775897EE21D7EB4EEF4D4A2FA6E6F63E20F7FAED36B83FC6118BF3A8AD5B8B7B89004BE9FC38968F135451042DCE277301A148E0A44C5F8740A9C8E54CA0E36B39DEDD5E1BC40735E93B2165B150E591DA46877E303AB4FFCCE8503C682B924CCD9B6BD37B2AE72BC1B2C3D372918AFA9C4BC8F57E7C64C1CD320ADD140568FEEB21D030086A518F04F0C310497E375140A8ACE16998D7FA572922B6710C7DEBB3FEB88328C0E195EAE62BDC4812EC3026A00D460F794BC1438E9A4F151370973BCD53EA5486E9D2AB1A110DA40A22594626A368B9130980CC2F3AC117AC7B42599EFE8D7184153AFF979548E700E4D6C89244F7C8BC3AD1CFE29C50722096DE537AED62ACC29265895F6E45B5475B99F1C1733DFE42B7B5AB3945944C96653F16EB903296F4BBD023C42112B27C2F68DE9678255B698D64D355E5208858510C8C162C2AA036C75CE6180F44959FFA662D5EC0DCB47BEA7C86CA8EAAA99255BA71A0D39E947037FDEA953E5F72A49DF2BE5EC623B4B0116F81DF82BC6F8B767ACDEF47724FE58EC5284C16C52C6E58F2215E38D06AE06D03FC7DB9DCBA13FD7BD64A56C2C1582668D031F776A0A6EBC31AE38825706A62742DE8FB57D784169D7F4A74EF6DD1E8DA366D7065B862A82A8BA71C19727350F2064D6F596690EFA9BB77D46015B8BE657E23CB98B06B179139DF4CA04583B39989EF156465FEFC227B3D908AE077BFC4BC6FA3EE606D18B5B811FC8AFBA3087EFB653A0F9BC015281D917A1D6D1A9CA450220B405398F096C5A47134231CE3A4A1E8041714D0FAC9D2250426C7193C674362B1F577237C8A6F4624450CE441A3BB2DB73733B1E25EB5199969B3F09F024D3E1C068188DE5243A677B3D51108F4BE48BDB5166644D11973D7C194BC6870870AE067FC144FFAEE5D714CD413B79297BEBF9E368104B59FDF53D647F8B9CC276835B0530F323BB431BDA3DA2B04FBEE5B9DE2F4C5D4E34291EC1157A068606F2269E1B5242606B10E7841A491A0F14E2255F2D9A5CBD5A9AEF93686A0131CBA1AE015D18020001EAD5B7CF3D71AA372D15D9709232E56B38FA9DB2B3BC96EED276EC5AC01060BC88DD660D6A9DC95B94C6CED6045DC19B9E10B51C2378D37336243F253BF5DCC4E4D5D110B540B04BB7D3ADBAEB817589AA41E985EAFB414CEEB2F84142E4A56D5ABB2564DDDF2E767795165D048320E1C95EAFF8FFF21691304FB8245C8A95697A5787E0C2E96FAB1FCB9BA6ACE352D3806C36096BB1EEF963D5FAC00734D3C9B7DA04C3C6FD5D55D2A21251E30188692F2BB3A13327882990EBD0DCB0EB778238D672CDD3ADBBDBBAC81DC4F9308856D4CFF5D682B06FB2863CF49B0E17BACDD0D9D2639E1281EF81939F820D8DFA1EB50158AA1C7C376B3E4D8FF9017B37DFCFCEF075EE0690831882192B496EBE3DE45A73EC8385D447E131FBDA15B63261452A14D2210BE37AD8EFB571724CFB1D2CC0D273F4961E352D72EC24382DF62D273D1CEBBDE06795C83F1423641295232F3285B152083F6797955536079608C24E901AB2F835AE7E5D274A75A6504B35DFF4BAEF62B60413F7E1F577B748DC7D5A4E2826485404BBB98184FF497377667AC8BA5148F947B7D2273E386B5171DD2B04ECCB9DDF6F72388645848802FC1DC2FBDE98627811E6F8D3ADB253EE4B75823A0FA332E018E6170C45364F3CA1F2E27453C71501AA69542C5A06C807356B94524095528165FF1EA7B0E0CE68B412B12382837BF9FF31E0E1FAA0F7A345BD9F2BA06718E5BA63840C899F36CEE933439F22D5684F2389FD9424C34FCC59909CA6E5B54190926ADC4B893F5D3216D5C7FBD7DA5655D557E5DC5B04CA6F4DB48C7F157851E1170DFE34D5B376B09A1D5A453F8223A65BB1E0E418A96889EE51F2B02ED38574517D32ABEEC58A076D33867152E5B74A59A08438AEC2CE38C40A6B5653D9CF5957429531AFADB0B559CFBCF62C5F3D65768AD643A42A9CC5FCA701268DFC8AB610253AD47FEF539FA4BFCF985A50494B5818A2B40CFCBF8D1AB4317377953622032B85D2E1E70B8F1C06DBB02229E161A67FFBC49885701C27374B6E34EDDBCA4578C58429537AB9C088333BC05E5B74FAB8871E719FEF8DCA44AB1D8FBA30D5A904CFF0B065A0470C467DE68271DE42038940B0F71EAA507FAC974D4C4007EC0962DAF1BD72782C0C66BBBAD9327A8AF744809D52E954127310E63AECF32EAF0D11E4A863E2D71BD3C3FEC07F58928C4D42151440C241FAAB8731640CD64EADFF6EAED5C9378D3B989B5AD323CBD0C08ECC621BDF92D050763C17F128179125A00CF9AEDD8AE27AE0206BF632905E1556CE8030E78E8B44CE6102614FF7E5B3F60C00E6B9BE21DE60018A04D765020E90615F5DE0D417F88F4DFE24A8993D94727353D3665A801FA2842F02012294ACFA16B558532A10D5C1301EF6C23C89026F293E02EAD5CD3D7A0CA44397221DDD3A0B872BBE2AA7F4D63BCFDAD9542835ED9DF4A6304A1D8C9787D893B172645DBC4CCDE4CEC4C09C9ED30104961966F0A3D54282A4E2F5D13B65BB77F80745FAC6BC4AF970B8F45F50D744C6B7F39A21FEA1D64A2D1A9EC44C561E247DBE4427CE2271A347E60CE7A985DE5FECCF04657C6BB313DBF0D6E886634FBFD4AD2B2075C018A4752314C9A10150314DF6FED7CD84056D8D96A5C1683CCADED7110BE940DCF6882BC69B7C9AA62BC2885EBB5DAC63133C83A2F0C00BD5D5F4A63A3556F5DE934338C5C23F11595BF66C80124883C651F030ED98E5FB1AB7B90B9741044614A112D8DE59E689070E6B2922D4C78883C52DEA2F750D5AD5F684BE4C3F8347ABC876CB846102EE3FB32C2429A38119F5518DCAAC8F7460A347F7F18E832C8F2EB4813E8390C343B2F6E682D5F0BA4D37F56125BBEB8BBF0528B409FB69F9E0C2932794233E057BEA34C6F7AAD0CC3D6DD93D0584F9FAA534BE19017A9F06DD2F75DF0A1E537E0B2D6148D059553DF38D410DEE4803A20FAD190AC195727BE3ED9F4D7F525C4D93FAE5DA0C87A39424644BEA1E95D7D220333B70F003770C1CD59055F155DF0254666303772892BDA3A8352794233E057BEA34C6F7AAD0CC3D6DD98043145AFA4D8E9D3E5C5AC3AB4C5B67CE7DBEB165BAF85930C88F4364BCDA6E2591F59C6131E036836625E7C9DE3E17E85A0C72010F2CC8A9CF0AE1A517852565FC4FF19F36E86656210043F91ADC6DD0F07DE70A76596D9B77C116A4A1B2D51D81CF0790A2D7C7A99795CE577564726A8DD97C44800E434434897FAFF87EF17800217EC6545FA2974D031054849B779F0CD47BC023B505A864DC377F2E02DF2794233E057BEA34C6F7AAD0CC3D6DD9E30F48A84407A67C5F7BBBA7E0C0CDD768378DD8FD17D6E09B21E36EBED1E1196EB0518A845F051DAC4BBF7689E93DB0C0F38CC2F1A6EA289298CA28F5C17E100FBCA45F2876670E25D21C3BAF20F3F72794233E057BEA34C6F7AAD0CC3D6DD97F9115D7C4CFA2DE262A281F745384134D5B6266AB3F7F0A154C711702E1AC9AE5398EB0A14A661DABE2945FB4940125A8DC6851249B8423965C3DEAF38C5F7A5547F0BCE88C86BD1F4E46AFC812FAEB2794233E057BEA34C6F7AAD0CC3D6DD989BC37FC3DBA7B6EAA31C3C8BC865AEF6A8DD97C44800E434434897FAFF87EF151905065D2D33214A8729C9D42A4986F85010D2685BA52F27B12E8A10A8E306B2794233E057BEA34C6F7AAD0CC3D6DD9B6A9A297B3D36FA6984FB4475D385C34B5ECC98BA8D6573F6F5DC1AA03C537462794233E057BEA34C6F7AAD0CC3D6DD9E6FD3436A64E8AC92D6EB92E78D3FE0E2794233E057BEA34C6F7AAD0CC3D6DD9A04BDA7B61ABA36D15A1F29EB14CA512AF78ABA7327974D9F18B2906CF3780D5E44E4487BB3ED7916539C9D8BE4E9C4F7A3D8032AD39CAE9C0DA5F6F8384C9052794233E057BEA34C6F7AAD0CC3D6DD9745F606B50CA9B4127028913A71A5D7F25BD42B4C0281563AF6A1347C60D6159CBA8B72F680F565911FA53670F927B262794233E057BEA34C6F7AAD0CC3D6DD9A77CE927628B7F8EDF39C3DE8A1040A3034FBC27845D446E7DFE547C536D8CB1A66FB3722840B89F7CDE8D956BB1CB692794233E057BEA34C6F7AAD0CC3D6DD925D32FCFC1DFDA8D643F023A73F48E50C4BB1A09DDCB3B3CC9AC57BBE691CC25AA29C79C817D8F0848A610F0AAE1DA3F49136E894FF821D3B71D8EC57748BFD013AC65A9B526B8B6379E17D1D04CEEE7A437760CB8B0114DB2FC46AA97DEC15495D284F66D822BDC574881E2E3C197614ADBFE8ACAA62DBA54AB8271BC12607B65FC4FF19F36E86656210043F91ADC6D49EBC526C343C2BCCE9E1E8340604AEE784E893D5136BA855B2AE7492D7C04BECBBFE7F18B4088A6C3AA3F981B2CC2E1BDCCB835ADF0426BF23F19853694CFC8813E8390C343B2F6E682D5F0BA4D37F576A93AF75D76A37EB5AA12C8D5B1FCD5D8661F9A8DCF429D72497CA3EFB6F0BFBF522CA41DC06CDDE92D11E6ECBEB889D23A667197E745578A67320C9A9E1431B9F194F4022A8B7BF07DB9A4DCEECAE9C5649EF97BD00EE644C1C680C064E62F518FF5862EBEB6055D0B81F01613C21EA2A51B42A727B26AED434760783D23635AD9C7E21F02F1262B7031471A733BF54BC5B70693B5C07DE49205A27A94BD07EBF9E368104B59FDF53D647F8B9CC276E26A08697BF2C0B1C6E37942B2098F16ED4BA80637AB0785699DE3B509B20F2FAF17DB68424DB72922E759B4FD0EC121F3C2FC7D226F52AC80EB0A3D0875C88699FE82FA0D261BE3FDDE85B1877CC75C69715A11B99408718C4EB4DB41046C94F348C0402B0909BD52912E98CD7F8B97BF813517639A4A11C218A8806F9E90B5185C0A7FD6D16D7E7DF75BE43A76E4BAB4D5DF6FF91C3AC473ECF56C9E0D3E1CEEDF3F98CB9CA912071F05F39427ACB2E7B11D7F812650FDB0E5CE5B6216411B88DCC94599EFC456F482ED14BFB907CA1D95E144B4410676DC45DDE7A0E2099FD07178F8D8C5E27E8E48DA962D5B7E86D33613216BF9A8C606FCA2DCAF648428CCF5987304BA8C9F0650D6249EBCB3147D3F5BCF02BFCD35F535EBD2E3D940B34F4404959FF7E9D54B3C620A28F6691211BCAA0AB97E57CE3CF76D1E573CBB3E813E8390C343B2F6E682D5F0BA4D37F5E26E8C434553455043005FE24A74BD2B2794233E057BEA34C6F7AAD0CC3D6DD9BBADF90167BED043C26F779A9ACCD8434F4404959FF7E9D54B3C620A28F66912D45989260C8C22878748DB383D6DAD7E660BDA6A4E99569029600B9A54E04B662794233E057BEA34C6F7AAD0CC3D6DD98398AFF818E5BA3A590AF18A8C86D4B4E1DEABDBD88A069A86BA03DEF75F8879A98C2FA02DAE5B74D1C8A9A99BBEB74A5C072B714A8C5CD55ED38D3AFB2D1DE8D1ABB19E7EB63B6B1F9A1A1B887C1AAAA448354F312A90DE22CC0C544E3008A6430F2E8B341272F6FA1C1E13E821AC2132755BD917CDD2ADFB882825222FEA6B5C105CDCE387E7E975415311CBAB7CE3D66351ED93DDC1A29CDDD995A176B22076953AF98549FCFDC13C2060EE15AC254358330AF37862378491A53A9C169AF931C47A500DC5DE200E7E6D1AB79F25C068CEC6B9ACEC1A7110F6750365E4051F1D44DFB0D0398ABD1AF262E51C5A5C962E315B15003C6EC7AC2A19F5F8CC8DB3973657683030A276486734307725BDD8FDA2B30D77218281D0F48F07694AA14F16F5D1C5AE0158BB9B7FF7B5C28BC7FC7981C03387FDB02926C92A09A57A0EE261146DAFF060FC10F5511D3131F7C9188F09D5CE8C76C326C7F34C9F76FC915FF48FD0E7BC838DD9FE89049B208BA72BC783C60E7C0012FC772165B5F0DAE38A9BD9B9A3252072B9181CE06D76D5DBD568BBD9E450E90040E69217E49D0FEA77FD26378E970BD39A27BB382D9F4617A9FF41597DFCECF7292C10B10DF8AB39FFD15908245FB6092E1CD9CA70DDFB5D21D7506B5CCF1837E6DBDEFDA744B469CC96933C7FABDA3F61B0D93E407DF09E2B0C79D3C9AAE39605826A8A58FB70538A89487C2B443720E591861F1366F731055A112CEA442C3893F598507AA99D46FB2677F98A4772E2AF1DBBBD8B5B3BF4C75CFCAD6D3B2FC57C19EFC11C355473E10CFFA465CE43D7D9B5AB4FA3431B7BDF459B395815FE10502A12CCCB417776F8F9E4A4DB11ECC008DA8976B0D131D95734E177A4BE8BF61D61DF1B0894D1472578E3BB108FA00881673268F55A9DEB9585B78840FCA20C5B38C0B00BF44A443B060DBEAB897CFD9D882C0B1720882B13532A51148369E0D0D86B63C79B933060C3BF7D15BDB873AC552E07AADB0076322DB450B90607D1F0942E7ABE156C2AA1BADF8F44058E9BA1A12CC5583ACEE9D8F7B3566EEF2999F4CEB3E5EC21C9649B159D004962DD953FD10C4405CFF9058DE8A907DA6C4F46D6F819B90B6B3E8F8CEEDF3F98CB9CA912071F05F39427ACB2E7B11D7F812650FDB0E5CE5B6216411BBEC20D0B820340516A19E8E9A585527F363F9DD57F9F3C6A3A3AECE3F58C0CF6A0D93519D3B19CE957DDF3A8F0E6A1488A73B903EFB5CD8D41DED8E04106944523CB2AB43392F85E3FE53A33568D36322DAD25E72996D241E570BC14586C5D9330B2E659070D0D1136E41314477324972B7679BD44E7079CA4A9466296FD14E5E7C24F40342D12B5028362FA74ABC7464A93978E1BD384AA6E7DDE0A4C3DA51116E85B2DE4070B8DC4BBC6F0F2258AA508AEB5C2EFE5632A2AD10CE95DA5B09563022A9FF225045E7FFCD11D7B76BA4C74D37CDFB7EEE7C670C8500C442D90FCED000C497DF9164E357F4D44A1ED9313ECE9DCD313E05E6208DBE36518D755372794233E057BEA34C6F7AAD0CC3D6DD95D89767442BA897DB0068686241E148C76E21E06A35EB58D771AC039C929701D9D384E54A388559067347BE6BEAC47E4103DE71ED34D643C0BA6802EC2F41D05634763F6DE7B23199D042E7A965959E9CE3B9714D9084CC02F6D14439A48A8F7CF31887BB6A54D804DC3FF72A24412FABE1B9DB91FB2DA11829FE9F0249DA052813E8390C343B2F6E682D5F0BA4D37F5A648F9ED510FBE70E31EE00E49D8A9CF7DFE93D9071A6376A992A2BA811812938371FEB79FA7612AFC0BF26E996B90858349D66E3174295320FC8964C1AAFEF912D1E48A91E6EB37BC0DD2CBDEC564495BC1DB660CF4C6535896A22EC3E647DD76E21E06A35EB58D771AC039C929701D05A713A22624BDC051C4D8D547BF817D236FEC79DACE71B0CEDC37FC3ECD1E0870E4E32852F155DA7EC4D275BFED0954C72FDF8EBB0108BFE9E0059FC1B51BD2C85220F70D6D8915885C606A258FEAD7C3BDD695B61FA3323636BFAEC835F7EE2C7A12367C5A48103FA3D158EFEA827179BDF44A8A2539A486577C018AA42C8D4200623DD88877C5FD7B6CDF29ADD14B0C328BD85B09A5E35149D4967AFCA00DC351566F9755A6E4FFB2B9E6C34C90AF126A37581AB59B182EE357F8F514EC973E503D52A27A9EA2A114CFB52573CED6F86750293336F4DC6DD1F1FE9162E24FD70DCFDB07B881236F5CB4E98CC5328602C500ED9474CBF316C8AC2ACDB5C9A93CD5EE664FFAADAEA27A0DEC7F458A91221B6030A1F28B022EB78FB41EB692CA6A8DD97C44800E434434897FAFF87EF1767FAFF1347E244B02428819D846BAA37114C9C8702C7551CCE3E7CCCBD20885AEAF473E30708778B09C9CCC3402E76689C74456A07391322C3B4C7F5FE313EDDDCC4420E6EB75A158312E1D1E6537F5F48FD0E7BC838DD9FE89049B208BA72B70195ECCC102DA4C8DDD203F7DB9D14BB66DBACAFE2590702F5C7006C3B9523BE80EFDC3ABFCCBD4C5ABF1A79C232829E68BE4FDE6B4D35B8363BD90C6AD50D617BDE53DB8F374B0D24583C60A63F483372D19D5C35D9C289294DDEB6BEBF9BB14280C04778684A662B1A73DB3AF45136002CD048052CEBF698CDCE8B1F07E0BF48FD0E7BC838DD9FE89049B208BA72B26D4AF0CBA360ED78065EC49D1FC3814CFE5D139AE3CDF962CC65188CB46DEBE30B91B71AEECB7020C030FCD6860E58388E5A08808756FDD719828033C4A283317658D4A3E089CECCD5129448A0DCA6C1741BF6FB616AAEF42C5E6C824DD408FDE06870C61DE9081F8ED97E6D9FE7C9872F6299A68FA8CBFFEE636A13E24F423AF3CF8728A8C6F33EA12AE4A210D3161933BC03D99EFBC6DBE326B5AECDAC6AF3471936FDB9E4F9FD475AB5B7BBAF5E4FA8CA4B72A4D3F5DDD256E405978F3C2A0D93519D3B19CE957DDF3A8F0E6A148A2422D2988208C302C83A4FBE2AF11765C10EC414DE9A2DDE7491F0FB5D1778A273E386B5171DD2B04ECCB9DDF6F72388645848802FC1DC2FBDE98627811E6F8A14DF48C78E078A017C11E85CB5023490C231827AE5DDB9C75591AF4559ECABE8F770738C896DF7BFB24BB9E369B6DC853840F170B1887B89A4A4F3A67BBD2AB994CF08D27D0A14C3969F6559D7476D65822D6506CC8357D724927E396B6E972C870AF2A79966AF7E3B61E53CBBD8B620200D562E4685D82D49A7F7C569F1B2C90301199632EB339171D1AB276D173F2A30F856D5D0B5BB055442F436AE9A19629AD21C19052A15B0862F6C135ECE7F25FC8646C05B8CAA6B9BFAB8661BBA7268A75515E61ABC5A96D5EC3A5C9C05FBA8E199BCCCA896888E9AF19E65878746DFC440B1122A9FD634668BA25A338CEB230C63A95DD62F9A34989CED9464667B44D31F726D477F38BEA2E2224519ADC3B5CDD1086DDB68E9A81F1905B839FBC5EF54D82AED3AD5312EFC65F9C9614179471BA6FF7BCAF6123E880DC45130429811371B01BFE89CB59F5C0584664FA6F466D6B6A75BD58A5BEE786F84A8AD5D544F48FD0E7BC838DD9FE89049B208BA72B564E03A439602BD85233B9E80DE849E7BE298DBD5B2A044074C486DF6B9497DE192B1DE55C89CBE940C1E1E7074B1B2B3CFF59BE3362D48A47EBEF23ABECA2C38733EAAE1D664C67D280E6EBAE5D41D6E077C9706AA9B8528F89DA1AF495C2B6813E8390C343B2F6E682D5F0BA4D37F565342E37DBCA26C7E0268BBFA356A045573719FB496D682CDC1E18F9487569073BBE891509E72CAABC21AD646E70ECE02F841FFA391BC9CA7A55ADC45F51DD9122F5D34A2552866B435F1081C8344C26D50AC33521BEDFAE564DA69AE91D4868F48FD0E7BC838DD9FE89049B208BA72B9FF86CA711785442F9B4263BEF02C95B2A8084AD2119E5CF545CFE7207DAFAE346A37C1585D15AAC66F458DEE29B48485AAC3A791B81D38BD2F378AA27543BC7896C120D021C3E1DC23AE2B2A4D03C5922FB5043AC110E22A0D61610BCCEE8489668E34D0E7CA2B0414FE1B32FA83CD28DC717C2EBFF9E5C87018A8BA4970D87D2D38DB7DF7E2A9E8884C4A00BA20510F7598330F8B109A2B51E90744F5FECC529050B982581D8F38363B6A5CA4E6B6F64551964BAE27FA2817AB6FE4FA5B5030A7CB5E39D0625766EF19EFDC0E95550239A1B9D254620CC1C49794A7C5C83BE8F9E58FE701D9A56E823516444D84E533F129CC8469073A2C7F71D4753A7F0880BAAD1E067AC30BA6AF9C873CA59C28134B96D8C071A8336C6B0E84B27C966F52CC551077D010B1F32E4D93FDC74274DEF3CE6D85FD7708031A6FBEC3F071C293617FD29689A31D06407B0A6F96BD67E58EECF3F62C2579EA14EE806A05F4F8FAAF8F2E298311F4C23CB77C7356038792778C28DB5C471C214EA74D71F04F41DA3B0EDD66574AE8D897B9FA70DCB13E56047D336EE0C8466E14E58EBB0F30C729129251C7BCE96BFFDADD475E9ED1497561E1F2480D8BCE431E2BF3B237BF19EDA11CD955C5CCE7F620893C39C650432A103F57525D19E1C07AD276B97F1D8705EC3C0A02FF04FF1E9E683EFD63542F1129853BDE17BB74EC87209593DF818256937DDDD0656E9AE6A492F0897C1D39E76119A4D38594A16AB3E782E7A28CC4CEFE9C2DFC8858F95A6C528BAD426356C3E2E1F793E2911CE3B079607D32657CFA9599026EBA8C119920C6927E90FEF98B7231491461078E6D4B33240546383F6E89910142664B814E4BBAE6A49D4D83FA9AC393223539B475A50DCFA05C1CA33A8EB8A05596BAD849347C0D76667FCA32839600EAF6F2725BEE2B7D6CE612D809FAF8675C13B614F6E4884F3ED2219E9817BF6D18E6CBDA25DC061FD6C108A3BADA7AD769E11E6B2C64C84C6B81373BF11F324BB0362D3A153C86B01BC7E6EA24967F448E360C322C207AD55DE75BE6CA6E592E72B84CDD4BB886701780EC67AFCF9CD5C6750F078B96AD0D5021AAB2CFB105E05F2B89630420F96352DEC138E275ADBD4A2BB5394A46E401AE7F9EDDAF7A0433B57E5FA267A2CFF4A0B415035FDF648AF81FC287FB7B0707576283823A78F2FA68AA3AA6202BA4439A774B6315B8B28E2E1DA1ADA290844D76E2E60A4D811051B42A65D87DDD4E4AFDDE42790444AB69CA29EAF2C267CE3617C18ED11AFA3412DA57935EE74C2AAB10942C74D2B01CDD38FF70D2317EEA57BC12D4D54761F02919DC3AF109CB5CA752CA349A15334855B6EC6C785AC0F31F0A92C9CF9A2671845634B666A6886D35A14BE35E3E0039A8462107B7BD1E0CC5D550B3B280C0837246C4CCA81637B9F88FDF2A19160B99524EA9F43F89727AFD866FC586B16A5D210385A675A9E9B0B504BDBB114B38911831611FBF9D7C34BD4061353330FFBD004D95C55855BD8FE01A1E0636D045ADCE13315A0DABA378CE1AB6D75B86F4160642C9FE89BE85AB3B934F514B94A9084902A1D70FF3424D2A686C0DCBE67A0A81A8748FD6397C4EFE6253C695DB2229E9DD913360D06FA2716CCF91A7433A49D6BC0A9DADC384DF239B285416BF7DAFB607C9E52BF40B69644E9596BAFFE08688A20E24ADB2403547CC4B5F16734B20A69C1A9FE9C50DA2EB02CC72E0FBBA3CB528B46AF27C4BB8D0EC34976144D0BDCE14B0BD5F677CA655CC3F4EF507A8479365E8ABB96976692CBDD0A94C80DD521B5C0D7CF84D1CC3F0FE92772E73BD6B6677042B2F0FE66F3EBF9592315003714945FF26056243CFF9C91D68D9137D20C6204700FB52A2AC8792748FF066CFA29AA664E646C1D074BFB86862297C2BF3B9B4B989A47BE6A687B8D844009B4A80417FF330C93204DC4B5CA47FCF042E1FB26DB3C8BA1339A0ECBBF580280598B2A2A52BDEDA7C916C62CD4D6E015FE771908E8B0117B19042792186C79A10B80754B6664238F097E011684F86C570130E5653DD29B061C8FC7F437B0CDD68AA321459D8FE6731B8CC39CC49B20D7AAB684EF45DDDAB0A47EAC91E95EA63C7C24BD90E0300EAE09DFA51ACB4AC08C63DC96F61D49692CBCFC8DE97B30B044556195FAF6A9D4FE4ADE085BCB2251207DE0CACF0C9AAED4A23E62DDD21CB196F271B4196F56D8BFA8BBBF1B718A495AAF96102310F5C02459BDDA76A7AB5027F717D0F3BC6AF9C1F643D5637431E01B5E3CCF44B853F1F435DA7071B2F763CFA1B3060641E0A34B29A0CC32D795558C8785D8F4AB169738114382D024E135DE4B6CE607D61634F1DE21AA3D794C8AEA29D5CF20C72D1D1C2BC059850FEDDA51E214F45B7D247AEFB2415C02A88DE93B965E845240B855EB69B3A1B932B47A784D8033588549EF6D24F6CE57086A063C645F84D0FD681FFD6113557A7BF670E67E10319C29CED990D20AE80E23B67C5E8B8FFF9AFB0DCE0956ECEBA02E44F2C443DD75BFDBB171F432A8D054A08A59B8395C005E2C25470713AB3AE09577D392407E3D16B5301913400E1D90557BFBB4147667F81A1DD8103303E338C983A15C1C57A765336862F2A1E2742924D21D8DC941A367C22110C0FABEF846CB24AC89FC5F031B8DB4AD06BB1822C50BB99395FFA484687046B3509890DEEECAA862313B74B7E2D3ABFC7E10C3A278ADD99B74B663FB5EC8AD48FC6DFB1E34ED028C74ED0A4A6A78BBD72A7D800403C34F1E204372F371813139CA5322C4E749A312D58E199BCCCA896888E9AF19E65878746DFC440B1122A9FD634668BA25A338CEB230C63A95DD62F9A34989CED9464667B444144ADF308CC1ABD82B5A7AE03C61D6DCF8EA882A316EFD4E24CE6B825FCC7F01C7D9D3BCB2D8F66CA4E3314715C61A6968F48122705D38D74BC5D97356B80B14C1E4B40E43DFAD7BE9542CC05DE4D5852F2FA8CB8C1E571DE91AEEEFA3280A16F274BFB3A60F642C13C9C87703D2FD37DB6B3A3B409ABAFB23AC21CCB98A1E381A01165B7726DCBD65E5334F3D24FE07A4AC4A86161AB026AF9934455EEF72CC2DE33B110B602A1531478FC93CE1ED0DFCE60A8116C27D8C7F2EB42806B6BAFE213BD5C42E9DF49B26D460F11B130E33005735D2F9E003CA4439462FD1C787C0E94A18CCA8D7726974031DCD1BF3BFA7176FDB5797D500F3136D46C2A045765B08C59B07795456788BEC9EE19BE389502366EA7D7F143D280EC17F182B9D94655311DEF70FD3EFD148854C528166022D36F4DFBD7E68ABFB68DA917046959A01F0A548F528BAC4FCE12CB355312E162932DFA81032D42CE1E22B5E5E2A97023920AA2531FC538CBAFD47C67E24F684C10FCB3F5F7962D27B5E82F379AC7036FB3AFC6888B88495AC59F363C344DFB0E6B054BD1BDCEC0BA17FD1432A31F24382BAED751FDE44BF9F837ABAF7065C20A0A2562B6935801317CF5BA128BEDE17FB1E484A81469B7025A5E8D201671F7D63AC4BDED5F70F271E44579AB98B11769A304D165BFE1FFB77EE5109BDC04E5D42FD96462CA08430D5D87F46112D7C5B309EA3D9522B72E221677E2B8702A64FBDE02CD242D6AF766F31FCB8B4215C0F7507B8406EF654C620201418F4FC425CEA65AD3B98543CC50380733044E77E5A15D6F2617E818B130745627EA915EB8C7C8F5A26C93339FFE9B2A8687D4A425ADA7290438C08ED681C14614F9AAC8A1CD002EE6D404A81215EC78B9ECDD513C16125BBEB8BBF0528B409FB69F9E0C2930CEBD9E5E919B1547E7D6BAB039D02CD8CE090EC7895D560DFF6C4374DEA1E873A5C608A7D72EE12EBB58A72767F1EB040046EB9A8F2049416CF5393C4B58836CE0E2F920BF599142DDBE31E513E3BA145AAB6443AAE7F95D4CF9607E753DB1CFDCA5E75C6C6874165D76BA6C498A8B9D8F5A9247CA5E6707FFCC00655358C674C00ED905953BA8C932DFC6CDA7492A53E8F257A8CBA1749111F7E24C465AF5B660A848595DEDA438E4E1C1B7AF6793428EE42A7BEAD8C78959290BC6F2A5459AF203CA33266E417EA8BD3256323C5B08671E29B41CAD8B1B34FB5616512338EE1DD932EAF7963C660625643D52A4D4527DDC5C4E2BBF59754F5D36B50E4822DFBBD8A07A9B2662BA154025EBBD12B20B3BB33EA1EE3E09998096B2F25F6960984E20A1156447FFC269B1532EC6EDDDD3E9201140313B946B9F89A176DA219D9BC14209D809A7608B0FB77BC6F78FAE1977C2561EE31E9A3AF7BBDB7C762494146C4CAE3810270C0AD5C0A5E480065460813DCDA6C2613EAFF51CEE3919C1130EF867EC21F3B9B6AB1B573F5D3C3B4B878066729BDB9171FC46E00A65FBF58961376D552248A4C08646C9C030677C59365FC4FF19F36E86656210043F91ADC6DA8A5F6CE01D0D80DE1C070A4DD4B6243F0979FA78198474A56FFCF3C6BDD196726552F23BA1324DD185CBA1947A9893D954F76C10A6D1029B996E64A57CC58A481C4B6C9AB6A8EBF4AA48061E13F55C958D004A15E63C5A48524385EF84DDC6FFD790CB01B5D77A178BBD6E34111760A428D45C405ABE15B4AE0E447255753CCBB107E6493A8633F962A2C5B9B4C6DAE7AD45983355C9A78A9A7F4552CDE3665DB9E5CF3C76E55D066982D464A705BD24C95D9AA84A2C874A7FC8BE5EFFDD9CE7D623B8D604E736ADA8E5660B00A1387DA25E59667C085DFA398CB17B542DFC61F23AD458B6A86702218291B13ABC7DC8BED82F87330530B1DFBDEF1C074C55887F804E91EBAC257C886B85847DB48F9AABEA05FEF4378E25A17F77D8C366997239DA199DFBF9F39837A1984EA1F889B6DDE5FF73A5F0CFC5CF8A5E9B2574765D526019281A3E14F80B16CBE274CB30F9013B9DF3C165BE835EB2F92167FFC60406132C2ECB5033F941418B48B613EB220360E06B6031643DA2DA3882C36A71286E1E0F2DD08B2C804F2A2D8022ED3F6814086E0CCCCAD8E4CCA5F411EC642C9D78FBCEB57CADB14D5F0BC3A873338D5A648CA6F260D2ED32C1226A2CDBB264A82C017BEE11B4968F31365A87A4990BA90E386B30FECAACA2B00F50C8D5DB2158EC8B5BB1507943DFD05F3A41EA16E9667E4F5050A6E14EA8A5DC47E6C9CD9BB579A65A7D48D05155567038C261433E88F491485736E4A8D53B87E495C643862E44515A43E2EC341F26DF4F4C1440F50068EC0A2854681F9165F2274696EE777486EC561CFDE0F721A78329EC6E2169326552F23BA1324DD185CBA1947A9893D4D74C675E45A335DDA4E1471B37E07D28B965C6C1CA049E3F62F4BA46E238673239DA199DFBF9F39837A1984EA1F889B6DDE5FF73A5F0CFC5CF8A5E9B25747654BC5B70693B5C07DE49205A27A94BD07EBF9E368104B59FDF53D647F8B9CC276E26A08697BF2C0B1C6E37942B2098F167E251B7DEF4932242A72F83579A9988CDCC2514663A54EFC9ADD51DF3F9DF0E0923741523909B73C4ED3349C1F226E8110C17E052AF344589E2C96778E5412205BB6EC6C91CB961DC0A647FA4ACFFB830F80E6003596D715BDBEF5FFEFCEA8324C34CA70C19A4E0644243955FCEDA1B7238A6AB95F9D0CA10AAB5BCE5C862CCBC2534D5B87CBB2E8D42355945521B6D798FBC7C588642E07C966C3F8AAFB080549959FE899FB1124972F7DF8E9791B0B9CE7575A4729C107AABBB5692F18A96CC3C13B22AF29DC4326CD5DA37363DD813304ED0C332E1D1E8F2934F33FBED86344F7AD4C5AC64971C12440FEF67DE92413D0B6116E842456E7C9191BDFAF2ED6053CA5DB66AC3C21C9F01F911C2A30AD75F8FD7D71996ADF5B4E8F644610B9190347BA3C5860BC420CC9B971B7BCAAADE389B6DC10AE3DC2F3D2E26AAB4A2E2AB378ED68781D5D3CE4D90FEE9898F89E811F30E5AEA9A2E0A42194177B8A1162BAD9D4A05A8084106B7336A3FD34CEC5DBCE0CC5782AC693F7454B94A335C0ED1C1BC5E6DE5E9DF17242F0D1DD4016F73C85A68D65675131BA2E044BB2DD94558FEAED3289C75BAD790BAB69DE6624F62A56C667C07E625DF7646BE7EF6B11C2824DF5B4B4D7E27664F77EF84F33A2782762DAD12C0B7FE907B21BDB728EC986C2EC3138FAA29330E8E28D3A2044AC56255F2D9A5CBD5A9AEF93686A0131CBA1B2258E5EE55C46FCFE2BC53EAE65EA3F4A3B94C587FB9888390ED7B8ABCC3C9133A8681A20EC06F5C8B22B0C1C4D6B3DF458CA2A48014255F574B60CFC1F2A55F1801A6373CF956D3F7E818090F12BD23C256C2D21CA8A61216B34223B2D5600510674E5998F58A7259B0B87BF75D54CDF63842DA7945D51335B9DA72710585504053455FE7FBFA4EE1BC9E57FAD722E4A2D1A9EC44C561E247DBE4427CE227132AAE657A71B85084AB30B8971DCAD30B12370F47E5731EEC85F502BC4BD61FFBDCF396A0B215A817FFA0E3CE73AF3E277377A7CF659D37DF583A1A72A08AA6BDD8DE7A1FE0914560F9A3637949C2E4C6789EC930A08153DBA272FE608E0A6032A0CD31D85CD4D73F1C9879C1DFD93173A23A548BABEEDBBDAC05A00CB6E14CB7B82E64E54111D72DE2D79B35FC1838D0BC3EC0AE006E555BBA3051619EF131409EFC80533ED5C74CB5919662BE9D86C43533FD280E8A619A890605D0DA1508CF724C5BE602393686A689665538498D0CE9F0F5BE836514EF13119B9E67ED12AE4CEC4C09C9ED30104961966F0A3D542E3384329116EA800FC86704D6B6AFC07A5A309A3A0614096B1FC36489B108572CD33A563919561FF724AE7BA67BB48C508EB3C1959016561C3B5ED18D8EC2651F52ACDC993BB94C8F4128FBFB261515DABFEE47D7445B7C7515A9A2B00CAB7F4D03F820921CE8DFB8478D675D74DBEA78F60EB775C6667FE544CB746435E3CAA575F8D70F956447FF322C8DD5F00A787CF2C0357FA191E9AB61249CE1290D7C13E2A3B9CDE41D6D29FE28430D42F1C3ADC894447D2A5DFA8C8FCBBECF2A0FEC02E8656EF91431033936A76B94BB33A67967419370E0D9E373473A0992992BEC881D1D0140637AE3C669F77F1D1D52715AA3EB1AEC584E0E0B26DB15750C612A4AA4DAC1D33EBEB782A32182F2D835FEBD8FEE6E1771C30D84C0C97AA479A5B0BEA8156D8C12968FE813A501762546AB42DFD3F48D45055E33AD3B45AD2D1FC78A625AD7130EAFE269FBFA6C6A3AEB878F8054225DB256C4FA06749B03028602D6125BBEB8BBF0528B409FB69F9E0C2932794233E057BEA34C6F7AAD0CC3D6DD9CB81DE96E8CACF69DA7878DD1AF5A0039905D5A6C20BFBD0BA2E3A2ABC7CE72272321A01997190ED6A0A052D9A8A96672794233E057BEA34C6F7AAD0CC3D6DD9EA793BD428F9922095A1A035B57A30662794233E057BEA34C6F7AAD0CC3D6DD997AA0EC33C3101596FB7945FF326BB6AF48FD0E7BC838DD9FE89049B208BA72BD06169398DB3A1051090D92E3789A7D3F2211DB2A6FDE924EB21420051BB21592794233E057BEA34C6F7AAD0CC3D6DD92DC72112848C19E45EFA865AADC81CDAD61B71A90A3D59EF287219B5E3D8AF2070840F4C21E9A7DF3A7F14692B9CFAB917EB9A11FC8B162F5EBCF26B6D7DA277AECEA545801B4F78D4A41C196D7EC74917658D4A3E089CECCD5129448A0DCA6CA0F758D72C5876E9A2F688D66FBF3BCF16B67F4C31ED6D028A77D5066B619AE661BF7BAE0AE0C922089595126B64A59DF81034D9E8EFD073A161B19283F79BDEB75E6D4B64D85DA1FC26DC9FE77CC389A13F6A6492CD4AD9994293A90AF4DB2C1578AB23A206C6142CE70C94EE27A25315D66A63068A06291DBFAE1DDB8084C3F8ACA414F1607DD5B6BEF8180C36B289468AD94A65A0521CF19548F954D3F6F20823699168EC1AA88DD581EF9D0A984D988590566E2BCF10811CFC77F44B57E858FB84B3117A37C621566FAEA88B0E19BE96E021139277EC792438913E096B1B1F411C52740527C6C02F68ECE03542DE5FEC6499BC965BDE0AF73B8C3DD1DE8CB6143E0C48F21DD239CB0E2DCB8D2050A47559D857E4DBE3A0986CC7692B15260376139611B3F438C28566817F354CCA54C6EB0439BD6513DF60CB2F5DE2A8887CD1147D1C04B9A9B92D0DB48CB809FE8984BFC663DF1458DEF1957E53A226F9C38816CA8CBE2A2A3CF0322F71DD300284408CBF780E667C32C3DC85C358482F5D7B301E4A0C281F0851A001D9A981853D325BBA78C2E1CE1FE4CD0E4CE1BBA0882008292AC1EE6F49AB2384D934DAD3A58A775D36035DA3FCBE68574C2AF0C18A3CC5F67E2C0B67D9629B1110F6B791E6A7BC3CACBB9AFB28E86246D7E2255025A3606984B6EED2595C6897A9C734587B2445D3CC463986727AC9525AFD899B9C49B70620E0CF8B461B36FEACB97083B190F35128B00CABB57B3D6F6498A323175533A7FC1643FE4DC70DDB704BC5DB861F7B8B7C7007DBCAE78DAA4EBA54760A606954380AF93513D3047C83852623A0ECCFD8F3D96404360146D87BAE04EEF2554005493086A63C78F2DE57F1F1AE99881D987FE050564169C33C483C0B5987DF2E87CFE12915F2C61F1A0F65F06E16B67F4C31ED6D028A77D5066B619AE661BF7BAE0AE0C922089595126B64A59DA28E6D8F1EC55BD717A0955F4E88BE4F73A0E832ED656F55688252AE61F17DCA488FCA38BE1D84DB6DE3109144689A797EB247DA801BE5B4C06609D78D5DA5732B002AC7F103D9234BC3B68C900D985AFF3FD03B937ABE4721E110F7DAD7DC89813E8390C343B2F6E682D5F0BA4D37F526552F23BA1324DD185CBA1947A9893D6FBE83147927D29A51AC35BFE60CE018642D578823F5C3810168A8A32D0F1C512794233E057BEA34C6F7AAD0CC3D6DD9EBF0C03F40688A88E56947935A79713784173AD5E2CBED757C371AE0A213EC0AD8D9FE1B0EB218ED75F4F8F458E699A04048AAD306213A5775867DE1B1624DAE6D8D96A5C1683CCADED7110BE940DCF6882BC69B7C9AA62BC2885EBB5DAC63130D7A284E1C7A5956611566E8C36C768A0CC7995DB89A8FE814AA2D1D6557E751081AFD3D3EDE17839807267BBC50FC6001E2F5BF94837D4ACD7E9D619C4EB3D598966CF407F44A233A47C491671F585BFF744448116CC8D28149F6860A2343FB1751C9AD2DF9CCDB7C25B4650C2842A5EABB4E83E5F651CC9CB5718E68CDCDD1281C868F092136A1B352F5338F98B0DF19557AC9174512C73489667094EB1BCEB03ACDDC7AC6A00FFADC62D50F4D078BBC072AF2218EB9447632EDB715574CD30F88EF6ABE760E9D4EE66F163525653003699E9E9584C36F7E3EAAEEF12EF862F0E5C3A2552F398B84356BA149AB679BF81D64B5678DF263952207D76A7C440A813E8390C343B2F6E682D5F0BA4D37F5745F606B50CA9B4127028913A71A5D7F327CDBFFB614AE2933CD76176C6CF1B740D4725DF14B57C77A034D9B6E16E8FF65FC4FF19F36E86656210043F91ADC6D47E6A279963998E448CE647307AF6A53054456B75FBA1BE94EF09197DCC2A13CCEE86722B46704B1EBD8D7B5C67BDFB8684CC14A80B9B3425D33484F540790056E2020022394E2BCF082A091C13E4706C2FAA0BC437A0CE63BF5A2D92EED172D198FD913298DF56EB179F7E92C416E7265FC4FF19F36E86656210043F91ADC6DBFCF4DAD81C2B82614435D6A884FB660E05FC21D26E3C3AB39C289FD8827B843D63BF2AE56BCC2208170F2C1D0C00EC4052999ABB9D8FD9772717A3BAB2DAF65E05FC21D26E3C3AB39C289FD8827B8431DB199CE19A582B021BC4F323AB78719F8B069007106B5291F47AEEE001215386A8DD97C44800E434434897FAFF87EF17F9115D7C4CFA2DE262A281F7453841341CDAC90B33D0F8412B88469730FAC846E5F7A2509C2AFDA19060319C0073ED6AB8E0944E0C3B6B50BCFD29CA22823A8F66895E98D97E513EBBF2E96CB2D91269F33B18B56E64B541D10ABC2EFF114922B002AC7F103D9234BC3B68C900D985A51A0CD36F183C4DE446FB974F2907D1638538E2F04B66C7D90FA9C43D108ED4A9BD9B9A3252072B9181CE06D76D5DBD5EB4A8FB6DAAA743792E13839926658B1FE7864098C4CABE4187281C1893AAAA757F94EF31CDBF211846A0196A8090A438A2723ABB515EC9674685B7BEB06F85121E3C7742EEDC1351EFE12AAE91990D3CE5BFA3543A1996564A2BECA0AFB5A4665FC4FF19F36E86656210043F91ADC6D7908D22D397614E4991550ABD1B6AF9521B473DC8115D6E55486968E6196D4C4471397DCB6DA52CDF4EC9E15FA44C5A1409544ADDE4BA63C03C122109625579CC189DE4B2A5D763B9977C3ECC12D7FDC6F57C7784D97203D7F73B52A22AF6F99A7CF784236E494A717DF3C7D14F5C7B7A8729E281266B1C8D69DB5411C6233C0A758638600B0343814F00891AED6255A0F46F99F74A2CAADF510DFD363CA02DA123BE6CDB8480997FA41045D9FA8C6E465FC4FF19F36E86656210043F91ADC6D66B75740F68FBDEB0CD9E9236833B8782B002AC7F103D9234BC3B68C900D985ADE2E4D64D13016011372B93BAB0987B2EF4B1126F48B3935E87ABEB1F9A77F1967D93806866E9D1B04E7002461E4F01A19E5761CCB5578C5581CCB27C903C195F48FD0E7BC838DD9FE89049B208BA72BA8A0335F3EA8FE1965E458A8C25B96A28AF4C69E1ECCAD33430EF88FF87E181FF5CAC38C5D91D209925E8CBAEA361A71FF36F3E2C16F068781CF971EC777676044A66602633A3720F9731F64340177A98271340E7FB43734CD6D709DD34303C3038EF2178E27B4126671ADE212850D6E35B7BC274AD2575C3CE36331E958E0E0BD46097CF3D70483E462AF0E4EC01692CA1942E97AF31F49DB921CCC680CE3D8A90CECC2B79ECB257CB47A43D914BD679D15784FFD84835CF142FBCDBF7B7CF73FF9E9F0B73DE0FAB4105E2D1B989FB161CE359BF3742731C71D25DA3E6C80CDAA29C79C817D8F0848A610F0AAE1DA3F21C984EC0A806A47820BA205777B48908F56CD1AD5EB66BFE27BD16CEFEC3D4235DD5C60ACB9FAD2FCF8799B0C6A87FDCB4DD3C9383980CBAF77E041E44C6AC7F1762859DBBF684F1019A82C43EE5D5EC86FA317A9CC92D4D1DFE4FE7F8CD7CD2B002AC7F103D9234BC3B68C900D985A92016F71A4F4F81258AAE1B4198BA0B117FFF99CD4B37FD0305379B4A5937F34EFE12C7FEB90727C4EA0507C8E6AF8652B002AC7F103D9234BC3B68C900D985A63915245910C0E9036F52B7425099240DB736881146C46721F44D481CC53418B4ADBFE8ACAA62DBA54AB8271BC12607B2A5178E46085547D51717BB24A51E1A5C42B6E4165E9F29BFFEC35CFD78B6FF69058176F74BDBED36D2730BCBED196C9CBBFE7F18B4088A6C3AA3F981B2CC2E1BDCCB835ADF0426BF23F19853694CFC8813E8390C343B2F6E682D5F0BA4D37F58C814517B3182D1379F68F44D58724EF0253D1A346A541838C442A4705061F75EE65F2F935E30228D1D4ED9CE0E6237597379940CE76789E9B7452A6A016F5B1712E0C0B10A646C77002452F1B202FF9CC6BF6C0187736DDCDDA2058C7BF57C9E1415AAD33964CAF24166F18DC0E4970E5964C3EFA4B3DFB5101A4C3DBD828EF0BD587428908794685BD38B8D1BA54CCF8B069007106B5291F47AEEE001215386A8DD97C44800E434434897FAFF87EF1E78FE8AC3AFF6BF5142F0E73519486A473030FA1BE2E600EB672D12FB3DA46F3CC6BF6C0187736DDCDDA2058C7BF57C90701AC07F7331BBAF5BBCF7B3E9D9A94430A9C8250AB10D0F10C40183F9FD12600E68E49035E328F930A0B1BF2B3F7DBFDB8EC3E5B489390E8A9D844E2FA3FCE34B96D8C071A8336C6B0E84B27C966F57C17FE74CAA1B58CC2B09074505AE9900F88EF6ABE760E9D4EE66F16352565301EE7D098CB6A1A9B56414BD69EBF0E640F5CE4E6E66B88ABB8F19DA021E682F99E025E563A658D0AA71415B01D56C56B2794233E057BEA34C6F7AAD0CC3D6DD94F0ABD8D6B7BE77A9DA0DC326E848FBADA167D95BE228E3771D46AFDCA4B78AC40D4725DF14B57C77A034D9B6E16E8FF2794233E057BEA34C6F7AAD0CC3D6DD940802DFD61C8B5D7FB6D92791153877F9D0D0C94330C04F4D47C0E09AD35AFE6C1990FF8F3B0703ED547A4AA985B1BB47800217EC6545FA2974D031054849B7795A628C9207AF0FB8ADF1D7559B03C4D9FEDF3026C2EA963C3720D7169097FAF8BC8DA517126CE28FC3A17310E4992DB7189F2F9B6321CAFD1DABBA80C5D487F2794233E057BEA34C6F7AAD0CC3D6DD933B7672A26716B96CB4F24B3122837119D9D028B6E06A6AF6231BEE8D1986CD5801098D1E49C00FDE0CA5AD30DF80B10730704B3B248D00CA86AF665CD1876C714AA496288D1C33B429A9A43675F591C2B002AC7F103D9234BC3B68C900D985A4062C28825FB702549F43BAF71DEDFACECD527A492C9871DC8E5DB1AC0AE46FCB90681E2FAF82C041F6846E0AEDA998F19E5761CCB5578C5581CCB27C903C195F48FD0E7BC838DD9FE89049B208BA72BDF13548F9B82DBDD4169E1EDFE483B7FC3BDD695B61FA3323636BFAEC835F7EE98A2E41DAAF2235F22294A271F37E37F273E386B5171DD2B04ECCB9DDF6F72389C1104F749A15295C2299CDC6671F31037A1E16A9D32EB00FB887B5B6099345287D6DD612E60F6B3F613969176CE87D547C8BA09B4D9F146F36A378B3F6FD7D952D92C815E46BC5E4D74E412C198B96ADCC2514663A54EFC9ADD51DF3F9DF0E0EC11597A19874360E416E30951137EA6B378ED68781D5D3CE4D90FEE9898F89EE64F32D3AD2132BAF7F8E616D14329B8EA373C7DC6ED22696F2544481147AFC2ED4219BEB9FEDF2FADB51B6DC48C70974F90CF931CA38C2932355E10B8CECC521ABE11F61BDD217CB033FDDE1858E0390DD781A59C0B8DBAC3DEC630A441C94D8EE59A2925C1FDA26EA938A2A1673E7325482938766C0298D7501EA5005832557EE5E0DDB5D63C43596BE95E40C62BF48AA5F4724214B25496F27BC1370DC01C2D0CD4CE9DFB4C36429BF4D16929F03ADDA98DB8499B79C7566E37191B9DD9CD08CC4036FCC0DC5DCD50B8B846224378DEF1EFF5729A7A2FEE84BB95795FC5BC2D0445A820F5E07442AAA6C531D58F412DB84F7057D554843D635327C0F4CFF1516A98E3284D18BAA1CD1B2CE384A7FEB17CAA17D6DD8F32432B3FB21BA66493E3069B15565F1C8911458BFA14AF16BD3E6CC8B986B60DE53E0826BF219109128E5D289F4794764C293B09C108E13517F76816D5C46B574CB36D789CB496688993150871F9F5114CA6CC77AE8E9634C1237C928902D6D029B84AFDF3E9E2CFB61CAA5A685E73BF1A0D51483C339F6E745142818B067761E74CDA7106F40BF22194B8AC0FF2EE6D0CCC819F6219C0420E6723AD0C033523150D0F8B3BD92E4C6C88D7D4C694160E551CCD828F3CD24CE0FCEF93E282DD8DA217ABC9B766A61F6E0752ACEEE0F23CA8304452B51CAE43ED7178714E3DB434950CF9324AEDF8668C3EE876A15E2B4C2EE89148FBC7148F2AC49A4B57CFCD3B965DD7A0CFAC70CAD5603DDF3F6DC77C41979BCCB0BF0C56942FA29F14530B41DE02568728C4048D7FB996FB41A167ED5100A3B19A415518F70F2642F42C60BEC6482E504B0B06752B1A334A74D1E477852FC9C8F9E7833B4AFBF00EBE4379B32E5C0CD3D6AE8F58B9EB831EDAF569550EAF1A262DDB4A081BE8026E49CD4369DCA5C2155B2A1FFA5AD276B650912C5F2CB48B43A8E7FC44EDA1D7F3CF8A46FC9ABAF51D2A7E0FD0FA06818919FF19BEC8E817B254106AB4E31D37BC32CB80EF3D1E561098EC818C9901C00117BFE8AFEFDA13C5F278F8DBB352C5229F49DFE74E3541104CB871DC6654D08DC555F67DE1D6A5661E0CF35056CE194FF20979AA1505E30503840152EF8DDAFB5B4DC0332DC861064750FB0D880CDF83229CE0E032C492F6BC4A80FBA50E6B4940A878C661E1BC208CA3B0C511AEAF66EF7D739A404BFF334AF77F75B4B9F3E91FEF163892EF02251B4762AAD33C2260F046F5E3CA8ADA19F14188D46BD5630C8B58DA8F0C2AFFA55921C20592B31AD13EB9327E8E04468B08A98643F6A33D29EE6626799CA6BBE34219851F651D9707561F9FA958A944CABC1927615D13EA6FFFBAAFAED344394EC538E3975C5018DAFC46811B95B21C569FFE56BE750507D580ACB9088994B49AEB373C6BCDA58BDCEFF1FEC72DFDA01B9580503929B4B9DD5EDAF4152CC784B3E765C620542B48236F9F89E90FFF6C571639617D5E860B6B9BE6C2279959223FFCA694E261107DA1604430E863754F0493985D5F06D9F6416C9BC25C08095979207D62293CE872A9D127571ABA6E3D89A32B74276C02B51676EE052277B5352E006C283BE815299A413F53C4A676AAD6EC22EE8772D957F5FB9E6BDB70FA17561D4E29ECAA3822C83BD683C3BE7A1B4FCF397D7B8404E1C9198F67DBCAF6E561AA0673A84640D2540C6964E302E4BB20BD904655382D3463F6991E6E91ED2AD06CBC8CE9B553AA622860E6A22733105762BBE62E1E819F8B884E40ED4AAB4E6670544449B69BFA0F88AEA2F66B45C0258744362802493945B6056C92A9817A4AC4DCFAA239259247E9A1DBA88BC7208E82A2803DF43BC30E5A4620B15FD27508E0CD0D63F97C2C11EB57C4C4950C58D01EF916C6ECC9E17E429E63DC5F40053A20394CC905A7004B2397C90190A138E014A9B6187AEF4981938103E1EF65FBBD2E2A06CBC090C3029EED358878734BD83B9D0252DA2E942C3FABB7668599FD9D6243A5117AEF47A7DCFD0A2DD8840C2DF0D3927AD0D9D8D281912322653B8DB39A89A59011743D90285D13B42C260DFAFF8AEF7CD44F1EAE3B8E4390778FBF6D0AF6BB4A189BF012E57CFF36628A7CA9B827F0CA5287F6E403CB97BB4FA3F5AF13FEF2923CC4A798ECE38B3D142B855962E30A24E449A6362E5285995FC53DB3373187845462E5F33701CEFD119B8DD9EA45AD6AA04F2CCAF2BB676CCD7FFFEA24AACDD85C4E5DECC6BB87C3ADA386DDFDABD448A5AA64C632031737E670B6C50E3738C94F4BC88DA5C24FB76746225785F0BBB2FF3A1ED0DB6C0F26A48FD8DBCDC98368AD40C4A6A8E1D9B1EB604F91A978A445A54321E3465155FCFB1FA11A338C8D8CDD45B05D502C97D1F3DA57BF7BC0A9C78836D3A2A16F240074C8E24FC97E6E3BDC41A653CF2C5392F9D32DF4811A101226A4099C83B81F3170513556227B740B67D7C968F6FC592F0C86D7DD7866770AD8D013E4F9697C8F2B23DD30736B9C2DED6AFCFF8E6F6C1A9D5EFA37F4CD0F579F639866167C6ADEA66DC7A96C45A2D1D9EABBD434F651CE10602145A69195E4B54900A9849E471DC18826C39E003D4A9B2B8D40C0AF612B456E08E6D8E3C74B2ECB3436987F15571CAC565F4693EE198A75A77C7D846B8932C7832EF7F2D0239A4D1F7C7B60B44E1C061945BA68DC383081BBF36F9BEF724C53BF4AC507E12DE65A846E6FFB2140A8CF466DC76D58E4CD6AA6D9B93DCAD694E9274922FF2DD9E62B77969428001A9BED6D677FB1EAD28947630B64D6D9C4321A649757AF2E2DE6AF0E09EC75626D296729EC1F2961D09A909605AF1CAF2F55DE6D03F3670253557B42ED69509551C85F135DE8432B22263ACA1259B664F567EECDC932C6F51CB1E8E3BB9E68C08942E19A5AE9AFC9125519805CD738B59143206973B961724BD34D255FBB1689CEB5BF6F44039C303087B4605BAA964D635A21395944834FCA41EC498B390B50544F026DD4796FF7DC555688835073D9085FC01458858073263369D88CABA7713F48AF22D8856CE1E61B11315F4B7D737D266314D5C2B5D898EF83D489D214D322A277C0A9DB24B816D63D5E31E442CD9BC9C83668050637A3C73589A648E18FF4DFB8FC25C34C89E84A954CBBB69B8C024667DD52B0099173C709AB06627D3DF4121D1921A6F8ADD18661BB61AA226DFCC4B87E3D911A74935D22F25CDB187E1A468CE47DF5CB69981377E435B10C49A9CBD633CA80776236446CFA85D88E2F2ADBA8587C0485C314F59EA948C593A05B8057B9EABD71902F4B2CC998DC884C184CE5BEFBA413077CA2D9C5D1D0FC11EDE191466C689CD0A100050BB2C80FF2817ACE11A8990695F563EDD6221DD009D5998E9A87D88D940EB395DF50C4F2E16282C92D21EEF68E3234D2AFD1AAC748A258A60713DE15BCF30E15B2708A333EEF208BE1B7A612F6FFB14F1DCB06DA7A241936755E6437AA9621C330497A93953057DDEA63A47CC2FAC63AB86979CA17237DD43B95603B0398D22D3091B9EC9AEADE51E05D8686AADFC776B9F5A1E80010334D91EA21DE1CD7A7B9A3FBBEC3EB96B8CA1EB4B3B20F6F51F9390514FED5583FAF94AA03F76A212FB3B0467F279D86C9C17F1526193533390BFAD78819A5F2167CAC410907AC344BD4EE25C45E56154127D71607A7D8B2452EAC67F51095DF9A6DAFB17F2C58D5227F9E0B3619A7131060E47BE767E65A5340E2F1928666039F404454B8EFBE84E313F20971B32278EBD7BB48ECAAFCCDF49EFB1DB2D4365AAB99535DBE163508C6D653FC056A650D0740F030B8B76F6048D12C64A96365E1D9E5C98BC5BE28CE5C4CF91985F03184FDFAFB0005828999717415946B9681F68540F8C80241B6DA7B83FDC2946BFB89446D3DD45BF098EC6E0AA52F691D3F78554D75006A5EFD8A0275C006B7C6EF56FCCB074359FC9E48232780252633BA77F0B8B244BB97A163ECE671F3850E40F332CABD80468AF983EC099D20F0639E7BB50073C3CC2144A7A64C57EDE6680DE6987C9D86ADA012EBFDB8C6E6A162E5AB1FE3599F25E378B97BEB161FE3D217897223C29FF4636E963FC06CB48AC3C98B4D907E5F08936C088AD2D7C0166A6C058D6F1DE54BD2F897E8AE40EA7C9415F69DD693CC1DAA06BB6CEBA729F39706841A0912E141935B5F1AB3A19C8D0826FFB8A3DD8AC2B661E0727B76112390546E1B8FB62EF415C5CCB7FAF7E811F45FA85279D119807C8F11E1123460E559CAD8BAF960708F3020517764470E6AC7E18F0B252449D686ADB3AABA740B429D66C6A8D1DDFBAAE73CE7FC8D57C2D7402EA598DD5457C2FE511AC0C555BE30552CDC5BDBF53A6D5916CFA616317FDAF9CBE16FFB95124522C7269C3375889FD39690866CEE328AD19CACDBFB24A8562657D6411941121CC38651B44BB97734D355ACD40D2AE21A2E6E30E174625189BA7CE12D749F8015DF2BC510B581E7520CBB075F5F988107DA8803AEB603AA3FB79FC45B4AF1DD86E44261656F71FF24B85F249C77C6C968FD156759492579EDF02E2854B3ACB618F8BB07519BDAA92606763D256C1870E4705DEC77C941C595C914E85503CB3B797CF11AA49E5A372CBFF215DA73592F1ACDBEB181CE64AEDE16A5EE52F43147E78F3B01358863ED432FF269BD1DF32D641C5F1604E41169618215337A5CCE6C0877551286DB7342B06F2A3E28207F956B0DAA98EB58A3567C784D5A5E37EDEFFB7939826D60B7AB0BE36759A87666B01362D5AF13C56057D952F482050343AE5632C35ACF19E6840598414E4F2CE2AD955BD501FEA27D7ED25C52CBD854B35FE4B64AFD59CE40927D7282E9156F4AEBA712FF06C82D14CA118F474FD9391E26AED18C31C00A8826F4530F9431BEC696AF8A922E202AB594178C6AF2EB5D7C7E6D58530A33B58D7EAF823565FCAD683E88BF7CB47F8A1AE8D4E1860F804A372153D34FBB46004AD202D1BAFF7CFCE13E63100B3587C7AEA221939C2ABA6EE04E3C46B9575594F6D98E1D678CBB57FB35795293B2E1FC5710F8E038A496A0F59F760C62D741AE6FCA8138C4D0F8FA68CFC403BCAE0C9D3824E14913EC725560CA9B6F8FD850A0E2ED056CB54244952E535ED71B6FD0C89C0772054BF8F0EDE3E3B0FD9B2B24C829E8224222AB1FA19A44A8B18FB88F7D3289B3233A4CA2F2E2446AE9E1BAF290F3D48CFD8D7419070C93F865CBD1A2EE452F9EC9DC0FD324A5B40E21BAE836CBE2E83BBA5F00C89B3CDDAD0C88217DC4683A18A4C0475DB47204C70C00C8F1D4A0F59D294939F4A662B38D04098E94A3947FEDC7A9AFEA01B11C69BA18A3F5A7B7CB3D9C4677E37F4E4F856E489C0A45E5C231FD853AA16990A78ECA4AD9212A6277BFD02379B363540419AC3F88CC7869087098AAA6F99C1D9509B5BA305997DD91F4AB86B69E0A71A9CCD94F02F6993A52BDE6D26001092B7BEC4D16DB0F84A82902B3CF098137DA243A9EB8F72E03D919DFBD37702C25996EAB844AF28E66EF88E14894A0EE952FB277A260F3456D3C80696CD557C2AB51DF28769F8D833E40D3BA541B26FE020EBC9A1D052C44DF9DB9AA55FD3318CBFE4BFF954E1015EFE3120996247E777FD39617A6CF33B23888088FB2864E259B6F98DB25682C0016D40D482CEA63D87DA7F319DCF25DA2D5FF67F5836A58E54F82F4B34B56F9F35DB0E8E133C692725877A28151B18F660638109FE4C15A62057BEFBD2C8A0CEACF5D831A95C695CDAAF1283E163C183472EDBAF4702A25992280C77C8CB48D36F13ABB6E084847FFA771FD32EE0F283789DB47D4333B01A74E15B8BE99D9F10373A61E60473E80179C98DB72F1DB0654010F04028385E2B9E6F3A9519EE09293555E2A828038209993B0FB4286DA1A9B3F7E1196C2F4AF36183E55B8FC2ADC1C5E3E3F9CCA93F9D7BE16EBE7ED8AB66773F9433C4B988CAA8A5DDCCAFCE8640607E205DFD16A351E724CB46F7359CB5454D64329608D9244B87D149E286A6A50FF9113F2193E6847B06FDDA5F9EF3164C3A26D5E9F140499B482106BB534E630FB0AF6A6BC1AE948FFA182E4F1502E459342695DD07E8B6D1C96262FFD476891A9C3E8579E2E60074EDEDFA17E73ABBF5F61D55CAC55C4BF07EDF51345096F0CB30D0686F6C7CA7F64FC87486B49352D16ADEE836BE294E039451393A16DDD0222641AD2C3F82F86889E57FC7E8886C3757C4103CA0982359B488024E68ABFD26BC089BF47A8812F1554519156F7572A872E7E1045AE653ADB48907FE6116D9E255FD09AECBC760E6F728C18BB7E7AAA8EF251053D76DB224034A6440E90138938784AE779FDCAF685AB1113024A3A8B8204B95F99216BB7C78F8CB4BD9B0333FA478F65C5948F8D8082AC678AEEE75F801B1DDF4FC9C2DF762C7B45B118B50632CF119B0E582CA91D7163C75EAC0FC9E9B4145A1E1861A74FC787FB39C0874B95C53FEF051B6DF6799980B183EEAC22D6B62FB768D76F9A56F5FF9D309E5889F2B1AC28AC6D084A4F46A1C42C5B3C62A4C5F40BB036FFFB04CF4E8B0CEBE465992863D1FF8937758097FA8406489E672ACE7E6BFD6D9426828522413AE4B8C65F566FAA3E8A8CD1449156EE432AC47D0B55F2B4C215F9A9548AE00DBCA6F0048337680EB59E556CF13948B4C7CF1A9B97C6D2E9FABAD35A8BD94E2E77D1FAD89DFB13513749F22B3B9CC864D105563AF31E4EA757C6281E168D46C4D19700D1574663044D7F65B9A356714CB5E2E604919D7180F88CC3F8D0C9A70BE7BCA90EF5783847A390DD98A3A856C1275A7D5B9B6A2D1167D8C3C07A805D421422B72ECE223F08C2701B0BC8FE1C0646697A238BC2B597D866432BD938DD171D7FE6F14836D8694515988C0E198710FE6DB27E705A1E72B80B37C49AB460D3E3400AED944AE9901AF9712C2D569D0A25F735DB16F82DABF694E687E308C4CFD12275737909E3951B778C4742AFD7CE34C1BE26CFF7E95708B4B991FDDB3930117F7904156C00034BCA26221F6CB1BD74F19A91748BCA40519A3211139015CCCCC9C3E8E9565F39549891E10651A5479CBE50CD951CE9F1573966CE124F24E7C07430B769200C2C807880069C1A4085C7DB8E3DAF19BB569CCBAB32627A52C17EDF0161716694A5A93AD1281D40D201A2ACC9361342A11225C3CB2C3CB5DB630FF6B421D468132729DA09E4DC9EBE13F2C863EF3C1FC5C29C327D7C0042DBCB538F0C089C1CDC456A4838CF09EA8A5F5389F800E5D069D850F0A2EB749A64FDA5B4FBC42FC78D3F797E5278CA8F45F01A0F26B288EDB0EE762CF106B9DC73E918D5DCA3449D3B6F38002B4C6C47694C730B46FA3B333EBE0F5BF65648F2F9FBB1A92A5EAB841A8433044B920775FE04B1BFB9C86B79CE83D577CF1A6BF56F84ECDE18DC8E4A2B1C237DCE35E3CC811DF853D43B094B75FEC3D81977AF6D140B9F972A3B757D9B7A8A31F980C0B80042C14B44F44472AEE9C84CE8AF0AE38498E86219058757E7626FA10C0382AE595ED91C317AF783A72FFBD111B74887D8D011493ACA6A079FCF87699F6AB9E1C5FF74AD405128A206AB963A34E0FBAE5E684D1F9ABBAE310781365830A6FD6E17FA57DF6B8CAB9FD8693D6C9E77FB59CF470BA9329C8670DDD61135A1EF9FC905E6580EF056D676B40D1F56D2AB470F5C8ECEFA787F11955DC70102F46CA1E0094A3F8B30C4EB0DB9375108E6A30A3818E9FFE1FF1BC3B4F667DDAF3B14C12FEF09CF11A1B81C5758D2B39B33FAF356C44D8268FE6B5E852D433B458EC61B21C465B71B6FB4E735AD4674923A643EED49B7D722AF0572CB790EAA246381AA7361C58DAC7CFD02F73D9DD029192D5A7A1DD5F92591550B73EF6E510AB8BB42BEFD43A572A7F8F79F85B6E62CA640FD9AECF6FBFF71D067FD86C0BADCA230993B44F59D877929E7F686A37BA49CC8540E9B26BF97DCF5DC7A6704F373A050606C22201CDAFC77A2A9E5AE3323626EEC573D01CC3E40AAC327B9FBFD50E3B89192E0A08D18CFF0423B524FFA6BABD23999E35786ECB8D2FBD511A94455905CCACF6CA3D389B74007AB9920146636401439F49FF9CD81A5374286F6880B4A766E45B744247A5C51E7056B9ABC20EF09EB296A9C379D518DD54A0F95B5198119179726AE85FDEE9A475E828EE4878E9545A159C940FAE33BD99617C85DC14E68368E83DF39A0BAB552591C2CE27BC597EF7EE6E023343D8A80FF2ADB7A13E18CF0DDAB40FFBB6B696DBEF1D4F823037BD19D25FD79C5B84B002E13039280331141187D2D271DED365B0E4BC0D2361F73888A884A35F0958B153C76B93C87CE5256C3E85561816A17280B988425C0FE135036BA2C4A35D6451F7CFF7FE1AC25D88A55E9395A65658A894169BD32808E4FD57F3FEFA7E08363EB2857A1EA6A2442B2AB1D893396FF65D26FFF940C7D90B494B215431643AF450B9448B4364CAFB614C757BCB72A54F7A79BA31C8C655423CC6C2E006C283BE815299A413F53C4A676AA372868F901FA131972345B3ADB3A1C2C7D688237B1BF7AE44FD1C9698B443EC966B9548BE4148E5BD9A8A184706796BAAD4606C3FE073B942792979FE56AD724DF757EDABEC83B4AA2D0CF56B8D90691D09DD564AB2D722C209D53D70A307065A70C4B6CAB585CB57C76D3441D5D8E6B83710C79B20EA2C611554A3A30C51CEBEF9E3BFC1187D67D6ADACADB00F86161DE19BD57B4003D16921084B3717CC26281F417B0EFCCF8D2BD6EF69C64255F20B80119DAC37E1A544DC33D9F02E38847F696630DBC58911B95B1587D04B96929C2701CB8D68660C0BEE4F7536343EE9F4629A174138B40F43B0AE32158D38BA38D553D4D54645D0E5462E6168B566A65714120CCFA1FB36157C835E6D3ACF439BFED61291DDCC8638BCA6BB430E71BB798272B0FE23EC88CD2A2AF395BC5093199627ECE3A572F77641A99F55DEA51C2ABEF0E129A32AD2B2F388AD69336691D3C5473291A98138A439750BC8E62C225E110535868F99257AFCAB5928F58ECD43C4273EA74D7A0D4944864B95289A492561180F8D7D68AF27295E4F0C35A183E24288A81C9122C3D73D0B2A2FCECA82B5AC282D3267FB0C36C9506392018EC20655C1149216CC6AD3E987769F3070E069CFDAA16C0D7D3465A8418D9047E520D38B81BDBAF445CA73DCBC84E98F719F31CBCC613C73FCD52D29AB9ECAC17EF2CFDCD38E9047428D18D1EBA4886D4222C61B605CB8AF07E0D1143FE0C03A868622F57CDDEF37B57AF9D90BBA10B2A2DE228522A4BA97F0E13B43979BFD1DFFC12BBFC65B8B19C3F35DC29BC40BBBC31C00FF8D21BEE7BEBC536AEF6CD39C7D037F178376940811E87DCAA828D560F79A72D88991373AF472B2A8C04168E08B5F7F6CFAEAB3069696E7DB7BC79E3A6F6E338F06D9680A6E3BA7EC00D32DE544CB33E400CF7DA2766DB2FCF3F7896BC4B00759C5516B0D39B422AB9C747A1AD1096FC79DB4E18547E4FB7E2F18AF4D29A58759E428952075384068FD6FB0A8FD1D0F07B16D9D41F3BB38A752091B80A2BFF4B995BEA998AF707557FCC93373F24F7F83BBD1B19055BC3763BA8D9F8902DD78D7A5EB599916EF59A4519C5A4006CC5F3E964004657B56915B4B3945D39582746B3FDB791B3BB5E935EF576BEA26B53393539331E113A6E8D32F94D96E1C2DF8EA74BB9A3BDD014AB45B2A7F86BFC0E91299A55BCC9CC0344B9D681C7442C329246D62A0993E423E509D372034C7844F6A42ADB873C51E6BFB28DB6236C7C9F7902DEC6BB99B3B787B563B47425A4228C694449F977992363CD7B068D9C2BCF3D9A29E628DE28A0EBC860A01341133D6DC34334BEAA1ECAB05304F7D9D935BA0AFBFCAA4D3009DA32172D6EE59EC4F1E2297CFD26253ABC8F877FDB5EA30CEB3FF791B763C0529C46B5B485453AC720E979326A456A2175DF15BA5CF64457069BB76D90F402A72E145D12C8D1BB84053457F6652D7DE062EB7F3D459E5485CF4A224A7B7A41083A0B7D875E2F8293270FAC6928816BE66F35185E82CDCAFF589BD7741E5511567815DF5294227E56D5949906D5476C73CD9183E9A0AC0357DE0D27DC7710581A13E99C0B07D9D1148AD3519B0966E134D353441A473C48C8A9451D90B5473977F15FFA6765CAC50E7CBF362875E475D769F71FF283071512581173D9936DD1396F13CC45DB9B685ED5917C58628EE2D302776C24D69170FAD3960812ADF5CE1BE587EC3B6DC047634D3421A66967E51DD34A48D418303E7AC90219FD714C746121EBE25D5F23107B2BC7456D11DD3BA5276E715125FFC6E41CA0CE23437AFEBE8CE8153F9269DE230A75FD0516FEFC4D306D5AC51320D216AF2214A6F28E7353BC13BDD2905BD0C66DB4C6D8F82D2DCCAB70ACFED1AC4549C4014F90EBDEF7DF8212A18AA36BA78FA19E34382021A6D3751BF83850FD7B5F2635681501E21449F38530BA3239A3703261B08A7DA8F6CCF4CC4CA2822688E5C1082A12F641292835FDB29B28CD7EEE7550E7F6081B469729183FB5563A7DF9E012FFBD200EB8EAA663B13F226AB1D9435A7EA62F3DCA88C7E42FC0BBF8BA55EB8BDB5166644D11973D7C194BC6870870FC83DB1FD1990FDA825E5FAE675964A8F081618050188DE359B195B1999CFE52143D5AD6E4997F1B7DCBB3C027F261E19FE7B3D6450D4C780C971261F26907BA37D2F2A3499FA743FF0E93D8BB0791DC989C98C61D4457FD6A02C3913A596C4AAF785D8741F9F5CBFB8E77038256AB5988E870C27CAECC1C56C84511502332D6E77B25D1B1C552F26C3ED9A36559C99D7C02161A399B03A6752B20F539DCE00D25FDAD0291DB39F34EC14E4A86F74364CA9E1B78DCDD4DA6F0E719498A2317A49CD825DE57C33E9EDCEEAEDEFEBA7BEE9FF63BDE27260D923388C6A50806EF2684D09A2B8B518192FDC99C1FD49E5543C5F3E78BF6CC0C6971FAE2D54F6275323382BEF828B9BF8F7241850A55DF8249D2E4CFF800BB80E25F43ECE258E076A9634DAAFB1B4A57BCF1CB01A79F61AC182AB13F1B21EF3CEB2E53907C584B3E3FE667DFE3A004B9542232930365B2C057310E9BFDB628F4822D09DB66D1DCCB5FC43BDB53AD7AB85FF0D91DDB11FEBC7740FA535FA3AFBE9CB46E1B284D73B8F4E861B68A482F12D4F76321A08CE0D998D2FA0A2E158D798A0705C185D8D4AEC0EDEA966EDD87204C825CA6B44FFC2D9DA8557D6ADB2ECBAB595DF1961400A9EE10194ED6A4DCE16BED39F3D9F0E36F6B962CE5DEB1BF939D3DBFC557F55FED9C6587DE4834C13BC28CF8887C982F9171FEE706216F13E089ED848CF0034B225E9A9CDB75AD6AE49AD865F2FB06039FB84FC8A6D2C83A251530DBC448FD43A579B652893E3BE02743F301E5658F615494C004827451E5658EBFCFC87BF1DCF55FA9DC2D6D53EBB1E8B11AED235D19E693AB8FBB6A8F0948FDDE0D7ABE79916BEAA95E2F4DB1AC765A4E42C233FFC3AFF2A2E18C07381A6E47334F4D46B72671271D945D6243A5CC3E2859DF104D1B031BFB105E05F2B89630420F96352DEC138EFAED0AFBFBEA33C08979B3EB390A459B5FA5EAF913C4AD2A6251720DD6428624994E1BE7991C23E2884EFECD652796B416F274BFB3A60F642C13C9C87703D2FD22932D03FCADF86FF6C0CFE632DAD1F71295A6A82F9A52C441CCF18BA51748B8113CFEBBE6B9833153E260F576BD4C809FE7B3D6450D4C780C971261F26907BA37D2F2A3499FA743FF0E93D8BB0791DC7F3EDCB7E9288828E762E84B302377531DE10A18084FB75CF7321DFF45F1E53CAAF7B6C31038B16C7EE047DD3E641DB062C366B36081833BE21A511EF0751BF89AC5BDAB8D47B1EDFDF00BF589433C210CC5A3749C9F160CB64CEA314FBDE613EFF8C38A64AB71BAA4419FBF131D74E9897729B1751C1A850365D7559EB1BFD94206547113A6D9C95737EA1DDFEA72D8E681A7FBC76DC061A0642AED748C2A041FABE29CCE0C68FD689F432E3846237CAC1F49B2C0668BD5CEEB436560643AAFA1BF2D99B5268A437A92352044C70D538E4C6825FF6412999E4865938CC68437505FF9C22E9E7017C20AEE40CA835AB731B8DA7410796C6ED9882F6AC55AF932A852E2A2C03457DA377871493DDDF9F84924E50671AD8EF35DA44A0F4367B4F9A7E67820C765B1A3A5B28411910D0E1C5B8AA0376487A279D9A870E745EA6515AC7FA0E43FFAD8A291074CE559233BCC6AB7BA0D98818BDC577EBE3DBC58DC12960CE6B6AFA9EA655DEE9BAA1B6736889836775C32DC63ACDA6E9FDFE9ABDA9B378B29A1E421BC9DF381AA2BD7CAFE981C15E81D5950D524E0FC654A928FED21111C885C638D6367CC82E4586C27C9EA0D7898640209CB7EEBBDD6960C0F66963F33149431CD86EBA843D8AF6378190C171808B96E15280C215B264F14F81523A0D561C3D63666CAB5E63EC00953CC6BCEB76356288BDA1718C052F4E30D4B35A0F429B6C04AF1728FAB86CA4702CC87105EA25E6923345A076821698EB2F82B229DE06E63F0D7860E2A6A55772371E4D2181763C4E5E37EAEE0893613F8C1DB8CC7D3F5710C8EFF6AA792324E82CC498E573FCF3D137F70858C9020B7449A1E24FBCF133E234712738883E140BBCB614FF28CE7AE8995211B65BAF0A751345128B7BD5DF19979E9171A61320D65227B58A0F372E23D75F1C88F6738C80D5A7210E820CCC8597FF3A8F5204287BFE987A4326ECB3C3861FEA037A1494C3DECA5F5E830FB54CAE1644A9A5FDCFEF25742CDD5E368AC44CE7D8D2651415BCC9B5D744340EFDDF40865B919110B4927C60E7FC8A9E5E71F2C5F49AB715FFD44F6768CC7D3F5710C8EFF6AA792324E82CC4917131C52D098A779D3809337DC8B4406A7A2A13FCF164FC982907A66731C88414FF28CE7AE8995211B65BAF0A75134518331F6F5C8754AD195C04EB1FF1CAE78F4B1D2CFC0B9B7C411DE25099048F86044D3037684A1CEB58B88F91CD4968AA177E2CAA974E42234CD8C6C82B39AD1F4BBC85FB517D6C207C812B3A1A36160FF2E9D6200069F99AF5563F3A7FFDA6713794E122E7DFB4C5FC0182CC2131810D79918DA4FEDF69DBCFC7AD7073FA45747D8B9D87D9C2A5D3878897DA62031477FEA865AC1AB78749193BE47AFC8ED5E4AE102EDA9A3CE139F978C4147ED34B958C0D4727B90E56DF882AAECC04C64B3A3D8BF07D6B78DB4ADE52A22D48C8843299E74403355E92D369258D0E3E267E9A59B067C4B4E972FC0637469881591054122A257D573563A2D7CB926AC89E4F04996929FA7547F57A8CC0480AB5B5B4AEB78F69AAB31C36ECEBE215EE29CB884566825002DF8A4B5098BE8C3DC5776810D6A755496D67FB11ECE7E50E1EE48E2F6403A531BC31 \ 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/catvod/\351\237\251\345\211\247\347\275\221.js" "b/spider/catvod/\351\237\251\345\211\247\347\275\221.js" deleted file mode 100755 index e09b7dcd..00000000 --- "a/spider/catvod/\351\237\251\345\211\247\347\275\221.js" +++ /dev/null @@ -1,124 +0,0 @@ -/* -@header({ - searchable: 1, - filterable: 1, - quickSearch: 1, - title: '韩剧网', - lang: 'cat' -}) -*/ - -let host = 'https://hanju51.com'; -let headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", - "Referer": host + "/" -}; - -function extractVideos(html, limit = 0) { - let videos = []; - let liPattern = /
  • ([^<]+)<\/a>/) || [])[1] || ""; - let vod_pic = (item.match(/data-original="([^"]+)"/) || [])[1] || ""; - let vod_remarks = (item.match(/\s*]*>\.\.\.<\/a>/)?.[1] ? parseInt(html.match(/\/(\d+)\/<\/a>\s*]*>\.\.\.<\/a>/)[1]) : 999; - - return JSON.stringify({ list, page: parseInt(pg || 1), pagecount, limit: 20 }); -} - -async function detail(id) { - let html = (await req(`${host}/voddetail/${id}/`, { headers })).content || ''; - if (!html) return JSON.stringify({ list: [] }); - - // 提取线路名称 - let sources = [...html.matchAll(/]*class="fed-tabs-btn[^"]*"[^>]*>([^<]+)<\/a>/g)].map(m => m[1].trim()); - // 提取对应的集数区块 - let blocks = [...html.matchAll(/