From 80b21c5ed5e69b999db62b76036c580ed2ce0d0e Mon Sep 17 00:00:00 2001 From: txperl Date: Thu, 12 Aug 2021 12:01:56 +0800 Subject: [PATCH] =?UTF-8?q?chore(actions):=20add=20workflow=20=F0=9F=93=A6?= =?UTF-8?q?=20Package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GitHub Actions Workflow「📦 Package」 - 支持 win_x64, win_x86, mac(>=10.15) 平台二进制可执行文件的自动打包 --- .github/workflows/pkg.yml | 119 +++++++++++++++++++++ .pkg/py-pkger.py | 160 ++++++++++++++++++++++++++++ .pkg/r_cloudscraper.py | 131 +++++++++++++++++++++++ .pkg/r_pixiv.ico | Bin 0 -> 113557 bytes .pkg/r_pixivpy.py | 44 ++++++++ app/plugin/do_dl.py | 2 +- usr/templates/multiverse/index.html | 2 +- 7 files changed, 456 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pkg.yml create mode 100644 .pkg/py-pkger.py create mode 100644 .pkg/r_cloudscraper.py create mode 100644 .pkg/r_pixiv.ico create mode 100644 .pkg/r_pixivpy.py diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml new file mode 100644 index 0000000..d9a6018 --- /dev/null +++ b/.github/workflows/pkg.yml @@ -0,0 +1,119 @@ +name: 📦 Package + +on: + # Triggers the workflow on push or pull request events but only for the master branch + # push: + # branches: [ master ] + # pull_request: + # branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + inputs: + releaseTag: + description: 'Release Tag' + required: true + default: 'v' + releaseName: + description: 'Release Name' + required: true + default: 'test' + releaseBody: + description: 'Release Body' + required: true + default: 'Enjoy.' + +jobs: + build-win-mac: + name: Package on ${{ matrix.version }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + # os: [windows-2019, windows-2019, macos-10.15] + include: + - os: windows-2019 + version: win_x64 + pythonArch: 'x64' + - os: windows-2019 + version: win_x86 + pythonArch: 'x86' + - os: macos-10.15 + version: mac + pythonArch: 'x64' + + steps: + - uses: actions/checkout@v2 + + - name: Classify files, prepare to be packaged + run: | + mkdir ./.pkg/code + mkdir ./.pkg/public + cp -r ./altfe/ ./.pkg/code/altfe/ + cp -r ./app/ ./.pkg/code/app/ + cp ./main.py ./.pkg/code/ + cp -r ./usr/ ./.pkg/public/usr/ + cp ./config.yml ./.pkg/public/ + cp ./LICENSE ./.pkg/public/ + cp ./README.md ./.pkg/public/ + + - name: Setup Python 3.7 + uses: actions/setup-python@v2.2.2 + with: + python-version: 3.7 + architecture: ${{ matrix.pythonArch }} + + - name: Install Requirements + run: | + pip install -r ./requirements.txt + pip install pyinstaller + + - name: Run py-pkger.py + run: | + cd ./.pkg/ + python ./py-pkger.py auto + + - name: Compress to ZIP (win) + if: ${{ matrix.os == 'windows-2019' }} + run: | + cd ./.pkg/dist/ + mv ./main.exe ./PixivBiu.exe + Compress-Archive * ../../${{ matrix.version }}.zip + + - name: Compress to ZIP (mac) + if: ${{ matrix.os == 'macos-10.15' }} + run: | + cd ./.pkg/dist/ + mv ./main ./PixivBiu + zip -r ../../${{ matrix.version }}.zip * + + - name: Upload to Artifact + uses: actions/upload-artifact@v2 + with: + name: pixivbiuArt + path: ./${{ matrix.version }}.zip + + Release: + needs: [build-win-mac] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + + - name: Download from Artifact + uses: actions/download-artifact@v2 + with: + name: pixivbiuArt + + - name: Rename + run: | + mv ./win_x86.zip ./PixivBiu_${{ github.event.inputs.releaseTag }}_win_x86.zip + mv ./win_x64.zip ./PixivBiu_${{ github.event.inputs.releaseTag }}_win_x64.zip + mv ./mac.zip ./PixivBiu_${{ github.event.inputs.releaseTag }}_mac_intel.zip + + - name: Release and Done + uses: ncipollo/release-action@v1 + with: + artifacts: "PixivBiu_${{ github.event.inputs.releaseTag }}_win_x86.zip,PixivBiu_${{ github.event.inputs.releaseTag }}_win_x64.zip,PixivBiu_${{ github.event.inputs.releaseTag }}_mac_intel.zip" + tag: ${{ github.event.inputs.releaseTag }} + name: ${{ github.event.inputs.releaseName }} + body: ${{ github.event.inputs.releaseBody }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pkg/py-pkger.py b/.pkg/py-pkger.py new file mode 100644 index 0000000..ff2dbbe --- /dev/null +++ b/.pkg/py-pkger.py @@ -0,0 +1,160 @@ +import os +import shutil +import sys +import warnings +from pathlib import Path + +import cloudscraper +import pixivpy3 + +with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + import imp +from modulefinder import ModuleFinder + +ROOT_PATH = os.path.split(os.path.realpath(sys.argv[0]))[0] +CODE_PATH = os.path.join(ROOT_PATH, "code") +PUBLIC_PATH = os.path.join(ROOT_PATH, "public") +TMP_PATH = os.path.join(ROOT_PATH, "tmp") +DIST_PATH = os.path.join(ROOT_PATH, "dist") + + +# 复制文件夹 +def copyDIR(src, dst, cover=True, ignore=[]): + if not os.path.exists(dst): + os.makedirs(dst) + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(dst, item) + if item in ignore: + print("[Ignored] " + s) + continue + if os.path.isdir(s): + copyDIR(s, d, cover, ignore) + else: + if cover is True or ( + not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1 + ): + shutil.copy2(s, d) + print("[Copied] %s -> %s" % (s, d)) + + +# 清空文件夹 +def deleteDIR(folder): + if not os.path.exists(folder): + return + for filename in os.listdir(folder): + file_path = os.path.join(folder, filename) + try: + if os.path.isfile(file_path) or os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + except Exception as e: + print("Failed to delete %s. Reason: %s" % (file_path, e)) + + +# 替换文件 +def replaceFile(ori, dst): + if not os.path.exists(ori) or not os.path.exists(dst): + return False + print("[Replaced] %s -> %s" % (ori, dst)) + with open(ori, "r", encoding="utf-8") as f: + data = f.read() + with open(dst, "w", encoding="utf-8") as f: + f.write(data) + + +# 列出路径下所有文件 +def files(path, frmt="*", OTH=["", "pyc", "DS_Store"]): + tmp = [] + r = [] + for x in list(Path(path).glob("*")): + if os.path.isdir(x): + tmp += files(x, frmt) + else: + if frmt == "*" or len(str(x).split(frmt)) > 1: + tmp.append([str(x), x.stem, x.suffix[1:]]) + for x in tmp: + if x[2] in OTH: + continue + r.append(x) + return r + + +# 修复 ModuleFinder 可能 BUG +class ModuleFinderR(ModuleFinder): + def run_script(self, pathname): + self.msg(2, "run_script", pathname) + with open(pathname, encoding="utf-8") as fp: + stuff = ("", "r", imp.PY_SOURCE) + self.load_module('__main__', fp, pathname, stuff) + + +if __name__ == "__main__": + silent = False + if len(sys.argv) == 2: + if sys.argv[1] == "auto": + silent = True + args = { + "-F": "", + "--distpath": DIST_PATH, + "--workpath": os.path.join(TMP_PATH, "build"), + "--specpath": CODE_PATH, + } + oargs = [] + hiddenImport = [] + finder = ModuleFinderR() + BET = ";" if os.name == "nt" else ":" + SPT = "\\" if os.name == "nt" else "/" + + # 支持 CloudScraper + if silent or input("是否替换 CloudScraper/user_agent/__init__.py 文件?") == "y": + cdsr = os.path.dirname(cloudscraper.__file__) + replaceFile(f"{ROOT_PATH}{SPT}r_cloudscraper.py", f"{cdsr}{SPT}user_agent{SPT}__init__.py") + + # pixivpy + if silent or input("是否替换 pixivpy3/bapi.py 文件?") == "y": + pxpy = os.path.dirname(pixivpy3.__file__) + replaceFile(f"{ROOT_PATH}{SPT}r_pixivpy.py", f"{pxpy}{SPT}bapi.py") + + # 导入动态加载的文件、模块 + for x in files(os.path.join(CODE_PATH, "app")): + print(x) + ori = x[0].replace(CODE_PATH, "") + dest = "/".join(ori.split(SPT)[:-1]) + oargs.append(f"--add-data {ori[1:]}{BET}{dest[1:]}") + # 分析动态加载文件中所使用的包 + if x[2] == "py": + finder.run_script(x[0]) + for name, mod in finder.modules.items(): + module = name.split(".")[0] if os.name == "nt" else name + if module not in hiddenImport: + hiddenImport.append(module) + for x in hiddenImport: + oargs.append(f"--hidden-import {x}") + + # 图标加载 + if os.name == "nt": + args["--icon"] = os.path.join(ROOT_PATH, "r_pixiv.ico") + + # 参数拼接 + forarg = "" + for x in args: + forarg += " " + x + (" " if args[x] != "" else "") + args[x] + for x in oargs: + forarg += " " + x + + # 清空 DIST 文件夹 + if silent or input("是否清空 DIST 生成文件夹?") == "y": + deleteDIR(DIST_PATH) + + # 复制 PUBLIC 文件 + copyDIR(PUBLIC_PATH, DIST_PATH, True, ["cache", ".token.json", ".DS_Store"]) + + # PyInstaller 打包 + os.system("pyinstaller%s %s" % (forarg, os.path.join(CODE_PATH, "main.py"))) + + # 清空 TMP 文件夹 + if silent or input("是否清空 TMP 缓存文件夹?") == "y": + deleteDIR(TMP_PATH) diff --git a/.pkg/r_cloudscraper.py b/.pkg/r_cloudscraper.py new file mode 100644 index 0000000..08bb6ab --- /dev/null +++ b/.pkg/r_cloudscraper.py @@ -0,0 +1,131 @@ +import json +import os +import random +import re +import sys +import ssl + +from collections import OrderedDict + +# ------------------------------------------------------------------------------- # + + +class User_Agent(): + + # ------------------------------------------------------------------------------- # + + def __init__(self, *args, **kwargs): + self.headers = None + self.cipherSuite = [] + self.loadUserAgent(*args, **kwargs) + + # ------------------------------------------------------------------------------- # + + def filterAgents(self, user_agents): + filtered = {} + + if self.mobile: + if self.platform in user_agents['mobile'] and user_agents['mobile'][self.platform]: + filtered.update(user_agents['mobile'][self.platform]) + + if self.desktop: + if self.platform in user_agents['desktop'] and user_agents['desktop'][self.platform]: + filtered.update(user_agents['desktop'][self.platform]) + + return filtered + + # ------------------------------------------------------------------------------- # + + def tryMatchCustom(self, user_agents): + for device_type in user_agents['user_agents']: + for platform in user_agents['user_agents'][device_type]: + for browser in user_agents['user_agents'][device_type][platform]: + if re.search(re.escape(self.custom), ' '.join(user_agents['user_agents'][device_type][platform][browser])): + self.headers = user_agents['headers'][browser] + self.headers['User-Agent'] = self.custom + self.cipherSuite = user_agents['cipherSuite'][browser] + return True + return False + + # ------------------------------------------------------------------------------- # + + def loadUserAgent(self, *args, **kwargs): + self.browser = kwargs.pop('browser', None) + + self.platforms = ['linux', 'windows', 'darwin', 'android', 'ios'] + self.browsers = ['chrome', 'firefox'] + + if isinstance(self.browser, dict): + self.custom = self.browser.get('custom', None) + self.platform = self.browser.get('platform', None) + self.desktop = self.browser.get('desktop', True) + self.mobile = self.browser.get('mobile', True) + self.browser = self.browser.get('browser', None) + else: + self.custom = kwargs.pop('custom', None) + self.platform = kwargs.pop('platform', None) + self.desktop = kwargs.pop('desktop', True) + self.mobile = kwargs.pop('mobile', True) + + if not self.desktop and not self.mobile: + sys.tracebacklimit = 0 + raise RuntimeError("Sorry you can't have mobile and desktop disabled at the same time.") + + try: + with open(os.path.join(os.path.split(os.path.realpath(sys.argv[0]))[0], 'usr/static/browsers.json'), 'r') as fp: + user_agents = json.load( + fp, + object_pairs_hook=OrderedDict + ) + except: + with open(os.path.join(os.path.dirname(__file__), 'browsers.json'), 'r') as fp: + user_agents = json.load( + fp, + object_pairs_hook=OrderedDict + ) + + if self.custom: + if not self.tryMatchCustom(user_agents): + self.cipherSuite = [ + ssl._DEFAULT_CIPHERS, + '!AES128-SHA', + '!ECDHE-RSA-AES256-SHA', + ] + self.headers = OrderedDict([ + ('User-Agent', self.custom), + ('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'), + ('Accept-Language', 'en-US,en;q=0.9'), + ('Accept-Encoding', 'gzip, deflate, br') + ]) + else: + if self.browser and self.browser not in self.browsers: + sys.tracebacklimit = 0 + raise RuntimeError('Sorry "{}" browser is not valid, valid browsers are [{}].'.format(self.browser, ', '.join(self.browsers))) + + if not self.platform: + self.platform = random.SystemRandom().choice(self.platforms) + + if self.platform not in self.platforms: + sys.tracebacklimit = 0 + raise RuntimeError('Sorry the platform "{}" is not valid, valid platforms are [{}]'.format(self.platform, ', '.join(self.platforms))) + + filteredAgents = self.filterAgents(user_agents['user_agents']) + + if not self.browser: + # has to be at least one in there... + while not filteredAgents.get(self.browser): + self.browser = random.SystemRandom().choice(list(filteredAgents.keys())) + + if not filteredAgents[self.browser]: + sys.tracebacklimit = 0 + raise RuntimeError('Sorry "{}" browser was not found with a platform of "{}".'.format(self.browser, self.platform)) + + self.cipherSuite = user_agents['cipherSuite'][self.browser] + self.headers = user_agents['headers'][self.browser] + + self.headers['User-Agent'] = random.SystemRandom().choice(filteredAgents[self.browser]) + + if not kwargs.get('allow_brotli', False) and 'br' in self.headers['Accept-Encoding']: + self.headers['Accept-Encoding'] = ','.join([ + encoding for encoding in self.headers['Accept-Encoding'].split(',') if encoding.strip() != 'br' + ]).strip() diff --git a/.pkg/r_pixiv.ico b/.pkg/r_pixiv.ico new file mode 100644 index 0000000000000000000000000000000000000000..0946b6fae9d73a726bad0db0d5b101b9d7dee78b GIT binary patch literal 113557 zcmeF41zc2F8^$l)A}U}4Dt5PPfr_2D*oCocD|X9Z2M8FS-HibVDmJ?I+8wAP*a~8z zYkkjiXD*H-0|O4~uFlWD=bnDgd-~i!5cC9NA!9}XrjyX!P!RqU1i{HkVP6aPlW;3p zQfbd82t#ZI!PQk^FIYnm94ZTfOs2G(>It<+BLV!FUV-w0(078KP!M(`1s8=o3_(D; zntq}FF9ao7VRiy5!2u;JV0r@jiQ!b!QMwtzYzB^kf51mI-oSnsxC4e2lc1@zd%=7H z;=n_YR2ob(9`pkSK$7`)Tt5YG!7$JgFdcOnBsQiw*Un%xm<|HLV9*fwgXe&0FpejnZlyG7GXDnPm5Mv_Wqu{VP{49B z>~ipz@>)uR@maR_YVP!304Vbbr61$6Z1iJzl3aYo%LG;e#@VlOy${#HfaTYeKl8Hz zA%O9hfzE*CVSEojJ<&``{*iDm3St1;Q%9g$f5%~O3aWv>0d>uG6bt5pl3*iX-Uk77 zApo`?wh6j3PB<6?m>ApOZZ@C4jbno@Zfw=XaN>V1HE zI|5iXDILkA+ZX*u>WA=?WMP>)0oD=gmaG7DlgcYa6XLA}A<_WjiJpQ)4-FSmIL2XI zjx$cc6>$6!e;jzy@Pm9paKI~;!x9t&YzG4X+Z5>!SXXQV7MuN_i)BtjR-Z*dT1-bD zI0NRl6Wj&~fPGJ@1J)tk=(ioP&hr92@L8JLSQtl1PM9p;O`w{el&>a}VXp%j$PTpD zhcYbXt__ZX)a0u!596}kx`7mRfiRSl?VoLu156_|?VtUtkun~~BF;Nf z9yF;Nscr_U`B6633;o-I9Dw=H1FC5xcg?nErHse6_eL#lU!@<@Krn7+~65H(Uj(VWVJ|>Qvb5FRraQMyZD3HQVcMzk~lom&`Gt0itY)YXF4F363QZmT{he4S?-68=$^g0B^uKf$fa* zSp{GUa)A+ma!PqhCfh-FAm%NtL1+hcz54KT07{8ePogL`Mdx>t{zl$~wr1>oM4vZ?lO_3)e*JAyCrmIP7y8Ng(FO->K$ zX{ThrhxC{?`?Qo@k|zBD^+4USJtSAAl=5XW&Pjg#F$J+yJSOgJBs* z2Bfh=J)0Emh)X%BGiT5r>;P=joG(=6;Wgdp$1v;*d4bktPEHu4BZLQvvPkPSu9*ga zu}XMNH?HL@!j$?;E=|erquk`gh!a`Ua7LU?3L+6JX7>nFsS?p3Ix)6eWQtNIa)-h9w<-p*WWa z3wX*pH33-x*Pk3eYAE3~$5?w{9OC}~VV|diB-$qlIixnoakn8D55mA%!1l&*h2uVn z1#~+DLIK-HeZaEnq%CSAd8ls!WhR`nI5tlQ7Xin}l*SBof5tfvrh#(62xucln!+$` zz%lqYupCgQ>N2I({s?I;0p)={NRy07MPgb?*ml`Q9)h&WnNmE?O)_8$gpjW+hnPly zcxG4u*O4HpxjQA9QtQt=c7w92YoVlco17csQ}66sO#$be)XMR-;knjn1XA6<5r=ih zcx}J~@U^m}H9prTSWjwyZjA6!dw16$JJ)8fk|3#db6VwAjmI*u9z?m4Z!?NBNQC9s zcpa#w!)waj4^#!x+*BRcE+7b~hu4*zZHZ&7!1FmxKha+?r|i`r3;Pz=K~6(OGKbr{37N+4i(aJEbs7kWS)SBP`%GWhY!C z#)6dM(_fSP+)rNsAz&)tSwuVF0h$BuZO4I);1Wmx+T^DihHDMB6R{uA-0UlkJ6wyY zhD+{RRsKwf$G!d#z_n3kkmMQ$?vkzyam{sW1F!_7c8>9t?$;nL(_~s)3nnMCs-LR- z>`z?R(PUrd*xD6P&;J9^3Js`y$t3@2uG*#uN4BVHf z`fGE|KD`^1{dCVrY{!k^c3X?|d=Q56n*i>Kw8>Knqbfh!%*9m3m(rohI%B^Pbs3^K ze`^an#{$)G>en12IA+>|)ap-{@>8xuASaO81N-+3pf0nj{WSb3dtnfhLRi&!yw)+1LE8blWB8ay$V#rNY`9PaNa8h?rI1lg-w9J>YS8b`sbL) zu}A6y$>~OpaQQ)YklfmVW$OWM0;xRdCI3p)EBmb^e=c0#P{~)ypXLd`xziMc164oi znmYFetj9|#VbV+fiO55eon~2}scmn7pSX^O$#{~?s^(vC=N!Q~dWA}u^pO88(r*D& z`_P~H<09KzH2n3 z`wH&0RKn)K^~EH@zJ!}MFb7Px6gUaESCI0d{K@qnZOec){d3i}Uk>SN+RJk7z`iZY zusJ|niwDAu?nSgRKJm>WWD&r^n(AUa;0cmh7k#xd7xfCQN58b9nVWoRAuvYRn$^fg)elNv)54bi4)`0b60i?AJWuTi22mmoaJzaJCPK2=q zDiVKwNk1jj;dP*%MsjxQhyDK;SOP|XcAycc4eEhbfOWF~us)uF{Q!M}oA} ze@b$y`!kO{pqwVvEx&HXctqJI$$G6-Rqb768#K$!{yt`TY| z;WgdpXA(ApYo1T{f4)dC6%OLVLehguH2EMAO*tMeq8W(?jcCg791%=BV}z%S1mT^k z=^_Y-hKP7a3hzk?cuz`B)8v!M_ooE#$uxLPyUYOp>xrfisWiP{(yt;6npu@*PNiw7 zG&87~#!9=o$#BR=$iVb#3W6ogyb3ciGt!4&qy_S$37mjjkO`5LUvSbBV8#o^rYx6K zPO~|aw9f2F@17?5?U@!O^oKnKICFjQVfa~5~fPFL< z5XV2RRet1OSYYCPHwj<=M$5&>L`^5_J(eGxmp3MJUHt#XdCy zwsy7E9P!jT%Q5yU7ob2 ze*&^q*2Vr(YAY=J`st6^?i&F)NULn$Djv@y>V_)b|Cfr9CKKu4oIqU1u&LK zl1n4K{kY%YS-V!nXE^2kKGv%?a~#`0_b*P2%O6cp4zLY=ul6tLm1P(z-J45J2?!_2 zUIo`&!%JzTWIjaLB8;z6{<^TgNktkda5RjVuk}7{4r17Ja zpJib`Fb7E%Us^N3zXeF`e)Zo_e(syNXA<}2ja`S@CIC;tn&lb+1kNS=LA3Z?FOk2dzO(zbLp(>46aYK(Dh4=CO5svtCRKhbq?H300HzPb z=R+)TogEYgGT8UK-q5D7TCtbesdXSYSO=k z@VS_llCPCDsq*W3{l~d95M-dtpZb`%Kd1cBKwBNKuT^I{N;sxy8#k$NNtHi6)_)xP zCj(>3tS;*!Z(V-o%k`mp z8{n9}Ld&x3MmUz;1{~8ej#OTC^Lv#4AY|rxQfvdf7shMg0saB%GOF8;A`HuJ3r=Yf zUOm35{d<%@9y0$1#PX|*1G(WQ*P=Wp5he?;1ME+#;}}!&vB4#JEbz5 zK$y&cI-o963ZI<+_bUGs$o<(E5E3BnF_vmko&yNOwFLJmT$3kPzLfmFSNZ2d?$64v zv~!)5lFZu>#uRYO4ACOIlpfb%>U+@dRsMzimJ83WRQf;l;jcw`xK_|t?tx}&5uW9L z4tj$ywea7o`~i^Tv-;oW`=0hiYVt{Sr)qK?DaM8ArA2tg8evk5Vj&DkNq#v(AHgE~3%BOzK zc3c%m_0Ku;W-@W!z^}W?SxGh6@2P~>RDO;X>UrzR9)q;Sds}MdcZEC4ms~!V;h!By z9@HDKZ6sCxT5wOV^0VzHwFk@!_mtMMEX$uD@i`Ik+Fv}My^m{6 zI-vV@P(T@;df@!YF^FSBQJ`8EZm_2`rX-hFa(;V|b|$s_C=bsu)aPdPbl8>)N^vPL zuL0{*n|<)#2v;>k@mvJsaXzRAa)579ex4=PWnLnFiO*f;hy5Z@FR!|NIl>q+uJq%z zlHXx!(`Ko|E5DMBtIxPU)lTM7lqrKRro3gI^Z);Y%mDJ{0sVqhXT3$*E5s%gg|&Pq@Y zaL$$9pJe(R>t_RP(+omb>PxlApIs}SVG(8L+UPz|l|`Ftj!nxzb)aYu3SCfk%G(HV z&$=6M&Ug%1FDwJw+*2S2d%*-y5r}oZX)d3s7iD96uLJ%9Y=7Fw#r9JHe9`YeQ-IV4 z0z_SpM)>zyrmrpo%fY_Z88`p~fcx&x@ppN|@tXC(`Jx1H0W1&4%ksbpm~(y+QQYHT z*%^oJi~5ZR>UnCizeU)=!HVzbsuwp&JHj(R&acU>2{q-X>dw4b&r1MxQ4%m+Lw*~8 z_wv+#!GVe2ZDZfb2+DvyU@Le6RMXeynsd4}P|r%Eone&}hRN|wn|x9V!?wiseiCr) z>I(*dE}#u)3EBgebv$6d=URhffVS;iU54X`%XPtL_xm*#T*ZTNl&~+$fqL4yw6lEF zPg={Flq^w5uQCwJp;9!hFU7cucZ4}^aSTZ+pC8ff7GzCaBWhjXBm#;$_+Kn_*bt<5 zJJ5wLuZv|*!@J!{6kl|r4#E`A9k}57G|+{d>5;|>$W~eU?w+Rdq{W>&P*Mmc=Y^E^ zS?N(eO=)tT;g~9pbuiOX#1!MG{9O?nryPdN&sa( zoS!+qh61kL(kg#i<1x=*z%?ReQIRnHFSq)OV7mPgxoTrcw+hW9xw z0QOv;_MYiq^3QYNbA+M{ihU*L$<^Q<(B)jtwAjv90nS5KfX~EzF3fkdg(|+!L|t(I zR}}OGA%OEyTG|EUFfQ9T(<%bQy=B-m@on+%SlZ;X=KUys&j#`u0|&spO>YngHh?{V zYui&wcuhC@F$}{p4&&nQdVIRam3+=Wt}n}=JTo*znb>c+53*IlYq}Y3g5H9^VY~kL z@ngI-{`)BYMH!m>t1mPo`GE%fs8bk<4-LB(8J}mkdvmPWX3D(_**lgJN}l9Xqrf7q>$4DY2feD;{pECIbn2a%8Du;;RdyU8BiDB0RpzAXAu%^n`dlnJL|2X8qiZ zv=q}fh%{E31~3gG4dncigMrLME{x)LD@??4X5in}!Pg{m^6%>)X)o#npNWVRTo@X- zNO}|1&kB$sTk18ECiE<)3B618Ak_;ZBl@R$ORPt!Ud0bZz+X}C{Chq!M!~l$62B8c z%dg)r4g6#p2=ad`tnmNHGeSMgVTLd{f0+WFWfEQ+Jd1?&&U^IS(=P)Y8*=cyblw;I z^-H}5Lj4qd2zun+*#!iGtKdhfb4_iHZQ(qa0xAMi&Q+VhuV3;S2=;%5HQXDHpFB6= z*=sm>3N-2ZM|Y2f>>EI1kYO|K`3K`UnqR-qHNa=pLll4Ci|1y%Z+-x9-Swl_ueS2N zL|M4U;PU`Bq5P(z@0ZU=e6E%MzHKw!Bj@=kbi#XFUBMB+@l#v9{@cQ_P45G22jabs zaNnPEUO@R#@6UM-!#(;A@CN+b>NBnK@*XGmlCF67K-@F@_j|jdgebFk9|UGz!2Mf1 zNUI+I?eTaF)9PdqGa{N07{_XXr3;B7*^fzb<#QVVir8;L>#P_Qd ze^=fKWt;t>flF6gZmtwJ7)*ty6C^q4!HLXQ#^NF3*|ZvzUDmkN2sUtl!3b8 z-lxi@Y5)FnM3e)4UwqELJ4`-1l%Bf#FQt78x@r&pwetaFXWesOHx+RI`(LV`Z&8+K z(3uQaf`83a5X|=$72j;h0l8OzZ+TzlThz<9%;O*EZ!WL{>`y;*JJ^i#Tcw=b+ie5i zQdj@gyxv2P8$m801PA_@=f}!#_Obp8fIZ;9S|8u5Tw9>i++0udto%p%DbH}Fy5Ik+ z?$fR9^SLEHXCMU6_%WVOQ~5p{$NXRYKK;i%jsv;)T*SuN-+wM(TLd}5ulYXx>t33B zHbLLwn(%wh1IoJR9*^sMNw!~R`s;${MDu`n5Al1n0p!ho&u4A9zyF#&-bKWp1$g$E zT6_^_35W$hTf5`lP^Nqy@|!jR)ith(@!}Zsk?XV2ZnO<7{4yJg{5cu2x@t1?2 zt^QeFo)L8joq=!Xf1`KH#J{bsxR+BCdEfa;y>lEJh35cLeGgpu-af~mtl(!}cgj)z z${_K3q3JmoFdyIu_^ggD`W)LZ?+I2wJHWSa5|I8AjYSB>_a8#=>=eRh2F<{3z%vqE z$ntMZgU=L+_YE~FP6r;ygZCvl=l?Gq^vyYZ0}#)-G>Hp;)_rxr=l&DGzqQVEDL2Pa zKg@^ecE3pF8>wxPS9;yQ=RNRBKvVUz?#W1?Oa1*9(xUEu7uQ7Tuoh72oo75}(^2=@ z)<4z^zv@2;9q{=I2cWV4q{V~j0gf%)r=@3K(YF47^?%LvfaeKca!-_&s{SI*X1~Pm ze)2uoC%@`HUE9Ad`qm@Vkqhg8Bfd5HwLhiq5Td()iW?b)(~rs4ENbRn@P2 z$C~xu0ep-1akQ=f5s)XjcJ#CRy@GyPfUi0eV0>T;4uWr?(_i(kJ3a43on-=P8Uq+l z3D0=nf?xIjZFHb3J@b94j>vsVhTU9!O33(<`iRJf__j_$isky20U#H3$Y&Z#0X`ok2kBL}+N65{`R4@5sv#^eo^q46;9KqaROL^x z{*x_$nrE>8Qz0175}+)s3-(LS+r@z^XargUPtXetP|^?PAD|gvxWa&IXoC>t-@sDK zMx&MA1r+BX#W%iZAq|dMs^$Gw*WAN22f~&GPm`5*v*KBt72iqDG}r(7*>U1CWL)1t zc6-1%p%cLU5b?PJ?qhF&hu|e(`+f(KTEo+g{tU-4ocGmMgFb+J1IrU(AGLlD1L37M zSOeEP!1w4|i}CjauvXFhO**BVd0<`#->Pk@=98-W4;9}x6TcV6v+fe$Pr$Z&9&j)J z9;l}OwbyJH?8AEi#|vkyGr9iL*rz0qP59Ok`WD*($CBZI-;exSc~TSqD)Msxl1N{e z^aCD%<3(!H)>T-o>Ys9=pBum|0Qv&;w3B08;w#?`i)4#KrD$5BmXOmBDA|8L+6`6LJW zfzcqnWl3xLTpw_Lw*->d8cl`)nZY8E-fiGM@+bnnkcV!dHQ<^!t!4k(c-q$gsP8DF zHf4MR`4#}~LxgR!ztp#C1uzWA0@ea;(obs`?mNVN;Ai>c?23PD41WA(aaz;XMZEt? z{Zkj*D+B?t4XEl{{d(7UoXsiP0MBT40bS@)QyLSwPUT)i`YG>+7$aYN-!V5pWC^->*}A`-^8!+UP8`VNN4m&NYc5@EUyYwI9o&ZT*i#*wmK$dxn1od0X=D zL4@IX>g0Y1$9)0s*K$vg9x{Ki24tRq&p;%kQ=ZzEVtHRuR9Uum08dW8Eg*FW_&1M`3(%tYNMb-{bd8+~81PR6D~eQ=N2 zPLg5sRB_$Uytrpgk2+0Fn*a6s=h;9tN%u+Bz2wF)fa67atObWM{arJ~zo#XAuh9=~ zsi~{<2&--VkN5#(O>6m=;%pG>?-a%Wo)6FDyYS-k18L1mQ#{-g<$GPqzCT@j-iP}X zP2sBhusjQz;LQN85Cqho-!fbN^ZDpZAF}QUA&PRKBO%Pseq$ zRs2>2|LzuK0H#QPr;hVTuAVjd{Ves*^MI-#x#}1F7zS_-hy$ARmRfh#wF!{=zd5e= z>M;G($|HsUS?ZtV9Yp=}U7O^73gzn`^-57P#K0!}QZC-_KP4 zYZTvUNd7#K<8!d$c_BX6mR7y0$J>qZ-%R{gjG}$9Ekx=tef9jbwf{`@e;oQwaSag6 zzZI#dBVFF}=6X`R2c%4!^}h#bTTkf~?q{n1+t4@9caj_aRo4LR5mr~vhipJv;`*QC z3$D2q*bdUGUbRj4XR7}v(02u(ZT;6lSU$t4tuB)bw-j@J;{G3PfNdd2M`jGFh5iM^BMbw)PG7p zh5ml`r&&=u|OAf(MvU7W%@mJ zl(xEjACNBFI!$#yb-T9pKT?ad)YJaa?9@qJkW%$a>;H-f8?S?Mv)#F?=F4l)7*Hl% z>xuiEmOz{J<DSfv|jb<7?~Z3msDiP!4cS`nBn+#%Eql0n@JzQd+-! zA)C5#lxh7J`akdKm1KER`YFro0rMSD)o)s^qmf2lrYZe+4RU}hAT4QXio-P}pDSki zPJnr93QPB&ss3-EybeIBh9u3<8KUl}zrG+j-D&HGdrvQ|dH-Mf@m?h0ea4+S%7e08 z1O)(PC)(V@_?hbeFm!@?NTL2e>2DUoYFo$2g~P zGxE`9oi3%N$>jN>B@oL*6L77Q()!|Os{disON#FbD|F|Cu-AbmeQWE^ewpN%17!hR z1AgrpLyR&VVbhFQmHL0qGvf3)Fb(@8FmAHnY>Yr<)>_D}uzqqEq z@>`q}lF^waT_)we6LY6_uxM$-$ zkyIMmxLrWp${_*IFrK{nA}_W-yz7c*55&LWq&}x6thkYT>Yrghg@Fk>CgYP;fm7gLX@ww)4xK?cg+SdON z#7oaQO0TrJw;I5`x9a=%NmYnaM}=U@byU{b!T5eL)7A9bto*(q!gK~H?bTD#(M_b$ z7;B9r^Q0Vr`&#y8);rVFw*Chz!>1&}kLG_0a_0n^sz7w7Kh{dr+eDy?GO&*|X1Xc; z7zXfs_y+jeyze0mPdsO62=j}q=?3WM3GM>UbH9Nv!b%SG1B1ZV%J!|}zk$r%_>O;w z7Vm%~Y%w6$Q90S}vI0qs8cjYEx>@{AJwNE4{|M4TgovXb(ssbZEW$O>r z)BjQJtVho<^grHb)AS3UDn5h9vK0ngtLv&fK^RX=ke;Tjzi?Oi7Evd}d#giPSSI#` zcF4n+d%@KH5SKdg0=m*qdZxqk_ECWQ-DHbOf55SCBhZyhuaK5_)_`|NlFeV;6Y(ey z*9?4qM_2Ml>99}C09XrikN8YIdyRI)p%c}*gqs85uP&r0T^FO>qj%$zJ~*| zl*Ty4x=buT_rYv4x+)9Do;E;SQ%L$tjVZ=eoaZ0apG+m(td?$-d0{ zg6)C0Pu$`kFNyJ)$#1D54BIrrwFAe1uGc7ODId?v4uI;=n_kNOUMvU8Fbcc?X~|Pl z9H!?E#P11yrodEMmy79>uX|!hKTB`a~EZVw%_vxOQ#|mI2shtGIS zU6uvgL3+se0O_*-2;um9PP+KHNFOe4aL^4nk7^XPZPZjUwQ8r72{}bE*Ddm$^|4E9!^OJmAOl`Y> zyll7m!1;8PT~nFwAWx15RWNp%DaTKZMF_>(0bzmot;HO;t_4N{-q$<|*seI1{R1@Z zBk0bt`5`z9g28COw6fq^uDCyw@}3%D(Jr5}?eYwc`Ey;ashnTy&U?!_fkxe=)gvSj zbCzO1*dFP#4}7hxX^H;?`SKi{`^=84<-i;D-F}e#0`RqzCo^o+WX9=URdD6vtk+ z2mI|$eoIwX>tXyYKko4rX_tn{HQg$39dJ#NlB~M)zlr>Q0~q(ee!r(pgW)*45%)2X zXLsD7QUume_@FF988tbzMX{ms*#pk5#I{D$>QcQarE zxUMPz>VZChb0F7SkHLG8l050*{}lPQ4fEr@^KVo4Qu{!@tbgim8lbMzqkOtfll8>2 zvD<+Am;-?4q#=NN#bsb2SfIS7JIB_)K@{M*#B0E@M%S{XHa(6rV}KEms^y!S%olKc zT?tYv|F7_A*BRR%#~+?2eA7Bt&y4v4j*SuESDk(v9c+ZIIe)2F$2Yf&YhT5BFfZic zSoy0Bq^}OPK*zaQ_Zw1pj`UkK2CfGw4=4ckgI{eReRQxLy3GxQ%`-l|zx}Q1Ik|kq zzgwnwc9Csh_pdgf^FF|SFV;Ql|400Z&vJd53%34h13GB~+_P}L7sveIA7sqe)c%4M z&vA1u;C+|n;4SzyAEZqO+~aYbFRt}qYEt_T=T2FHHJA*3)H9+Vxm`Yk-elmr@A1;w zxF9>*0MCecKKdh_75~WfPT8VC2k_m_cG6ns>hZ*9M-=aO)kJxC7R$4XA6@_7OFru6 zAgCIw{O+5&KL3q<=oGi36@@1D?TtFCG7=`SL8E_ltAz9Z^0f_+R^>Y?u#Taz1DU zc>n&_SfJ3sapv#Zb%pDFkqgo1zb`hd@)Z30b!^=5tpF`b^x{T}s|UU`NFifaS5 z19MOXOb3_2zu_#IXSNpspNp!5XL8N>jQS?5d4K&l;4izvnDOT48UhFAA)z_Yv*Zg<1;Vjc^CwO z<{%HAgTT9B&xN&f-zQy&U#>dS0N%giT#7y>t`$naYy+kO?yLB0r!Jn=s`Yt)9EqnezUF35DVr=*_-Qux5D9GX;CVHc_APs!ny0Lj7?6Vcctea~0zS-w74l zh$-rn>q~-bx;;1Kw+J_^{6`3#`>T#qt>|A7{&gC_xA>C!$xS|U#d}lMDh?l5Oz%$) z;%ikZ0c7;oOY|3mt3-HBZ$WUB0?0+HkeH=RVV4q6Mo&!Oox)oXToef;zSO0ZKrZ7I14VerDhQ%IF?nSMiODN7P)f^Kj*O6)ol+(hj{U>@14OYu2QwXBUMXRw@EDAlfOFqZq-F zp-dRofF%*E6?Pdey?Lpye~PNGGZDd>qFKxFpF&;(u_&MHdScN(*#%KmDt2#Nn?wpy ze8GiMsMslMa&}gus=o;YO3qFJ4P>A4qg^i2HAgfnD#N z($4y_Mjell?5I!Hxr@@y`la1*68^)FVcFiT?-TFrJvOqq3VKfG7jPF{ttgK~ z48`nJGH{` z{ve`?Hyq`OnMpyE=9H$5>sXm*H%A6m_3FAcLq5z)Fg1qJZot|<1)*GAJ-6x}!>-0I z8a}sO^LdkQK8Xr?*tq53Zh3omGO2FVDN90s``TA#$oEJ1=P7P&;JipaF{a|l`xOg5 z=;@WM|4keFvlsMQX0^3(ukO6mX?(5!xjH#7iVNxx^l)*%4;8PUuD4@mhrSnfHy?fD z-u5Onhpu_J@Y>)p2@Crf`5%sXI;@I=|KT!|0wPT0>q?AX(y>X!;QH78=X*K4oUpp{ zV|j-^tR^-qTJddo#H9P{@^%oi&x*BgKV{3rK|5v`xAAE)p}fzV$g!afI{CPo1n8Z0 z?YYKc>5g;F^^TdpAN#`RmEP!rqo?+@53KH)^|4pJTpqs7?=IO^C9ZAsJiQJ=rjxHm zSqJshH@j-s$0UQ0LGP8xE4^2_cE2*PGbk1@q1smYx#RDiY^Ydjt#Ic|aNbBedElZ= zlWgN`hc)fwWEmSU`SP1yEe?#fsJe5Xh3nX?5%=qd)OK!uBF_$cFGH`}_b;vS9n)Sv z{FdF0DI<@sXtl89EZJe1Uu65p+T$y`UzvR0u1&0y?U|_UMWP*oyJaaMlqwyvRkqxx z)Y;r29%s6^PB)rvu-N>uQCAagM9TvxliIW4Mqzo=w)dBNR|@gqBDy=)n!AGOqLT)`YpQRcM=u5Wpu z>YmkyEL^P$-mx&9Jh;h=7+GeYmeE^Z=6e0MS~k1;hjZDF(R-&q`&!73T;uMxc)Oy; z`PbH+-LhQjQ0tGk=Z+MOD7Supsfb$MdE)B1IC=-KnWleD<`Q#2-gDO8YJD9Yqe@)% zZ+Nd>fB)b@@s7t@H+xuIFbsNZvP-{WuGe)IHYi@G#Xq$V>-+2P)fxU=$d7tMlcy8jg=n@8vji>M5ey_@wE?I}VMKoL1a`rXDiPv^#JZ@S- zIJb0eNB{X{JJ+$(FJW}Ua97TR@PvfTdkXe#=~XU1mxuR)6IIT;cwCvY_2i{PRku#u z=iPmcYpIT|9tWphsdIX!tb%?CVL*TX69$n(Ly1kjuYU|qS@1J^}8m4#I>R_CM z>4v6O8T30C?9$tIC*j(=uB9!b3`g0ol3A7SwBc@x64OSun{v=1((uJl`;bCKsvpdm zC%%%rSme3krH3Dy+51?evApf%P3zk{_jC%1T{NanNSO}8p)*TLcy0`gyj;`hq;SLc z`SrIGZqD_PU6b`Qdv|2@SQF>)`EBnEHS97>Pxz-qhdQe^He7vqbFfoC{gVfZy{qlF zFX3@r+X99iGu?UH=lHeKPWdWTYT`e#U1*tmMxIU`g(Kz*4Hr(bIM?WK)#6PWW!2kc zRNVbm_F2oek10AgOuy-|1pSp+Mi=+W;8pmvLqbHA!ON^8^v|4HcGu4Buc(|S>Mk6p ze|qnbsqm77Dl)LXF!fD` zrftsOpR#?ULcL$@~sN{q{z(7PgJ`s!cAk=gf=euZ~a77~$}GrBlly z9s?Ggh%Mdw>V%SWJ2r8jIB!ic6Z0MBLoasB2)V5Gt%$VGQmxLGf7TvfP$zET%ovB? z8+EU*S8&Knr=UXR?%GD~y>)ir=$@OpRr2URrM6>}+2d|rnRRt*xf3meHasv~I;(`R zx$c>;{&BlUM?1`1vuwNL5rd9K{|s#Or+xSANF`za>5}1FbDF&?u&HF7{E&YB*xd4+ z=8nIYv!1)Oe&0U^y4jSUAXwfVS+_^QKkDtaeC@pH?54w+^O?6Z7s-ZRf1`=q{H>9KPM7XPi*2w_mL7?4ECfZ2S9nYf&~_$g*=}#;!hvdwG3$ z|E9nH(J}=t>$h8b%-*eF`OWQ@<~CY7>Zq+{^WU?cS*8DRLW65B?~QI!HJf2^+gq6n z`ghCWVVJ$KFtvTvfO<7o{WC~@E~4n3i3=vJDYRC9PLXz=`f=8imc_jH%V%hHCr63) z&78tKmNgq)>SVEjrN=xS8J*W?S3UW(?9VLc^((aF$hnEjIvIK{UGMrwt5v&>E;Jrx z)bBxpvw>a*qfw2FPfQD2@BfE<#vvP@iNi+N?X;+Cex`JAt^4{Jd`C8PIAs`>Vc~+R z(bFzj-WY9nrTI~R+q%01%R%<02Bp>oeJJB)Q*G$YnZ4umTV&We{ayHA=M}lkhGx#+ z2JJ|{cD|71{q%|-nDlZ)_HiS9ns;gta>HZ8*5HPHYj^6*#_j62+IRL);iJ`K+l0AQ z7WeRSIla!Ob%BdLn>Co8@2qL7;|VekztCqb-bE*r&2OT2J=4`c{>oRxpx4M~hg0L7 z8?%Xx?di09T?PG*<2t@ueCUp=)6J`Yc39LlY~j8wk76876?cAU+VpsWQ>Lj`&p*nz zcodTRoT0n4Brh;Qro~b9`zQTf6#y{yY9*Ru)aFN31N}Y;>r5 zr=>d!#67K2JbJElIEV?#q?illyp_gKM{Q1G`cFgjk#qY*g zXPf>K653~MwZW@Y{OX9qG1dF83bhScF=u**E~drJ^4HRLaI&gzd;gGaNH*WF#jd?A z`j+h#WP7ViWY19+ac!1Gte%^1scCN`2glw2)ZW{1|DA2t(GPDnKh*J|o1V~r(X6Ze z54OB(kSk_Sas5}ub_UO7<(Jx)$db8~>8MuCtL0tZsa>9o(}akEE(cnj)V~%zxn8~EwUagxUUAdU++6UeivF$~y)I=oyxro0OV@}vj#n8w zm%Lx3|8MV2E{wi-VqeC%LSwAG3V5yAzbH@Cv}XmIX1Y+_ZGXk}7c2)Sc)B%eKCVUc zK6P48jUTkI`r~2px}LcQZp-)9c>K0?0~$2QIODe`FTg4!F}nb3fnKYcO8GQ=bI{CwcB^QI&6Go`KWX651o0`Wcf4C ztP>6LbiQ_~qfJ8AyvuIucR3rhIpd4&0dw?97){?7n_>R-iJm?UEJyh4Z@P70t?M@; z`s&Loy~dMh>5Mc+=Z9!-MT-v~9nnbDqIAvgmv*Ypl{gw9s&r)sjmt4%2$o z9$q!Ri@eQvlQREX7UbG?T#E&MV|_;HO{*(wZ}_Z=XSVqI?Ve2fGdx?=lt=xZuBo5z z#Qua!dvf;}Z5a9TQROTzDo0v8e7e}m)h5Cy!u<8TfF zIy<&%*4t&<6?)z6^r$=Xkxg$*3EATCN;coZb=`WQ@bmNc*Z71zdb-$bwRx5Ck1AcW zXy-VwN?*&X)l9Y~xTCr~Lo#K0930Y6-Y2q54~K1?jb2Zl_^d~%p1l_R*6Q5BiUCjZ z)tqPkV4MDu728jF?Ob8_&a9x|V)IAdrY8?Y+}}3TwrNS9fKuJFJ2$nzU1mr&*E-{5 zHBM)Ab}N@9+}CRT0kI2>)b9bM(VqbpS8~F=gYL)q{ z`(L$Jma~<09;z3xX8Av(qlWY;zAPf_eYr^Mrf+kE_HLece%sy4@Am6(yyGFiC8G^a zRohu$Rp+a2NA%uCpWTp!E`=E&V{z`2V&i^Q*9~d%&L?iLpjUTiK@*>xRyoacmj36Z z=fVaXZMU_b9$jx;#ZCpI4|wL?R5-4qYXu=8#~+n5ZoSaz1!iogjB6dHR<>JMuge_& zV138t$3M!hzB+l;&FrF^P`{VO_#OcPWeoR>w|Lxr$g#*ik=txX_&$8^I(Oq46Yr4; zBZ9_u-EhLJsL|qsGtUgJb1V9J+b+B8M@L-W=O}w*`@yNUquaW>JzEvtIqOOF(~+GQ zo9uGD6rOj}+mdY^gc~xi(IqXPSIhd|U}b*JiLMp1+!-MMt=yh^{mbY_S&p(OI6ma4 zZ0n`Sv27netUh~Ism*SSI=(ZCeD1TNzgfk4@0VJ~JhK^N+|Dej)yDcuO4UC+KYP@g zcYm0+c51hF?l%2uDAI>&+ zzF)=&+t)J+yO=i|9(JLud-k5j&mZ;3{dY-&*0ucge#>TWl`zODM}pPl$%Tu@->DpV z=YsF zKJjd& zez#j6%Ew=DeVnU>pI_;NL!53J1Z28hccIrFyL-oaZY{X&y8FSZ4?fsOSO&)W`nt`( z-s*8&zQIR6csv<>zg4^S+gqNgxPOOPMT6Vhu2>hXdF;%t?t}E@i$A-|^uJhY+@gr+#eRz)tihVQ z%;a~cJ8$b&wo$Kpg#vfIcHJ{4VAjgDIrVqt8djw5tqRA^t#ybp8I=2()#(pwM?Uha z7Gk$_!@l8W+4nWS?*D9vXD+?K+rDnkd~y_vdy{3t;EHoR@;=WR-^i!LW%Hp8rXE@B z>VNyv+2VDUWjc9yfbmJEmzLRGdTwlCspnGT&8gTvBQA}tp8HsQkA&^BThzK~HOkW7 zX5OGp#vigRuV*)Z3l@16YdsG6J-}Prh4_PmtChP78H$4_>)8^H*H~IyQZ*8xb zaQ|pTNZV6!?hALm{3~zaODo-DC$}=pQqN+9U3~u;PjY9iJ={P0o@^e=V&7f4KJChq za<&Ja4a&ZALxMqMoeK{aG@C8I7nJ=$^plR)YGyPw{oCisE#FaNo;I=CH+%ZqHq-96 z^{d}AeEA?}-)gmr1U>shuHSCaLFaQ*z5W-vd7E)q$G%~iWS#fed+0}Hdo?-Y$#Ki9 z#+!tbAwl+?Z=boCu%v#SZsAjk);4UrrFA>sfA;lnvZMNM!u|dCN7}YA$}y2{G@z3jS6$*^li z$6E>YPsZEJqYC=H@UZA#Xh%OYgWZ>xkKR7}@1AWMwY14%lRaVbEayrx3y;@prxq<( zsbx>+@S(*IPj2DyVqoMd-(HhvxK6ik>QRDqOEf^^Y}|t~j4-StHL2=Dzjo$@ls^3G31}+&R$r zM%EUCgFWrG%CecchVC?ZG^0b~!*}La(9a<9yYWUR@l$IlEKwzW{I4pyQbgvUc25^ z@6L%AYcBfCdgLD1&EC-4aPnfirJ3(k9lLsY7N4LkrUM+~pF}KO>0H|Sw$G5&>vQ2|q4P97u-fffjFARn^A8kMJTK8NlFV?avaIlZ_Zo#cdhS!sW7Ct?)WMZ~f zIs8nEwi;Ne#nKt|-`l-*Y*#4U`B^iM#V+T@9Sb*dI^emX`2N-(W-e?yxVxa85On8&sAowUuE zaH2=&>G~gPdYme3GVg@@?(ThG4w%}0LiJfq@>ZGI_0^V&?r#1EX0KdeckZfQL{FC) zhgyFa*#FcB{bP|sjc)W^zwXgkPp{h3yq#{2JGSfK=~uz0hioWQ=~O0f!?h*_axYx7 z{DI!3ekHTJRli-W-SV~_qh>CY8Q3(=VDPuR>`=XPRrCu+Oy1k_P}LZ-%hM~bU$nw& z=;61Gm-U(d^8AxZZ>?t)kJCGz$^A{m;@#f8u3P(_(NYJ)$ep$B*d8)}Z-44|={;q( zpR`>%wbdB?Q5E9qHI@l$j4u}5T6*{I$7UIM%A*|)WOwaol`~6%@6gVfL(K2(j9pOg ze;(drs+#0{D)hWlxB6aTm&H)G`GX%A8J!=g_wZnc$hSow$5abG6VP^d&T_uO9C^EA z^!qc-Ogc}w@mAI2R984&rsE^;5vJ~j)yOF z%+cV?qAj<&MPd8j)UiY1{guyF_pEPI+dL?AZs3cXUhOy4j$VAb@okyy+SO}fGAw#l z!{PnyY9=kcs}+he-Bn~~*RH$WFU-s0SMbirQZq{58UA*ou%|{JzbYMshc(TIwJc}p z-`ww{*Udf82K8R8-{PWPg#%TOnhi6`bI$zuf*Bm~=DNDc-Ddp$+nwz4Ma7zJ-}<^$ zkt^9e>J++b(W>{M&ZiDsSYCPa#F&?LWd97v>b1JnS@+e}Gn;wU>axf8@BEE2ndp~r z_pIoBXV!;(=Z6U9<-3~2Z;t48zxH1v8Cn*w_tv)%+eUXxR&g zU~?|53+z^B_@#mOHoE#PKh`|5U-V*m8awr5ncti4Ugw~8_ZBcIsrG@snlJTO6cvSNC< zH;XF@!}KOk``v2ZIOqM>Zr*s4eM8#>mqUYn>t{Mu^N#DFQ_rt&WYzV3v(I8mffw8K zN4dLU=e>2_|EfD2NoZ=eVQpZAn6s5*Djh>Ilh)`La4K1G+QZ*=E|pyvDO*onKc4pN z@w#`>aYF_3m48$#Gr8o{$&TyepV(a~f9~#r6#J`NT?%wUDsfZ z=av(-FZ<-)HsZIeX8rX;s>aouzQX91QS%K~&V_c{roU7c(+Df28{0;g2-Ju%I{1qS@y4IturqRiuyfj z&xG5vGgQ_e`r=BlndY)kThoTd_Sti^4BorKXP{x5)otUBPIx`va(~Zu_N`7u9TI}e z_8d55V&!8~x_8_?_OAiY^~OwD?>ak+o&Kq9t!nScU%|cM7{jJU>n)}f&NbxjuGY6M zj4d6|(Pfo)$ou7kkN4`euXD$7e?M%{?AeS0)fP`V(rotp-UmKdM%gA94vl{8I`-n! zc~K3vubI%l;?pGy?1g(p2FMzg+EvQba@@GT>gYQf-aTC9L#cPKT*kaQ+a_Pwx@yxO zb(-tE*yn1k$N6*jtf7~6$BQWiVndy~=>Tfyp5g z{b4qf=d_Uj_FzDZ`t5(e)~NB6Viu8g*Lr2%WInjrriXjlz~*`T^rF zUeJqgeA<3$!E4pZZMW%GHu}Q^*T=T5Rj1s3UH?xnpT=PWPR1P>9ldHr`)3i^F54v- z9xmm$ZQ_-)FVC5IcZ$v{Gxr!*;6wi!tqR9iD{66W-IPh~+g)oAFRx+PI8U`x2RFSo zl{xpXcsXCWLh(hS>TF)FZ{^jyyJ?%_M^^4Fd8PNO!&ihE2i9l2(Pp8`k@2-0{W3Y6 zuHb)rNRelyGrHt*3Y@g1NXF%(orBw5E~EeVyQAf+?6eU^7d~3?qkjQ|cfB_4jjcHE z$m|uK8zwrJsC8~;&a2rP^}1>2p4modThC_l_{c*M1tKy}GJRa;%FJ%|dIg8R&-lo} zqfWEKugDi`EJm0t4bjv`ea(`dE8Z+$quEi_#lzB0eLodnt!{`$HUJn75%$rDa#>Mdt~hWK62gM)55#yU9)wm zax_{l^jXkj`~!!}rdeVZ%PS5DiaSzf#>X?oU7~6U9)?Bon^egc7x<)aGpAnq>nB~A zGC$AMDh&q}UU*XUpXpP!Zk`z{WLQ?p?sO*ksA+M< zB3*};uwrZ@J@RC!uEhmdoOf>~!=C zn=odcQ@85n^1UtU+$VOuY}{;twdB6I_=A%wV8!|PUHoWU2)A-uUW82^NlbxtDKYPyp3GYvB^P9c!=JbIz3*`27 zXqVx@;-SLW&ia+twQ-)CFCx=T$NjlFE_XhbS7tx6tCMeby_aKeUW|TeQm@4-xp3LK zh~=oeQ*(_8Sz#4D8Lf4FuuUxml~7qx%0|+Q+8pH5pEh z`wWP;j?JI1Zu9YH$IcH=$dTVi-)U2ULm3)osTDT&P`q&7?1^FbX8Jii>b30ItZ3mW zP5gG-_PFw9LFN-jREY>OSA>;;Wf;gb7P4IlYek*m&6U z(V0tIR>|~o+UjGu?aDc?bBdj5*ie4MyN2NAG~4cD#Da_^^cx*=3OZBZv1whWK9PDD ziS_mK7aUu~@7Tii4~L^!gv*PBRT%e^;zW9%G-%mH~0?Y_3n+^uG9tzaMm;@u=Q{3TIazxjFkX?f_wKXNw!#YmDE0yXq8q|A1@D$G8{iv$n_@??F+SqpWHdE?m0r1pjMsMe1Iu z(Pwks>~?!UEbz0+v!l>6m-VK3N;_7}A7>UfacVK^wz(2~9qmm#&C0wj*g+U_V5G3X z&!xrs>h292mESQbrf{v`VwP-X*z$S32RR`$8` zAaHm02hoc|S42Or_h-h_S!W-sJNxPGL5pALdsxlb%<$(O4dzd*Y_$8ghkX{c5w1?_}(o0j|>!Cn+Cr3>2|p4zN$@a z4c5gh>Am31N_+XjmVWQMHJZ9|c$HFRgmzu(^(bCH;pV|dhaDD{$bWUfi!Gj}uG75_ zXTCYN;Qcj@Ps~SUdRbnUzmvhHKBHE)>Sfd2c38Qi#aG#vu5SLqO+O@8Ka=>f_PM<# zRCf#xtbVVgd_w;w1FSu~dW|&MB@DZ(pCGhsGyc`dKTyDR>+N&2lFh8wSO0WY+ok>P z^&6oVf85Bt=l04ES1g>`xki|Ee480dYW{wvP^>Vk+AhIu+Nn_AnI^Ko4`jUCqE1U; zP<&X+<(vLFH6_voi_Ahz?N%a<#va=?I6g5OmZ_h9??EFBx0maAbMMCSlLLpymvp)` z;F?+2xrMUnO~^gt(v67L@o%xnw2p6HtH_m;_ZNS-R&C44_qk`=$3%JiMXqQV?|HMj z;NiMRsFE|Gd*<;S!~N=+j%wej=J~b}gRU;^ZnA%5-9PnCPZ`##)})~y%04;k(9Uhr zQ+;`lQGGpb=aBXBkAL7Y?2PS-JJ0Vp%g09)_|2+(y@2dD-VEP5Q!ve5-xl5V&}!?~ zS(e@CH+FK&heqaITI=t!@4g`R(FM#_&CGuPvqJ2?u=W2d)5iX_&8V_V*OYRXU+cWR zzGc@_ZEdbt%-Lc7?!$%W?q@E|d>6UosfSM;xA7OBH@} zy$zrB%sS<4tjWnIAETD^9ck=U{Ah-^*XMPLntx-$6eHK|)-5Zai0R?HRdB2`cBIE& zbvs5}zHU$>M~wpU=OYSKx_-+bq0x(RM~4XhO~)Zk`-5}rf`0qUwdLe@Zsk^ve%UL} z$Xl`Y!7H{bG@fni6zo58c^P@9LD4UFO%o@~?~&$@}HjF(?1 zxbZ`S7k5_nj`Z5`Pg(hU*Wz(UPs;qv4^DBYzSCUReCu7iS+bqc4uhY|yZH1vK0@B5 zbG9QJr@o5vesXYq(=!Jn56fbr7vHzMGPl{p{|7Y{%IYR6aJ`EFw5>Uvg~javcjJO7 zJkjA*AbPF^-iPX@a&}>)FI>v^AXd{T=K;|b5v(s2kd2{%jtRtZ(hMe1?!*}nJf5U- zBd{8ky9k6u?|w;SFWleVOWW25P?6UF=P+^JOe0WK^v=nCIH>@ra5p|Dz>{2VLgXB* zy$J)kl@Uh!U9uh>Dl@p~pqBw32NuOW*;(+YsN6f*4=2xI79Iy~k9+hU&rT@tD}?t* zb>#O10l8Jt75OM$TtKUqRBQTpg*$)G-qf*@O=WiVDRi zSWTA-a3%1@APl}C8vWuQ%oki|ACaR#K2P&3et^G;$fLVEcupz+Mxdp|^Z_@;J&Pt` z2)IA)K{OEe10PlN9oE`EkU_JnqdV^d6uS#($s%wp@OfN)zh>gb$IGqT&2GZbCl!FU zRm&I|2^IJqbGb&KL=?ks=>xuq+s^s`tH~(=-&c6O3Kt5prDM(M%scPYNZ(}e;vQPk zRne?D0r(p5inu2pF(u%4i_*iity(eZFHI%5;U-*i&>?ZpC^lv(0=drRl)8HnksnxV zHvp#r%W+ShY~0gJ2qVDtz!!iY3lyUc>m8%Cwt!Y7J!0hy;1j^XaZkPmJOuoCProz; z0nkk#e`2l*fJWoF%Q|;eSj-pxii@xK3eM7e8t^h;DUgnPx-l?>%i;f~puMd@`LU5O zO zXAs;nkS|<;Gjbg&ZVUQq;90;Dpe639*O&p|x4^dq{kheaEl8E%yfbR@X2q@ormR>v z8uTN;3YzyGTi|z!9Z6^A?e-V<_{lEj3n20$R0}k(T296Ya2D|GsIAZADXv`ys$hbw z3I_^27guuXdBCAS2h9;gR0TEzzXHAo{APH#b$cdLren=%HG9)ycP~H-sw;pG1J8*2 zM%0)`5l%+AX-}WqDJ3|l>R$!^LgUmxZzytWp4je|fICY0o;w6Cv&bS9o-WD@aANRu z-0mO;1T%ZLx>EtR0yhIcSLGUnJ5wRU>z@i|ocgL+zWn4A^F1IHP(2iv(DP0jr;PV( zxLK9Ert-wtqX4KFG9!aqE1A|`G8Y9PDlfn^$h!XZ6<~95?YhkyWA1pEQGPT&TFdqlz_h_liSCs5=7e#+6cU1}-iDq_DJ$`+FQTdL20Kzn20F zabesoqgRwhM#XLEhjHimzXN{*?!Xy2Z&Botz78|8WNS$4`)YTD8c!)-z!){5m1h8* zxW=80SjA_iJc@7%%8gSwwoW<6SzKPgt-wp;p5a`l-KD-(fo-GL-NX5-XkiH@eK>{D zHjq{zi?RfExH@q4>{~~#d81#mxP_8t#|MMrY$1cV`Vf!f(!{rp{`Vs&_o&EYf)=yw zedWqvJDKz9Ycno(^y?` zC;$<90cu5S=M(kbtGFhRV~rqp$OYf}*{c{#L&vZ+qG5m~5D|ltLNo=k^S)65t4Ii1 zC8V4i$y6(;kjw|74bGVf{c8)fKWl{2&AQlTDz6dcJ-E_cjatBCHYjklF)~o8OfBks z%Af4f?7$ZS1z@kZXIdvB^8#$Y=t^++TLI>uAOGTMWCpr=39XTo374qwPXhk}yokA- zx%%+v_W$OoeqN_;6mfMeHvl)pJ69)t_*_kT|p2YIgl^3sw!JAydv)5 zH5A2sFN66j4Z^{xAUqeLN8n4iK7Dg}@dI3|mOG|Xfqu%{B#XhQE#h_nuVQXSt<4x! zI)L}!D$G16a6_rP_y01P;JSgXp1+S+JD3t-!J0;`Jy}~yU1Pp{jk4=3z(aJ`zR;^mmy{dDNp+!Py42~uX!EC zd;uf;LY13=7sP!BYD%;_fGA1hw#N%`*6>pq6$KUDB5-HQ$UVU3V!p5$Cls4;+r=WH zBUW{|YJ1s7GgFP6iwdIZQ32v5N+_OVFB zMBdZ}4FWEREdXDk5O)yuI43tR@=75Cj}0GeSZ;Ykj+0zV4MhU_eiyt9j@5(BBs z?Z9_oIuXbok7pKCfp1jG?f1%T9{?p)%T9B&8k%$7e0 za)lzacg{W(JerIuql820`+zUw{Kua02!82%qF@lXG*r9Um}ovwkqAI;;P2yH8)Nd${7r`j1KA4B;pPu8RJ?1x5wjYUM%nxRsD?uxpPmM?dwfQ%}Ohe9tB1eJ_FoRtM_RP1Q2gd^h}4}0+)(N z*uHv2&EBM@wbjEA+FH01xRAz-4bTX8?}|5ydZxys2%kiFu+|61nhL=D^OrL;q9}R= z@KqY`AY8)L(u;yYeBA`ko~Z(#v+#2T+G}9=8&A!RYAjpWScH_1+Nhbsz0=}nk z9&p|4RZ9^~ZwEdIbOC?C)#&qhcIKy|^7+6Lwy&yvHRrmVN&E8!0u>ZaMD!cLe)V|Y zX;KAl2QF9W1-%RA4GUYzb-po@Hz;=X0HhGv4|o~y23))K?95-)(?o0p-lWPOThbw! zb6->QH>{gcs+8{q2o!xk@G+9r)bTOkVuf!@iuZe{J%ckt8eC?3>C+e1V15jhQ5 zjca}7nY#=EtAk?r#jq{VT%Fp$)zvCp%J+h@NJT$~d-^4T$AHzL>SabGto4~|JjLz; zW)N%}N26SdYYXO?dsyIqfe!$sS_J$|MqN9sW;>UmMz1FakW^s+zDI&{5_WU-;sO4hPeo;tIUMl%F`Kj zj{F@Miu-1G>ds$)3&ik{jiHoI-31`GYB?&x2e#wgpT*uzlURNk!hvw0zj)O9|g`QzUGic;47d%R@hem z8zKR&OV9bhkulu^PyHDJK7!B z0XN1y9*?KS`~vupCSdVH0tBFA&1oot5qle==L2`gJtmK*M*InQAIRU6k{1@c0Dwz) zsET1AE5F7W!yb%#Ode0f+>U4`sv8g`*EzeEUQv+<0RZS&xt#W*B2p;;YjD+2Jf3D? zBg%QHmejQ%#AWfHh{Om0xXeLIMne<)5O_b)rQ&)#ad98Q%IsdLt8Fw;67lLO;>wOHPGCu z6>*PeQW9kY*qQdu<%m#KvERVu%zMM2C+XY;{A;$dus2oCBzo~f!UX`F2pB725LPb( zR^ql+9#0av9XLB1uKn>~dW*KLJ}vI??2$wt0md`X)r+c#F?Jbn5pYD@WAb>$aTBmQ zd*HHbi}&}?zB(Cx{yot!0st`By^eXNM9H?l6y?*vbK)M8XU?O*&w=+T_Ey0VXL8SU zN&FFDJZ-B^XVB&t8Oi(_cn9zk8n4v2r+KIX-&J_G1l+2X##;Q)U^{>@wq0^1nQ9d) za-cXD;TuR+kx@_cGYDKN!Y5E3PeNkPOlU{}7^BqH3&Ux1NTf@G6^`PMe0HD9Smx_qA zSbGBQNZ^fwo~Th=F8>9^z}%D#ZOz+u)q{pI0!~K9s%2zC)olCFFMziJR{$~8SoJh1 zL%^3sIn$`UsV%I1&%S!m6gz-1Zv0k(r#%K1-AhEy0nP@N#62!gLvtU{h4OWTfkw0P z?}?@ofH8_)1q9M6oB;eA@O+wNx8tcB6`X(n<6;JXkG5!bRqcEA)tjai0D$7!b+C69 zwhSH&vKn|h-b>(_8k>R7D*A;WkS)VQA&WlHe2X8NSOCTt=qdor6JZ&yJ@;{QVcwn@ zQ3b98{#&g5rHWK@4Q}P%6LVrAl)8IJx0ERlw;X{>D0&O9AntK_62&IqQdGYbWLh2$ zM~2C*X^eRO*)S&rV2sg5B(1pq10MjMi?GQ2;9lT=LBEzV=JBCQm4)Xw=UIO;=9~bGF_7;;A!FeA zAm`zB1=+aA<*5r>fgb{&4~*VwRctmvm3r&RoD~3oEuZ`u^V)_`EA7B(z&W@m!nhih zdTPrs@Jo7`y;XK&h)2V=-Z%hv9_aO~6rjA!roJz|FunfgW5jS#7p3-YD<_up5eN3z!gN zQyhx$TAUah17zYJn`c4>fjM6J# z!E=DNxX0!Jpbz*p@LkYrsM^OdGA9CgW{VeqX;55SfDlOQVvtwjYy>X>7UJ(^y|KU} zxE%fOApFi|78T9Nmc(@4Z3O&>b_Vq4*VA6PpHljO9PmtFIdBrLRJlJM)RsZuFF-GF zmADE$BaK>2HdA zd`*G>P}YFbMFqrSY(^HU>a)a&!7Fev1v&hEp9~9Z2X19_(IfmdkZ?c|ZCmNj_EF;=aIhXC_|mbk}RV+Mh3xGuVXRJZ7F1|G>&Q-h(gw5>^gQ_W_<3qXvN z@;v}4h4~-{0LPBIj=~ud+i2Vf#41n(9>rPfH@Hjw0{XBe900K$-fb5fUH}qAe^(Ex zYK$>iQT9f73hZzl4lKrrLJP_H`&nG3T?uEc--vs{Zv^gC_jRkd?Y0C^^% z*xdsVptOOs0|$>@`{4?i><8>S`gI{r7}7gkGyOYTTqfQ4YZ%uW>k-`9ee>vlZxXmy zU1_ZWqcWtZX1$v((Rcx9Q2O#$Q8tD)Tg`xN#g#g1Q&=Fb-r@MG9VZMa+;Ji`?gF<} zAAePV5n#JQpSV!lK2iD*wktY>FpSD@w!fuXX|0m^z^QTH63-0q0x-wu`}}&sc~#oB zWW=Ve0Wf8;MSVQZ literal 0 HcmV?d00001 diff --git a/.pkg/r_pixivpy.py b/.pkg/r_pixivpy.py new file mode 100644 index 0000000..2b5b4ce --- /dev/null +++ b/.pkg/r_pixivpy.py @@ -0,0 +1,44 @@ +# -*- coding:utf-8 -*- + +import requests +from requests_toolbelt.adapters import host_header_ssl + +from .aapi import AppPixivAPI + + +class ByPassSniApi(AppPixivAPI): + def __init__(self, **requests_kwargs): + """initialize requests kwargs if need be""" + super(AppPixivAPI, self).__init__(**requests_kwargs) + session = requests.Session() + session.mount("https://", host_header_ssl.HostHeaderSSLAdapter()) + self.requests = session + + def require_appapi_hosts(self, hostname="app-api.pixiv.net", timeout=3): + """ + 通过 Cloudflare 的 DNS over HTTPS 请求真实的 IP 地址。 + """ + URLS = ( + "https://1.0.0.1/dns-query", + "https://1.1.1.1/dns-query", + "https://[2606:4700:4700::1001]/dns-query", + "https://[2606:4700:4700::1111]/dns-query", + "https://cloudflare-dns.com/dns-query", + ) + params = { + "ct": "application/dns-json", + "name": hostname, + "type": "A", + "do": "false", + "cd": "false", + } + + for url in URLS: + try: + response = requests.get(url, params=params, timeout=timeout) + self.hosts = "https://" + response.json()["Answer"][0]["data"] + return self.hosts + except Exception: + pass + + return False diff --git a/app/plugin/do_dl.py b/app/plugin/do_dl.py index 7b2b8d6..611d468 100644 --- a/app/plugin/do_dl.py +++ b/app/plugin/do_dl.py @@ -47,7 +47,7 @@ def dl(self, opsArg, funArg): rootURI = ( self.CORE.biu.sets["biu"]["download"]["saveURI"] .replace("{ROOTPATH}", self.getENV("rootPath")) - .replace("{HOMEPATH}", os.path.expanduser('~') + "/") + .replace("{HOMEPATH}", os.path.expanduser('~') + "/") .replace("{KT}", self.__pureName(funArg["kt"])) ) diff --git a/usr/templates/multiverse/index.html b/usr/templates/multiverse/index.html index 4346c66..320bb20 100644 --- a/usr/templates/multiverse/index.html +++ b/usr/templates/multiverse/index.html @@ -118,7 +118,7 @@

搜索

-

PixivBiu@2.1.0

+

PixivBiu@2.1.1

一款不错的 Pixiv 搜索辅助工具。
基于 Python@3.7(+) 构建。