From ba8b163b047187709c833aab5ee286fcf419271e Mon Sep 17 00:00:00 2001 From: Meng Sen Date: Mon, 21 Oct 2024 13:58:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=20Bitmagnet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Meng Sen --- .github/README.md | 1 + README.md | 1 + apps/bitmagnet/0.9.5/data.yml | 82 ++++++++++++++++++++++ apps/bitmagnet/0.9.5/docker-compose.yml | 36 ++++++++++ apps/bitmagnet/0.9.5/scripts/init.sh | 15 ++++ apps/bitmagnet/0.9.5/scripts/uninstall.sh | 10 +++ apps/bitmagnet/0.9.5/scripts/upgrade.sh | 15 ++++ apps/bitmagnet/README.md | 60 ++++++++++++++++ apps/bitmagnet/data.yml | 15 ++++ apps/bitmagnet/logo.png | Bin 0 -> 13804 bytes 10 files changed, 235 insertions(+) create mode 100644 apps/bitmagnet/0.9.5/data.yml create mode 100644 apps/bitmagnet/0.9.5/docker-compose.yml create mode 100644 apps/bitmagnet/0.9.5/scripts/init.sh create mode 100644 apps/bitmagnet/0.9.5/scripts/uninstall.sh create mode 100644 apps/bitmagnet/0.9.5/scripts/upgrade.sh create mode 100644 apps/bitmagnet/README.md create mode 100644 apps/bitmagnet/data.yml create mode 100644 apps/bitmagnet/logo.png diff --git a/.github/README.md b/.github/README.md index 5a8eeab24..de95ae9f4 100644 --- a/.github/README.md +++ b/.github/README.md @@ -87,6 +87,7 @@ | 🟢 | | AList | https://alist.nn.ci/ | 一款支持多重存储的文件列表程序 | | | 🟢 | | Artalk | https://artalk.js.org/ | 一个自托管的评论系统 | | | 🟢 | | Bark | https://bark.day.app/ | 一款注重隐私、安全可控的自定义通知推送工具 | | +| 🟢 | | Bitmagnet | https://bitmagnet.io/ | 自托管的 BitTorrent 索引器 | | | 🟢 | | Casdoor | https://casdoor.org/ | 身份和访问管理(IAM)/单点登录(SSO)平台 | | | 🟢 | | Certimate | https://docs.certimate.me/ | SSL证书管理工具 | | | 🟢 | | Cookie Cloud | https://github.com/easychen/CookieCloud/ | CookieCloud是一个和自架服务器同步浏览器Cookie和LocalStorage的小工具 | | diff --git a/README.md b/README.md index 97f18a1af..b10c29316 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ | 🟢 | | AList | https://alist.nn.ci/ | 一款支持多重存储的文件列表程序 | | | 🟢 | | Artalk | https://artalk.js.org/ | 一个自托管的评论系统 | | | 🟢 | | Bark | https://bark.day.app/ | 一款注重隐私、安全可控的自定义通知推送工具 | | +| 🟢 | | Bitmagnet | https://bitmagnet.io/ | 自托管的 BitTorrent 索引器 | | | 🟢 | | Casdoor | https://casdoor.org/ | 身份和访问管理(IAM)/单点登录(SSO)平台 | | | 🟢 | | Certimate | https://docs.certimate.me/ | SSL证书管理工具 | | | 🟢 | | Cookie Cloud | https://github.com/easychen/CookieCloud/ | CookieCloud是一个和自架服务器同步浏览器Cookie和LocalStorage的小工具 | | diff --git a/apps/bitmagnet/0.9.5/data.yml b/apps/bitmagnet/0.9.5/data.yml new file mode 100644 index 000000000..d8b1def5a --- /dev/null +++ b/apps/bitmagnet/0.9.5/data.yml @@ -0,0 +1,82 @@ +additionalProperties: + formFields: + - child: + default: "" + envKey: PANEL_POSTGRES_SERVICE + required: true + type: service + default: postgresql + envKey: PANEL_POSTGRES_TYPE + labelZh: Postgres 服务 (前置检查) + labelEn: Postgres Service (Pre-check) + required: true + type: apps + values: + - label: PostgreSQL + value: postgresql + - default: "/home/bitmagnet" + edit: true + envKey: BITMAGNET_ROOT_PATH + labelZh: 数据持久化路径 + labelEn: Data persistence path + required: true + type: text + - default: 3333 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelZh: WebUI 端口 + labelEn: WebUI port + required: true + rule: paramPort + type: number + - default: 3334 + edit: true + envKey: PANEL_APP_PORT_BT + labelZh: BitTorrent 端口 + labelEn: BitTorrent port + required: true + rule: paramPort + type: number + - default: "127.0.0.1:5432" + edit: true + envKey: POSTGRES_HOST + labelZh: 数据库 主机地址 + labelEn: Database Host + required: true + type: text + - default: "bitmagnet" + edit: true + envKey: POSTGRES_NAME + labelZh: 数据库 名称 + labelEn: Database Name + required: true + rule: paramCommon + type: text + - default: "bitmagnet" + edit: true + envKey: POSTGRES_USER + labelZh: 数据库 用户名 + labelEn: Database Username + required: true + type: text + - default: "" + edit: true + envKey: POSTGRES_PASSWORD + labelZh: 数据库 密码 + labelEn: Database Password + random: true + required: true + rule: paramComplexity + type: password + - default: "true" + edit: true + envKey: TMDB_ENABLED + labelZh: 获取 TMDB 元数据 + labelEn: Fetch TMDB metadata + required: true + type: select + values: + - label: 允许 + value: "true" + - label: 禁止 + value: "false" diff --git a/apps/bitmagnet/0.9.5/docker-compose.yml b/apps/bitmagnet/0.9.5/docker-compose.yml new file mode 100644 index 000000000..9e90e4517 --- /dev/null +++ b/apps/bitmagnet/0.9.5/docker-compose.yml @@ -0,0 +1,36 @@ +networks: + 1panel-network: + external: true + +services: + bitmagnet: + image: ghcr.io/bitmagnet-io/bitmagnet:v0.9.5 + container_name: ${CONTAINER_NAME} + labels: + createdBy: "Apps" + restart: always + networks: + - 1panel-network + ports: + - ${PANEL_APP_PORT_HTTP}:3333 + - ${PANEL_APP_PORT_BT}:3334/tcp + - ${PANEL_APP_PORT_BT}:3334/udp + env_file: + - /etc/1panel/envs/global.env + - ${ENV_FILE:-/etc/1panel/envs/default.env} + volumes: + - ${BITMAGNET_ROOT_PATH}/config:/root/.config/bitmagnet + - ${BITMAGNET_ROOT_PATH}/bitmagnet:/root/.local/share/bitmagnet + environment: + - LOG_FILE_ROTATOR_ENABLED=true + - LOG_LEVEL=info + - LOG_DEVELOPMENT=false + - LOG_JSON=false + - DHT_CRAWLER_SAVE_FILES_THRESHOLD=100 + - PROCESSOR_CONCURRENCY=1 + - TMDB_ENABLED=true + - TMDB_API_KEY=your_api_key + command: + - worker + - run + - --all diff --git a/apps/bitmagnet/0.9.5/scripts/init.sh b/apps/bitmagnet/0.9.5/scripts/init.sh new file mode 100644 index 000000000..77b849120 --- /dev/null +++ b/apps/bitmagnet/0.9.5/scripts/init.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/bitmagnet/0.9.5/scripts/uninstall.sh b/apps/bitmagnet/0.9.5/scripts/uninstall.sh new file mode 100644 index 000000000..c86c4fbca --- /dev/null +++ b/apps/bitmagnet/0.9.5/scripts/uninstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/bitmagnet/0.9.5/scripts/upgrade.sh b/apps/bitmagnet/0.9.5/scripts/upgrade.sh new file mode 100644 index 000000000..77b849120 --- /dev/null +++ b/apps/bitmagnet/0.9.5/scripts/upgrade.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/bitmagnet/README.md b/apps/bitmagnet/README.md new file mode 100644 index 000000000..c4e1ab3cc --- /dev/null +++ b/apps/bitmagnet/README.md @@ -0,0 +1,60 @@ +# Bitmagnet + +自托管的 BitTorrent 索引器,DHT 爬虫,内容分类器和带 Web UI,GraphQL API 以及 Servarr 堆栈集成的 torrent 搜索引擎。 + +![Kimai](https://file.lifebus.top/imgs/bitmagnet_cover.png) + +![](https://img.shields.io/badge/%E6%96%B0%E7%96%86%E8%90%8C%E6%A3%AE%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E4%BD%9C%E5%AE%A4-%E6%8F%90%E4%BE%9B%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81-blue) + +## 简介 + +DHT 爬虫是 Bitmagnet 的杀手锏,使其独一无二。那么它是什么呢? Translation: + +你可能知道,可以在你的 BitTorrent 客户端中启用 DHT,这允许你找到正在向分布式哈希表(DHT)宣布磁贴哈希的对等方,而不是向集中式跟踪器。 +DHT 的较不为人知的功能是,它允许您爬取它所知道的信息哈希。这就是 Bitmagnet 的 DHT 爬虫的工作方式 - 它爬取 DHT +网络,请求每个发现的信息哈希的元数据。然后通过尝试对其进行分类并将其与已知的内容关联(如电影和电视节目)进一步丰富这些元数据。 +然后允许你搜索它所索引的一切。 + +这意味着 Bitmagnet 不依赖于任何外部跟踪器或磁力链接索引器。它是一个自包含、自托管的磁力链接索引器,通过 DHT +连接到全球网络中的对等节点,并不断发现新的内容。 + +## 功能与路线图 + +### 当前已实现的功能 + +- [x] DHT 爬虫和协议实现 +- [x] 通用的 BitTorrent 索引器:bitmagnet 可以从任何来源索引磁力链接,而不仅仅是 DHT 网络——目前这仅可以通过 `/import` + 端点实现;更友好的方法正在开发中,请参见以下高优先级功能 +- [x] 一个高度可定制的内容分类器,目前可以识别多种类型的内容,以及与之相关的关键属性,如语言、分辨率、来源(蓝光、网络翻录等),并从包括电影数据库在内的多个数据源补充信息。 +- [x] [一个用于从任何来源摄入磁力链接的导入工具,例如 RARBG 备份](https://bitmagnet.io/guides/import.html) +- [x] 翻译文本: torrent 搜索引擎 +- [x] GraphQL API:当前提供单个搜索查询;还包含一个嵌入式 GraphQL playground 在 `/graphql` +- [x] 在 Angular 中实现的网络用户界面:当前这是一个简单的单页应用,通过 GraphQL API 提供搜索查询的用户界面 +- [x] [一个与 Torznab 兼容的端点,用于与 Serverr 堆栈集成](https://bitmagnet.io/guides/servarr- [ ]integration.html) + +### 高优先级功能尚未实现 + +- [ ] 一个 WebUI 仪表板,显示诸如爬虫吞吐量、任务队列、数据库大小等信息。 +- [ ] 认证,API 密钥,访问级别等。 +- [ ] 管理员 API,一般来说,一个更完整的 GraphQL API +- [ ] 更完整的网络 UI +- [ ] 保存了对特定内容的搜索,以启用除以下功能外的自定义 feeds +- [ ] 与 Prowlarr 索引器代理的双向集成:目前,Bitmagnet 可以作为 Prowlarr 中的索引器添加;双向集成将允许 Bitmagnet 从 + Prowlarr 中配置的任何索引器爬取内容,解锁许多新的内容来源 +- [ ] 更多文档和更多测试! + +### 管道梦想特性 + +事情开始变得有些模糊。目前所有关注点都在实现上述核心功能,但这些想法在未来可能会被探索: + +- [ ] 原地播种:在您的计算机上识别属于索引磁贴的文件,并在移动、重命名或删除磁贴的部分后,允许这些文件原地播种 +- [ ] 与流行的 BitTorrent 客户端集成 +- [ ] 某种联盟:允许朋友连接实例并汇集索引努力,可能涉及众包手动内容策展,以补充自动分类器 +- [ ] 看起来像是去中心化的私人追踪器;我可能指的是部分基于个人信任,并手动剔除任何不良行为者的东西;我可能会对创建看起来有点像 + Tribler 的东西持谨慎态度,尽管这是一个有趣的研究项目,但似乎已经证明,在协议层面上实现信任、声誉和隐私的开销太大,无法成为与原始的 + BitTorrent 相比具有吸引力的替代方案,尽管 BitTorrent 存在一些缺陷 +- [ ] BitTorrent v2 协议的支持:是否会有更广泛的采用使其成为有价值的功能还有待观察 + +--- + +![Ms Studio](https://file.lifebus.top/imgs/ms_blank_001.png) diff --git a/apps/bitmagnet/data.yml b/apps/bitmagnet/data.yml new file mode 100644 index 000000000..2b5fc0fc1 --- /dev/null +++ b/apps/bitmagnet/data.yml @@ -0,0 +1,15 @@ +additionalProperties: + key: bitmagnet + name: Bitmagnet + tags: + - WebSite + - Middleware + - Local + shortDescZh: 自托管的 BitTorrent 索引器 + shortDescEn: Self-hosted BitTorrent indexer + type: website + crossVersionUpdate: true + limit: 0 + website: https://bitmagnet.io/ + github: https://github.com/bitmagnet-io/bitmagnet/ + document: https://bitmagnet.io/ diff --git a/apps/bitmagnet/logo.png b/apps/bitmagnet/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdbe1949fed79cdb0149fa7fa643221d1a8c0c1 GIT binary patch literal 13804 zcmeIZ=T}o}^e!5dq99h$F%V~4EMEh0@nxc8Rh)oc2XIH_5r2Y21N zhh@>jvJa|PlA%!OnXBBY4?=uJF4@u9W#(2j?yX4Q8D2pZ^Q6tKou&%zQGE|^P@I7(nHl&%*7!LI^`CyRL5lgsEATNrj@6PzrlzA5CK{FoU}cq& z$eyt4S_^Xj%Ww$fm-PbQ$aAP$X-0CN;XifJ3d_Y5?kCC zggpD}mH4byl{79Nrb#c#qNtkK^5N@GAU^Gr3~c42RSW2es`kSuW201g`DHvhp~1(Q z6L&k>ILsad}LsUA`}5Tii}cUQfknZD|& z_M7#w6ZP~pujg?)+q`J}euAf(RM6pGyXxKeT-IW_WprIqN1D^2&PA4z&FUKgiSGwk zQlIIX-umwjFLl!a--;~nxPnW0h2E1WpL%#14#xGhLJgH-n;1tie{y3iOVt19*Pmjk zo;fP^0}x2Ho5||M;=POHD^&k4E$+{WUoxV|A}LXEe<^Ud(bR(QB6X@4{J?)bFAUmM zW%;OuG*$VelczfNXy1dVq#2hcA6%M=a1Kcn+c`4pQ8B3qJB-_i#+?RgZZNWIagQ6C z29S*j(xBCR-axVx_b@hegJb3vLR->mvotB7bL<|C-}73X6*hjW#E?65-d9lUbYEY` zCp-`(Rb8Q`tlRQh|4UybMIpOxC_S-$HBwc-aS7!8w4j@waBcfZ)_NO5l@t}|mCOGD zj`Y9RjsJh!UH@}fy{BL4#wEWjOi<*x_S=pa(*iVpF@!ZROa5xLta#O&8Q^W*#rAA7 zi_(V5FDz3#BVP=2>6`FB*@DR!TQrXQut>fS^sd-o+Mf;k7PbLGEEDUZhio#F^WtJx z)&MI)%%%9H?zBM94JCvyEpM)eJ)okLT`;N$g`MXwX&}}OE8=!a8LfRnE^{@Z;;{R31|0iPzU|n4FwT&vE2EBV<`P9^H|4WOh zUD-!E2k}(IC;img?`v{BO*B;Wh7QW#iV1V_kdNb8qWFVfKkkfQuXY~t=G~S%MS>pL zWl2%pt~c4T9dabfUrtRgZ=GUcR)`p{SzfM9v-93eq855CKHsv~cMIvRspQ!%z{?5j zJmITv(!p0J>^^hK=cpA7dG~&JlfOC>o~+{%QD(MsFSNXNVqe!jd^{I;Oyl>G94z0X z$1XRSm*w9)uZ6f1SgkRo2O_i7x&*x3 z#F^kH!bzSC^wp433sY2MJ?i{X%iPS~2Vz-Y7QEFxB4qg`-4nWA%C&cX?dcKj&9Ct| z@g{Z)Yul3DM0**n%anx8!B&-W%bx{^)uqetdHd4dT(1^(pE|uI5b-i(dWfIL?eAx| zxNg!XMH|ilwndjPQ`JhGp5cUk<8(w* zQISag!J`X=d87FSB6U_ zMJBp!rsYyEc(#k!a2~S@n0Cq4bE~fQN`<`SQWa%jH%!(MFA&wNk|KV5T4k-B-Y`um zO*%h-^FfJr`l36^s_JdSKbhrMR2kqVn>kh%JY8eMGu(B`KGpx}b^IYulafaYfsbFP zK-BT@Tnu*6rtOcwJF=+oZN_WJTUodw4*D;$TyS--`-5^F9dYfdf)d_n>gt+`?I*M# z>}pH?)L!Rx>~r_E^^?Z}mT@hpK3#gUr|neBTGuq*+79aa@Uziqz83@fRZHnRowYtP z7nAVXhWurHsn#KOSaav>SU%hTKn}I(*|56S-k10HTGR^6WpXm7EhwK}R9Oc*IV<1# zbAAV{3uS;Q_4)5>eYFzN@}kR3k^zLFYu0PSrEt*!Y0ZHePR3%bJRo#@L1x-hWBL1i$iHq$;nQztDWGuZjDl$!*}li>GGncQw0e5kz_ z;6C0;{}9mZy^h1)*+wZr>WAkb0{qOGUiJ73{8)f$duF^P)~RYIkSGKJb4#~4F%iP* z_ddI^$6Pv+veRtq8_mgpV+ah7tl#!OU{<|H|4>FWuu~W^-6-ez(-H*^*x+lssgLk3 zkP0+wEK?1ddy(Yv40<_f6L>G?zjY(TE$V7_ZO zg^^tbJ)HY^MkT`1G65?+~mj@mxt?!9pGokvT_r1T~pyb!-g-uNS<*_GPluG3qt zNSvVPT>Q&UwA6qQY37B8gtd9!HrTxw=A1cD<-2)_U2d&e?hODL^_y7jDM(IbOFw=_grew4`vT7tE5+{FKSfx1)Jf zogrQ@3@msc{nA#$IKKKS!f?{8PW{XmGaJYezwjdH29@7@9I{nu5#{TcxxCzzsVWM2 zEO>=yOe!UHUS5ZXB_CaDXV-D!|AuEM!s!hC)y}Pyd+x!N&w_6sG7Mmwe!!BiCrnXAaDhm8OF_40x!%YmR3Ha^f&#O6cx~Sv!b3fyc*WiT(h+TgaT}1Q}5PUBq zW*5R{<((mcrRV~{e-2)js#xKIs)?n>d&Q9foxNY;40Gli{pTDo=gSWHmR?x!i zDMT)E?{76@kF52a{N+E>>MgOIXCyBY4r%gYMtSwaukCN5P2EY}{(?N7%VRhg^7{VI zYDUwZ%s=TX^YV;;BH$ipzSm;04~Zwb;mDCfd-e6jMqryb$*@Bs<)x8Onkxb3Si88H zG!BV;JHBb8Zb9^h_9Mej{R9G60Bwh^Sy{A~SnYZbW>V5|hdCHh+m+PncF<<~Q5kFP z@BT(vxgCnABFH`(s=^1UT2B*g$|d%>3v$&N$~rT*lU}9;2=^8uTjz(Y6yeYD1znSp zQYYfU5RfF0s^SXDx6^WeE_k*qf4kSz6WJ=;;r3B7L63m+3Wal3cFAJ!@sn;`n#=j|OVS9dJD7G&%6}W^pCsWTDZrSb4}h z0VKux3}q8DvMAmU`+9xUi4ZQ~`xAzA-!7DDdl-+Q7$jp<$mKMtQVtFZ`BE=L=Ez3} zU7~w)SoWhB!|fx3Y=^Zaw#3+LvA)6t`R`KuLE{xT-3fQti0x9U(=e@n&>` zkNbiW6JPV*OqH*%`R*gDudi~4NSY9K>UsH2i5+e_gYO%$-BkjpZi7RUBf>8fI! z9fMN5fS}pj1x5I64*XH+PpCYc{b^5*j_|7taw}^R)pm9New9+uk};fwxZiA17Zit~ z%-7BL&1b+-bI2P3p9!rn8 zN6K^<2w!d&de1wm9POt2%FL%X4#V!+4k!>MlH!>mU%XG}Y55)FH zH{34&Yh`C*YDzbh+&I7X_@dGg;|E$W;io9yG*Es=00OUOdMc`D<6v{5_HYb!Yjq2q z$)d*$x7TCqu13Igt}*+nI-Z&}d4{eB*lM=bqW3)kW1a>AhByzwd*G$Gxyg$BRj$+yQSv#3o{%b2Fx9s;`Y}O4 z0SG%xR}bK63J`gk`B3J$&u*&YRqZ4!%vT1}h4=tpuOoS;I;4Ca#gRN+iBlVVj5p<- z!^)ax#R*?3Lem3JbY9T?&1iyZ$tA(d(76jpXwZ6-6WZIVdE@!7L5x*BPlHMxzO*&6 ziswOx)TA1a@luwq5{U0lewim>IA(03j<2oi@nDH#wq~M*ang>V8`?^7Niy2*84hJh zyOvTsa3ej!n~KoX2VVY%(d}D{#0+@Sx!Q0JkIiXAO=m2hm@j6fPeM*p${h*=Bzk|L zLU~aq!x#+QR`9%_TVK>6c{WsIWq$-~77SVOpWK}ZnloRo@6`|tYOv!)$umdJq%=tY z=BS_CL&sb){P?1;{xghU@Era04l&#!4{G5G1F+ec^={@BlpZ&$Q zyD9IbdCkCpg&%@RW-%S1jT4?1EFT^%M~wK~TK)OPxY={sxNW`<9xcs$q(l9I5>x)X zL6C#+ddN(LjJp8n@CNpz>kGYrI+91eBYjzEAfju9(~-~er{}rFRXM;3<`p*vj0Og* zhW{`Tr=+kS^_SD(_xPrHqIeG}Zr%6wXehhi#4g@A&OdzRXOoPd*xEW6%OP*wq;2#Z z;+{wUGtyx@_6(QP59WDAFPRRvdQbkEYtmziUmnGxmXxl0&&qV}x^P&y?wm@BY%mz& z7?7;a46hh9c0}7KEJ9E*QFaBJiXTNNseq8!C`M7K~uQLCgoMOZy8p zD%HVF=DcewJJ11l$-SBzv&xyYF!n@Pi`BsHSu34ZFlqQCkof3AWRk~k zpR!;Xa-L`g{8nna^=$fz0Lfx&AGjDwv|#(UT6c||`D_4&oHo=Cb1rR`8DnPVGAkU6 z@B-rd)D83?xo-GZUgK&Pj6fPc8ZCx6a;gH%*G+kU^PvX4jf z1ZX_Hpb@joCK*$8 z>X)ffYWFf|8fTeG@M zRPl&0bo##QI+xX1Pi4KN)qMKRImv7EAK~k^5Yx^{0;Owm7C|3&&j;mRnkRMBLM|;( z^wjbG^3^h4VOdEvVp?WlCa+s<(r!3>eIKaYJSy6Cjg5`Hy!xl_kh~yknO*(Ngr{dS z`ye>Wzv+SdX@i)iuPjqwZTFLW@O_ECf%Lta<&`?X6I5RB2ccY0x{$8 z7nod=J0H50qc<>EzW$360kstr=}ucgF>~|6Vxl})#e`_&0f#}{CnWB^l~Ub1P{qvK zGEY;IoKx#;t(p$aiBsS>;W08Y5i^3-X#2-A++JACZ2WAEjx)>u#!8*7hWv(mz_5Db|=jLpcd}Ch*ot7n* zPc12N?f+)i^B6NF{FU#@6dh}=!Jj@jxaGBiyH6%@2uaB-mJ9;X>;c)OQ%@`PbS_dd zb3>jpz;COa9m~~3#}|FM_G}}|c^1LvTmDrlaK5!=d7?fqd$NmXFT+WX^)I+L87^^; z6wz`}#M_il7AV=|zRcV9C8vQD?y6Cg;_Pd2YJwZ-;K*m?{{c3=Lvn;9(N3nxH*RW< zjm}ulDRHTWKMnWB>x>59o$2MUFCnNQ%50sIClq^*eUwip{OArDLu1zx@xz?pY zEG0idx?pLel24Gj(bP)bOyVd&a}mF2zDp9vUyJIMp`&B&2jofJi%BK;K; z@os~{VQ2hAB688x)O07wk{ZaVXKr$oJ)jfe3Rz1k|Him6baDvmJck&BtqS&ZZvz`{ z|JQBec=l*cnMFqIN7c;ts^{*fa{YLkn($l_AngM>TdcijI8VH0!585+4V_Sgxj@|b zA4%4IBftY4N}H{4rgNj~--AE#9Isr#wp5Hx?+@LZ55x1ybp#ChWX2Yo(aR{iu}2Nm zixtyEj#6SiG_R&5T^S_mjjd-+zPI?!X#pm#Gh*o4Cxa0#F&>z>_Di31P=S-nnHDi0 zjDxhkMur7ghj(jshlT_KNpJ z@MIKDZlwH`=ipNB#~f7tBc#QuW#=*(H)@*hwS3C;KPf-Sy=1Zk0NEvQiGk%rf?ibY z-E=YM2Kf3oyG_6r93%4&(K=wOJuVb`4>H7;QDZykKOHdoVRK`9A0g;!&#h&_mNswD z>^bo)L1laopk)fN*-jJ_Wgv@3#(3PqpBxW-Ch9xgg}S(%t!um|?2GNh0Rh z(vz^L5ZrY&GE?F$yb1K!_Nau*%4gmJT2+FFDafXtP-=#z<+-_Gr~Mp;vSH@3|KSyr zPJ5<4wz_sL`K8uk8$5IHzV7;4=T1%kQ82j?$Pg{CRvhc`xQ2~2A-d=7_@{}@jpb}t z>wK}usgWcmcMjruT;$^yeNx3mMH7zIr5t?D}vwi5CT3iZrgL*FZ#8u)tn0Z6>z>7u>p8%X!o{Qjra4RBB)+-?hF~Ap{_LCiN@ODi;ob*9@)+etI z$#lPiWn|vr)AHw%T5D+~9o!ks{EK_O0&fa0d;d`(~P%7na6Z^8>pdH)9Hh;BozDLHj!u~lj` zHpvp;joXY%qd}d7h8H;IC}}alYPxiIAOG}KZK1a|D7oTiK2!4Muo$aF$y-gFfoAg4 zzE%f;H6}Q!j$|5)%$mEpdGf?j)@gb!wydmBl(NWdGg?xw%=OJEnga*vj9SkMJ@Wbe zCxm-G$u+xOVx|(WDqN@-*Y!HMO&2Jf9FavOXhAJSStvpNnC-q@x28@C272c2{-tBP z<|`5%jPh;}-ns1c%Enrd#L$r(e9%A4@u^k{jg}4No`n>OmW9Do2h!QTG^X#Xge9%*iuzc7&zY&XSGVnQjQ#8X_H>LOJZw6fN;Ujj`B*bUfQ=U7B9Q}AyNo_LU?Q6nO(V@wb++Uy7sbT`lzL%5lYn&m2t99hdjMQlJ7{) z#!O9s<+X9FNZlyd<*vOcEZb5N_GbCsO$E(RcSWC)-}kVD{pN0{l2lxm_6}FkHC8V> zmz9`5E|7gExf)z5e?jHALzi;*qVMlu&#X%JG(7#z3tpz{p+zr{z_SqotY=$n! zm(}Cs>hV=y2pssP8yOj)d9z*(w0X@%66m{MV~s=}$R=pg@Q*({8WF^}1g$mrHv?VI zju@M3nG_V*C)%X6Y=@iIZz&s2yWHbM4MT0pAz?S|gBFww?M04Shw-@M`dn8R{P0Tr z4r-5^jwTznQG4|Bj+IwO*U2y%-eYy*3eOHZBgDx9nLt+{t}9agIuPJf(EIujIxQ}q z(&kAgr?Yk30^0xJqq+adbY7}T`KxsqjeCf>nSpQ9czDu7`Hp~2pUe&uk+5-V91BDx z$dBTya@uQ2_@e(PmD73m+qz-j4jt@!F9zLs5RDY>^;8shU6VnoRs-$1vdVJT^_J7>%wyM zY`e$KV);B?B*2Zb=TdAl);2%HG5~dr?Rj}0WB=eIQdrwvywAQAJqzh|5OwidUog*X zk9aoPRDYR0Y-13e;O`A%Wpkl;JIn^Uw&)fZ_pmXtbD&+uEyba3R+Vpu^2Hg+;4&-I zaz=DoD?Ivv>xyx8zN*;Iut@n=gE+?7yF@XMm=1hZwx92S!=}2vMfoQcxwtOn4JA{< z!(UGo)bpV@F~~`SrweD<(e}FUy5Q4!`cH1Nb9=MpKAjZZi>vi_=uG6|^Kv-{Rnafw zhk^28z#;QCsnGUV+Zz)U(1XC-qk8oC`+E1RqQr>k%yE?TA{hHI)nrhm^luJ_eP*I3 zCU@^VgxQ5E9mY_D{9r$Sm%84kqv%422~hJQ7N=xs;#56yow>ubz{aUi5;PX}lzF|x z#!>YeEYmsaFp{7a6hC!EhRupr1@qj z?71=nVyB&Nxu~@K?ZtGU#s$zujA9eQGBJcft68l53j8KWtQ%***)vFl?p_RdV8N$5r$5&(=qx{Tpfk2~-)C#9{d0nNBruzEr3b^9_ z6x}l|*%>W!kEeEv75EwUSUqsiL#!R>N9a_b&ARF}mc^J;abe5mxh1{bOYEJr?+k#> zf%hy%a$CDbPc65819X_%plGRa00X)@T7Kr$Ur-7)Y5Dynmr-sPK<#duKQyObuRS;R zzQk^dorWUp1mX(G8KS7yg98#kbdM9e2uO?ouRuAB7o>Pq)g2LN;LrdWgNzfq~NAcaew9M;p`=7W-Ta3)FfJbXz7a; zps;t%9=mqa!}(rqIbVnx6xCH~qLfb-0AY1;HULDu4LJek6nIq!UoI>w5(YuG+g+eYE=x>puta=>44(?eHDF#vkBb>U21>rlJx@@J>Xz>#8pg4ei&8was5;lxS%p zAmN$N`HE8hzR$z<#yS~gm9SSGt6YRikZtVl#;7&6CZag7@vVA6CX-Z$+h&F+;SG}v zm3{Bgfts%a<*ikq@W24I{`WGB=PTg-5)2c&^ph_G2nPl6H<-n};Pz@sqCEmkyK$|x zy3XQgXA3YoIQAnwUBpYDIQY5=#FTn|C3vgM`hfg|@C9`Hiym)tY|v)uwXH?*j^yHF z0TD5lMGv3}6YH2NNIeO-0dsjK&AR~arHcNeJooRvh`dN$=?=I3w4w_AWW*2fs5MR& zGA)O9#~TxZbpXbCpPK>X?ehDb=5#>YH4#aMfmZQE*&iI$9X@4G+Q7Xkwo!#$j@3!e zi^_#yWvBw?(H5X!jiCXZ*^T|tomCYP={d3`sGF~IIwtdm!q?$Q9tMnWZ)V*yo91PH zKxA7b@>D?7(XPkMEAm>3;~YEj8`@!?I2Su@qx=kTqwn7kX4X6pP3g`ZoUyFw)oVac zT0%Xs5v|9`QPMt|4XPX{Kh1|3!HXc^X^+nK@~Hk}FY+IBRD3(?{0Xf>XvigKdVTRc zxbsvgf~?Sda{AW&+rkA|kWMkYliup=jA`T}YMmSF87aL8#TI_L7*@}&#+(@>kf6ye z?0jaeGWkuT4&RMwRY?V<;?V?-vU?g{vGQlmr7GBlhKWNR<6GaCoFqTt(*vcGooOc@ z9*~s$Nqg!Tdn%!8sH4BCN3$c03^$rYNMo;@^;T(mrO4R;g&;{fH4;HXae@!`x-uPr zL@-!V?`l+?KjWheQWqH;u=LuxdNOK0`+#MI&&sK^`JdioX*8yq^@d||^iME35OQ_{ zhJyQYL^o|PKcO9m1+cJ2j#{KO7)ORu}Gi4Wdud19jyS?ol$8mP2t z;b`U>ye6sh2IyA*n+^};Zx$;wtvG2$HI)WqE604qKNY6?Kk+5rVg8@+ zakaMt&L-G~=Ok1!KvBQDkZ{X9Fc-!xlu=_qYt7X5`M-SJg*T2op z@F+xKB|fOZZ8CLao-c5Mqk0%tglhr!3a~U)mZEd`rrF(sPu@R18rehrR#kygVrXy2 z?k$f)<*a8lj*|67{ z<7Ios1dw;7Xq%60nvZYHeJ&B~um982&5e|}R4b@sm4_n8>B=l_TAG3^_5k-MU{-Cp zYf8mJ13MNvI5JoNP=%4cz$FfL^fa(FzT>ocXJl@jOw5FjEGUx^ps-f3@NW&?9-P>C zxem(>d1L4KTlpFt98OV{<8J%(kiF@~8eFM-4899RT2tZz#p3L_lVCGF!{H_Sk(V_+MFM@i{EwoUbBI7mlw-AVp?q2A-U z^pzV4j=qy!HwJn~Bt3XHl-S-0Y-jW^;~|}Lx9t2MCcAOjFCOWHafGEfrNh_woB4$; zU--=p8y}tO&EP*7-wf2a`1`q{vB`&?Dc}g4SXpD7T?a?mh2DGVZM`xb$y}~$5ag+Z zbI+fUgrhC5dtQ$S-k2P!SJdZNDSDKfEos3KqDM;sb>BaeYU{P{uNQ$ZYb{@QBb@!Z zGQIbM1qi0A{O6MU_TR5k!*O;bCmrXvmgy(^Nhp>d7UgEnT@}}7@qT;BJ0$mBl=RNA zKwHG@XSYn5j@~c3rM?It3t7FEW6q(a9}gp50jl#K^1?mGoj6~b+W;c}ysIse@D@pX zMfvN~c3^<8sGN|kt|j+s$C0RX_g(Do%BS_JfP*$1^w7EVzL=%T0ZM5z*K!US$6guC z&TI}a;PFF&*Z}d@F~Hg)#y>#==Mm*DvvZ8V8OOqa4t~T+i#IUFh?TvkNZr^AyT8iB z)WybO-+LXx-Z^Qdh#sz!;9QnxdB^MpkZbTB9Hikxm6$S%Bh~#5WmxH&r0|UZLzUyt zWk)?3C|m+NqX_`nF!NNOhqJ6w${Kv1AB^96aJT7qU_-9w=J=xCxi9YYvY-{B`1 za^QDo>2}FjWoQH=z%Fy?BcDYtaRt%ByWcjT_wROBZA!O^UGJ~2MF!m=ArH`vX9~vT zQ-=nE?W4%;^BT0oM+ULr>5c`S+JBXbOoo?5f5*nRg29pob^0=-<*ByPLrB*DX#< z2*RyQ3hp1*HFn{ixsEH<)IdFqN9tDKGet;|NT|4#b+IV{nvFQSuhU~v@mXo}1%P!6 zcLr9=?>+N29PJkmO~PyW18u@@xdk#QS9;u8!SvNX6^Jo$yScfq!$JW>?KNLc7!tje zPvcEGkGT`&%nTdlQNxgKNY}fC2q2B9%({GBA3dK2eBtuS-Ep+tNYU-lY8Qjx?c!{7BrBQ5_eCJz!@z)1;tlCEW z-wr=-0QmaPSfkC4tzvITp8oI9KwuGsEMuK_5;r$j8|RZ&N-KM0VN`XWvpjca3Ut1& zuVm5r3@)`b6#4CJHvA$XGXV+}DE}(1KfjzKHTVPU^F9~B=M`y#`16IcVJAd5D3nU) z&zvOhH@KeCZE_wzvR|fSvqbZQkhWVVSYD(-=v$VopsrP(%~{Yt-z${#-Tr5O_X^Wl ziv61!v!lsVss6-NB%#3!*Yb&nSI+OqrFnn7BSP{rE9q`Wqh=h~ZlP&vB2pTp#^Rg+s^Oiy4sM-)|ur#6Q8TpbEvW?Ei zptJkap9LLcho=8Hl+vj!uoJ$KE~L#Ouuo64)Ps;AN1p2|!IaKhD*FTARF&AB^jFF! z`>q9sdSiO1hgkzL*uC^z$(#0U6iE0#xG5hHOTso}yXB(SA!OT*!wq>h!>?oF3EbUnbn@i!x>6?Puc5TOW`%4P zN#(_{9*l>sKwCo%H>@o%A3#)plO;8{ln&?S3seU<9XG76dS&{J?X zi3WPG9ttZZ<%Bs&z9wN&*ZeqD?p^_e>m%&G(&_#M$V24rSLyKf=)qheZC^uU0Zix&B_X)t-NCi3`i;2=YMJFZ}@eA_{F>Q@yh}E2RAwy+l8G zBd0T(adI3h1Mq|@yGz>5yNcv|Vopx%QkG=Kgx*u8)$hf@3xN=M&7(%?@TYXYELa5S zp8%w8HDDBvYJ^%Jxw=bTRC!`he(-xOd_o5bj4ZQBjDO{K2>!66_UWE z{CcIKbYY>-<}`!#UiWb{VRnyj0? z2HIm}uI*GWQJp^pjaSX=dYKH#UeZq%@(1)PZcu2%3!%_rOVFX&@GKSnz0Wm-7}L3P zTF1C}#i+X0S}SSP<(#QUr#{$&R=VT>vz#0=q7842GfYN@3xf=Uc<01j2er8;l8Wse z{Cs2WJDFIQjm)$5*Sd#+&d+(uE6cD5pG;WmX&d#{7j!PmRY$UErn;17uw=v?-%ji6 zFDxsY`AZDu{w*t{9p3~5>3%38Dz^n0PR%V^xUqhGHfB}_78|39#78iQmlPEa0izI5 z3?NF$)Hn2soV)lGdT;h2IsEJJMBOyh*L|`^zaWNn+0s1Ad#$?-w=$B0-X{$zT2?gu zGfHIlaq#wh?)7A|2TCyzh?}K1iI=6nG4@z7BN1H-Gs-;S=awMdK)yF$j^IhsE-DL( zTwa!os2-k(POGz^l~P4bRQ+Som5}g&p)PdX&d(2?kn~{krZkI+rC&_$D1Y-E=hWPf z_=)ipZAaLN{JU)6|3H_)M5tOG*b=7?Y0l9|qLCT$#_R-scrfVprYYBN@F9Vr8^_U> zWpq9OKgIwAAN_pyY&s{OPaUOv=;$DHR1i%$x&~kSuVXRm;Nj4eeg4+vpHudAX*oOp z-V8O*Ldyx&Lm@Y_gK4q&XV~|ncnI{4vx8Ls%lq+nfxk#`5v-N2T>w3j{y$~XKIDuS b1UftWN