From 0474019df46a836518e56d9056ebbb50e8ee896a Mon Sep 17 00:00:00 2001 From: Dark25 Date: Mon, 13 Jan 2025 18:59:30 +0100 Subject: [PATCH] feat(src/es): New source Katanime (#188) --- src/es/katanime/build.gradle | 19 ++ .../katanime/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3837 bytes .../katanime/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1932 bytes .../katanime/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4955 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 8740 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 13463 bytes .../animeextension/es/katanime/CryptoAES.kt | 220 +++++++++++++++ .../animeextension/es/katanime/Katanime.kt | 259 ++++++++++++++++++ .../es/katanime/KatanimeFilters.kt | 143 ++++++++++ .../katanime/extractors/UnpackerExtractor.kt | 31 +++ 10 files changed, 672 insertions(+) create mode 100644 src/es/katanime/build.gradle create mode 100644 src/es/katanime/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/es/katanime/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/es/katanime/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/es/katanime/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/es/katanime/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/CryptoAES.kt create mode 100644 src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/Katanime.kt create mode 100644 src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/KatanimeFilters.kt create mode 100644 src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/extractors/UnpackerExtractor.kt diff --git a/src/es/katanime/build.gradle b/src/es/katanime/build.gradle new file mode 100644 index 0000000000..e4b5ffd6b1 --- /dev/null +++ b/src/es/katanime/build.gradle @@ -0,0 +1,19 @@ +ext { + extName = 'Katanime' + extClass = '.Katanime' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(':lib:streamwish-extractor')) + implementation(project(':lib:streamtape-extractor')) + implementation(project(':lib:filemoon-extractor')) + implementation(project(':lib:sendvid-extractor')) + implementation(project(':lib:vidguard-extractor')) + implementation(project(':lib:mp4upload-extractor')) + implementation(project(':lib:dood-extractor')) + implementation(project(':lib:playlist-utils')) + implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1" +} \ No newline at end of file diff --git a/src/es/katanime/res/mipmap-hdpi/ic_launcher.png b/src/es/katanime/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..0b2cfcc86b6f1425da9ac5513886acbc06548ba9 GIT binary patch literal 3837 zcmVi$2>fuQKP_g_bec5FhpJN zBj8Pdo$j86;8Pz7KA%DK1rv$Jh=iytmG7&nnXRdrU3O)68GQS{_r0mEs_w4(`v3p> z>zH1>dZb5sq({DEl1_{7rPB*GbjHV9jP{y#P1J?^CcWHYj-Njk_<7Qg&U7=;`K})g zOM1}=Cx}KWgx4g!a5!+8<3;B>+2{;68=hK=`&>Uhv^LY!FEJ^<`#pj`s z_u~sM)SWUqTIjfq*7CnKR@C})_ecfNk3h_+AZ34O>Fvm0Y1gHu+I{JCN!8EX3R-e2 z$O-XMer3DCqMU^G5Ep#|ECZIhQfs15f&T(4Tt)uZ_$}a@lIH~RrNst9OK6ggAH8Qi zpYP5XUFlq*T|1fZO1x;1ei>g%b6|r%!}SuaNpF!n_=ne(DTLEpFY??Ve}^)^{4n^K zGm_c(B>1PFz<2D24qk|_>!My7rtkpnxi>W-8bD1Q0)KOP{mhN7uA8?BG0K~{8T}8Q zaXISIY*!?+`Qf&&K|4kR&jZtd8L0PiGShUNS*KHlcFdRH%RI+UIGNndbgFL9cs@Iu z^x|V^=SjbDA2{e;YBKq4(&!kSw*9Pv)MRo$OaX}S&GJ!R_+z>NzW;`Y8aD^ITbkunEo?NMD#(5eqZhT(S6XP9M3BF2 zb&i!Jqwai={F>v(NAkl^n@AuCzuB(eWg=hPNsgvsy?o{ZlAtsvQ2E z@zeX*&oHG%j$qLO$IpEzP1}#Ib;ref!C~KF*XKufJ+xZ<8Y7S!}`X-JjrCMV< zaHPHHuPHyAiu6>33#Zu6GsVq<6!nZn$w~1J&?t|lZ`bP*VbI6nlj(*A(Hr)>fnBFP zlFUR$pz|}5L3|@S-$IB@P7xdg7C|enM|?95!}!naXBNcGgeU0d+(jpj0i)r%sc&s+ z*Xt5t^okeOFhZ7TO;&q_4w54kI{PUc>ocwj7!6ViX-k7zi24*7lpwJj1nlQiK==(H z@^i*QPB!if|MN?`PM3*vcv+4x;Z4pIe4QY?Wu!mn`*uBBI7D-FLsa`DJE=*~(Q^qF z`0<;r3gS3*mheSEdR;EbOzt=|u~LpSWdVzO4<#^a_|oBSxRc+{*JZ z^PVU5^ItXFM1QCPRE&Cld@J*yMHj;y2b=Y}O=>5HTca^`zz1!U&}L^9yzn9AT%+Y6 z>Yv6v(>MybN?V()XFP1SCl%xd7M@G(y*9{g&k=2Pqh6Ov?Qg@4)rgeCL{*xbK|s4e zCp}dzhxp-8sX?517tApmU#CnZ?2qfw=Df;wzVwHxEc}Y2myEG+4}Z;iYfb3Dr4|Iy zJ+55RD3Y)ed2qbZJYbI7GSO^O(~;?Y)YlIes^yWW$>n4f{)kfqXv4@taGLO=L2`uh z!(}P>m%_W@duowaaQ;oByrvyFDB#{S=XouyT+1RSkA8F_XZbLjUvtt4ziTrw(_M5|GX@8rmZynhuw zNHWOP%ei132=b%xe2?AEsuH=9NOHVfnz6~1m@pQ~5FMZGsuci!d986W-OR}_R}SH1 z{~!tQqQ@1P(f_y_Fa&|BG;d5*3gi$e*74%wQ$cr3ljwS;ex6%vGxkrci4$it*Jni7yZMP zYdJKtFaZ9fN%+zt#dt>a0ZkY_XcT@wbo|C6?e|x`a5=)2@sim&nf)Q2|?5Bwp694{WQBHU4Z5~%{+qBiWN@@GLwX}UL(M%u;pSEXV*mOag>ry*&VVwS1D~@+i9D;a2eOP zzwL%*r%I~+&8-rCbSAe}+-&1%Jb|H1DHD|H}SNaeLiTN5FU+l)uO%KsS0Tut<@_@a{~Mh=UrUZ zwcp=uLZ{4~1+@+wW_TopX2vbv+Dv1=RNd?!@Z^_0_Zd?+b6Zy(wyZ#a4Vx_cx-=hw zV0a_Uisy%TK#Hf#P&Ra}#y?Irf1o-A#vO==Ln_T@fvdK4c-(C=e9v9<2`NOf^iRmk zm3Oi;03Vl)M)U9{PoK~SakG%ejJc=A5JaLu{{q}i=@wJuNG4CX$~|?tUytFOTgP@M z2wM{>nM)-1 zIWGJIx!H~%KeN_%JLnUr@}seS`-G{F&&8;GpKB@+$=B%X&hLGp{PI;qbnooA@u2b!Qgcdk_@T`j^G3qI#U zxr6G5GfAFZr^^IidsAS2o!*eJ>!<_X4ZYPLS4z)8qy8qqRvH|Jx2$xO7kL{4Wv5q> z*BjUjkk8d^*vP805ug3y+{JrLoAv%)W|=PMdkcM7U$>>Wx`^}!I*;{;EzI&Z1r_Uo zK{{On+-KLJ2_!J2a*v&+Qpneh84Jt=mI6({8^8|c{kl=F0uzNM!YJzxd;}b&udfBt zR-G$>H-RO<<5n3vz5=*FU$YOaTO<g=|noP>U>R*C8OeTn5(*|ESfV7R^>0mzeMMjdJ?JNYQE zwN=LVeGN4U7X4qJnu)lG*5P{-4Xvr{JDG zz;nQXb{PfvXEis=4m<%IW|fhk`9Pqr%Lvl>Cjk#v$lnEc8+Z*kiq8>Bqx%_kNYFU* zzIKaH1l_4$u!UVN1*GwyCQpLdMHI1mf^HIue3DdhmQW~ycrU;FcB111?ID!;P69l@ z1zraZFzS$?2h980EkY6WG0^J&9C-FeO(~*@V=(O6&H2?#FVZgms8403KxD*&E zI!;hu;EzI?_afkM;1%EyqYeq8526wfmzmFKQ-mVuE`4J~4FWMEqz2W+SHsk#Gc-lC zL6A_6JO%~;LxAV4@&Z9S2&F~TfQ}Hve-^$Vln;GaDE~r#STtp!2&xD6)M)=&lD_d& zAPL+jtf5%$a|O{P24sl|jXN>zOqsu3FT+>y9??2M{GuGCUKUFAIT95=1k%R{MbLUe znIO%iHG=9Z+f1r4B-pHq4xJIMwUpqqixS_%_;dM%c@?RGNS^dl5n!iQIjr0*D z!@7?%_5MPQMn~)4l6VZ)Cje)ddFz}AZC*k}!kk=QC5)vMU59}p9XBDD8(0n;B-E6F zLELCyS6#NbP!9Gw)+sz^ofdRN{`Vcf!>oJ~!DR~)c^t5}S+48s3>1&^@)<&D6$vbh zvDR5ZXZ90%jY(fq6NUrl2o?FB3pmz23?eTBb~E#J843E%XwbnznUWd%a6}sxTf&^ClLyEqq#CP>bkMu~7eAndvaj{^dQjm$Y00000NkvXXu0mjf?4e#t literal 0 HcmV?d00001 diff --git a/src/es/katanime/res/mipmap-mdpi/ic_launcher.png b/src/es/katanime/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..3a135eb565ad2feaa83e70ef90287e35eea00d09 GIT binary patch literal 1932 zcmV;72Xpv|P)GyD*`7PLW}G%XHgaqg$>8l=yJshUvZl{z|kL zf%mXsdz0|q&~WtN0Nr$!Yqs;KX1aH4W@2*Kb}pMzTH1!7&Y2U@fJQVi+_2o$P~rfT z`3+z-aHY1)YXD&MylAo?jJX+$u@SsqfTO^dFzilMmqt+}!dlY`VX*H56Q{wIt%Pwu zYnJl`+DYAXcLA^Kw)2)@Iq!gH>;A612y92+D|l}&+SQurIjQ+*2f+I?J+|#VCF|2@ z(yYWPOtf1!y)C-sJp*?L<9VtP^Txoi>x_7L&$(vVIjtBwEBzAMhUpag?%{6Y##n9z z$x1_Wg6mB{-%+jD>yhGVF>mt1ctw}6>GX}19ywcD0HGm?KvbNmcO>Y4d`&Qm5 z@^e+52(ly5e;5lila!_fQf0Ct3RHPz>LZF1DEXWpuXOJ7pw2W^&W@J zDLv|p!tYwOKPw7bhkA!goD1ey2fC_#Dm@af=w#ULHYm9nI8|3LYnJyW`L#MGWis@; zv+YE8xaLE0U)B^}?{tI$ajZ@O2E9S;CnEocpHG9Ie+AjCR=WIH{uFwk7 z)~8t9xXeH%#1TtlvHJTc13}@B5JLlD=$`a0n$EM}O{e&fr#Pd8RN6)7hnJLb{NGce zq#)PZsZ&f??mubeeH<2t-#6;GXM`y5;8y!;6jvb|RVQ$UIMoZ*e%4tXzsbq3kpc=S z2B^GD_=TXuPz&`}$8U1dmAWT8_`6hOxwot1Hz|o!Sc^g`>e~h3W{3rKjwU5uN(7Wq zQJ?aX0r{#%8%n$a4JeZhxbnx&co5S`8&oD6@Lkf>@$*J>38%|D6ke~>)zEfLsw~ky z7E~rH?4&EKI&aPtidS4hHBQ6)I2-VB-)l10p<0HndSUB8Zz|O2>jYN+9{w5Sw1=09 zy5yWF)lllZxf5!gdIyFKl=v&Cl!`i(x7{Dmx7UazhO0y8O#UYocaHchQi4M33A*~B z2pd9o<?B=eOVJ@Yz5E;SR?cKS7^&AkmN=J zTYz1FF3aNiV<4T=(Ov|M2igKpOZ)}Ehrk$#w-@LF6aupZuPd+zhythoRtkX_Fb5bd z^NpBOsN2bjG7mDnfrT=!lChrPmCNs=y&hP}d;^MQd74jo-VaOzwgZa=uN$yJmaCH> zxKkaJC=^THM0Zx1$7(SE&r^XZz%q%qjuZeov!DUC^1KJ6F zXtI~-Su5Uu5e}G*ss0fEHzmkm*ix(z{myJX7JfA8@N^Q&6_As$v82J42GEEJQ2L z1D^TWFO_*H;0s0jT;{tLWuc|1_wz~^xSh&t2;sf_*Z++rC@~5cAuEISIH}!E$czR$ zO8ox7z|?cI5c9J|2nYis1&;#oMphH-l>Q2+AK+y(ey;@b^G8Om#Qb0$Aw83#t(g=b zf`13l0q6l-O-9N6%|=j!f|KWG=9dBY1GkX@0RQld-?2a+<-Jkza|^XlGthsM9$b|( SnS`YP00005JddoXm4Nhx_RX_d$xJ& z-eq?NGaNUVZF!Yz9Y0)VyWtOPFRX$Og3p038wRi9s+#0B@D=@?A8x{R1FjYDPTb$> zl%iW4H@eXA!jD^S@EN%O55L3n^_IKtV~EQsmK&aCdle6HD>;5;-i7X(zdXOZ{`KcV zsMexKcjkRF!krLcMCW!^-j0NR%JG6*Y`5|wWaw`|WKy8&$qY=c$-R7^=O-hC0hs&E z?@c_TbF9VDTMAxuo?VLeM~(O=#QS%6|8sDIM1hK;|^xJ<8~in zdEvE)^m?$T|HRAZJT~KJ__WH^Q>;bNUpWh-y^-Nj7)JFnruCn3wvCR$sR(><) zu-&d>Y*S4$5$xkYnW;01wdFZf@n1u^-bcOQ_|Z40BOu&cEHC(?<%e_N=W{w^pfLFi zKl9-Ke9P-T$|^@q-gcDB2V4QrF3B>+u`cAe=v?;&7Wx z7eS%mw^rFkzsQ31TWis(o#i)U1~xZEH@wW&iZWBWs0{yz`(GddU9pn&knkJud?{-S zGf-PI<;s2+_?0(N$;sc4Kuy~Xwd^{C^^^%axI0#+Pw_Vq_SIDO^1D2+jko>aJNRxZ zg+715hH26dK_OE3{Oz{R?gQ;|bPROF%!}Oaw@SZb3R*I@8?Dfmj2pJ;Qt1YVGea3! zS`IWX<#$7cu~5P%km)x%i_unl)ejf^=yJGyjbGGH@+8q|(VdWbeZtrgrq-dE%52?OB!GK@x`B1LUl%bE)zf2Hl_U>E5J zH6*u*8dvC!(`eho%zQt*83Oo`HWfW>)2Ev{LG8AlowUJprdV6~8*v!uhXl*nPjNcK z4=^0K|5$!A?m+MJ=#gw<@_>Zj#C9IK=I`w~Mc<#5q}_gRm%<-Y5NINgkzw|SdFAzc z+PV{H!lgwAUG?6A7oLPT+%El(JLdYqN2S|H0ww*)^{=g9j<7>d?XNxYL(?yj+g%W12g@LZugsEW1p)Bx^hc)`dZ^w zG*LI^2t{1`!h&EwPi3BDY#ykvWB*%|F&^}w3yh!eI&r`u-Z-J``EnR}8*OHe6tdLS z^a3)Q_I077`c9TA4j<@Vtocr4SR)4PFx?T4Ipw_1Cw}jAT>lKke7Q)12Afa3598m6 z5_e?&S5P>bE>vRGlsC;MJdmfm`Dc(~|AbK$SI{}P|!Mih$6 z^nEG3Ri=R`df}p4ZK~64;Yv;brTtDgXd$@4Rra7u^WbQC!T+{-_q>bOZ1bZB$~Y9k zK{N5KHevO!I0yA){LM(NE+pDNhg1@?RgYtpqW$GJ6A~PWq+&)Q1~ZG5WKuuRHTOTU zS338r)dOPphamK4Qtx}(mIQlH2iaz3Ej4mk7xo1*-cbG@O8M)V+gbf9}z?TmM#_(4)D0SA>A|*#ATvgc}N|)TrzdCKL9KTb+$khu&w{Lq+KLm1XQzr@zI! zB_?xfO80r&OvvEAvZ?4-j-@}Crk}NS2Td!|%~mP=SfNy%j^~JO>{+M7)vGb;Yn)hW}>fH^T3xznN6feS*$tCNlkWPJ*+|m44zO$rR%e6qz!%k+(IFl7;zMNuIR(bt?#m>rMT#~|C$GKt|rzr)?t9&b)`L#B^&&?0- zq<=Gj7QKai6UU8?z`KXyZx$4n-M2eR_0@Q$zNyRjgl%Y+Pz%mRf8hdr<5O@lPE!V4 z4VRSWdP+eUcfIh6`oycuvc0am^a3z0&fLuOCAOLPUxnu8oW|3Tqff>DS=+>Q8^jW;90&KDavpwo{a0H zgkJxX$&9h2%=lUpG&zPN20}cJlan!nrOe3TGI(0Xb<%-j`F8GP%6PH9sblY2#(7eK z^RC%U7zqxgHY81t`PT7*7aFamm=v(|f+(30qrqkbk`{pGav9%A2iV;!vKcX6OoU{v zT%9N5JL!Og{$DmD#*+1IenQI+e%9=|((M+U&9Wz)0*(bR6-qxRW3rjh3M0@+joj5jM;PQ`BFoXNTxXOX1U3+=`8}z>1OeTycwE!MZud>*#V7B}q zQ~huy6Jj@Bu=O(ha)Lfd_y};Q;h67ec5-8eOxJ{mSs@o*Y@0QrISCvI4mER|p$>a9 z$E0N1COp{0pycS)2V=*P5JoBtH+Y8g$TDr)1@!&^Zk%>2$J1`6^-%CD$I=anJl}RJ z?P!u*vyeA<#O7kEOlFK9xl-FHcfC!fYf^$6`)GP{e}Usi|KfNd55&l1%JA7-4YB-i z^4t22V7t*$nJJ^kL@0H_>2*rcjT7eJjb_1(YkI}NFiQ`QFUV$89%YG^&;7Yc*8I>q z`#N|V7&&hEFim6`zX=Pr^R_u)=_hDIuY_%XHaAGqejdz@Nmw1lZ07NPpv;u-8+{{7 ze(74Ljb7c7y+cqoF8mwxlaT_~e zNa&-@tymjLrfP{FEG(AS?&EX>C-8s~o{WP24EF^rq1DENL0EuvA>tt+F^yYz6FA`3 z*D;0JS-$T5@kVR*3$|o4U?kXVQH{8i% zy;MA6f_NqahcUPVGPMg*3Zj`z&wU?<*%t=)8|zinXl~4wq>5I;cy5>RpWIM9J5=q+ zxmVU22x?Dm$YW=X))o~wO5zy>H~h3dZA-6Sj%GoD&*ExmeJ#;FIIGRCoJqeZ)u(d5 zGQV#tQTeaki^mn%dckf@C@;pF{a!?A|99J*=(v?bl_YhY$@gy$kr-iY?FnjW&23Rql0B=Y=zYhE+ctF};8p*i| z>|Dur1iqaG{ulTI!F~_L!@G~f-}GBb@opJ-y>KgonG>_%b`rP>{O|b6uruR8aC4N2 zTkRe0FN^)x2@TJ00`H8&Ba?Iuy+enO+rZkoNZc2}U#24@;ke;<3rp|a*XjQp&LhBi z;6>mc!Nu|OxzcX}ymOVoMe<#OI9`7ScT2fF0DdOzFLr-#{G0q{C3rEoQ;xr-{88Za z_?ZRpIq+4m?x;3Azcuzt0Ws`^r=|T33MwT4T=xZE1ZPTrY1r%?`q9#6%s7Pg40xh+ zm)N$gcLV(W1iVAOOQ1|XKmd5ZM{lp%hU;nICe=@S4fn@E zAFLg&rv?^4MKu<{3h-8Cgs`(RHIX%zsA~n(C<@?i@K|sQxS#YRVFt6JNAmXkN~q=p za0>VYxC^)yJW%=}lo7zE!Dm5z0D^_7HwYO#p01 z9|o_G@AKf4s@C(upp^N}#g13Wb#BReC01x#3k}}Ge zvu5{$Z&Ls!8?TCL;$}8AB|Z%d22{eeNB)B zJ_a^yR(PN_+kbuUg?I7va_J`xHGly4d*I8U?wR6xCKD+A5y}XFX7g^!SSoC?nh`)F z9k5QtmX|WM??7;OW$44#Yz}_J-AUduxU-(A3o4lHa$vq=OKis(b2|g zU;!K%CyLh1ua!~4MCR0T6$>D~r#((#u*n!m0F7z@&kilb_et2(Ocd(-Bx)3OKwtKUj`h;q-T$yc$>lmnu{8sQ6BduT+E?{i5mxa2|MD{OxcA zK>b&zbCj`$h`Q-aHA(_t$1B|tyFtEd(qF|=BWl20DG#n z1N34yn!}l0i>h7#dni*|s;aR7*bitVfJeaD!i}9V>VvukTxt{raCho?KV^EGM<;Ij zKV$sxt0?K`$u0f*3^|_H>4!I~9Q$205CGg?tBe6qcRsZ;7D?s=6O|G`{Ja`u|Dllp z*v5Zex+hS^{H@!hjprS}9&md#LzFI1s=_{(Y2u zr;GqjlI{fDrz=y^_7d)gDwDB$3->2gs@uQ$6?K6!$ZM|7QKl(-1$;pn9g{g5TA4mY z-S^4}fc=r$x!JLMVxF>h|5S?qk@hzz#DPv1Zo8_K0^&0Bf&R*aD-Rs+o;s_6KsZN% zdrEgA{+&$iP^Jw&S!IU;sIc`hW$`n=KOWpC?Js`EVHFN5B%A?emZ8AyXe9(4yR&0j z7jI>(u5_3Q4^I1Qp@l3|NpG&Pao$R=Q0|AR`U|&y4RsnA}S!(&>}ekYZkrK;<&Dbws+0nSl5=IN9LGUH(Ilkq(V+%o^qsLU{g z9^O@^FL0jplZG;5LA8H7Ri+ckn)aUfK3C67Nhs5~(WP3MW+SiH%r?7L!j)07Z%q41 zpiI+N_pf+(K1jt17uWq&gC6pf1v2H6N=#x?FyKFfYrz{*qyR2W`;GlGb8l4*Oj*1; zK((5nKE#>K5?&k2NdxZ3C}V=wwf!nPAjY2O07ES{PzbDmhy(&A+tafNCUPhk z1^KvDOn~fk_iQi_Bx>RTqTy1|068F=)c^ges%L6mSI?E%&1ScM-}k=mu2)svUG@5P zy;s*Z+hoX)Awz}?88T$ZkRd~c3>h+H$dDn!xP}nV1NHWb=ks$MK}RiE@PgjnMK7!# z#S1F&M+heu7Qf2zf^!@{IHJ2a@V@T;;B9yo9xp=&AS#u==!HEqgYBnyi(cB*SAI=y z-qIaU=_~D;E0*``@q-Ugg?uXK2j6o1@NB0L%#h>-?FRCYmcy`KjCgmMb9_PEo=o9qJH@JTAGrI1h@ITV^9j+G?@h-g!mT$}R z$j}6&W*{NS`{8c6V)PEjiw>azp5*wY^Kymga;n5dvatQ&pLBeNXe04A(NR<>U`HNq zqc(ZIJZ_@n8ah5s^dJe_BG-$Gjvr0W7lJMl!iy+;rK{7>@o^Hkmylq7iLP@HyskOr zy>f*hufn9zFQs^&hu}e&;*V$AjAc6@?Zo+uUOZt@rnR=|?d^R&704?fEL70PxrNgA zsUUyl6iO@HLhwH-ObCds#Ni@SNSfhs!Ags3kMX%lY2o$AEa+;Oo5-&kFK(|SDUQeUHp81vGlQ2G2mR77;Q6IJ8wt=N zo_?zZG)~TozU&mruxwZ(x07V6u%-#mwSnkAr2hYmXLXf!Tu5{)HPo+{1S`&u?j(z8 z39L7&sQVo>R=Xj8g58_A-{fE@KN>pz16`f)Hb4Ldd!kGL1-WG z^jjs+ICM|}iOS{IJlw{&g_*oyBNZGqw!?p*;Z4_fQ(NL@I`&iexvn3b?s{QQz7Tam zp!3D@LAn0Y9$kH=EgjkIhac+d z8+>Q?-16?QV9*%l&(REe3QOKX_w(p}tMoJASx`ZEmQBX5Vp+aeI*6xl1=3el2%pp1 zL|c~8JV8!I-MmtaO244>cC0RrhiN9P65yg!r)pHis1*O9~_R>31 zIs8_a5%N8WN3{VC&7MN|aUMQqfXa5@{q@4L^Jx3f7l8_KFO6OYH7~!kb_>6QP`@ep zJ9&=2>L21agNDA_S2`G)j=~q~Um~1>3rWy_;V6xywNY>^&$CrP8@bY;kXrh`uugd# z4!ZYbYKNV}!!wK^l$HtC)7Sj_9{L4BaOnJGYV`i4Dj;=fQ{E3J@O;|=Y3pvM@+%8h zHF5-!znb+YH_{H+P+XZQquR8LV zFY{iPFNCv5$oI9ii|F_F8MFy`_?QAL5FR!fK$h4k&>r|@$+n2}fSZm?Q&19Gr>(`OJP;=IO@_&}$B>fQN5spwG!szls^s zsVRHvxnGKF98Q&RN*BOlgYd0DrP{PbtDt-ewa+2XVGUl1_7Ma~I%56c3Z8b%pnLY6 zyORn3WgfmI;dJ2}-xwWiWy7$&NnR&pq8i0*w9PdjGd_~j$uK;+?D3erbHg^qTE9*YBik4-4r zp$E|UNxztnQ@$|spX@1=Kf<4(6~qFmX(Jq%lA(UN@aC_}U$W?xEZNurz2Hgk-SF^E z`*4~8Svbc#aN7Cdeemhj3J=#dwqrf2#98ka%g0Zk!TZP;2BRdra)ihS@5aUgwZtyhM8f*3E5WgVIBwQ-+E-S>dn5aRxoS= z#LqdU^3X6pPihH2nobqJhRpE~@aJjR zSZ-EnkCF18N_W2({a98iwR_NBaMZ{?CIF@Ynb#&I1f?#Km3I-02z0g3^$I)-f4+K< z&n`z-s{6w`AM>Wd@#%*f(S>2A8FPS}p9*1RZq{8p#$o8Jq?ey-#_KpxabD^Du4*Uf zN|hC{)-D*fb52}H(!ygNgs9wu$D#Kni>f@CKW06sk%*Q1uf<@p3a?N;mhykVj6WuU zPEC5zr-+uhg(Z6ATf$PQ%?V4e#;yG3OyYvV^Gndv z`T2`piT*$m<^le^^`L|<&cC$Sw3BqJ|gMI zDMov`UNi;0^-iTLz^qjB+IC(`R5|mg0)N88lWBN<@pV;*(9h<2;g9J2xn_Sk+Bfx& z%JUSX3r)WIEthARVPh8PG(l}e#PGyK8PBN3^9f0ZM1w^_?fIwvuvrdZw&V|pl$ zd!3q&&333(7|`CJ-|v5u@mi)K?a#pgGyKw&8QuV{UpmDUMBZ2?3vVHPG~Y49#x&$$ zGUbZr1zas8Uyg~nfsQlbuBXE8CTs2Fd@-Er7NYM_f%Z{B-iS_4R}L0W=CS!CD{TeF z7a7-Wlk_*y$59C@MlLTKj=Xe8Vnk%+-fOw9M|SiU!<-p5<{<~q7lKD>zxHIICrQO~ zQ^J$^#J?1i(!y&9W0jy8b85Iog5My&MA(p196XdhoZDkQrt9!>F@e4`il@s+5w)L| zm~k-Tk;2zw6oDB&E&xrW+bKq&j}{90;Ge?Lsb~OGI4XobvwCBiHQ^TsPqGo_g{Sf| zF`!LfqUwY z2VPeDTxs_yo#0?hvL8O0%*#w$8*ch?Y|xGEn)I=zf#ARc-PKp#3o}RZa|ZY4>Bki? zDiK2|RD~rAyFrCp&TSQ5FJ2{VwOcG5ET5_WZVdc~#~NDZM*5ZM-;bFFou>oRB_S!vBa2c#Z*(Q-wiEGks%J<(@o&2|r?k?#o9h@tazVB#@ z%RKCoUEbun0m5Jq2sWQ40`oo@|twf&=K$ee+bDKy>@1k5MII)+_`#9vKKL zt&Cp^Jr@FLpOK<3jd`c5Kr=9Er9G;PWPe@VbLta>%sG2h{!f_T+6qNm!lLdqb1P@#*@vXqRExJay0DGBdvnBMkeO&jrWMj|am^s}XA&7Q%}iwU#0( z_E_{=X$w-u$L}+~uhJkt$@>>B$%jf1_I8k>_2H)IEE= z@Nn%N7t->R%iDopTEX8d!w5hQJX0eijfye_g;!!4PZBy**E(wM7QEN%Fqa)xqa|*e z6kLH3mUO-e7Mpe*pi}ly{%#qD8LGi87w)uGl%}+BlV=mG3Lmpm(H5$N#TFWiXBnQt z^Oj-ju?j0(3UalOLU5Dj4RPO7hGB#n6)F4Lbmee?;McZrUeSrl9wrLpym&R45UZ`J z@OGyPWoQFrbrowWq;92i9>2DcO#svQitHN5MzTS@QfnYAqAtVIJlh#MjJ#TP!%{J6 zl&tK=luTm^NPNzPCjGir!;#>u)K1gN@3Gmn6fTb?laCQb`QFIiH$w-JmtNau z#uM=h<5Vv^jn`@70wvE=#}{Sj0G!JBo@OXb5Yoc8G@%U?KE%g0W#|wva?epq3D*>Q z^~l5d7v3mMEAcp=$4kq{471XZZHP#Q4nQu?o2bOCosvS=T`a$kzk7xb02|RIE1+32 zzy5l}lRRRn7TI*R%G zXV^?&yQXA;n_q8DB{YbamL{nUl3_No@oizvP`1U>ut2XyuPbJ(Q7mxb8Vs=K@1CIp zQ0ooxf^UW@mD2*HrKxBM3pL9#Y6@6?`T87$CyvRf))YbPl{>$1+%29WFY6r(+{I)>U&5KH@FDob<^oj>ScA5pcJ z=~BlFf1}oz4lY;A0Ly*U+g#zyrVSbqPm`9;S75~4G( zQ3}>RcD)id!JP!6w$O>#V+`x}s3qjWt10{rM=gu8iq2O%#^kyxnH7=zl55X-Wtr(X z-xk-#Gt=^XY+*KjOr?LjV|^#Wkp^ZTU>!s(-h_1%NytthOYKssfW?mL`!c5QY$-|; zwzO~y`QcT$dDYGNG7K}I;<5NP_T+LBPQ;O>G(kzzK#Y89FZHPv^D}G#Y;@=4s3@b&Y*3lJR&NX>H-SGT$G*hre5f5r7)XnymD#kcO}VTH5Ne;|f@h z5PP&^I&aGsWjv2Z_FnkVByZeifd-Fj{y zO^40*R$w~@{+{Csxw+BKR1mPD{w){v_M_<-$BQ)uvFKAL9b|f|rkd5^la*(yP}_NY z-K+qa$7aIh`6et2Z1eNTfpM}cN3K`h)yZ<&>VN{mKzFR>ZnPnW^vFSM%Y8N0Ooj=c z^Pi9&Tx)q zX>-yvN}J%577FjfQoeY8w?8~U4wToM&BW&vZ&st-4#y3o*)7$xT<@=G|BYz@xpIFB zxyJ;w820B19rdd;R$v{Cln@|E*jfr@Bb}@Lr9I^KPu}4ESaVvg6FoP29ck#_Q{2gU ziYs@OmiuQY!gU;G9_crDB&fSTcpI;WtsW>O#~V0=3gst`)Hw0#ro4b_+6@+dJxXhT z)FU&Ejm-S`x$RkbJRU#53{$x;7w)s(nvk$y?+#1js9GN>HJ4#M;yw zD@QM&aCJZY+Rd^bbHJWiWQBZE?eit~fnQ^<6ka(ts+Xn_kB8aLd764;rsX^%Prv+g zlmi4P@0GrcbGco(AN&AXNbM6wMu-%O8sVu3Os3jybCE~$KS#o#QwJAFi_)4QG~Hzw{!mrZFd_Q-$>#8LZ~ zK}&1W+DB>~8CD}mN;B#E_~9Tg=WzoR9va|z^nvw-U!ejyAKM2|qY@j`KCKGRR-wG& z%`kq>so8j)A$p&q@UC=Dzw>Y^j00?}LYm|nv>Chl7Gr_UTb;h(P+Y^ehIwHSGPwbZ z7km&6a2#=+FP086$=nwt9zkz8pmhFJA z6+$L=MfOYYqObb1u2(*uG}=k<{!;tnRMG%v5vkn}somie$``xE=n|p}LHS}Z!#t0# z<(^;t;dyyKIEMuHY@#zrTWCAAZ$8cntu1_sY7!+3>q44&i~o6?!WgUxCiXLUgHx5I&u!O9RC{L#LU*c{WhIH|zV) zB&zHYZ}5*N`VrA(G-kc`j`%X zqfpOtwGK~hMu^cQKy-XD(O-o68sE<2HUdT#IvCpNM<>Rk@>+ptQ-FkOhpZ#|ov0RQ zYN5vv>emBYDAX5!Gvm+K{9zFwl8{q${E0$oLAh%o9j6G@uKJ4RrwWG{Wdbx^KTO+u zHn@Hixo`r@aJoOs{*t?=h;B< zzN_QI8vO+k_6)=Gqb>Lcq1rIOC%k1B?!3ec@YpD_s5H0TXHO-CV=@W@gAbi7ljU%`iX zyn2u`GW-yWOwEhKNDGzRLUgSeuMtFh0tAZ%sPQDz^)jIv#fdJ07=mda9k!B44vH65 zKTxXz)T{>T*YOX~*AN)^e&XwwX@2U5AgTh?&v_#PQEP?4ti_kRlqfMpHx1PGnL+}z z-tE8p&DF5MgGrz;AiU`7v>1S083jK7LWX1p|Dk{k)jlQ}<1^tX^ne=`Zt z{n&Mo^K2092@p(QINk6bFyCW>VE~-L)5H+fMs&qzMCb3*VKA-HnGEb^Buj;8U4X99 z@!w^HRl1Jo{6#|j5thYa9AcE1@umPljiT@$CrTX*70+|WTyOM&VJ;Yt(=%_$TO7;@ z^P_X*xaJ0?y=YN@gmOIqiAJWVS}?b9UJSw7KsxMsL=Om6xqX^HTpH*)9p7px31uMQ zkFbLCVu(>7K#z)KUszs0lr(Q`$Q6TQ9J$tEAkb*${1~oZ{+5IJ@qW0hr@_kXqICiK znvQ?Ag$F@^PwRie=wmV=ECQs%XAnIl)X)f^RRP+M^K1}Rt7{L=1B7}9SjG=>UJNlx z1gK`JtWc_Rxlj!{>v$l{kjRm6koENt+CZEi!zo66t{*IE#xEpV7oa&h{>M2F5Y_!*`9k@N^y}J` z_k%0U@HC*kL;{C-QETh187Qg()X;Z9adr}_fq5V2#Sqm#GOH(7R6A1>g}-|3Msu)R zssIshTcHFQ8hf>l{~*zyh-rhP7{Bm*6KO2N=bdI0N z({&6&y?B|21N!06$ac(dYR}9kVOTNA(r``#R;7TohVBl|iy@R*3xRGp0E*%qK=e;h z$9yH;ON8<)TN~0sjY!1(5*8$deMG3=JKn$Eh(wHFfWV-=^{_$EU0!YghwZ^B?0LPt^(`tJhGSP^+w++aDGlfh88T$ZkYO0X ze@{XUd9eCSMgxRu54AOxD#I{Y4a2tdgKXZ{#BNH*i z0!L6w3t`xJEyfb?S~#KB8td)v-eLsbNeqW@iYs?|k0P~8Ylg_s{`OQM8-tx4e78ptk&GUYtJm@>~ zI2pDus8Z&c6-eiY6Lpz!IE2zne4aV9L}j#OGobhf5haG?3e~=XwRMNkz4StjT+B4k zXoFDgkxEa$P}5-QZkZ(1EYq4{v_iFs62g!Mq5L`Mr%L#ogt83r9F*0fZ7jI1(GW3QPzkK-n*BG(spd8LMaXa-IpHO#a^rWx2tt z>h+H$dDmJh71|TJpLb!Chh~hiXj~U O0000Q{;g$5|+YM7K;AMnx)@c^WnE@3|iYwGfld8Y7VUGs8*R2nAulj)~} zyR9FOl>sSc*iTHOaB%aKBsmi)@K1Pt7+AteP1$`fI7sie!0(cZ^h^nrgZ8qe%iNll z3eDfsp1Jc{x$|}}aR28LMT9R4VOh}>&{axrt0W6wqR*moZr>&`R{GUjvtCp$>>Z(AxE>z4{ z93eiO5Jh!-CMP};a!VVe;!V_?aD6@#cNTz-e>955FOiX)_quYP-fN2q zf4tjm+m^ffOilb6`-)w!iMR0bbhqk|)NHTp@S;6)Ix)Xd{!sAtOifrpOX$mwBQd*o z7ZUdS%}2Ne2K2gP8(lICAK1vQR>+Z5J~F5DX~ugxx}rd`tgZ?Xsw97ji5F(*A|_T3 z&Zn_U&B46yVu$aYI(2%<3EZBMXvN;XW&tw}G=62AOQOz_xstgm4kC4qVw}nmn{^2* z$h07!MeN>JmjK-Po2kZOJEp2zRMSm4%7;qN=*@|G@?Z*g{KxxhKwFZ|i5q4=Gm+FR z_R{ffJ)!+r=l`P0XA({HNK0TL1i@xyL%L3@vnBv^1mC>3j>AX z=R-Riu1Wj%gmy>u3_iKlJ=B_Z)uB$qXV+CIvLfmxmmv!^7BugS?F+3YzfgDRZknGF zs-M_SxZ2GuC>(~5>s(#hf%tH_#YP0ALaazw|Cu|?86rx%=Ofa;!Lg}rwHsT=fuwPN zk-`4dOU6=m{BG#0hekoZ4+>UBx~!VKX>NS-OIHMUx8C5WECNJiBwh_QXkvU@3ldi+ zZ+#Pd$3{nJ$&uKf1}z)nhj?Y@CfGW9HP;@wf^5{_QJE0oS<1Xqb|^HZM6C}3F%y7r zb>=u-YaN+~Vq=+88g;dGr&L(pc{@F_0YA`djfKMO>rsktN2ippzKN&Y_^7N#{3Pi3 zlsLC~RLeB0n8TJ}fUn?fG~MqQ3EW7!{WHmTw@z(#ZKV~Gu0lCZ3(NU%&%~n+nDNrFH73^G~16@@sQuylrtXXWW6x&^Pp3=Ld?1Z?Z%!3 zQ5!$~_ADDzW>9L}T#-v%<6l%$G@Qb(Z{?o@J10?^9M=O&Ks{br<0Hf zyt^&WmI%*RT8?lmeN4`{qdnaW?>*UQ0;FrRv`xE5Fs&Bl;F63tv0w5w$gk4~-%Yv7 z)H9PU{aX2F?Jt`84#kw?e~hK(Rp;LvzBD&1&`TZ6kF1Mk_W%0PaQH`wd$Ha)7F8}> z-NRWFoMGTHb{X~|7m-kO793q)!d}h=5a}jE_&y zZA8{jrCWnAyp(?6=Q~wxBp5O!f@d83^{ykTL61EO96R*S zhc6@*>myErY94rD9!;^FbokyBMT(`rGd_WhRQ&uDBjBO*M8T0_R<7l@CZ4RN>_4Db1aW4>I zW2fO%622xlWvU;iR{aT#l>#_c!B5tI_e&{0ZqS(d8o;;=2l|u$d{-`#paEE}ic!cj z%s2kHDpK{r%=XI#S>>#P2+U8}aIpWpnf)T+B_*@wZqTH}t`|F_var;RVW(Tz+G~R+ zY#h1SV6m6L^9SSaJ~gF=Y5@+y@B^mZGLRq^$(cOF zP1sLLMKIv1_*w!U4ozVFn|lxZbGrZ^8oE9~(=dz){;SI)5rvN-NnWqiFfJv1_WsYR zT%YCF>u2p`(*vATdd0?9G7tW6EJ5gRmz@|Y`NuFt_N`$Iy?rfM&Vc_PCD#nat^zVL zI>Hq!{2QXVu@LB{c$)EPy19eE7=`Nt;>5A~-|=@{p)#c?AMJLT5t;3K#@!;D6Cej| zZ9wc}bTcK1N5*F9FPHX7`0lsqEtgzeYyPW>TZ? z`pWgK6BptuCLCh-EZpDyu?#1N^95s*0zicn5qHcx%97}6no7BCn5d1BCc|2D7Q029 z@H+O6*5i$&U8&An_qBwLhLjSw9Cf6PW!sKEzA;tG($KU$f0SVUno$q@tV|C+SM+ujiCk8D353OzE>t5WhNe6G|UiSsaY3NJ%Ns63*_cy`$4Q_drJLW1xJlG2n<=_0BeaS=^^*C}x{JbmN zp*;SP@vcL+BC*Gn%UITzF2%U-E2&55mQ_x|?Vq0YQ{bFH_X?P7Tt~8@rJinp$k&_= zKr=*C!YNJq(7llQW$@5;IUx`$he=>f;#~+}pZY)qnD6D6HCNEbO4+|sil%xc!QCKp zw|MrwSs03lUvK5sXHB7nouIWXynnDjY<-lruV8&ZijLxDLxS3`HIBx6eG7ID6S148 zXB=D)scWIgdsa-#ynz%Xh$Ce98$3>h4_~2ztIc|l;_Btl)-gX8VBR9x_sh`oV6)8C zy9U$)WX!*_bTYC37lC@ukr*yfC~=xqgR98Y5+kPH-Cbj45ri&yJ!p@1eC*gVMX51t zGC!@HG&XK#f^-403Q;%|?u>_3%aVjZ9DuQZH#UKB!`H#vxIr((GdGI*^x8N`w!jPz z$5kU5!BK?L2% z>gQeRt@kQ~*9#To|5f4?zdY04re9nF9GgVBcc{L&b7YBj&Mfz6@glKP#hI|M{2l6D zf`&gMmSQFHTI++`ICAb+vMgcRKCseydV0kw$R=R)?Xz8Nh@zpiB)W!Tl8d1SxN}2P z7x}2e8*+~8CI4M*+)&7u?|JcwaeN^D)KUT3bj$-9Hf|q(bAxj}jo_B$^?S4Jy}u!+ z7(wHxsqcm`HG;3#2#U{wJBthP90~}1)M0N>&R9OUh1~Jvilp|B!0UqEiII2aS(k4x zr`D5f#WC!Mj`{wp*|0|^M!)W;x@I)?$H!J!Gdg*A!k|EvhYN>p4-MXSW13Odc2sJf zwLSXAp)m9`H|3KDm3~~C=Al(A^1cqGG5a&Y9D8yL|MLK_kO5}QNB`&gyV09`bmg)BbtHU-b$(9du3TIhC5d+=SHQ@a)kRo8^{3|tpPhnt`uj*J8k6G z(ZtuTKj9;rHrFgFh|4~!D|Ha-uNlwx&S9LB=30_|SckQHgOA?N?e2@eMzn)(__vVH zv9DVljr9DDF));6`TGH++2X^HB2Cf?lBENY9y8X-tiD9H+SJWW5aLz*-vl8{MSYof zRt2TP^CnXs3~I$2Dn`k8jj>Djez(#OL^1iS4-?M^A0t5E_dF5CLB2V4uUgj6A-@W+ z)_(+ntJ((>a<;Usn+^U2{J7|nAu0t)Q(+^irruzL2Ru%&j3LVe78QcCx2YMTUOP+> znGd{diu*)4mK-JM@;RG1YtIyJrtPO+(yI-zo)TIsKJnt!pDR6o)Z0(6eHe)abK8}i zP@+gAODJ3f|u`@5L-jgJf+S@kG*b41PP z^SACZA05?1@|=49aFq%2{nDmPt_pMXJ%oqN96K1ymoCfHX9OQYx^xLP6l(mQ`<5%D zyPJ3E(0r+ImCBA1J3}KRnf@yJ-AfH-oH6AV4i9GG{ahO7$xeX-jNIj(@GldS&ou)y~4d8MUpRixPq16 zz9xD}-rpmd$wArU)U!fe#5_BXB*`lA`I!B#$(4$a8ZC!=w~N(g%XwRo&1zO@S?qt(0IY)1ux^H_FoE>!i`vq#x?uLZ7SNN*U(E zE2=*5Ui#D7h^2j(MwJw_l3>YP5NKYw)jW+dnE+g>)t zzeiMSwei(dA;2}{+U!)i^E}G4<=N&lwKYka1zOTWoVVnkdJL{)_Hy2-5@Uv%#yf`p z^=sHHLYPVplGEvs8lo59Iq_(vx7ry0@S#V!Q5OoGzu`Qe@fDZ)?8U3|d9|l=E;!xT zt(c>t(mIMSDAr@ZiMsDJjj%%4SR%{bT88NND$B~dj`hchE}0Bf(tk0w)>mBeEE44@ ziOA*97_>gIK(5vIs4hQjosd;;9@CrwMS9e&o8rtb31heSY<6_J_}@4~p<%~Ad|Opl zy*IP$d%L`)_x8#(WKFHg({%;x+xF3c@3-7ScPFT#d5)(4#kY;)s`mknJV5PrgfYy(JT_4;wBN-E&3sXB}Mm5ujJ2(QJ>$%i`cB! zAA9L$q`+w7(18f&Cp(UTBy{-ru&+k@#i)Y4ug3`weR3RG{j14Q+4W*XdmcF+(LdJo z*Pu8`B7i?2|B|KLAo21bYlqvQSfQ{z^6Sv~?}(~KP-L&frg&bAD;xUOR+qwZ>cHFf z@IHqx7~|m!cfnGlOd_c>9Fh#t_yPw)+r`_p<{X)>h)1egP3b0tN&0$CHaL&Q7teh!lh&KiX?#zIUjVn_AT#30}rVESyv zjNE&l;XAF9PY2I(maytw1AU{He&Kq+kM=VxvZTsH#$rg8$rHq13gjb2hy&4viv{4W ztpKM(5o73SD!wcoo)vk^Ck%b6cT36`myUS0USFGqj3Mg#$;Yi2Y5}osT!1kG@IU&& zls6}c1x-_$2S@?-L9b_vpm)A1my35zgi9A+0_U*seEhaUjK}Mz%?FO^Ez{p%xH;*T zkW5&m9_ysrTW@wel9+3^5AeI{?jw!9^dAZRR%CVAeOzD8%sAQ`_Dd&Sz9?sGKpDLHAv++0bAE`CxVsdBI_a{h&CZs*%}$S%<4c5 z(xPTrvvqp2dEhGzneYBXfE#t)kFF|FY+e~XOa7wHrQ0w6`tGQHX?}4}^_H4>M@pA@ zlonIukB>DaIn4=J1m8ppt^MHfy;&JwY$#t&e<5Gi za-_G9ap|dff z4uMj#mSxQR$y8|CnRK=DbRsy~xf^)k*D4-i>6{6ufxxXyW9R<~HPJ3Q_4nP|$oVxD z*DJCdVoGeNPtq5Q@m0!lWl+fKP)qc@!(R)UX!5NCRLI;+0!n?oy}r~6mPN#)_?7sk zn#PK=c^h71=8&`VAIxG^P`L%*H7A(At_kpMve(WEO8|X;F6=rdy}X9go648XT^1*r zu!1vTeJ29mHx_ZwFGAh18Jp=6JGJ?q$hLV>Kf=n#m%ru7ICGjHzIXdd9&;^!W3zp2Vf?1o6@yVGRZHvq4F8+=icwH05;Q;@&rJNRNWC9T>|zrvJ}XVRSI612|a zE|QaH;hNE(PwDblf3%LDp;6nE7pD#aJ;C9J3mVpX}ETqT~7&0=-Es*aJ*JQ&BWcAh^Y%~$1(r-`V-rF z7uJ%-%i4+ZgMoC23SSt_Ave=hh^Gi)rQfq12Oasp`+P@Dy)$^dUElwak7s;GFYmzP zZglZo*5J~FZ_-6@>WxB}ZJf?WM1P{HB?n{XXsVGILpOkWRG5v+di~Ryrh)DTqo;@2 zqSJK-=~t}ift4VLBqB0SWNc(tD7iCb47u2QTHYSEsRk*_p4K3t$Rq!dp*XIeOhkJv z+-R^XV%|ADcDT`7Hi#*?Px$IrO={zla24{2n+?PBVX$8_6O{VBI;`{(e+Et$5>hft zDn%i@W!|>5b}ij0HvP@uclgmec|hm2O!k_Q8iTr`?t5*FJiJTzHsQlPT``|4VrHtb z@+)>IfkJYLME~a9ravfQ5&9&{vSA&b5hti7WT}`dFE?;}2g~JagIr6#Kk#$gW#O3< z#E2(hz$~T4i4Nqfot4gsl5^&@qzMHGD-#U81KV3MZMd8c7oN2JSgJEX8>v(68bW0D=H{^VR)YCE=7!)%1!y zjf9ncql%wc=eav3;U&RQ*j5ir-g@=u^R!}4RO?;yOAgJxX)pdx~m zqBvqyCB;8cxz29Lj@zTR_9Q+>5e=`YqyV%k1Tvm*nxS=;&5!q@3IG1|lPetfeYM9j zeVhVTdZ}6Wuh7@10@iB7taw(lqNgP=*HLe8ragJ2iO=TB^tHVG6! zD`fs6YvXrM9_jrwNmi6^+|0fM=|7@CAQTTuB&XRF-UIDu5Q{%q`J+~acCNT%^((*Z z6JPoY2h+?vZIGbL)!2C3h2!<5w;|OymGPPl#e%jWzHt+XBE5HFoW(D-ze793pH!aC z&-c@|aE-^>U`wGnmeA%#b=*nMP%<}>5jvtb?xM|*ZU%k5q2S3WbH>g1ng^_yys4Xw zb!8XnEc7&ns2L{vh!1+F;cTsh9|eFARB4@p3D2FY&*pq6{usc2oo%?(6o5Pp%z20% zpHm|Ug?bk4qTldq{^j1TA4a|pS`b2}SY436{?YV{S(>F+H(%g~u9o!Ez1(#GBS@wUmr(MboRWid(xz z=_e|tXUKiHmmjH7cbw(s6JR0^sWd=T#&HP{(Yjn59x=kQN=`end`LdI)OZ`>&INl8 zo^F4!NCLH=qF*DNl#D}pPv+#NSbcgHkOyk8)dm7*XPI3b*qL#P}Q;ca8@Qa9j|A9%GQaG z^}>Nii_q4zqxqX4kj>53F?CzRQRGTepUal@8s8wg$L8F4dA#{~oddUD1$6#~Pd~oR zxSj#HTT_;pPR4!t>HML)er75XbV)n$?7Nml3&-ORVjcQ9sHkuNC67;6dt=61SZJPH zxMavctE{CXP$#UXlmSubJ(bWfu5T`&`5bZPUxd0rdvqe6dalb+3x_~loVdtBtnFno zo!$X>s8;c{LmD2wiJWS!*M^2*?e?a^lvl&_74e4a=TxA#uwUv?tq`+w1j6KKN%Qol zw-e_Rg5!!W{3uLJi}n=-Q$R#bc%=GmMpT2M|9h5>HX5flqFLIZfx9 zKUN2?b||HCJ948M&+fI&Y238N1E0+M;e*{-&_gaA4wj)M&WM!5PO+$-pzUZ*i95mz zzmxJHnS$b=7@JkYCWoneL=74{uz7N21hJQ*xiK{_OSspvuD`xfZNK*w#E@4q*jr)I zMF)LBSYl5E8Jevv4O@kkPWROKpU=RX0&@UsoSdo6PrI~xbD67{p#>Afw`y+&tJz2b zWn()W1?#CF_ySuix-qtyOVJ%!mvK>Lbfrq7Np?hfkp!2|%}$s`*Z5?9l}5Pa>u)oC z8SmeLL)T^6#iAdi4+6@_o`wIF;kDa*qF^Psn}W#yFl|tnL#55x861;)Yyjm?4t}HA zXvbYcYt%OO?XaBvjhT^z)D2=?STD@l+1BShT7PM&(8&{Iu*2^v3)W9+ZPQM0X1r7_ z6lgO({W+vuf63Q?`fYq_meyQlKz45c#ckMv^_}?tz^5Rv0Hv)MZF#c2ZtodAvspZ5 z;kLo8g0qhT&K4jGR%l)2a0ADY;)DRD#{83TrHHc60;Vbln9NVL;jOL~tT38jUsO{cvW zQL>9Z=gkT>RSW8@nKs!Kj0v>Pkr?|OfV03SO;Fs`hKXU2RSS{O;jAX4kPLyP8}bZ=yo|epVwfNSy`8IN>l!p;0ZU=PmN)|?&z=RuF0l4yBZ|(u7&$WENT*3goGXM zjUczzxKF0v^IX-oza)xG1n6#Cfquh=}ay zUX#Y32UT%eNHWn@ijLlwh#e$KuZWRTjmEq{dXxeAGr88xb(hPhi}v-kf#h}NYwz(6 zUdvaJX$&>|7h*Zjj@E-@ebP&aN3?$S4)(HBc0g9ch6b4`i^|jk&Lz?muV%0OlONHW zcHAszD%Q-hVv^dOui|{__6KjtWpkc~kbdzp;O?OCQy}%x(rLt?ZV#yxTnx~s9jRJ_ zh^nJyNDu2FzyS@PUe*{V zXcj$sxEG2BQiBn~%oAyUhYLept>hY~b+kB*!WNdo5W&TU89oyFeT6UfG z{}JN=FIM2(u`ZZO`?56^knBe|hx01xwP|g0;#PZhP;ovmq6kX>?qq*Pme+@0XnZ$v zmvrQQA_1hr9=e=gy-ZHzN$u~g^^^0!a%di-&l0&fIY|GIk(Gaq#fi_;CtbroP5oDu zt2g(PIlSdDcF;^21I^>5s6yn1&SFEh5_j{F$1Cj7O_zeMRa=dyC>EbYrS;#-4Vx=u z0mOuE(>CvX#0V3*wT#ANgUV=Q`DsuKYJ$*fM*3o^cYTIU{Q9z+znX(mo;U2}!Fh}? zz)wnJ6JNi?tikyNSTV2sLDn>qa?;?JLc!&h1%Q2seuz<=Q@cp%AeO9VSC z#hFN3H{Zmqij|m#IX65rXUe0UC>n|g_Czl_mGF~FxEt#ujleope}XLlG*Yk@^yY!p z%K9a|V0kM!ccBug3mb*noB=N7F#R`$6`U=V%oX?f%_O7PJ|``uM{ZgNJ$ry~Yi-=9 zGK@1%Bb{M1l&X1I){kK22Wz?z^<*o1_6)|6b~axoVAWE%W^gWsIj?k`NV5jUs)cQ^+}CE}tm@ zr})7zA-vG}vi!@xvFt+>{tB-D1Y^k(D$I_gu>GSA>Buj}@I%FEF=lN_3fs4ZE_;F< zO4A;(s&0sqUD&!1q_o_0=(ryTc(tmnMo*!>hxPTqal!w-L?Z21FrW)WzD~5Igct zM8rpRBkfA+P48gX0+0uDRdP7mn}Y=R=VO`tBj44gD~L7`@vH>zec{OeYf$9Xv6b4@ z)W_rOAF8kmVtGhdDkO}-IsdpEMRq;K;S29w{bK?j<&ZkNjLALXV` zol9WtpFTd%DZ!)IYIqqpYR^r+{%Bf21I(Y6@hae)buW0>q8H~ms)aq$E^X_AcnNx2 zyOiIuOq_B<4qy3%$cZHo^?E05S}^Jcy_OPg$=+5i)WmEa1t&{}RAJ=*#e~6TK!t?L z%T(GG!E1`5j5Br&Yrnq;ee3Y+%)e_Z&h|QnLfRN$;)&)&?E`M@Sn19p3ju0Is19GRuYn9ZR`*&R+ezNK=VDFXLs&jx7u0zbJ8VM zqJ0Y~A)EmSJ;op#*H#or2LZJbB)ph0mlE{Qehh-Kyys)zat3fFs?g~5{;A+5O<|7T z*3?5=77Q}1+TUD-_c5?^D`AUfEJ&1}UzmH^QMN8Pd@;^FyMY`yA!w`*N_WM*iB@lZMfa4L;xwjR+>jjc; z#JCHK(x8d{1OQK0LMcOxY;vC18+k!v67Av6UYGiWqp*u~QLP$M%xW|)gP2@{n$N-4 zL4_swLeAJ9Hrjl_4+eWaU7GXvDG@HDK+8hOEsZCBj`LUxIbu zVh0lG1A*v=;``5JMnv4_fI#Y?I0KVA89R}aBXdR^Q*`z?=@PHzpaeEIHdhoR`W;CK z{`!cl5bH-oI4X~u)f{~llwSjoJ`emBA6&RSdMVER16Co>we?XJguvxj@C%uy3t6~7 zmjJxC#LcaUtMJx64Y}xmR5C<&8-#Rj*Aq`194?pj!`%i;fThTyoG0k0hi@u-;4Gw?mo{6 zX6h=S&?1Hx7SW!Ieya~Xxh!^`Vbe!ve>nZPiXUPs#o}#pPh7y>uE?D%2M(C3I`@b@ zCVGP>SOAjt^uJjL`nP zzF>zdh%2g6zDL|;(@t$kB7F=knF_5PKDTqX0Jo$S){oF8RilyWR>S-d^chy*PQ z(_8SoPrjAA4;f^i;v#{2|*yNry49y)JE z1hH-;B%#8uP0ruc0>Ox7mdL=u)=$lIXJc^XQS?>c-)!f%8Qmh!Inh4Ehr-Wpb)uc8 zgSy0UT-siu_KOpVo)kqv`F%%~K=eE8t{+!&H@=o@k1G=_Cdpk124VWqm%I3re)UJR zRSa$@b1;rBI{U%*@cC-o3mYqchv3Sj^cEL2W`DG1IquYC`e_01QO#tK4K{l_Ih~w( zEe0OTKFRjglE@v&R8UtV;p0@jMOnYJCs&uGEK!Mc=WIAoxXnVj{-N3nI4IVI8!;Jz z#YcHyQmPKy3uf)V7u-*Z@vJ>u!<|4*lP?G)4d?VNbB2^zeukNTxoo7h$`eG^fnVtg zZeKJ1W8=!C-lxmoQmKt+;Pvs;FAXL!&z^Zw{d?>%P+j3u;#0YeUQel`ngjIi=g?L#;+LVA;?Na?34p ztYB$?^E3Gm*j@ADoKu_yx&^b|PxnD>Lw!*}1J+mQ!|IPOqdDpb9rQy+WgPsZOm1aH znt0LmxS$0`VIMqR47Wh9581XbWGcFmL(+u#7GkZOLPtb~jTu`~j!cdI*sv_{r3X5%Buff@%6A z-x*HVQ!~8NZlPvAqEXwL@rPSh!cRjWclw9!IoCbU5OKyM@RHzLJPH2AnAaYnLTp=z z(uM0BzFU=T*+AXeXMI>r1}{Ss@Cvw7+6y-=Ex;g-MY}z6B~G$H&wqpqeITqbXKji5 z_0-DMJ#~ayQXv<~Pmgk?EmEDY7#St$KGKb#of~K+Kv!F%fYX}DQ!cNkLlg;>yFL7wv9|k&XaNI;`N>T8v1hw-BUDtos&Ku1-_(y(eSYJUw-rv{DKtU zOP6S#VGF`qOR_9tEV7ob-Y|ihcX|2XVDwk`%_^{Knq@vSDw}jLa`_1x4F5TY%hxmU zeKm1{{LPyRX?EJLm}gU%Z8`(k0Fl*b+aH^Kym{Bv@UGd#lwd+v91p2R-cU?h|GlnTWeTGv&R z)4j&o_h*p+(i%(P#V8%cR^VbDuP_*#YEO;^1bg`);66O#cjPm-#*URJIUWu5n&)`P*E+RlIxe`?Mg-l`tbHCBwBn z(D-%+y0a@Hh8Pwy5^bHD5S*&>iZtx<+R?8_I`JCbkTN7{(MRVGayk2lAEBZgeSCa4 z5)%jG1kyBD7%85d;Hy(-IBadpCoEWsq)~?XQt_46;K#3K`C7-W%T`4W^oKnd5pP|k zQ@<1FUOe`y)k&{hk}m=Uz-}!gI0c{mh6_1xDnpC_vQ^k`;cKq1k4nPpK18}%4(0zI z-%f74YS>-~nmN_5G%#F5kZlkPpbZVEMaMIX$7~ZpASAL4+e@V7R6pq-2f!UJfCjeO z??1m$D_QXZRoiKNhkvERFe0YjM?Jg~cyT25oBU2s2w@x?%m{_d( zc5e2?yQ<=dh9^@AShyw~W~}n1GQ_{Vu=W`0{{O`a9{F>wjO9^_U@c_8GX$3Mrh+&H zi32G3P^}Yn6JhM4c}*3%y3aFRkh#!~L0sVZ(8-nBx6}R->#^rP9)jluyLUP`tVNeA zJuT+&zg=%k-1b=L#FNcU>&UpJl3|zRDm7f=6T@S*9i0sKX72-*Guno(9>?Cy)8;FFJNt#S3HygMR%VZis3;?#;w zc=je2=Kbcpa+GWO2$%C2pE#OA%Pch(l=MKvCvAJh`DFpmZ#(=3_~KMfuT_>Tdnky- z!LG%ubcI+w2yQw0g(tPyr?DU2Hw@hu6`#mBLbv;>}q6q%7W*8cB%u0+$_s9sZUV$q4c#b%+8Ef8qd|o!p{N zSQlFQq~ON1!eLxuGx)Wz3N2AS45aBt-xyb57bA)6;`FEn-5a58pDEs_haBg28kN%4 zly4PxdLVrqio-P7OqQ{QRPx;>R@Te14TFM6=`yi+I2=gIRG=QznH^;$Q*O;mP{0{Niy=lKR~lE@Ne(pcmw{MNsWwg2A+_md{;tR_ zZK@XR`}GKq^2nQz1x9UCCV5DSM#8qzU)%E?=O$hh!-n%Sk!(>!bh7k51A(@r6i#&Q zpHAn%)0qZNIe{|l1b^m6Mg-AVra4Ev z9zJ=iG)yP}%8Z(w#SU+=xMa05`uvYd_FOwR+l20aQHlv3pp&Wcfi9bjtWQ52p{Xcn K%2&%;hW#Jp2`?7_ literal 0 HcmV?d00001 diff --git a/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/CryptoAES.kt b/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/CryptoAES.kt new file mode 100644 index 0000000000..d0275ed8b3 --- /dev/null +++ b/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/CryptoAES.kt @@ -0,0 +1,220 @@ +package eu.kanade.tachiyomi.lib.cryptoaes + +/* + * Copyright (C) The Tachiyomi Open Source Project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// Thanks to Vlad on Stackoverflow: https://stackoverflow.com/a/63701411 + +import android.util.Base64 +import java.security.MessageDigest +import java.util.Arrays +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * Conforming with CryptoJS AES method + */ +@Suppress("unused") +object CryptoAES { + + private const val KEY_SIZE = 32 // 256 bits + private const val IV_SIZE = 16 // 128 bits + private const val SALT_SIZE = 8 // 64 bits + private const val HASH_CIPHER = "AES/CBC/PKCS7PADDING" + private const val HASH_CIPHER_FALLBACK = "AES/CBC/PKCS5PADDING" + private const val AES = "AES" + private const val KDF_DIGEST = "MD5" + + /** + * Decrypt using CryptoJS defaults compatible method. + * Uses KDF equivalent to OpenSSL's EVP_BytesToKey function + * + * http://stackoverflow.com/a/29152379/4405051 + * @param cipherText base64 encoded ciphertext + * @param password passphrase + */ + fun decrypt(cipherText: String, password: String): String { + return try { + val ctBytes = Base64.decode(cipherText, Base64.DEFAULT) + val saltBytes = Arrays.copyOfRange(ctBytes, SALT_SIZE, IV_SIZE) + val cipherTextBytes = Arrays.copyOfRange(ctBytes, IV_SIZE, ctBytes.size) + val md5 = MessageDigest.getInstance("MD5") + val keyAndIV = generateKeyAndIV(KEY_SIZE, IV_SIZE, 1, saltBytes, password.toByteArray(Charsets.UTF_8), md5) + decryptAES( + cipherTextBytes, + keyAndIV?.get(0) ?: ByteArray(KEY_SIZE), + keyAndIV?.get(1) ?: ByteArray(IV_SIZE), + ) + } catch (e: Exception) { + "" + } + } + + fun decryptWithSalt(cipherText: String, salt: String, password: String): String { + return try { + val ctBytes = Base64.decode(cipherText, Base64.DEFAULT) + val md5: MessageDigest = MessageDigest.getInstance("MD5") + val keyAndIV = generateKeyAndIV( + KEY_SIZE, + IV_SIZE, + 1, + salt.decodeHex(), + password.toByteArray(Charsets.UTF_8), + md5, + ) + decryptAES( + ctBytes, + keyAndIV?.get(0) ?: ByteArray(KEY_SIZE), + keyAndIV?.get(1) ?: ByteArray(IV_SIZE), + ) + } catch (e: Exception) { + "" + } + } + + /** + * Decrypt using CryptoJS defaults compatible method. + * + * @param cipherText base64 encoded ciphertext + * @param keyBytes key as a bytearray + * @param ivBytes iv as a bytearray + */ + fun decrypt(cipherText: String, keyBytes: ByteArray, ivBytes: ByteArray): String { + return try { + val cipherTextBytes = Base64.decode(cipherText, Base64.DEFAULT) + decryptAES(cipherTextBytes, keyBytes, ivBytes) + } catch (e: Exception) { + "" + } + } + + /** + * Encrypt using CryptoJS defaults compatible method. + * + * @param plainText plaintext + * @param keyBytes key as a bytearray + * @param ivBytes iv as a bytearray + */ + fun encrypt(plainText: String, keyBytes: ByteArray, ivBytes: ByteArray): String { + return try { + val cipherTextBytes = plainText.toByteArray() + encryptAES(cipherTextBytes, keyBytes, ivBytes) + } catch (e: Exception) { + "" + } + } + + /** + * Decrypt using CryptoJS defaults compatible method. + * + * @param cipherTextBytes encrypted text as a bytearray + * @param keyBytes key as a bytearray + * @param ivBytes iv as a bytearray + */ + private fun decryptAES(cipherTextBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): String { + return try { + val cipher = try { + Cipher.getInstance(HASH_CIPHER) + } catch (e: Throwable) { Cipher.getInstance(HASH_CIPHER_FALLBACK) } + val keyS = SecretKeySpec(keyBytes, AES) + cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(ivBytes)) + cipher.doFinal(cipherTextBytes).toString(Charsets.UTF_8) + } catch (e: Exception) { + "" + } + } + + /** + * Encrypt using CryptoJS defaults compatible method. + * + * @param plainTextBytes encrypted text as a bytearray + * @param keyBytes key as a bytearray + * @param ivBytes iv as a bytearray + */ + private fun encryptAES(plainTextBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): String { + return try { + val cipher = try { + Cipher.getInstance(HASH_CIPHER) + } catch (e: Throwable) { Cipher.getInstance(HASH_CIPHER_FALLBACK) } + val keyS = SecretKeySpec(keyBytes, AES) + cipher.init(Cipher.ENCRYPT_MODE, keyS, IvParameterSpec(ivBytes)) + Base64.encodeToString(cipher.doFinal(plainTextBytes), Base64.DEFAULT) + } catch (e: Exception) { + "" + } + } + + /** + * Generates a key and an initialization vector (IV) with the given salt and password. + * + * https://stackoverflow.com/a/41434590 + * This method is equivalent to OpenSSL's EVP_BytesToKey function + * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c). + * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data. + * + * @param keyLength the length of the generated key (in bytes) + * @param ivLength the length of the generated IV (in bytes) + * @param iterations the number of digestion rounds + * @param salt the salt data (8 bytes of data or `null`) + * @param password the password data (optional) + * @param md the message digest algorithm to use + * @return an two-element array with the generated key and IV + */ + private fun generateKeyAndIV( + keyLength: Int, + ivLength: Int, + iterations: Int, + salt: ByteArray, + password: ByteArray, + md: MessageDigest, + ): Array? { + val digestLength = md.digestLength + val requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength + val generatedData = ByteArray(requiredLength) + var generatedLength = 0 + return try { + md.reset() + + // Repeat process until sufficient data has been generated + while (generatedLength < keyLength + ivLength) { + // Digest data (last digest if available, password data, salt if available) + if (generatedLength > 0) md.update(generatedData, generatedLength - digestLength, digestLength) + md.update(password) + md.update(salt, 0, SALT_SIZE) + md.digest(generatedData, generatedLength, digestLength) + + // additional rounds + for (i in 1 until iterations) { + md.update(generatedData, generatedLength, digestLength) + md.digest(generatedData, generatedLength, digestLength) + } + generatedLength += digestLength + } + + // Copy key and IV into separate byte arrays + val result = arrayOfNulls(2) + result[0] = generatedData.copyOfRange(0, keyLength) + if (ivLength > 0) result[1] = generatedData.copyOfRange(keyLength, keyLength + ivLength) + result + } catch (e: Exception) { + throw e + } finally { + // Clean out temporary data + Arrays.fill(generatedData, 0.toByte()) + } + } + + // Stolen from AnimixPlay(EN) / GogoCdnExtractor + fun String.decodeHex(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } +} diff --git a/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/Katanime.kt b/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/Katanime.kt new file mode 100644 index 0000000000..86cc5825e1 --- /dev/null +++ b/src/es/katanime/src/eu/kanade/tachiyomi/animeextension/es/katanime/Katanime.kt @@ -0,0 +1,259 @@ + +package eu.kanade.tachiyomi.animeextension.es.katanime + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animeextension.es.katanime.extractors.UnpackerExtractor +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList +import eu.kanade.tachiyomi.animesource.model.AnimesPage +import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource +import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES +import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor +import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor +import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor +import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor +import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor +import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor +import eu.kanade.tachiyomi.lib.vidguardextractor.VidGuardExtractor +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +import eu.kanade.tachiyomi.util.parseAs +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + +class Katanime : ConfigurableAnimeSource, AnimeHttpSource() { + + override val name = "Katanime" + + override val baseUrl = "https://katanime.net" + + override val lang = "es" + + override val supportsLatest = true + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + companion object { + const val DECRYPTION_PASSWORD = "hanabi" + + private const val PREF_QUALITY_KEY = "preferred_quality" + private const val PREF_QUALITY_DEFAULT = "1080" + private val QUALITY_LIST = arrayOf("1080", "720", "480", "360") + + private const val PREF_SERVER_KEY = "preferred_server" + private const val PREF_SERVER_DEFAULT = "VidGuard" + private val SERVER_LIST = arrayOf( + "StreamWish", + "VidGuard", + "Filemoon", + "StreamTape", + "FileLions", + "DoodStream", + "Sendvid", + "LuluStream", + "Mp4Upload", + ) + + private val DATE_FORMATTER by lazy { + SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH) + } + } + + override fun animeDetailsParse(response: Response): SAnime { + val document = response.asJsoup() + return SAnime.create().apply { + title = document.selectFirst(".comics-title")?.ownText() ?: "" + description = document.selectFirst("#sinopsis p")?.ownText() + genre = document.select(".anime-genres a").joinToString { it.text() } + status = with(document.select(".details-by #estado").text()) { + when { + contains("Finalizado", true) -> SAnime.COMPLETED + contains("Emision", true) -> SAnime.ONGOING + else -> SAnime.UNKNOWN + } + } + } + } + + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/populares", headers) + + override fun popularAnimeParse(response: Response): AnimesPage { + val document = response.asJsoup() + val elements = document.select("#article-div .full > a") + val nextPage = document.select(".pagination .active ~ li:not(.disabled)").any() + val animeList = elements.map { element -> + SAnime.create().apply { + setUrlWithoutDomain(element.attr("abs:href")) + title = element.selectFirst("img")!!.attr("alt") + thumbnail_url = element.selectFirst("img")?.getImageUrl() + } + } + return AnimesPage(animeList, nextPage) + } + + override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) + + override fun latestUpdatesRequest(page: Int): Request { + val currentYear = Calendar.getInstance().get(Calendar.YEAR) + return GET("$baseUrl/animes?fecha=$currentYear&p=$page") + } + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = KatanimeFilters.getSearchParameters(filters) + return when { + query.isNotBlank() -> GET("$baseUrl/buscar?q=$query&p=$page", headers) + params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&p=$page", headers) + else -> popularAnimeRequest(page) + } + } + + override fun searchAnimeParse(response: Response) = popularAnimeParse(response) + + override fun episodeListParse(response: Response): List { + val jsoup = response.asJsoup() + return jsoup.select("#c_list .cap_list").map { + SEpisode.create().apply { + name = it.selectFirst(".entry-title-h2")?.ownText() ?: "" + episode_number = it.selectFirst(".entry-title-h2")?.ownText()?.substringAfter("Capítulo")?.trim()?.toFloat() ?: 0F + date_upload = it.selectFirst(".timeago")?.attr("datetime")?.toDate() ?: 0L + setUrlWithoutDomain(it.attr("abs:href")) + } + }.reversed() + } + + override fun videoListParse(response: Response): List