From 71f542bea521687c57ed46680f8aa10c29af4541 Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 15:56:26 +0200 Subject: [PATCH 1/6] feat: tulnex --- docs/images/img.png | Bin 65095 -> 0 bytes src/providers/tulnex/decrypt.ts | 124 +++++++++++++++++++++ src/providers/tulnex/tulnex.mapper.ts | 77 +++++++++++++ src/providers/tulnex/tulnex.ts | 155 ++++++++++++++++++++++++++ src/providers/tulnex/tulnex.types.ts | 9 ++ src/providers/vidapi/vidapi.ts | 11 +- 6 files changed, 369 insertions(+), 7 deletions(-) delete mode 100644 docs/images/img.png create mode 100644 src/providers/tulnex/decrypt.ts create mode 100644 src/providers/tulnex/tulnex.mapper.ts create mode 100644 src/providers/tulnex/tulnex.ts create mode 100644 src/providers/tulnex/tulnex.types.ts diff --git a/docs/images/img.png b/docs/images/img.png deleted file mode 100644 index fe86b367dd2a8f69c2dd69579089a46bed4e76a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65095 zcmcG#2{c<@+cq8)Ra3QQMO$snv=lX`16qoznWCkJig~O#{i&L%YH1NM6g7krF$Jlb z$HYubHO3eD_5arT)|a)8WuJZ4efHjG?|onQbzeLBiJlhoIo@*s z0D$??!v{|R0D3S0K;v-DLVRBPimxS$i(u+IpGI;VN3 z;yabtJPa@Wy$e9`a`#q0$=a^ds$T*8KAyd*LT9b}8mg-@yA_ zFqrPt_J0bA@}~_stx-W8=Q1BYSlf%HD-mVP8otO#8Sm2{@Yz)`IXr2f=Hb_!sQ3hH zs=v1@&r{ug&q;OrZ^(`Cx_w^T`a)&J*p#U}s~9NK`LGV_-H`_T8OQ*$Jy zEdTYffH_c)ejq@`s-)lZIg3MQE> z^3ae5^}}%+S=N8`S9=F(m zrmU>?r3`e%5id@5iNxB%DIId{*iG^dNX}548U(u}`xeDuwSBe!IHoFN@G=HW6*rfPe7FsXU zV)&fdw%2(g1?My_bBDJhP8i~j&`|d83CnV?W<8Y3I(X*Bl&4*uyzK7ywSH6r- z)3oaz>OV%^4tee*@I~1#v3S9D z1z(?xEr2xVT<9=fWf>wNv+)s~Fk-ExzLZZW|K^eD#s@Y(wLGkLrPegcU1}n9_9sZV zy<`HAQZGWa`|a75d2hIhSmT$QQtl-tJXyD8*0ZC}W!cPR9tZA(M?RoaB~E2G%+{1V zacLbJlxFr1lce5EetLiKhv#d-FH`MGPy==K;KYXGxgC4OyN4a*W=Q)B0_GS`Mf-q6B_ClOddz!FYQRv<2NpSCl@1a)O|H!qg&Ew^5R>U_tAo} zlGuCbee$*s4m@8_R^7dyS(-q^S&dh&-B(XzLxq}t@@xLmS?lkZ@c53uKm>nGVS)$= zM0A~KgTLy>ITq;S8$l7+(C($Ji-T8-QYKitMRUvzknR(Tw>DU~$M+5^Z^` zRK+dY*)1)QnTgkYW)P)k6MGJNgs(HcXqOjEvD-U3>KVGoR}u3uF|olV;u-Ac>zX6| z_w^>CYf&D9=Bx=pI|c#NPPtD5)ZKzCB$%ddg?MyJLg%V6M|~Nb2#Gn6JnBNt^=Uq=#8f*D?IT2>7MxG zmoIZxuZLpInaH%gk{fr|)EzaN=Jv{1E1uckL-gxP>y;Hc=keDclp8KSNsdvS}Aq~>H z{OcJ92ic-qa0^uvEC0|?2Ng@O*{7X>-NEevee~nJw0%c<%xKs?w3TVOy*hr0sxm55 zq3GansjOSF0QzMHv9_Cxj)LlMTr}8g6O0ZRM6dU7CYlJ74=feZ7wPp)2TQ~1HKv=x z%X&I!3*k1^vA}u}MR{>{r0eER4oWXrOKK&k8*c}vY%q@nu}H`LX(2-h)NwJV!3L`? zC;iW;XZi8(XdYF~3<#099^Ln2%-@9+C*r*N2=TUbm9zsdNZ3f_R}#NhovdsuFPw}R zA5V42RIe~)7isTl>a|ThvEUGrRnrHk9uBNOE->F_%PbpsqNb*x@_nOx>tYVA1B+^v zuuOb6L`_sZRW@V-0%!UMT)L!%r}%&NMC_^e^DIdFQBS; zPbsJ%F&f?lP&&M=i0)G+r&P^gR{$ z(Rx`MU{!+JN`_{f?!SgJz_IZUB)+p+klK_w3B3XcLUwaFj_z*b5AZn6 zo_^%4Cl8kZPdA~ITUp{F4~O7P*h|fmr_#Q^cM{7w!_&u`Ew^Phr=*wmb=5sBXFExR|$D?Q!@qA&_-tnGoSyz%H3x5|&OO98o>;jCZIlbRfh_IGN7N1{*%y{u(2(BtbUP&CO=NG` zGbzANbjF)Njl!HFP|`HEpI^o?IB?>eamO}Cg{FDjovn@fg2vlYSE~w-s}mipW>(g0 z6L5h%<@Pl9Z-5r9xxzYKxPY zopc*)-ROPq$Vz8~f7N|xd-Bo^`KnioIDn~)o=#Mi7MAyI{Bj=w8a$kP8gF|Ttf+&nYqzmi zLrkLK$m{%{wjV={W`t7=K>RN~KlG3Xt|e>30?< zEkgmf`0AL}Ho|i)$s2ZU6?~ShWviQ=+mTmatGoHT9Ar;cG%Lw~I+?M)-EXdyEfaNU zhcgFl-yN*O$7e45*xd4lgojK^5jy)b=*|PwceJ}NyS}QfiS@r$xt|uxoIviPhB0muSLGDO^T?v$AayHh zC&Wigc4Uc=IE~Y=8_8x~)cJ5Lc}|e?v7i*^BYMhi1lBN821}vzZl+Vg!osq!!`=Yc z4HHmu)_!jQ*Jrwv@1fMo=ukqDVL_;xdKj6A3k%#E9yJS6wp5zH+0#K>R-$>ScKN*! zgHYt7$S*%7Ok*!}*r2YOTHTS88dcdaE7v8{5G39{l|4+@^u*y6n|dh@a66_9D{68z zQ`G@-A-aLCvX3Rp9SY3|@+a&{qudmtK89HSsu%HaCdaxFKNS=s3c`XH_QqvVA+K+z zYaY)F9{b%m+-`o*Q)(2ehDNWa=H9b+TwJA(FfQKt<{uHGRgn1%%4J+C_WkHet>0UV zOOq=kOG3;ZPsN^@7`4+T;vXT|=rdG42MoNv7P%EG8T7)Ul%edTSq@fA2)0UHVU+Y+awgD0mpd2Pz}@t!XpdLRIIvk?QLjl_Y0E^h1)d_4wgGh=#?9! zw-Ko~1f*gv%w))ymwEsGm{DiF&`aa($=?Qtb$(^OJKQSFu)mMjGREBlO$W*3)Rb1?qCiQ;2zOKG*)Z(vpR(sIlOPdc2r;nKV{AD7*?*P0EljT7|A77w2o*22hItHTOm+sRPB>eCes0DJ}hX{&0F zTir^mvY;yAi1B1|eG<8`HD6#u#E$ya!TvHN=lTCMeHUX97exI(=W$H-0i-67dH|`>*ipWOyt61MF7FqLclpxjzerQ8zLH6uMH!7hJpqrb zUI4mVc@rm)BgY&0YxPvSWrB63@cpDUX@C8mOLzQdfAUpD7}||fQG$2&3G~LG{v~C^ zlS}FfZXXLbEIv;bZStlEOG)2upvQNzt{H!4bgPGR#7{d^~nDQ(ZH?S)uK-%tBvN6+lq ztK0I@XTECv58GkCuv~~>BE{nCpwT+R{St|)9{2hscRXrDwO?o9r^bP}!}Yb~q;40B zmo20c$Bl=%XXYQWIf5?c?0ucMW()ETz5V{ezxlOdF);a8McAQwRVi_$K|EHn`q@@H zWdNsli27WXdNb9l(5e#Rj|$eB0_C132R{@+H?|vVCGJXvV)S~h+EF`6DE^yRgFOcv zlSbs+H!b#20#*gm$Lpj4)X8hy=e{J!{8WRIxU!!EFjd@ZOg8JjxYuVLj}B@?hSK*D zcnBZRHk>7EM`J)aF}k4`Qjy5(7!*?U2-l{iS*0iSO)faY?)^ooKDFNy6CC7v8=g&_DV{OvPh@XT9D^ z|2zRvzW+Oen_x1E(kZC&sM|BuBKM6q+`(vdYeF6l{A~ z73n6e=VmDv~%`}VQ6 zd$5(<-gQ)i+3(_u`mAt$w#?|9`Tg4YyT`>cxPy0MY6g3(z@Q&*+n=U7YAN@_M}>di zvSs;7f?gxh|Kpo->uW6NQ&`$C4}#@5tUv@FNgj~qB8NS2w@<;Pxw;wk;%%iH_P$mC zy;BQr>l?l%|9&EGm&W>*>Nt!7%AR-7(l~80EeDw%J#w5MGA)y;8iG(k8s;n}y4C0W z$uFz)Z31$SF0N8ouD00gcY3@s%5F(4=~^b|#?qJt)6k4w29pokwTeT2(Fl5>KeRN@ zHHKT&JDtJ ztr)8pax_l!DJ>DZecQh8n~Oe@1RH`rpARglP-lIKvS?P*ovt*B9{H>+Cz{V_hNNQ$ zt{tx(1B_5<5VPa;WhjH3?;*EAkEgLlSW6GQ2e~&@w7WiUdy=a5#rz&1mx73_nX|EYsXoGgoE4M{y^yPi}(o2g~$o2b2}qkMjDx zCayIzjc}XOG3B((-?So97F25JuW*s;aDd*!0@O%=B#L2O+TEYnxv|hSse;D5VKw5OODdUD5)B8Na^S z-YdOs$uZi?vb_Fb6fsi{^Nts}YbE~soU5R@%?NL!$ahWumbv1xsXJ4acOqg+%g$!$ z`k7ejYiy)#oGa#FP_%P_9)Xqww7JXO(F&^{SKGCb%xk4F+YqEHbe*PsRBl%P!;O2; zZ#NpmH{6)jwH^FTKM7)v?XC^3&+DPQk(`;xJ(bMilNkmTS_IFR*L!e7Q9g~O;|bTb zLP(@Lki=bW3Kz|I16iVZbD9Bo{#6N#zjN{fAx&@jE5gV3kQGeZ;n7dixg}n7eN40)CWMXJZ87-)`vQea4PG+$wg?8yy4Oj8bL(khu=`W;sp91tw2Kw2U}dby8(mw zz4#Y&hxYni7vHYy>W8_?&-BLaE4Uln%UrZ~H*8b>PNe&WDsw1|^jESx*c%X;Ol=6X zlh{E_!X6&gv@lRt#&%s>o>WtS{jTRm`n$~(l(ALP4JGFWAF4?T%aH=zp6L`8U1iYa zSw6@Cy8B1PeB^^+?snU(G7gy{1N}aEDkuBWhu;hxIrr=+x1YVp$avILRaq(Q!-ZeW zQX4HgD)GYz?)Mpq1gmRmFy2GNwtxmSQ{fZlZ(Pgju83j3Spzrl3H|y<^Jdm z){Zv?KB!JNK_O>u1p%GRtuN}d(^}IfNyeGWo4RP@kEK+V1wBQM?XSP-ES~TO??YLq zVkT~~EBtC;b}b9Ja%GI2+%CppqfGIBmAa__(qVO!D0+vlOcDM9(a7zC!}X=4%3BYP zBXK)WKmDOaca{;t`ULH;;Qjc#F9`J+q%d#T1v$bhZHzk5abmbQDT1M&`(qnrDM61q z7qt@A3I=fQ!Shu6;HMgA-ZyA)m3^FLG6)k)rX^WV^gQX*qviPyuY*-2w8BMRc?I{O*hASOD{(c842UyX~3);12y z+nvk%dAYOme`kE2%C@R7p=|tqQ0HFI#sFjConod@si+eh{gl}(tlPWqje!wrmozI% zJ^ca%#5w9ZLB_#)ZBNG8iDk(nT5qiUk>QhGF`!rB<42d|Sw`?;I_|1>3y8mC4C{gk zH*N$K)j&)9uqU5Xpl|W;T!D;=t8MfMrJDCp<9WLW;<_`|^YD9_w(23PDMKQ@eMO`W zFEYyTw+gMVlYR^`j||V9frp&fGmf1E6}97SA@VBPP5C$Jj(~R>SOEZx3}sm*(3c%A ztQ?vI&@8V6nW}v!CF4Z_=Z?={_*1P2(I`@J?*1gaX=?EWYW-0Y*Uhzv@c>s7>5R5+ zqRhT)Jri}tulupnMbyjn=MU*hz&~n}?Wg@8D${INJ$k8k{{8iLSl_9Mv->)FLw!m= zqOE#3Zz6v@s^|*eF!2g(X)gK4@Tbh7gSN#v(Z%++31v@=ZR9c$)R65dYmA=6aMxMt zPerSw-*7vZl@YePv*cm1Y5P=jQB|c;%cp`Cvv8NguIu(0Tb#?h`vbhVTD~n+6O+#A zk76H6b!XRR@VP&C`d2%n_QD|(BHgA*IbWe}Ryjw@CK+UHZ9>w{07EBBu$Gu>T(Qo(Mktb+bX!tNVRfcU3Xi@X*h<} z#J^3?DSB~{`}g800DQU+;03Bv(z(iNN3&r#Hzsb@5~__Ls+~qy08bf&ux3NlUa8*z zNn25-6yy~p8Ylw;mN0i5mz*~>D2yp(Rab5;n+nW+N;DfsV8pD;yJfY#N2D6w z$H191{l~1__pL+^%x48F`!tb_9JBqg3OExA-oPLw$IL1wcfYwNc2(3ywj z?qm1;ppg>op}?Q6e4M2SS+O%=hC|;T8s%IUSL2}!4VL0)A5!w)bzJil0$}gPZAsag zk)E0sk;#X*d$=^#@@^fG3Q$R1KR5Otg>Oa}8i`3?3F48TDIRY7>>i$JYWu}@xw>Bm z^|IhK0XrMj8VXgwwyTUT|D#kJ*P3wQjE(IV^3jy@xb`eu8xMEcjleNHjnn2S(NE7f zH2YHRxT|N{5Ncv{JHpRyDcMX;tTLXU8VMZ-cHgOd-M_Ptws@GCf3FN?beD73_nMht zI0rhxq#ZaB=tBW;lUMi`bfKw=s(CgFxWkTt*u5Fx_x$Y`g4t8P5YrXXy3g^C+nY0D zO#yYokTYV7>Uj&b4QyeCQ9=zN*iQwG8p3tzABE*nS1I`tjklDc1AU`ePP+O=CSNq3 zoySlw=t_?@oAAt9>_MK8bgBYI-aT*>kSxF*~p_D1TRH=oRw4J2y9bhe2hMK z?$hi@u?ztGUujH0Yv zQbS#QcF*|-N%}X|9oL$c$;j(X9#1OwwdKtgsX~;)aCqg74Vv4bgr9s|A1QPMVcK4Z zzOS&sk@XHTZ9=K`ciYbe6UU%Shf7KdN7w~3za4|kOAUO=2n^3X@A;ycR9?#**=NS?ByUMi7wm+zRnwio7N)@p;!BWjnWH)@T z+DSs?l5Hi=`ne;VZYfXi^>l7<0@Fu}_<%tl+|&5IE%KeT3ZD32K&4i&-~IJ zOML+2-Gr;iO;l%TeIc3m^?n-`wtW!a2*4&oyBvm0 zdv3aSK67l2Gv7F<&;-C{jC%nV@~PD}5|l{VKg}Dh%cdfJ<*(8N_)J@X*nKdYPA}GU{3P*I@-3tU~weIlNbFPhqT$eN{Srljf|sNQR*3 zg_kpE^K;K$Vj3;Q4)D4{Zfy)%_S9~X-T!=xau1U714v%AtG?~aVWa1`RG?Y%s*lBQ zZfLILNf*y`=i4++LT#U7bza}|P>>$6({2EwA)}00h>P(D);BFmCh>kT(5?M5_xeFa z7riyjChJ9m9j`C+b>O-FQeu_QZh}J>v)9NI;iK#*HLnd}wcySu60u@GKG=J&Jzi){ zV|X}?7PAjMXkYE@uwn&#uqXkLDA9`AUZFlRQq&K;rW3Sbcd3oaf#IroU+zmDU@Ec; z9@m2veX!J;Eq!6#%RLid(YCn61hO;Lc%^$l<0NM}o{pz=T&ZJl;XzrL_UGIe9(l}7 zopzwl=$ zlz*NBBprY_s@?sO(A5pAHN9!yJklOf+Gd>SyB`lVLN#t;Yg(4>i+Rs7|ic@B5cP!6tR-l*LG5gH{9H) zIujX_2?r>koe?(O9~Bu3cbH6jo=NS~dA&6FsgtP8wJ{6s0${CcnpHI=YW=?$R9MQ|XS64yjIZSCDbqpJsCFq`<=Y??oThiXO{qEIBO| za9n|?TkQ44z|yE7PZ|K&XV1nSDZ(z9K)8!E;sF+{kTqJ2%cA43?*{an*5`%|g<(kPu>& z>BhxjQ4oa@_PBPl15oEik|+eqTl(C)R^?FPBJ%SYQT5Fi)3Ul9V{tY4zApa=nq~Vo zELUd0AlWLWk^7;5IA30X-q6VIS))~cg($%%0EnyfNm_3j_L+-9uD)mX+(C#p$$DNw zJa%_6T6dTw%b2!X`C}8PjX}gX)OoSqIae=z3+Aez+^r~n=g>@GWsUd#_!41pJ$h{W zVUQedu7mf~SzJdXz^BW{uuSBZK54so@{zQ4cmjEmEkc8gj7*xzk zk1Iw5OlLbVw<&OjW;t`tosyS1wR^fNNsWNbNTHaQ}@2%3f%_ zbC(HN^*RDQuw3Tiv&YTp*bP4D>+&OpuVi8BC{UO(3@JPnfIvS~O!oDV4TnRWv#?JP zjRU@E%Mc)xijq}ckqyTFofVXa6DU~&1wK<$?Q#B>GT8bmY(t^(l>>y^{NH=K%nV;sz?8MzLLWff=XGbFVzk{U<(aNqWkxQ|9?peBl@{a0RSq*IpOC=@N#AAU7-htq2|IZ0^4X!ci1tf}D3QzC&=EZu->#N$D^g1)6auw1yo^U!H#;6D{Gq@dpe7B$)rEN=2hS2B6=@WzRgT#*TAHgQbM*gfb8-D+Lfjm&c%g z4x4Ap9R!Z4zb9%+hims2Qa<-~F=yt;?=u*PcA+Fb-gv#JJIb@Sek`sCX;4@$JdN01 zrpTYnEqe1kG#{Ejl>S6}`VpREPCNbX4mh*sP_aBFVt3V;NGK+J*0G!m-gh!s25+=cV^jl5jSu6SSKv3WFCBxdxWINQn=iJI~n zQL!}-@ukUT!1ifgK~Wu&hp{(`f&*mTn(J0+F>g@GXXPS>M8~~P&oTf2&55Uhrx}e? z5*JrLEH1G7ZS|3H>57Yhi9^%X-@Z${vGEkV(VeOh3b{VI*4}u9#hEX#=oe_iK5k;6lDJNB#Pjoo{Y!F=4254@ikPb1Udf|fxz=%-e)QZLl#K0oz4%C1122jVe6|!$5G3ipUam9kQuG-)u=Qq=Dq7_!Yn}0wFc=k^q zmO)jSJP`BS6e=O%y#Sw!C0E`a?<#B%fBu%*$<@!>Q}QsU&8jnC1ncbqP(5}1V+L1H&`)vK&(jpK#bt-ee!PblH%RO!|;6|eo|rQLVKf5I_BiY?AW z+@H2~1s>viEDiXl$M_TMLW#hZR-4rsvfAYt`+cJ<(IQ^X)24xEDZvPiJeQ_WUFJLQ zFlssBPHG)BMP3ISguh_PDt?NUSU?QCv;kQ8)A@*i39kOj@a!F0)qCZ>)kPW$jLuWs z{RzBNyNcpnUqV5t>)&xEkzCFm)cDI`c^U3MycsKtv)~h7r=`^ON%cJqg1C29<2D>M z4;1^VCEiWQf6KYpP>pjvx0&>3oFuMLVn>uad2&X5SJ59??7g5KH>~7UIH9nAdTzi! z$b!Dls^&Mg^Oj?ftX%=8RJ|tw4Cn7;as+Hh#J<3A`j?uCS-c(c8=15Oz#8Rme#gMG z;wHNGK892~Gn#f~Y`c`L zBtiM=9NjA|8g!=N7t;<0arS*59sPe+6s4g;VrbSg#k6mdj6V3!xff$Zr~HU%b0RaU zXHWZJwt6-z2ndL$dv_Zw;8}2?__A4BmtSVOeb>lOe$7yrWa!P4+}r*4aSSt9KA$E2L9jtbebakcKpzN(JKgM9hq7rSL6dpqWQ#s@)2S@VWIOXm7XoO8 zA*gl_#;3i<0;zUKk{R749=rKgjmpUru@RKWzt2y?pqUT2-)H^LUq1-=#(o;7?%>Gy zY)Yo3ddj1ZY zCqS)Di{`C}%8RVulohM>)k}1kB)wMWEKC5BZWCe;%GM)f-VrXXq_`Vz+_R?jti};& z^+l^3DS^bJV;`N7cZ;HpIp--v%bzJlam8X_lby7aZ34Qn0W>8oSVIsJJOE?&`dNd2 zQ<6rb;|DF!5KVp_>woS;o$B`%o;3hS_rC0OEz6@d;jgQswC&l~RAt14(XPYMnwKpd z=_>EEu_WzvbI|fKiciR9g==*|5!>j(^lC@o8dYUL%(=THYf-8I-UOvU$iUE zC)XNFiDp;?cq}l@jisDGWP#b}@IH1?#6EHCuHuC&e?1uh2%Vp7QnWe7%_y?Ba_qXUUO+p)*B4bUSJitS}mGxehDjIv_ zB@Oo&7tssIsq_DeR4G74oEvCqbMHXVz20`Wc|7%e=(dm`*6Vn&wUpQ9-sdtoN?fR; z4z7|;^`>O%ll+9(*MHry0$5$rWPYwJYuZDwh{FnAC&S-@LEY3||BmGCAqJ0UN2vGtB z+gWpd?J}<>ezZiKe=7h4&14FP$)R*^{*Di>=#jCIo3qnjb#6xpWNvpKjn>>MuRgk< zE`EbW;8iNreH7)3Aq}f&;Kf$$M?17}kj`JN{`VZ{&jW=iUgXcj0gq@?jEY}gi&a^O z_3C&yYW2_kRt&*hOhxrc%B$-gMCS|U)}Bb(l21p*?+x zN0rUB!wXIGpMz-U=rE?_EvD3V332>U`ri$Y-n020@m0D1o98!+2@b>q6fgtU#2l{TDH!OjY-H zW$wgxeFJFUeP?@%>};kfYJamwkmbqjl8Py{&Tk{%@%437t=82AIgCCb_JFBtJZOuo zLvs#M@7B`qB&tdR*9CmX5MR+`adl=uiuue8a{v5m;BJ*1eO&#&j`X!qns&jK$0>-` znduDG?ypmbJHeXS&@*28h!@!Xd~K1-w6@%V$3+qjhq;W#Dkwkt!j?!$yd~Kll*Fl! zOH*MDvu3@eI;;yJWW1E{7CW;X__Zk;^VQYjQN4V$xrjNzBETf~JHmAKRI<4UH_PyqP@{Ni69S-DAn!^A_waeRA5YWaWz~6l`%6XaE>DSf!0M*yw z3|ZYZy6-YIUiz&x%@khihM0ofMU!i^OdCo@mGEdVbx3-9{QHWV}k9>#c07mjQ%0MnkD7pPZP_uy#BUE99*T9d#BjxOA(Tp z^Mb&04%R3{Yh7^;sc(vsO9ub~DeLO1TnUv=%8!^56PQW4<4D1YTXciCbd@5Pf9-DB6K7a6Bdv?LpAjZIQcHaU_~zx@Cdp>-w4zsox$tf z=iJkGY2&7jUTLMBny{$%RYkVy@vU<2l#erDTpih^+yuP*X$I_BZBDCeVf8YH*G#_Y z&S+S5!qrYWYA5*9aWCcyn+m9;{lx126JZY6o$f(ejfRS&%4eUhdcZWPDrL{^4L|G( zT7H$PY>R;Jh7S32Oh6u*4(X&r@rfUFmryj{JX3H~1`TDaIq0NQc{kv}pPu$mRd&2E z#!J{Os=Vhwwh4@9Cwvt3U?bBBv*2EM`hATGj6G}i6>TpdY53gNtm>tAsN{UzwuJ0q zA5R3IeVCqc1|hKyl*8Uog#TV9eW zFdqLDhOCRJd_l}w?otX}O1y*5?|WB@f9wU7JxltOVWnY$>3al8^J9@pEaZRku8)%7 zRG~-f`MlH3$3Ixvey+e)bxuLd+CMcZJe>yJL5jB`^`>(qK|Un%40~FPxh7oSaF^I6 zSCFqZt#sKFLh5J&6zX$LFf%^R1jEU?}w0!m1# zN`AT3N8i2(r|mp$xCIqZ+CLAZPxQp<7RW^yFbeq)C<^cOboUhjV0!)Fx2gQIna0C= zgyRG3lhzUZlP&p(AX~pkzfOzRpfO|lvMIQQ_&BmhQ~v5w_~K2&iP+}#lv_GG`E5m@ z3;|EU^T!W|KTI?J!S`2Rqm50i=`TYlu(Z3<)J`gRw$vmW)|cJd*a)b5{H2$XBej$% zORItj+eqcje12cABE6j2DM*cyCBQgW6l5mU_7_m*VyrLAceKpBx&l~!yJhaV@NFLx zo!`q(526aj;!^JMvK0vkj@r|;XfimzV%(t1IP?sqI|(`!6u(>oz$$jTF07Y4A3a3gQ0|@)T0(JSx;*EDij_0qe?E%m zX}U+U6M!{G{cQityup~&&4Y0ROoh-@>Oq-Z(e{MjMonrI?$gOJR``K;vP`n{tZ!e} zoSx~e8AH~+E@Z`K!9;J|nSn=58Pe-#Fea--g7aVBPmP=V=}p!{aCL=sQG6C3c?()$=zd%68 zHx>eK6|>Dea`$(!j*3aeY|}hE&}6!iM^?Uir3x21&c**Uj+VRV}7AvP{y32igU+%2d?7%dS_YJviC=ypLpNE7*(Zj&X)#< z+1v%XfTXoq%UwOrG_H zwzvA3jm!GctJ#d>5qc8s~OZlwfj>Dq#SfINaCBk zD0>mr=rPMY;U2mLSqBR~KFJ>=uL+T&kzrfua|hmNUVCR`s5gn8RJxf#E~u{zOPl7e zW8S$3wEYjVk)qlqmZw_>ymsAMOC1SzJL*31ToN<$) zSw8Vt+oPy}{g>Xzc#?;g(#iPrMXKQkQqSI^Y)vZEcxrp{H8e}mfg=gGwQnY{dsfL` zvCyy6y;>!Y)1ndm57$STBp}mg`zJ1z%Ehg{eD2oX_-q6{>S<~1t?HHhlb{JKsI&^b zckBLaw2h@~QesB;g@ri^`79xG1KjrE{=dN@1)p(MMejjQMswG8^PpC&VJ~$5&s53( z>7NJScmr|bjjOY&F6&n)wK}eKFF}%ZKxzfKK*KTpRAvJ>+gtik`&?*O$b_a%teq2PljHmLx(SLY=wC%!O3?y!&H0B@tLEBS zos!V#5w;XFX71ukmX)E=!)z%|$1>}Gihrw|jz4^(%H`lI7CIO3=I3Nh0~Y|CCv*vT z=m2y_`szB)OXE@HV^96*DphC9MRONsGRSYrw>5dLpA7Eey%<>FmvA7a*nq*2h2=Q_ z=gf~E%o6{VLcYf1L(2xFp9yX$HW(U(ZNWWfW?J|j&TMM%^0aG7-1MV~AvMY?F1ffD zRqXS<-yFDQL)!|DCNwI^95Hv$3>0)Qb0fn7fF$P)IQZOC;$kEfrB(i~Oo@J#b?~!Mu&*Z?{8smg%l*;9hmFt)u z&+Piolw=%AyhUC3{tJ3l(rFf9sQ2IXt5o!umlzI;6!+A4rM+v=q3es_^ZpOpC3Rzz zzs{%JXn}d{k{78~3@6F)mTByNYN9L`Da|=;9TvQGsL=HOlw+0frHIOGTpL#e1w?li zxdiJy-3%44)Y(zC0@S37%a+H$ixv980vd6Lb#ki@8NTQH&r%tw-a*zrh1Pk-c-fkt z`+XBV1|ho~jf4g{2Dz;#@h_GRJGOUOxtCf(Tv6*u3f1cS6dv`(+$cpvAg<4*WsPVz z{q?ZcYE0I^CDop!|G(IK@2DobZeKKrf=W}7B2`61=_tL0S42cWPCPMZ-)Xt3cQO97B%zuklG#^DOlY z3!fdUqME0YbEta~kcxGC*wa`%P^Kp|P?uUBMk;GmI+N+}WI$$wy__5srIvwfynU#X z+iXBFB>?i4vodDGZya0&B>wU`d-0i%uj>1g{Wn9ulX`kI z+r&Y3;~{>f@qKbkS&DPO3POxj{1?ffzzIES z$&=r<+x?tM9U8f(&y~x|I??^H)&Ww)J$_Za@3A9S)QDY2^{I%ms0PAjQ`Rf*A&;P3 zCLxpb$Q_0H)^7yC%W1Z8@@7Jga9iV8-4W) zSTl~-^4loD=B<;#fSZ<@$l`WjQIvN{nikNWasYDhiq21$QQYW&_DJ8o#x@ z!MG0h{LYl=IDIz#SfsP76{Vf+?o9G3<4E&v!=nCK*o~5#!Gk~2FgFF!Q`-UGvH9__ z5Az#RySruZQU9>zbXrB5XesP;A-zJuyS}VBdHh&;-pK-&b;@-Po*_|?YV%LqhWX4j zja@XFf zPgj|ZdAIl0O)V{=eow&4D5yIq(+|3?GaTPakp?qTm0(Wpx3pZDl1CPzIDXr_mMUT1 zL1DKBGfigqD>&xuSa0JtdoQWIqeeJx@hL1@mlbU2b;9aRa3Gw z%q%~9S%kHdtH`DnuI`gx;O#Xs+pVd7$?-Z2R63HK5zjP2v9u7D3rZ%O(#C-KAV|%sT(e9)AL!7>TQHh zc3JSmnCV*m1rIJ$;Z*3r!kv<{_KBVX`_7k5$^WL%k|&>!RFk>gSY7dY@ztuA-j9xx z&GAzQBj-s2b3L;!xOAk+MjvqKxoo7ohLzw{HQcQIpUD*AZ8Da=N;miFD}~3Bk!m&B z)IlwF-mdxg>mBN()#o|;ECX;sKVOApF>|mQ@Tr9sxWaQCFL1x=Ah~VNiQd8B5I+T3 z8jL)X>}5bn@^%vGD8fBHP*DFN^8F(~Ki2i(QMjOBE@id>`Mn^8=cGjmi#XaII;J#5 zc=>JfN&AB(Rg&-=kg`t{mlQ@HW*9xFSMo8_d-1#Aj+dnm?0(x}Q~=?0Js*Yk+k}Ru zQ@dd&3no7{e*JlmWw>evTV()mg*A;G+PlE+W59ppy5A_HmyCvk!OM?e^(7;zY*y-| zh@Z*g=05i&?3!KdqL*YI>&#u>F2bPhZL`mk|11RZeO8@9ZTPkG&*15wGZN-;2VtrX zx~Lp{u&8}AhVH%+x%fwz4pBTG27V)MPU@?q3UbMySnh zU+vpyGXJtB0J3snlw%Z7!ByvA}4z399=t55G4g(MMY3J zYJ-<3W7G&ulJz!?#P83e-WW=?4+pxU;BA!?d9DGRkHzKtvdk9gZ7YrB%C?iZ`3Ajz z5fjLNkqR#(A$~p|t4_D=3SL1y@6nUoPajy$*qif@B^Ej+5(U1b;EyWwShn8}FQ-KL z{Mg7C{@8e`ii~wwqTp>wO!BZq;&9(lc)4}l$nTpSJ-c+gOLa z&p=QHw` zzp#EbfV-!I*_nB#{;HdlDQUg^nU#iFLu#Zrj0U4yZvdZibBkU|+1vuR0~}IXmR!yN zfhdL^Pw4MXQAOM7yN$bMWcS1WH}1y2C%s7m_wN9j#8v;tU9iAvVpIivi2_xVOU^Nv zQf1`i(PV+1)P#cp+-s%AyOLAZ|6N`8a-Uw6HzIMhad1pV(uju%S?%(dR8>E+&hg30 z(DTVU;hJIOaGd_!uZ=@9Nil2OQg3683)dF9S!%eRRJrK)Hv;_!ApL_ti@5l|9B>;d z>syMM7rHtAIN;V*)_=d<{aJ$a@1L*UZ>zqih|+Z|M0~!xoM_8#acP`_EhRvmf%(HW z1oAS3lm|Tqbo&#|hwtn75}e7lzD+S$xZVh6{L6jt7Uf^{amq(qh0 z49&Iq<%EFH6jtv_3GqO2|6_9T>^{{cYs{fcyT1G+1Ma z;6{55--rr5)Lh}-72>`{XX$hgTmiTLaG^LMfo(#-Qzl88>BnI z9X!%vdH%>=`JlzpBo#8=`0-I;y`mKibt1@%2`7BsOiR7grC-VaGSVO2@YFyXfzq44 zDv7B=ShOO8OV=9R;wXaRf07T&z~`>3aK1iGlT=P(p`jq+MliKI`9|>8CnAN~IDl@1 zh=JgoW==~oh(7f(p)M$(-1|qT$k}d%cqQgz0LmS-;Ncf{& z7frgCc>s7lr@W?}41g60rVPAx9w7lZ68`Aqe64ARJgDt&Q`!m^btD<<&!k75TQvi~ zh4e_<`~DPaG=KkGP8L|D0WE;v)&O$h^

>(4&H5SqUNM0F92d*AocACdjLq65Ster{jc&MmvOawUC?V86`I zGr9xtn-;-lx4EF@{!yj*3Ipy=>5bj+nWWznB>|6I%>-&sHxlMc636S}9NE?j4nw>w zF<8BZ#WCFbsTXK+4S3sOmZQdQ_{KMLa_Q|N?#W>1|3JGMrSeMBg)a9c?3>N)8o~TS z4}(t@tc+A4T!??8UdruY9}o)`BywiUb ztW5>mkszLq9y_#o;p>Z>_LP+r8?pe8|E^XQ|MCAgzGQzJ2zh=5D;#h3!WR$;xy$(uVcO@A)b4 zvXGFMGXP>Gd29}e{tKQy6kZ=Fw%*1$XrrDRt>4?G*#(emKH)d{g}P%vX9w27v*T4m zcf+Q%EP36J;C1j!`(MG%Z7IW}0z9K18>KYB?QlQg3hyP%%MQX=KR<#;vO1WGK_JIn zKmS`0*#E0-Pya7>S^d}J{u^7?{)?FZBIe(TnE$N1@xNH*|DUYVOzcOdK|6KW{hsd< zr#X=trxL#YYBB%Z#T+tq!qGy(ygB&PX=-Mx*I&j?-Vg3+e98Xe9P_v93RL6V<*?9a ztBXlqFU*vo{c4my*gZ6aHYfWBVaT89alZM^Q?fiTU2C1Ex+?pfdv}$F8y3?26H;HrCQHB?udSaROp(HV6(Bvx-0y-yRe^*Bcf-(s`LnqsMj_Ku z_l3q9ccCuO&#*wZ=n~6;M{8IS*nQK0U&&s!4e-02Cs%#Eg+@&SO`VzH@4SCRPy`L+ zFGD@Emu?hNqi>6D*_GQ5C)YPm)-X9u)fU)8TDf&@+Uqp4AYP!`)bl!5Ubtcmw%Bx{ zHMH1!?LO8pBXiU8lLvQT$HisJN$V#EM3z&ppSO{@&P{>6QD|Jd_0FeGQg@D8n%a=L zmhqA+zh?Y<-BX3@_of~3A|5q%Ul_$a6fr)7_y&LJB@<=$gWn-B?e?ubVI80FcPrBW zr(1(r8I)NQ`qBvT=bxL7I=H0WHtHH&F(G%~(oF(6!z?5#7 zw5JZP|EbYLK-eh~fBm zH0-OR{;M@}oh{QF>{iHRT)1ZABgw$^w#0Vpx2o-o?Of`Kh2mnXpG)oVCJegmrd9VJ zb>~^ySk0+GOmu=o#$B6Rb-p}d-rp!)cKedu_bb?kx4hD>H%ra;dBhu?GDi0$eZ?D6 z1~CIk?Q(hQJRj|QI=qh$+>kus!KlD=-biOeT=;@QV#t)+v}GtvmZ9{;kXw6tkor`ML(;h1)*mlbQ%)ay0GH=a(WypB81GPpM{ zdTIa8a4G_Qyu-!i3FdOJAmvN-vdSBsl$1;7Eu@P0P7Kt)aT6a0G?$LoV~$7v7@2mY zVh@dXEu2Z7%(bvZ4sFDWkYh54_}sK;BUL7E{*e5B*4O*QL=ZN$zK*k0}rOLzI?sRt26Kr}PS)cz(;eQs(o5#kl!L z-w@g4V!U1!$dwy-TdAw8Mnd3Qov+2ZLHifR=J%2G1^g?e%Ut)Vs$H;`c+ zT&NGb#{ba%h91&`7d_Xu#z^K-^ThYP)xPXI0m^|Qo1)1y(kMh5rJ}K&xW{67=s!Zk zc4?d@)56T{9ckf(2U3tDpETG28!{)Z-FxRTf5|l=P1}EdR_MRU zN5kn%rctJLM!P8*t_)WuRmQAYn&^znOhHk4ZS^HDSoXd%A@*P>O8+AxIn$tBl8EtOssv#t+`_np)5Nj%@%CM#}SYHwmj=0j@j zN8VWaa+9OsV5N^z6O3duVJ?A*`BF}se`Y+ps05J{zHs^8+^irYqN7!UPJYjJf5n|E zh54JU*emtj7{|w{CkF*qFEpA4tmm|`cfXrYl0M7logh#5dGY5IBe~>&DMW4{shdYa zedH96Wg**gr_p1M_8irWS0S7+iK+=a(z?tQ`ZbdtBO8`+d#5W#z&LOhU~lnC7MEEw z?xa+a)N(Laza%+E(C&Qw!@SP?^ z&U;nKd(w5+cY$yNdTenNQYszC-YNH3jn{hGm=2fUI)WT@K#x>@^sm89Nw%=>oqsvf+7wF|tnD5G$ybUcUmLLfH>nSE25`Njktv@}) zeZllC$Vr30Om?jc3K@Y1E9xngbi-9~eYY`5^5JC@l~}f2Y0Nt1z}Hl%1!UmiluZWQ zG;rzuE$`L2Wr@I}X+%?<+fm78nR5DOf0N7RJe@aIS~%!PY~R3;DugjiklLKN9q-tG zEv`CXzaX3zzOjeyC29{J@GD=_AnkHflK>B>8E%q5T!ro>UJlsASS@c@PTdAO(P9F((D8hzSN&$^8V%}w zR7@Q&s4qbLwwJao&Q&_@ueDGK3D7<)pwIxFsK-k6=-Bo!S>YW0stDhmHP1t=cX(i% z#6y?g%{bj6L-$VZ&5bwk@AM=N8SK?QH4qYQ;NFuNsa-31%bEUc=0aZs1|hR5kE`dL z%e&r9psO#lK|xo*nCQHZKx56LDH+_+guukn1eMTguc}x7MNrOZ>a&=uK0(KR@Z@l3 z3fEXCej17lBDVT=s)&LO$9JhSL-b6232(I?Mp90|){3eGCo7#6N}ra@gC}fJXLir` zNbOdPBF9Ug+5|40rYFZt)$Mld_958)XEHW&z3k6w$(&C=2S%LaoGTnm+b6xeQa#ZQ z9x2qYwP9(7DCFvcEr=kERFO`OgJU$&EKmR{W{?{M znVK0of1P4r`rRw2sn;08L=}!!xn;fH1*eX^TVCxSsRf3pu&d?-k<&-5OBIJ832_#b4ZfrMZ-RB1Vg9Hb@ z6Brj1O^Q6Kj+oI}gfb!o8N0TXY*ea8v_&SeNv_pZI7pi zRfT|Y^^Mdtxx+%wW$`fAmolmNy$vNJ^BF;#lNtlOS z#3$dgDTG@D?az(2tV^JbR;rMGLbSN$k|y>P zCh5apRvOq}Uxd7DhV#Yf$5?G2UnBEiYrY!Hafvw2^C`?((Co-wL^;v;mQXp=%cVDY z+u~%zx=K55P1~4FMpeMRLsq4vcbkzAwfV$&u?<)~wEgFo#e$9(mx*Zg*Z z$#@N2-z*sgB^BuUDmm1CIdOoenukr|Gb){lxJ1G%&?+v6?EN^f)L*+x0=9+*jj9|8 zB_w`$+D(`0*0m^d3klX(esUjbs$aPqEkRT#95&Uh+C;XF#lwzp(;Y5r)5@bSN?a9V z97-Demv@?`%5a>mu{;URc=Ohv(-jTdeVl!g=*errh~263IU;SFsc|K&zcJ`^sN%c& zPp09q!rS!xWR$+SOXKm0w?Aqs8!kxhno&70b;)PD>gOY(lm* zsEz8p2IDm3nPPA#;tvz=soRnl6gXw~Tw^1r+`3yD{OQ;M3wonWSi4G08;*_+IT0ed zH!A&ikMzvoXlxa#Bq->&4b(yYe8WX@%ml3IFqA2QQ+i8f@+H@q%z}IKeKJfP*IWVF zW8`**o}a3EAsEh?5p4$qN9%oUsA0-THH*A#~DpFc&+W@4`#Hv9DE25g@D@9(1&MD(kE3w;2?L z|r> zdZC^LHSRW!_RH;-ZnGYti>HX2xu;rXZieT#j53r2*5jIqewK^vJ}!N!k)uUKI{zI) zYf+V)7HuXP-WuJ4wd_PS?g;I!9Cp0F6epMqb^S73Y;ri`Hubxz34UAFwCP~jX}u#U zC#)o78>+My=k77UCzL28HC0S&X7aN2h)c$ zWt>7y_$79komFH5P^Bf2uI|;s+6?`SWLxlEIp*Xqc7r<`3*3x%db9dw9hd#voEdE* zeGN)yv)0gE@vv~5l?0`7%ZRKgn`5PUeSmnBX-&Ft-?X>-7kkNs0K#?^np3zXGqeTP zLke41rdl|fV*uAsCFmhlAHxCP>U3GMf!4Y1u9COSi@SdH zKiBw*h`k);q^8|ETH)Xjbdu$EFv9&{CbG5Z?93noN1mol5(dbWfWC{WECVWd|L$8~ zkpG5cqJRl9S!w#>yBVHn>bF0Cz;iB)iynWn+cRYNlUlpMwkjb;b4xebTJoBF$7ZM^vdj(|Cj z`Lq=-LtM=-fC<_23EmuK#IYiHEPJQutqEXB(aq0LZQr*i-GyGVKI5@0mj<{gYi*=R z2G|M@U`*Q9uLTl1HRf@@zVL)O&-qs%GIDj#E6^bdG1O?6?O`Y%%cS3vH|LXXFHF*~ zR=)V_wBq{C1g^xOY&u>~x1AFO_jY1?2YeMjpo@SJxrI6(pRptLSSZ;Wd58P&-)R!2K#xI zee>=*nyyp7xKu;fshjyklJG{cu9>87siN~kk5ahK38{Uv;iCTj^&s%#v~S&JUz>K1+_l1rfuQ4%p}y@ZYP|Zv@L9u57MWqgOfcnS6&neSi&?Du@e60?`8p zFIlkR0U!|y_EIhL5j}6Pd1F7u!-Rqn%cUd;FiONX?pcZ2JQO+-+Mj7t-N6xUfdXVQ zc5zKQoT{Un1d4Tkv*c!K&{3$K8KI2C&tvW`W2Fqc$irrE^b1t`d{g^J(KBTfM(rBD zaejqt3Y0?ipYgxH#^@CX)cxue7jInrPHoL4tdBGNxCWFmFvp`ni&p`hG~E%-8F&@X zoaj`Ez|Szx10mZ%n^ARPg;3rfMdsKxNfd<+ANsg{ZX8@6)Mz?aE38edAIep2^8aLu zyqTBEh?KFAj)0!|#xo6cv>@~vz*ED(e)nbUh$~-CKZ~n&OG`BIQi@6`Y1lac=AGdz zr~RbJ2j1YF7bHB@^bTPDLlkj(-g-H$+u=RWo4;b9U}qLx->VE1Lt_|j^8~#&pdt={ zs0q-nTCeHBhkhw{L1VzDBo)L?l@X&X*sv8QU_^A7WcvWh;<|3;W%$ld`mA;BS2<1{ zRk@}t&XUvh16w%kKqM5)1rfVbI0Z%%P*nvW=H^Bu&@mAo5%ijB1!Nz)#=NXT6=UfR z_s|+ViPtVU^n}dBbbhKns=}l8KQOLqxs@PQPhhIgcRV9+qH4IoVk~wmWb8CGBA^F- zhzt4*0(hsWoNEr2mc4LDVxLR_2|2B`R!F$B<+u#W;EdUXYF0}5Dq*Mws}3ZIy91RV z?1&eg5v+^iy_t#pzBfc~?fZ0&?Apr-0j#|<@V)qE2v^L5y!9D#alb5I&VbJ~zkX&b z)%%PX*f1IK6&hCL{@S-V%e`nb`9=nhq~3^$KRpKF7?MvD1Uxp!u~M#~GK zCZoc6sy%BwL&rX4soiKOQE5~hdQ*N!fO@>FUy#)i|NX0eb-=YiQ{%yCNlOoTZK6-7 zrj74G{_3!w>Al>I52&M^=%?MfFH*uR>{x`_<=a1c>~D+;0&BG~2DoReY{7i}{q8Aj1 z)vol@Djq=UgbeE~(euP|Zz2>6?8=&FcO}uQzo9AG23PC9->y#d1ss4lv)vj~d}fZ) zh+@Ef-gs}ki|7PUDK#{#1Xgme%vZdp!|&cIv}y(feElBhD!y*P^p|J*rHUz~H{&e= zf$l`3X)lpWM3)7k{=w{g{aoBk7ausBx6WY~SAw0hP}cPn!R9S3K?o>io2qMTD6@w#cU=-@hU~QFET;eHq}vQgQlWzPL?4 zp@21bTHK&VykIr)Tb%10118J2HB7*D%L^yJHWMkt6zO90Eppo13@>zko6^$;qr`tZ z6s*ll)}e0<-$;=Se0^7}IPQl0HQ^`)%pGQ;rrZ7o0N&ft0jaHHn)97J8GNX#cwOTP z``%{<%3)z8%Og%_(NLSd8d&|8d`V{Hk7+o2H_tzLG+kgROD4e>!^zJO3|ZSP|eP>!!^#;C~U&_`O(O4{;AE=<(KI zm+a){kb@yDVjJ-24}sG&8MGz@=k}2DkoACa(u0A45THg0IR>bdfR*)aU&#sRo!w1~ z<9MCqMgS>UB$r@QO1S=yANMm+P*{_$gZ%HKYf`dooClC)H!4Go;KqhclPd zUT7fKpzNXC{_I!!hmwocXascWqt(X66G?YZ*yh#&J?u5#1Dhopk{+Qp+4Bx1d4RFn zyn7<)BKW=njgUe5*?_My3#@ykx54H8?q|$rX!9TkvvHI{je9>+oDX#N{5CpKT~r?S zVEFrAx!Si@Oq7d~r8d9eGe{mnuyYc4FPmn5kRkyKRDLyh-tjrw<)=!SGw=wFU?!3? zeJ++UoClLMh=5Us$WdVBJ-YNC7!)3UY^*;cMP=luE2BykurU2ixR zOn5#wC9uL9m!&^FQdGwgxc5qCRex*Jy<6OQl94W~EKBXiJ2Uy*GY>mOCB7orJ%LY!ubkyEu_$}=ZK0Gvi{Cfg*wEBjdZgos6G7lB7Ipz9f;CTL%4TlG#8nxRap+LRx z6Q#1wAy9tHX<2H47Bb^@!#|Wc9Z;mTGk?&N?3v$xO|~A4E)CIYmj{P|EM9j)!^Q_r z6Sw(y?qOl_Q^1XM_1c`rQqlzmv|oar2J*X;1F+akff>Qiv@yhM{pVP7LateYo_Jf*qA6J57f`OMz!f9KM8LCHtEctqfA*hSXzWCZL0KRUEz z`jDJ5kVzx-R*ZbD2V1|f42elt_hH1`1b$ykuLQ~o3mnIOArjMkyyZUca=P8(FNg`o zgH5+MfXBf90IX7EnNVZ(Rhc^ek|%0|qQU8Mhm^qJUf_KPYy{qMs}~mN4V#_hmqihhE61!a!hdm=yXxb2{d7SWu7Y?S8;=Q(DzH>en#G|Yoccf0iNR*HtkliBsJmE@LXQl{q*enxJy8|I0{T=iQeTo9mz{#{Y z2w!s013?;x{jq0$!nGEy37XbZwdx(5W)o3+Bo+>ctAaiuPg7GFtIp`-aA@+jSRE-T ziMH|CO!b+OK$Fy?-SUXZwpk))17YWI+F>v7aL0rv-N%jpT%+$o+OT44O;BCTR8rB| z-QJ+Zs!B%#Y!mrpz-Ccf>@jS4f%cs?rp~k)FwS99sM6v93olui=5oh|GbnyRY5I`4 zayFD6+tH!(hNVSfx&u!>OmQF+{LnB-!yp9_1vyZL)?SP11QF71sz@-Af=I@xD!=Um z;BS*8$0a*JN0Pa?7V}{-`zl~YW1RM&uxEB zFO;)&bPW#D3=5&|*ppqywiCnGMmz!28mz~Ju`;fpprY0LNk8LD1%kA?IDAtmfWFCa+atz|vQt0==sPrd!(fKhbN*`hrDQG&o@UR>bTKpcX!#7ptLinvY*-cN{J>ScKuddeR9YP7_{Oq)GS7 zV`{u%a>(b^r0utmMqYk>MaI1+|X zQ!KOgWc;F&^G-$oQsNA4yg26N70>fx-&i6n+hU*jb!j8wyS@2OU@V0eZ^UH9L8Nux zTzoj5nmysmM8QIiaf{iYc&n~UJMs|1el3XZdtMxaMXD@ z5oY52c#tP6bOil+uU&$|ilCYx%N$dGxQfBPaV25ZZ>#v=aD?fcEQ5!e-KSnrgPgJb&gn@)S(AIV(8dDV(Bj&yW~0`A)S(7*5C)9 z6qiomzlBNCWy%Be3~s%jZdm?$LFtjNh?|rh_I*dozmb!fwC&xTH_)m+m_=kDd9;Eke z9Ew992lCw$2t0MS>1Z3HBcl zs>i9VM!2F@-3e*Gtsgv!RxP7=ZL0n7bL}oQy%6b(z^%|&-+!@?5i~vD%h`zEa9GdZ zcup$M{OuxQz?aDslig0TPCzLtSkj%r;or80sw=ZV4LQG(A%RCZN+xE%r{!}jDHOa+ngMuZ#%t(fY zInVYVbvRmy-$;MX)6hl$6dro#T}t`}@w>1a3|zi?m;JK-MtwG^qrJG9@rnE`DIfg$ zIBnZ;$~6Chvex7K$QPX{LFTiM1OvssRhhYpL*E#Enc22X4mzBW*f@c;ZSt0w+FH(X zq{A$v8!^(#6Sk<@I0pJ%FSIA6W@*zlm4~aw(^B)0$#@=%A zp1{nI`o#Vg{%O~x%b>tyur|}~Wby$7h{CK7-PHUb?5!Q8mY1H!swbj`F)*euz!s!@$i^rWkUW41g$jCvC_9-|cO~sWC12~Ou zCm124_Li_>)ATYAA?G!(D%8M&aGa>*GfvU_)iQf>AS>&(HJjes408y4<%=16KFsKR zB8qWw6KHSUoc`O^0Z&0HG^^Xu26P}OM1F5;3Mane{iAWme>X>n{qQSg zO1QnlFO+~hS}cG*qh;-F)Aoq};qItc)vwa0%1H?Q-$3sNlFZ#$$J0u2>w*?IQ1?#B zq$-Obn`0k}EjQvtwz>}47QitPFDrR3KAb_jsKiQDo>t-E4tB0xk!FmHOzW0#`Vo2! z7lFOh^=2&hdU}ZGMbJ~)@!NT5S<&Jz+H!UUChoYt>y$>619P5pHEr9IO})&u+mdfh z+&b&C4(@d*bHdg3_xc09&>&X|f>swBfnB@!pk3pyy1g0I&9g?fe8ETqRFv!l`N7B@ z8q{CCn~eeFl=G*Z*Qu~u{AqKCX8PW3n{;NGN>}s_&_@%#oj0h}Z)mN=4Ce0SW%KWc zJ`P09D{oXw`Q9s-)I^2PGBs68L5Fh@e!E35I8P%V@ESOfJL|szK5yZQkv%VgIc>)2 zztZV)E&Xg0ZKdOF}WWW*9bH+C` zUZ0)Yll;U3zP_nt%pnsCsa5)NHBcaYNTBvLkGT$IO0qfah#iF8zskDz;HqU`T>|H) zd;$CMu^IDQMPJC2#M7Wsubu=N*#hPP>`(=!CA_kb0o^&NRAv(t^s!QYyA`1ep)0?n zP-RM%mh$^7&|FL7E`w`=5r_Vc$a?r^={Z@#*6p1YsdAhCulTq#Mh*ZPY}_=9cY16p z8ydMB^x+1wXT4)2MmsAlFF*95zxT{5RMZDJz|sJ~oYa6zGr&h*VGEj# z<+<}NYrIm(fd}3bBfXS_PYg`)qgP1u_7x1|7kVBD5))P`e{&8(I^V(oleEF?s$+L$ zv-$oLZQ1~{!2Jrh_p41OE5P>gBR5z0 zBNxq~!S_^c>@ZvW$+mp!+3RmYJXru^Z8fkBR(#*hw|5vH%cVr?v<-yz(Z=hlCBo!1 z&5~GoJ}%P#G+cK5e%6e^y&GhjcG{Tq9DjZ0X1gi$^5|zGf<_sHlQz~`HBy270o4y@ z=2~Ox^G9;+Aaa`H?oWUc3>jW@#{>j4g>LMRjR448n#9G^Zz~HD)4S@AZpkXR{^jka zQWgEd+l;U@nh~%u_Wl#do)Lz$`h^TOemhl7kz&WKu``Z0#uhorDaRd#fw%oV2v=qG z^|=ue_;shK0@E_=vw)F3Vy3)e%5WQF`6R;8@EALXe|UF0T=DZxn@9C&%CKat{G;sl zN3oZ=*DYhCX@`k*K~kfFS3gzn)JVn;X-g=290KGJ^^;-c=zhMlI#Ja_GRS8lKV$1Uiw=y98nBk(o*QIBT^j&ygeNo~q_1xwI!)1>L?O7k7 z+iJxivm}GsM}pjZ5J3SurT<8{k4?wkTAd`m>n#B&NHnT)u> zdT;#cT$z;qli%04#^DWN)%4) z$IYvoEw1#w>DW#NM~$r3uYW^VnQjOn_6wR0W|)Xek!I3!_!JI}TNq9Gl5GFLjmeB1 zPJDOe0!OEY`ZFa`P%y@OuA^=_cA=8+lsJL2>B!UG)*Q-poKS{#B5o@ogEoJgA&uX1 za+WN8$`I8HD98Rbt6jS|jC?RSmq-fgDQILHY*d?~=`ulZ>a&Ph;Kn>>Knd?5g;woZkQ>y#$)oiGow~}F@RvfE>hA1ocvR}%#Ay^c!J$Z(y$w`cVnU*2+ zW?#9weR%9t!4iRPEp__euv)I|B^edWR_diwZvAzH3TCCcu+L0w2yqJ{O zT`uAG=-}SNill>1t@UO#?R|m~$J%u+v!Fp5PFU*9(z^#sj?Aq~S`0W+@mFwXtaT^a zFB%%0E`jU1W*oSY-mS4eqT?<5*yt=AZBQY6kh017uW-lD7B_Mes25cU2+354-X%o*-`X*k{Po}w7lG#)ZtGD=bLT-=GmCNPC3Q^|v@^-G?O3<`ucp`nB zCO9;m=7ecEjb-yQ~P zDnM1}U}^v14soRHX^H{$ z&}>lQXgo0_K$L}fcr+oGn9~sO*`Q+1iStdf;MjS~LdlYJRL<@LbX%oE!6ih_y*lB? zMv)IIB*iWu65EA4>n)i!MBVkP>+J$Y?0p?KeLS>vwsbvZ2bo!Dp|GM=$!7&DT@uTH zNy^8zDR1hUa!zmfO$1QBneNQjW9YYzv{x+?#%tz8tBl~qhlC$r#=;69((ewEdKhV( z9rqQ6KOjFNWa_LIu?e*1ttGKf1l(I;Nseq`R8JTXwrZP$5I+y*K}fBI!VD{~Y0}o0 zwoj#%8D1nVtjsak_`W=(r4ha89uNxbarwY^u$CSy(lLJc_AzDndSs9%o_p6i&weBS z-urhO(p^7J1%H@+$k@OwqP`O}Yh{=`<#0_*^-lAe)tjIkGVeH_FINKX3tgg~GJaI4U2+{7x*hy za#i*i#BzJ)Wln|NbUI zOXcj?wUvSdWJ9)&nIXl%c5p@K7zoAE?3Qowg!Ah-S_bHe86&->-6X6S$M%>)r%%?) zpUydxUsZHOupO6i!^e2X6I0_(4XE2RO`8uc)ZC)z%?tE+9-(YIn-Z|v6{Ea=SU;Y; z@=h(IxR6VILiW9#MtBY^Jb6zd>0@QtfUQ*oTa8yo7@-t>av1lxudqXMcpxgE5lkO) z{YLMb<&x#%Izg2TG#{Hrr;etT?V2|iYeja6!Yx!r3x+qzNZWcdC8RBh!AZq7TR#;@P z7r&$enab9`J#@b?N8xjD7^woewre>Cqam9kl@7`e@!+ubR!w`S8mk}0Ob zu@FCGJzt#5e*0~b$#UEp?#9{i(-(IY@KWBJK0Fw2e%vVT6=)cnC?;cCH)=~8ALgu5 zJEaRrV|NU+Un$IgjjR>K`YV08u2&%vydSEQ2M)$le^0!gubNwA?`EN-n#>M!RS6Xp zXa68aUu%C?nao4lBgaP$yNIz$kmtxb!|rgQAY*v?jn00c^z~>xF}4-SV2&cM+11{B zC%-z68@zMt&N>?)khXO|YLLE#`0@W4O~>w1W|1$H zG=2!?i)=kMOjMO)w19M*6$8EFj}J6CI(PWv)k)Zcbc6nMPtX_)Jb`X5*0KNb*nfV( zx~C^d#gVRoMBR*e(vjGOP)z2ISswn&Xftr9u;BO`_>Gt+{s~>VBp^YWx=nxW@tHyc z;T!iWZ9X~v-@QOsq=ap61vVno6GO6O&E*NorE;oI2TAyhME)`xlN=`N!19CIrj?}K zAHMsa)(y*-2D7p(aQqMZABG(wFCBO+Z0bGj=w3A-hWA5d_8#p2W~CB5*yLJRy_72A z(zgPIy8XcQUy{-}{1PZz^yDnussDYXeAA4>*!u5TR3dA?b{#Ilomh_SW2f+3d+{0DkUi^RxE z;)DwN{jSM8{LlqCAS4zacB`Vgx?-LpHxI+YbcMPQRho5A4kLWW*6pn*2fAqkkH$DF z`5k5TRXf`06{=1L7y*!G7?->dqJF#4uQI(N8`*1-<5DC~%hjQrJt{8dv$W95>sS;} zSGoGr$AagPqd=|G_+0Gw3c*VaZh1c$b zopbs=&+Gm^_w)PR_w#z@k25)QEuU-qT<_0%kP_4Cg@$0&+C@#tzlRd+J)+|lw5 zmFUB2!6GS~S+ciSG;_R%qJMS2QI#Jz27QO{uJx9%TW zyw+Xo%)Ff~{oDAfq%p=)Id6yR)6cDQl#~){(?a|ZO5tYX>)ofT>qBNfmKwM&IV5Hp zX1gqRmNRBPtG@&B8wx=ysiTTBJKyxrwZLz|HTtU<%r3a8^O`;*>cGXV(yd-35;Nsa zsWtMbKYyf`h~8)Z(r$@GNU<}aCHU&cdn5>%H&Ru~Bw}+`ms1&wgDM(evXhlt;hM!q zFGF?o9ArYiM6-AlK2<}8e*gYlMcKmH;ZC}QVU8)2eN&oZr{tSWnfQxeodo%HVv))z z<#AuiX$b=7{e%QVt5LzhlFU8MJ+Cb*T$RHnO+EEv)U?*Lb5~NXw1GM;?U>R23jvNN ze_vmspEI!(bwHi#k3~ZQ>BN?)ke8d{a0ye)^reu|c}2Ff!wDr-G==9ciB|(fx_} z0%>Pr?6SWUO{Knkmbte0B|3t_wL@|`$1`I=yH-a;t^Y=32G0&m^@pqL#BwI#`>V5W zTOGXJ#T!l4DpJd~|5WbShzf8ft{DCHzUN}O{o9w>nWwH@!6hKumW6yGEVVssSGB9p z)^Q6Bu&+S1Mb?i9HxJ(BxRc+&n`4W%?mhX^nTsv>RbG?VI7e>Zd#$yYY%gz&IZi7a z{v=BJa1T*!iRe$d99bGU`ci>X<>T;!WIIGt=3)L)fg&X7_PpSMkMr^GaNj@pMliA~ zr}K$OY2upAJ*26rQ(0AGco7IsR$XxBwJzU|UKFdgeYa#srEY(YE}v0e;|2R~tg|+> zoIU%q?tSoKlkZs^T`ZnB>zr{Q-cCn^&%yCSmTP3)I#e{kkyBYgHQUB0Y%^m#Rk+K6 zVMRxEUKXh%gW7{#WFJywsri(hf=50<|DGJIR#W~uw~cyEJP~E^`gO#ox;KLEUgFZc zyw7+wZrI!n&hwZ_OGwT&k?j>OM9pc-;%o2H7r88`iM|@IPaM%~NOM@ZV4>9hLR;3y zW<>jVqf#PK?ziR9;Cm*D>=WLNr75SGdbmHxx{iM-{-_{}_Y&e^mT=C;uH{k_=Scx_ z+D+uC0uBKy!cNV`Ane3$2LMI6;>9YwK9(IRmZWXI+13``45%NGgswdOP;M0N4KbpQ zR_eStvBgrDsR&I!5?^1mr?`p2HTPbs6@ia?gX?P|L%S>P@Sr;4Fiai* z3IRR9MS(jd=^FFL-Jb3Ncouoq1{}4ndQ6KeN??4zMOpI^F9!SZ)7Xyv&Xk-?` z;?O!aO!8tHMo7I}_UiJUz>n3b#)-V>~a3-|Byz_?%)!4KkRO>yp?& z9baEU<*G}G4ff>~DgJbs%R$E&AobS*D05sMxC*H9jX!=HgM~DK>}- zXKpG}Y%7Cqm(C=TgA*!g(3YCFU93)ty=n4f9+TIfez${zzOi8*`;tp07yx3ru1GOq zS-;HZqTk)-Z>ty25EgDKtC&Rr)O*$PGZM-`BTNmye1dm2G*#<3rc0Y}uFIheF=qQEj zrf^l8W;CJgA!8>M&oj(EHW_gz^VU=Q1;A`d2GCQu;v>$Zgf*T|vx(lCp@>bL#T#O3 zyl!m{R9QB-O+&ovavmZ)3Jy3wgGe5Itr{I?E8Tlx@_Cxw($#eKoYNV(Ue_2!id37H zC1_qZ;SS&Lu$wMCX>%p)gq!C<2Ua_VJLt0^r;T+D+VZ(P&KremcYT}_v77yzTGw}; z*NgRv2TzEtlI46+!WxynXiyBZVtV*; zKkUlO=LS5Xxk~S6l~p22hRn|eAFfBp)>oWxUydOG#Km?UMHzA~KIak10v zVke(3oqE`#4HO^o?{d9>j1a&ogYi8F*t4_mDRWiFm@XI%yOQuqhAU6Q73TdsQ}@Po zzAlZvZ{B=$bLo_n&P6>Pc1>%QNn9U*EPlE!;wqUsFs zeTKzZdG8hK>Q8*w^r;jF8pA2aoIo@ERCvblP`b$*RwuI4C3*LjAC3op`IEo}P}lwY~ZZn&V5 zL6-M?x^+PNkO0}&WU(gfoP=655IS{!C3qd@Oi^~Jh3;HyKqyQUV=!EvRB8snZFh#> z_QCzXB@7|L7~WhU7&*@&*rK)+-An0A(7D)zmdw@u@1YM<{I@?Vz>V+Tlir?GNWnx# z%-`_ai^C%}H%6j!1;Y|C!|T?afl0F4HcTX<;-NbN-#_|ogSY}m8-4GX9-%dgW$WwG z$`r0LZ5fB+r)Mjyd$FnB4LKv0=JnITbv-U{@`-_5zyHg@cU^9591YDrV{Y(JsR(8asdesXig(hal&YW>@ ztJ}HN)s&uulSjh+>vjx~oLLoeR-SA@?P3b|7a8a{)@%k|OI*wm@Di^>W#(qs9VnTG zK2XZ*-7tHWS2z=ghp%qtwrpE&Rc&f3;hGT8j#=mZ5XOUhAx{g&%1edagh zu5jvb-ceGs>FFKe%?+2vv}epWd+hJuUHa@MuEg4q*kl)BHx%j^-fspl1I`t zB<@^5+=V1aabAtj10T|52Z8*{wzBtN&HLuBQex}MQtT*Px2}2Hf?Lt|@QZs?z)2c> z?!NgKZ-MsjDBraOY~-%m<@5{$xiASgl7?Q7(b9_{_D z*}mXv<_M2W3r628{ODwsvl0+T8}m9ZtAt{CN0e@%E`P;JHob6N_!pBAL9r}f?fBVk zd|%abE2VpA9K`#ZJ4dAOL=pbGhk0aZ>yro@r@piMTZLXRL=>E6axiWmq8&R#5=^VD z!g@VPW4x`sqhLw+CTx@8$*Jqs+Fgo}nda%}1D6zhrLVpzXN**?a_^490O4?uaERf|$aA4JW&{zL*`cK1kBjKCC!<~vM!YXCIw9=tIwVDFFg66N4^fkXoy=9 z9iLb(s4qY=%|ezRO23M|%Y)AN%i2kdWMFc)X%IxyEt$TBV*T#7wH-Uw$9Si2ziL}~ z%62R9K#T*yo_U9a3P8g9_c^oI{yOkE+es)ngB@gDk+nfxJlsL|FZ22KuCCM&B{{*x zzvziPin5vENd${8rwl70fZ;m%B>Mpj!u__f0Q#3@RR5R;u+3fM)c)3dBdO?lCi0-~ znuBFIn36O5w`G~L1$GC6?ae^m!?|OWys~+u)CQ2@&#!(t0f2CpR~9CjgI(RH)ByQ4@VaHob};2Q#&*)kpo;#rm5n z^DnFOf9wJ!5?AK_9p9@ygq~`hs@ucKcP&<%htcDJ+SRLj5A9EKV^-oT zJQ4WHXx}z7+KI2kzLCC(MIdL*x5#4=4Q`36{gSZM_x|$5&6NI< zFS8Q-gT@1wweZI96+@Tj7bgeoUgWQ&`!%og`8`6e^t;SS)am)KHfKf<$VVES3a0j# z_SxF7s||uSFHW0j*Xg^(7Tly^lig8WgNJOCm2wKSyXAi_P^&^d*T1g)|9%8h=$~p} zKs8@RXriLtjAQ-C(Br?*ELGu&@|T{_D8Gx2^%&~=IC~gD8@dsCeauB{z@6YA=BqBV z`>Y@2%G5RY7vKf%?oZqZ>B2I(p*S8wjKh6e`_g9VrW|TeT|JCoYvO^w2K}xJ^ z>)qX#WU61S%0I(piWyB4i&UJ@Eq1c^t{t&w%Uq4hfj7#`eH~jcfiClG$hlOrbOqki z&URaRB;~jow%s=_PkV7AWm%7Ow87_d0!N?MzE(7vQ3D}68MlJ_1s$bPHWT z9f&V^_HjGFlBg|)^w4sd*N)vonD|GTG?1Yq zUYJkP%Oj)+uRl_*&-Bep69z(Mx2EitkEnt)Aj7FE8RlX^k>{JV$_|c)BLQ>Eg=l_{~td4nU=J1`0Yq3a+o z&SS7TRxSQkJ{zMT4mh5Q2S2~xUTA0~ZWM%ivas_zU37%Y3=48($ni*quc$M~_h?XS zj5Bs^FIIQH+L=ViZF)fP#Q==1RV!f#J__PHu=gq4%u6@ZpYdcEvB+<*asJY|aOq&7V7>_2tZU${%!A=DD6VxDZaSnR7jw zBk*K+Hq6K>`{pi(xc%h6`Hv~6&g#I>-Fbw-+Fz#ghKn5gzjLhrTg%oY<-ctuCB>+o zR!=3l)yOO*g^Fx(4ejYDU+7;8^S*XjC`L}BG3v0Cl$;dYP$n)Z+Wgwuq_J^C(Nd#? zoBR8)Nt3X$M!VV!_szAQ{tTF3uh;C_*leoR_DZ-azck1lwFxvTp%rT;CZ71ixP|5O zhSXj7r9$rIF&MWoF*CDtJHX#pvWqh>!px_kJ$a{c}PN*CDYdOD6YVVfj^kdt0|F_*|RpM$edZ{EiIU zst+_SbosfiLMwhhC4QOx^WnF_--5ap8A5w7zMFNJBTe;@Rr^$Ee8t2~ zqY3w_jg#EQ()hnorzW7kABtfRxk@Na_||kZRm|UOugKuFZ%xxBA0%HMwoEsMM)M=S z4_fteZ&(dJglg1Qlm}^TN0yu1v)>s+`i*_mqe=x9#W#uPX15Gs#L{H{Jr4eob42qV z;-#C(>=-=}1MSgC6P5<`@Wza@3FcA4O4Jv5?x2dBQ&q|q>iGTfX`vvA%>45CcZcrs z%6U>He961|v{YrqPR(9}sx{IemcmPScwvdLAJ#+R%EQ!RZzJv0UkV;#mkKn|j71WL z7+K*ukvC>no1FIJpD-VmnSQJ9k3R6B;&EPBC!L~@p2ikSQf#UJyIV#wvqEgdxys2N zq?66ytAsRE#Zr;eovx1J$nUz$h<7Ra$j#)FZP!;ladvIKm&UBb^o!{+CF$kXku5VO z{y|y`ne#}kCOHDv5Ze$L<3XT%?>!k0wG3fvH1-@(i`10Z<(QV-_|4WZ&X`Y$KJ(aA&_zP$#L7F|N%Y2`B&A!VXEQQ1vS>yTn-#SljAa6DZ{ zuvSQg=*E2TaqZ=asX!0SdrvN9=Wjka%YD$`nXTJzAL-LSYD#XuPlzmQEOGQOr8aT(hh#fbn6{6B(d4s*?j?{MNq~&aSFLCdwCX-#4 zo4SrIoxGTOobN5UPs;mSF_W|&$_Qe-Y{}b>SbcTO{?@@s?juiKAF>q;!=__r>t8#h zxB6vWncr_vSWvlVv*iJ{qP3+NF(pfzK)m;fZ%S+3UPa*86mKTHpl7M({Jap~DC$GR z)?(CphP;gFOxVTE+%~An2jg?6o@%hk*9*tA3F(M;ZT6fl^SPbm?Q_&G_;FKdacn2s zsEvAUf)2lHSVBs|Myl8i-o)b#I{E$|bd~%Ujz_F}nQiG$x#31WMShq!gEeJTocp?j zhEKW@(7{eNqaj_gT7&1xiZQq$x3YW7at*lS=KB4)=RUC+6Y*DF&UnFp&tK-R*!CNs zC#KCGZR@#WV9EPEvtuq_n=P8VrXr_oS}XaBaaWL5TjqSQR{1AgDbnG;y>EdoVxuT? zE%Q#~+I&!tiM8zOD{@+OjcV4doA&69Pon-N!#Bk_t*=tj{c_XbDY1{|{pRPlw5hrlw@$?Rk825a}y&DQ$ehelx=@3bl zi)rr-GNf~poE@SECYF67+)_%=blvYvcs#ywpBa zaF%|@QD#d>8N>T;WvQ=KbMD117tj;CbG|4msGu`)WjEF-b8strzMI(HD8t><-`dshYfom^tM|W*Ngb$ zj(3Pl`o#zRD^Hf?UC|=-k4wA8uHcRupDd#)n<*je#otylQJ-tV zspGff9U0ss1Bq5LD9_8Qv=zU5f5kDEa`y%&Rn|^z^DA^=>V1mtZfbvoj zGXOy^k2OzP7iM`~D-+M$wclRgxbr8{*(94F#cQdh+g?R%=R#t_M&)2s`~0~ae$4>{ z0{p*!smmgxrapqOL;rYBARZ1Vy{vYj*PH>>2NdmoMOI_b>H zG^rb8o)*cXUx0Xzv$T5E9L6)|7Vg1Tc3d9K43`Y|4zgZPC#=7>BoG*WH`GX5;~FXR zet(Bnk0S4uMr4#IA*M;{Zi&3%h0IuUx3Wb^8@Hi=>RQDLt^MsdU^am-VnkZ=`2-v` z%vt11z7=wA^hsxAGReenmqIkUvOm+6f|KdA*m- zu=E3*H8i1^qo&(OJGUZFvY+}j^Ftm0*_r2*vWFB;9)55ql>afo9(V9)8Y6!moE41YuP#6X4>`~2Bnl+0Z&Z@ zi%tf&IMTm$qvfu7XYYXZg%sO;`jeqJ4bBIe`TgUm7~K@(n}p-rw)i94>~7oBsq<*y zr!fvU?Mz9&cV$^5;z0Zr{E81=9&bQlGE0P2W4;7sIt3fr=Fl$?)HxOsMz!NH!#%px z!YM5;kCJ`RhCAiXl>Y{2ez$q?2G?gy?_3myw`KPCIs195TU$#`0-U!F?c);pC0BOX zgJmPG5@mf?Kgdq2Dg>*E_tKgo;6nEE+FL#cA|t{ySmg=ENh92-5lHdZ6TXg?7fl)c zxb|TEQu*B2TC+dodG`n8jh< z@zAXjB!~o@&#yV!Q@aN%+HSjV4{RPWgQP;=a8sTScnQ(Re3lq~U6BQoa$rFPYZ>uy zKhQmMk~1W-mGKa%K`xmN7I)pd=$#_JBJVvSJu0{QfkDij1y}J@2$%OAJIok zY-k+VQgxb_vKwxCc_<#O`O+Kuj&C2|$@zn$={{!`Jm*#4+A8q3Muw*8nz-@kIL&C! zFL1gA?caGMvr=WYrE{=F?;YFmYPltx)k-^L>yGDeU^%8TT#mRxh5HqXz&#i9Ux*pn zi~(s<;aw>ZAvbi%=XFg3w;roE7_Y#$RpZAt2#~bO3a=RktwJ*A3OMe1R!WDbT(C3ou$OqaQ{BTy2w^jmpZY$#~L^kM#f^udT>bf6e8FC zu<55hFPXb~clTF8uSQBmuLG zs>QIkZ>?`_Y`tNp?aD*{5d3n_<8f(n?lZlVZ0oHNtT{~OE8*3Vaa#jDonu!l_O^-< z$4rQWa>j{lL$e7K%R(9$@2Lij50g%BZc@0qEm>u0ac&C)3Phx-TVQvlCw~?2`5V%(FB`BM73bNF5?IYu9_$Z!u zZ+hCpD{;NZD*O>rvy3XzxI&d*abw6>cH4tEWFln@F&b~1TX2$P@w_{=8kG6*;KRi3 z>dbx-_l5YOZ~7hW6D?)@fkW(01=Dd;bt`cdCsvgfxN7);RJDQ*X0WeMAZt`3mQF>z8shk1x<$8-#njm&v8(-AkB&Q3TY)U(B zyR)a{!PV^xWCFI$;Xt61tXNJ$)A%o3KYrj2RZF<}g@DW7P`1x$Qh>n8j%Fg$=7i)-T zZSFQvzsW6q8xcdG*bJpd`)>|GW7h?q1HGiAGXu^bZi}YxgD^$j;Z%b$O6-=y{z2b2 zPa-*VDV7PDuQk%x^l0T>sCKt{x4cr0(9!sqmH_bT{JaKdl^~4OlPd3RDFRVv!pH7U ziC3j+kr~_s8b^Ej3c9m*gnp*k9LmeR@^i4b8qXN zHr{F+S&1qGsC@PT(�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 diff --git a/src/providers/tulnex/decrypt.ts b/src/providers/tulnex/decrypt.ts new file mode 100644 index 0000000..0f02063 --- /dev/null +++ b/src/providers/tulnex/decrypt.ts @@ -0,0 +1,124 @@ +/* +* Credit where credit is due. Decrypt logic was taken from: https://github.com/vyla-entertainment/stream-api/blob/main/sources/cinezo.js +* with permission: https://github.com/orgs/cinepro-org/discussions/1#discussioncomment-16937840 +*/ +const L1_KEY = 'U24wMHBEMGcjTDFfWDBSX000c3QzckszeSEyMDI2c2V4'; +const L1_SALT = 'eEs5IW1SMkBwTDUjblE4c2V4'; +const L3_KEY = 'U24wMHBEMGcjTDNfQUVTX1MzY3VyM0szeUAyMDI2JHNleA=='; +const L4_KEY = 'U24wMHBEMGcjTDRfSE1BQ19GMW40bFc0bGwjMjAyNiFzZXg='; + +function base64ToBuffer(b64: string) { + const bin = atob(b64); + const buf = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i); + return buf.buffer; +} + +function bufferToHex(buf: ArrayBuffer) { + return Array.from(new Uint8Array(buf)) + .map((b) => b.toString(16).padStart(2, '0')) + .join(''); +} + +function strToBuffer(str: string) { + return new TextEncoder().encode(str).buffer; +} +function bufferToStr(buf: ArrayBuffer) { + return new TextDecoder().decode(buf); +} + +function hexToUint8(hex: string) { + const arr = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) + arr[i / 2] = parseInt(hex.substr(i, 2), 16); + return arr; +} + +async function pbkdf2( + pass: string, + salt: string, + iterations: number, + keyLen: number, + hash: string +) { + const keyMat = await crypto.subtle.importKey( + 'raw', + strToBuffer(pass), + { name: 'PBKDF2' }, + false, + ['deriveKey'] + ); + const derived = await crypto.subtle.deriveKey( + { name: 'PBKDF2', salt: strToBuffer(salt), iterations, hash }, + keyMat, + { name: 'AES-GCM', length: keyLen * 8 }, + true, + ['encrypt', 'decrypt'] + ); + return new Uint8Array(await crypto.subtle.exportKey('raw', derived)); +} + +function xorDecrypt(hexStr: string, keyBytes: Uint8Array) { + const src = hexToUint8(hexStr); + const out = new Uint8Array(src.length); + for (let i = 0; i < src.length; i++) out[i] = src[i] ^ keyBytes[i % 32]; + return bufferToStr(out.buffer); +} + +function binaryDecode(encoded: string) { + return atob(encoded) + .split(' ') + .map((s) => String.fromCharCode(parseInt(s, 2))) + .join(''); +} + +async function decodeL3(data: string) { + const parts = data.split('.'); + if (parts.length !== 3) throw new Error('L3 invalid'); + const [ivB64, saltB64, ctB64] = parts; + const salt = atob(saltB64); + const keyBytes = await pbkdf2(Buffer.from(L3_KEY, 'base64').toString(), salt, 100000, 32, 'SHA-512'); + const aesKey = await crypto.subtle.importKey( + 'raw', + keyBytes, + { name: 'AES-CBC' }, + false, + ['decrypt'] + ); + const decrypted = await crypto.subtle.decrypt( + { name: 'AES-CBC', iv: new Uint8Array(base64ToBuffer(ivB64)) }, + aesKey, + base64ToBuffer(ctB64) + ); + return bufferToStr(decrypted); +} + +async function decodeL4(data: string) { + const sep = data.indexOf('|'); + if (sep === -1) throw new Error('L4 no separator'); + const receivedHmac = data.slice(0, sep); + const payload = data.slice(sep + 1); + const payloadStr = bufferToStr(base64ToBuffer(payload)); + const hmacKey = await crypto.subtle.importKey( + 'raw', + strToBuffer(Buffer.from(L4_KEY, "base64").toString()), + { name: 'HMAC', hash: 'SHA-512' }, + false, + ['sign'] + ); + const sig = await crypto.subtle.sign( + 'HMAC', + hmacKey, + new TextEncoder().encode(payloadStr) + ); + if (receivedHmac !== bufferToHex(sig)) throw new Error('L4 HMAC mismatch'); + return payloadStr; +} + +export async function decryptPayload(payload: string) { + const xorKey = await pbkdf2(Buffer.from(L1_KEY, 'base64').toString(), Buffer.from(L1_SALT, 'base64').toString(), 50000, 32, 'SHA-256'); + const l4out = await decodeL4(payload); + const l3out = await decodeL3(l4out); + const l2out = binaryDecode(l3out); + return JSON.parse(xorDecrypt(l2out, xorKey)); +} diff --git a/src/providers/tulnex/tulnex.mapper.ts b/src/providers/tulnex/tulnex.mapper.ts new file mode 100644 index 0000000..cfa51b9 --- /dev/null +++ b/src/providers/tulnex/tulnex.mapper.ts @@ -0,0 +1,77 @@ +import { ExtractedStream } from "./tulnex.types.js"; + +export function extractUrl(data: any): ExtractedStream | null { + if (!data) return null; + + const wrap = (url: unknown, headers: Record | null = null): ExtractedStream | null => { + if (!url || typeof url !== 'string' || !url.includes('http')) return null; + return { url, headers }; + }; + + if (typeof data === 'string' && data.includes('http')) return wrap(data); + + const d = data as Record; + const headers = (d.headers as Record) ?? null; + + if (typeof d.url === 'string' && d.url.includes('http')) return wrap(d.url, headers); + if (typeof d.stream === 'string' && d.stream.includes('http')) return wrap(d.stream, headers); + if (typeof d.playlist === 'string' && d.playlist.includes('http')) return wrap(d.playlist, headers); + if (typeof d.streamUrl === 'string' && d.streamUrl.includes('http')) return wrap(d.streamUrl, headers); + if (typeof d.stream_url === 'string' && d.stream_url.includes('http')) return wrap(d.stream_url, headers); + if (typeof d.streaming_url === 'string' && d.streaming_url.includes('http')) return wrap(d.streaming_url, headers); + if (typeof d.video_url === 'string' && d.video_url.includes('http')) return wrap(d.video_url, headers); + if (typeof d.m3u8 === 'string' && d.m3u8.includes('http')) return wrap(d.m3u8, headers); + + const srcsPrimary = (d.sources as Record)?.primary as Record | undefined; + if (srcsPrimary?.url) return wrap(srcsPrimary.url, (srcsPrimary.headers as Record) ?? headers); + + if (Array.isArray(d.sources) && d.sources.length > 0) { + const sorted = (d.sources as Record[]) + .filter(s => typeof s.url === 'string' && (s.url as string).includes('http')) + .sort((a, b) => { + const qa = parseInt(((a.quality as string) ?? '').replace('p', '') || '0'); + const qb = parseInt(((b.quality as string) ?? '').replace('p', '') || '0'); + return qb - qa; + }); + if (sorted.length > 0) return wrap(sorted[0].url, (sorted[0].headers as Record) ?? headers); + } + + if (Array.isArray(d.languages)) { + const orig = (d.languages as Record[]).find(l => l.original === true && Array.isArray(l.sources) && (l.sources as unknown[]).length > 0); + if (orig) { + const sorted = [...(orig.sources as Record[])].sort((a, b) => + parseInt(((b.quality as string) ?? '').replace('p', '') || '0') - + parseInt(((a.quality as string) ?? '').replace('p', '') || '0'), + ); + return wrap(sorted[0].url ?? sorted[0].file, (sorted[0].headers as Record) ?? (orig.headers as Record) ?? headers); + } + } + + if (Array.isArray(d.links) && d.links.length > 0) { + const link = (d.links as Record[]).find(l => typeof l.url === 'string' && (l.url as string).includes('http')); + if (link) return wrap(link.url, headers); + } + + const nestedData = d.data as Record | undefined; + if (nestedData?.data && (nestedData.data as Record)?.stream) + return wrap(((nestedData.data as Record).stream as Record)?.playlist, headers); + if (nestedData?.stream) + return wrap((nestedData.stream as Record)?.playlist, headers); + if (typeof nestedData?.url === 'string' && nestedData.url.includes('http')) + return wrap(nestedData.url, (nestedData.headers as Record) ?? headers); + + if (Array.isArray(nestedData?.sources)) { + const src = (nestedData!.sources as Record[]).find(s => typeof s.url === 'string' && (s.url as string).includes('http')); + if (src) return wrap(src.url, (src.headers as Record) ?? headers); + } + + if (Array.isArray(d.streams)) { + const src = (d.streams as Record[]).find(s => + (typeof s.url === 'string' && (s.url as string).includes('http')) || + (typeof s.link === 'string' && (s.link as string).includes('http')), + ); + if (src) return wrap(src.url ?? src.link, (src.headers as Record) ?? headers); + } + + return null; +} diff --git a/src/providers/tulnex/tulnex.ts b/src/providers/tulnex/tulnex.ts new file mode 100644 index 0000000..87bb1e0 --- /dev/null +++ b/src/providers/tulnex/tulnex.ts @@ -0,0 +1,155 @@ +import { BaseProvider } from '@omss/framework'; +import type { + ProviderCapabilities, + ProviderMediaObject, + ProviderResult +} from '@omss/framework'; +import { generateRandomUserAgent } from '../../utils/ua.js'; +import { TulnexApiResponse } from './tulnex.types.js'; +import { decryptPayload } from './decrypt.js'; +import { extractUrl } from './tulnex.mapper.js'; + +export class TulnexProvider extends BaseProvider { + readonly id = 'tulnex'; + readonly name = 'Tulnex'; + readonly enabled = true; + + readonly BASE_URL = 'https://api.tulnex.com'; + readonly HEADERS = { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache' + }; + + readonly SERVERS = [ + `onion`, + `vidzee`, + `icefy`, + `tik`, + `vaplayer`, + `vidfast-alpha`, + `uniquestream`, + `vidfast-mega`, + `vidfast-vrapid`, + `allmovies`, + `vidlink`, + `vidfast-vedge`, + `vidfast-vfast`, + `moviebox` + ]; + + readonly capabilities: ProviderCapabilities = { + supportedContentTypes: ['movies', 'tv'] + }; + + async getMovieSources(media: ProviderMediaObject): Promise { + return await this.getSources(media); + } + + async getTVSources(media: ProviderMediaObject): Promise { + return await this.getSources(media); + } + + private async getSources( + media: ProviderMediaObject + ): Promise { + try { + const results = await Promise.allSettled( + this.SERVERS.map((server) => this.doScrape(server, media)) + ); + + const successful = results + .filter( + ( + r + ): r is PromiseFulfilledResult< + Awaited> + > => r.status === 'fulfilled' && r.value != null + ) + .map((r) => r.value); + + return { + sources: successful + .filter((r) => r !== null) + .map((r) => ({ + url: this.createProxyUrl(r.url, r.headers ? r.headers : {}), + type: r.url.includes('mkv') || r.url.includes('mp4') ? "mp4" : "hls", + audioTracks: [{ + label: "Original", + language: "Original", + }], + quality: "Auto", + provider: { + name: this.name, + id: this.id + } + })), + subtitles: [], + diagnostics: [] + }; + } catch (e) { + return this.emptyResult( + e instanceof Error ? e.message : 'Unknown provider error' + ); + } + } + + private async doScrape(serverName: string, media: ProviderMediaObject) { + const url = + media.type === 'movie' + ? this.BASE_URL + '/' + serverName + '/movie/' + media.tmdbId + : this.BASE_URL + + '/' + + serverName + + '/tv/' + + media.tmdbId + + '/' + + media.s + + '/' + + media.e; + const req = await fetch(url, { + headers: { ...this.HEADERS, Accept: 'application/json, */*' } + }); + if (!req.ok) { + return null; + } + const data = (await req.json()) as unknown as TulnexApiResponse; + if (data.payload === undefined) { + return null; + } + const decrypted = await decryptPayload(data.payload) + if (!decrypted) { + return null + } + return extractUrl(decrypted); + } + + private emptyResult(message: string): ProviderResult { + return { + sources: [], + subtitles: [], + diagnostics: [ + { + code: 'PROVIDER_ERROR', + message: `${this.name}: ${message}`, + field: '', + severity: 'error' + } + ] + }; + } + + async healthCheck(): Promise { + try { + const response = await fetch(this.BASE_URL, { + method: 'HEAD', + headers: this.HEADERS + }); + return response.status === 200; + } catch { + return false; + } + } +} diff --git a/src/providers/tulnex/tulnex.types.ts b/src/providers/tulnex/tulnex.types.ts new file mode 100644 index 0000000..e3c337e --- /dev/null +++ b/src/providers/tulnex/tulnex.types.ts @@ -0,0 +1,9 @@ +export type TulnexApiResponse = { + v: string + payload: string +} + +export interface ExtractedStream { + url: string; + headers: Record | null; +} diff --git a/src/providers/vidapi/vidapi.ts b/src/providers/vidapi/vidapi.ts index 959b8cd..ab5816d 100644 --- a/src/providers/vidapi/vidapi.ts +++ b/src/providers/vidapi/vidapi.ts @@ -18,9 +18,10 @@ export class VidApiProvider extends BaseProvider { readonly IFRAME_URL = 'https://brightpathsignals.com'; readonly API_URL = 'https://streamdata.vaplayer.ru/api.php'; readonly HEADERS = { - 'User-Agent': generateRandomUserAgent(), - referer: `${this.IFRAME_URL}/`, - origin: this.IFRAME_URL + 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "Referer": `${this.IFRAME_URL}/`, + "Origin": this.IFRAME_URL, + "Accept":"*/*" }; readonly capabilities: ProviderCapabilities = { @@ -72,10 +73,6 @@ export class VidApiProvider extends BaseProvider { const diagnostics: ProviderResult['diagnostics'] = []; const sources: Source[] = (data.stream_urls ?? []) - .filter( - (streamUrl: string) => - !streamUrl.includes('strategicgrowthpartners') - ) .map((streamUrl: string): Source => { const sourceType: SourceType = streamUrl.includes('mp4') || streamUrl.includes('mkv') From 3800ef10d192ffc667e3d38e69e9d4772c4dade3 Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 16:01:57 +0200 Subject: [PATCH 2/6] feat: vercel deployment conf --- vercel.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 vercel.json diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..fcedef9 --- /dev/null +++ b/vercel.json @@ -0,0 +1,34 @@ +{ + "version": 2, + "builds": [ + { + "src": "src/index.ts", + "use": "@vercel/node", + "config": { + "includeFiles": ["dist/**"] + } + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "src/index.ts" + } + ], + "functions": { + "src/index.ts": { + "runtime": "nodejs20.x", + "maxDuration": 10 + } + }, + "env": { + "NODE_ENV": "production" + }, + "build": { + "env": { + "NODE_ENV": "production" + } + }, + "cleanUrls": true, + "trailingSlash": false +} \ No newline at end of file From 626c83cf3fff1325ad21f43a49b83047c29ef840 Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 16:02:19 +0200 Subject: [PATCH 3/6] chore: format --- src/providers/tulnex/decrypt.ts | 24 +++-- src/providers/tulnex/tulnex.mapper.ts | 147 ++++++++++++++++++++------ src/providers/tulnex/tulnex.ts | 42 +++++--- src/providers/tulnex/tulnex.types.ts | 6 +- src/providers/vidapi/vidapi.ts | 16 +-- 5 files changed, 169 insertions(+), 66 deletions(-) diff --git a/src/providers/tulnex/decrypt.ts b/src/providers/tulnex/decrypt.ts index 0f02063..6b0fb8c 100644 --- a/src/providers/tulnex/decrypt.ts +++ b/src/providers/tulnex/decrypt.ts @@ -1,7 +1,7 @@ /* -* Credit where credit is due. Decrypt logic was taken from: https://github.com/vyla-entertainment/stream-api/blob/main/sources/cinezo.js -* with permission: https://github.com/orgs/cinepro-org/discussions/1#discussioncomment-16937840 -*/ + * Credit where credit is due. Decrypt logic was taken from: https://github.com/vyla-entertainment/stream-api/blob/main/sources/cinezo.js + * with permission: https://github.com/orgs/cinepro-org/discussions/1#discussioncomment-16937840 + */ const L1_KEY = 'U24wMHBEMGcjTDFfWDBSX000c3QzckszeSEyMDI2c2V4'; const L1_SALT = 'eEs5IW1SMkBwTDUjblE4c2V4'; const L3_KEY = 'U24wMHBEMGcjTDNfQUVTX1MzY3VyM0szeUAyMDI2JHNleA=='; @@ -77,7 +77,13 @@ async function decodeL3(data: string) { if (parts.length !== 3) throw new Error('L3 invalid'); const [ivB64, saltB64, ctB64] = parts; const salt = atob(saltB64); - const keyBytes = await pbkdf2(Buffer.from(L3_KEY, 'base64').toString(), salt, 100000, 32, 'SHA-512'); + const keyBytes = await pbkdf2( + Buffer.from(L3_KEY, 'base64').toString(), + salt, + 100000, + 32, + 'SHA-512' + ); const aesKey = await crypto.subtle.importKey( 'raw', keyBytes, @@ -101,7 +107,7 @@ async function decodeL4(data: string) { const payloadStr = bufferToStr(base64ToBuffer(payload)); const hmacKey = await crypto.subtle.importKey( 'raw', - strToBuffer(Buffer.from(L4_KEY, "base64").toString()), + strToBuffer(Buffer.from(L4_KEY, 'base64').toString()), { name: 'HMAC', hash: 'SHA-512' }, false, ['sign'] @@ -116,7 +122,13 @@ async function decodeL4(data: string) { } export async function decryptPayload(payload: string) { - const xorKey = await pbkdf2(Buffer.from(L1_KEY, 'base64').toString(), Buffer.from(L1_SALT, 'base64').toString(), 50000, 32, 'SHA-256'); + const xorKey = await pbkdf2( + Buffer.from(L1_KEY, 'base64').toString(), + Buffer.from(L1_SALT, 'base64').toString(), + 50000, + 32, + 'SHA-256' + ); const l4out = await decodeL4(payload); const l3out = await decodeL3(l4out); const l2out = binaryDecode(l3out); diff --git a/src/providers/tulnex/tulnex.mapper.ts b/src/providers/tulnex/tulnex.mapper.ts index cfa51b9..a38c8a1 100644 --- a/src/providers/tulnex/tulnex.mapper.ts +++ b/src/providers/tulnex/tulnex.mapper.ts @@ -1,10 +1,14 @@ -import { ExtractedStream } from "./tulnex.types.js"; +import { ExtractedStream } from './tulnex.types.js'; export function extractUrl(data: any): ExtractedStream | null { if (!data) return null; - const wrap = (url: unknown, headers: Record | null = null): ExtractedStream | null => { - if (!url || typeof url !== 'string' || !url.includes('http')) return null; + const wrap = ( + url: unknown, + headers: Record | null = null + ): ExtractedStream | null => { + if (!url || typeof url !== 'string' || !url.includes('http')) + return null; return { url, headers }; }; @@ -13,64 +17,141 @@ export function extractUrl(data: any): ExtractedStream | null { const d = data as Record; const headers = (d.headers as Record) ?? null; - if (typeof d.url === 'string' && d.url.includes('http')) return wrap(d.url, headers); - if (typeof d.stream === 'string' && d.stream.includes('http')) return wrap(d.stream, headers); - if (typeof d.playlist === 'string' && d.playlist.includes('http')) return wrap(d.playlist, headers); - if (typeof d.streamUrl === 'string' && d.streamUrl.includes('http')) return wrap(d.streamUrl, headers); - if (typeof d.stream_url === 'string' && d.stream_url.includes('http')) return wrap(d.stream_url, headers); - if (typeof d.streaming_url === 'string' && d.streaming_url.includes('http')) return wrap(d.streaming_url, headers); - if (typeof d.video_url === 'string' && d.video_url.includes('http')) return wrap(d.video_url, headers); - if (typeof d.m3u8 === 'string' && d.m3u8.includes('http')) return wrap(d.m3u8, headers); + if (typeof d.url === 'string' && d.url.includes('http')) + return wrap(d.url, headers); + if (typeof d.stream === 'string' && d.stream.includes('http')) + return wrap(d.stream, headers); + if (typeof d.playlist === 'string' && d.playlist.includes('http')) + return wrap(d.playlist, headers); + if (typeof d.streamUrl === 'string' && d.streamUrl.includes('http')) + return wrap(d.streamUrl, headers); + if (typeof d.stream_url === 'string' && d.stream_url.includes('http')) + return wrap(d.stream_url, headers); + if (typeof d.streaming_url === 'string' && d.streaming_url.includes('http')) + return wrap(d.streaming_url, headers); + if (typeof d.video_url === 'string' && d.video_url.includes('http')) + return wrap(d.video_url, headers); + if (typeof d.m3u8 === 'string' && d.m3u8.includes('http')) + return wrap(d.m3u8, headers); - const srcsPrimary = (d.sources as Record)?.primary as Record | undefined; - if (srcsPrimary?.url) return wrap(srcsPrimary.url, (srcsPrimary.headers as Record) ?? headers); + const srcsPrimary = (d.sources as Record)?.primary as + | Record + | undefined; + if (srcsPrimary?.url) + return wrap( + srcsPrimary.url, + (srcsPrimary.headers as Record) ?? headers + ); if (Array.isArray(d.sources) && d.sources.length > 0) { const sorted = (d.sources as Record[]) - .filter(s => typeof s.url === 'string' && (s.url as string).includes('http')) + .filter( + (s) => + typeof s.url === 'string' && + (s.url as string).includes('http') + ) .sort((a, b) => { - const qa = parseInt(((a.quality as string) ?? '').replace('p', '') || '0'); - const qb = parseInt(((b.quality as string) ?? '').replace('p', '') || '0'); + const qa = parseInt( + ((a.quality as string) ?? '').replace('p', '') || '0' + ); + const qb = parseInt( + ((b.quality as string) ?? '').replace('p', '') || '0' + ); return qb - qa; }); - if (sorted.length > 0) return wrap(sorted[0].url, (sorted[0].headers as Record) ?? headers); + if (sorted.length > 0) + return wrap( + sorted[0].url, + (sorted[0].headers as Record) ?? headers + ); } if (Array.isArray(d.languages)) { - const orig = (d.languages as Record[]).find(l => l.original === true && Array.isArray(l.sources) && (l.sources as unknown[]).length > 0); + const orig = (d.languages as Record[]).find( + (l) => + l.original === true && + Array.isArray(l.sources) && + (l.sources as unknown[]).length > 0 + ); if (orig) { - const sorted = [...(orig.sources as Record[])].sort((a, b) => - parseInt(((b.quality as string) ?? '').replace('p', '') || '0') - - parseInt(((a.quality as string) ?? '').replace('p', '') || '0'), + const sorted = [ + ...(orig.sources as Record[]) + ].sort( + (a, b) => + parseInt( + ((b.quality as string) ?? '').replace('p', '') || '0' + ) - + parseInt( + ((a.quality as string) ?? '').replace('p', '') || '0' + ) + ); + return wrap( + sorted[0].url ?? sorted[0].file, + (sorted[0].headers as Record) ?? + (orig.headers as Record) ?? + headers ); - return wrap(sorted[0].url ?? sorted[0].file, (sorted[0].headers as Record) ?? (orig.headers as Record) ?? headers); } } if (Array.isArray(d.links) && d.links.length > 0) { - const link = (d.links as Record[]).find(l => typeof l.url === 'string' && (l.url as string).includes('http')); + const link = (d.links as Record[]).find( + (l) => + typeof l.url === 'string' && (l.url as string).includes('http') + ); if (link) return wrap(link.url, headers); } const nestedData = d.data as Record | undefined; - if (nestedData?.data && (nestedData.data as Record)?.stream) - return wrap(((nestedData.data as Record).stream as Record)?.playlist, headers); + if ( + nestedData?.data && + (nestedData.data as Record)?.stream + ) + return wrap( + ( + (nestedData.data as Record).stream as Record< + string, + unknown + > + )?.playlist, + headers + ); if (nestedData?.stream) - return wrap((nestedData.stream as Record)?.playlist, headers); + return wrap( + (nestedData.stream as Record)?.playlist, + headers + ); if (typeof nestedData?.url === 'string' && nestedData.url.includes('http')) - return wrap(nestedData.url, (nestedData.headers as Record) ?? headers); + return wrap( + nestedData.url, + (nestedData.headers as Record) ?? headers + ); if (Array.isArray(nestedData?.sources)) { - const src = (nestedData!.sources as Record[]).find(s => typeof s.url === 'string' && (s.url as string).includes('http')); - if (src) return wrap(src.url, (src.headers as Record) ?? headers); + const src = (nestedData!.sources as Record[]).find( + (s) => + typeof s.url === 'string' && (s.url as string).includes('http') + ); + if (src) + return wrap( + src.url, + (src.headers as Record) ?? headers + ); } if (Array.isArray(d.streams)) { - const src = (d.streams as Record[]).find(s => - (typeof s.url === 'string' && (s.url as string).includes('http')) || - (typeof s.link === 'string' && (s.link as string).includes('http')), + const src = (d.streams as Record[]).find( + (s) => + (typeof s.url === 'string' && + (s.url as string).includes('http')) || + (typeof s.link === 'string' && + (s.link as string).includes('http')) ); - if (src) return wrap(src.url ?? src.link, (src.headers as Record) ?? headers); + if (src) + return wrap( + src.url ?? src.link, + (src.headers as Record) ?? headers + ); } return null; diff --git a/src/providers/tulnex/tulnex.ts b/src/providers/tulnex/tulnex.ts index 87bb1e0..a7007d2 100644 --- a/src/providers/tulnex/tulnex.ts +++ b/src/providers/tulnex/tulnex.ts @@ -71,21 +71,29 @@ export class TulnexProvider extends BaseProvider { .map((r) => r.value); return { - sources: successful - .filter((r) => r !== null) - .map((r) => ({ - url: this.createProxyUrl(r.url, r.headers ? r.headers : {}), - type: r.url.includes('mkv') || r.url.includes('mp4') ? "mp4" : "hls", - audioTracks: [{ - label: "Original", - language: "Original", - }], - quality: "Auto", - provider: { - name: this.name, - id: this.id - } - })), + sources: successful + .filter((r) => r !== null) + .map((r) => ({ + url: this.createProxyUrl( + r.url, + r.headers ? r.headers : {} + ), + type: + r.url.includes('mkv') || r.url.includes('mp4') + ? 'mp4' + : 'hls', + audioTracks: [ + { + label: 'Original', + language: 'Original' + } + ], + quality: 'Auto', + provider: { + name: this.name, + id: this.id + } + })), subtitles: [], diagnostics: [] }; @@ -119,9 +127,9 @@ export class TulnexProvider extends BaseProvider { if (data.payload === undefined) { return null; } - const decrypted = await decryptPayload(data.payload) + const decrypted = await decryptPayload(data.payload); if (!decrypted) { - return null + return null; } return extractUrl(decrypted); } diff --git a/src/providers/tulnex/tulnex.types.ts b/src/providers/tulnex/tulnex.types.ts index e3c337e..0b7ddd8 100644 --- a/src/providers/tulnex/tulnex.types.ts +++ b/src/providers/tulnex/tulnex.types.ts @@ -1,7 +1,7 @@ export type TulnexApiResponse = { - v: string - payload: string -} + v: string; + payload: string; +}; export interface ExtractedStream { url: string; diff --git a/src/providers/vidapi/vidapi.ts b/src/providers/vidapi/vidapi.ts index ab5816d..b1f671c 100644 --- a/src/providers/vidapi/vidapi.ts +++ b/src/providers/vidapi/vidapi.ts @@ -18,10 +18,11 @@ export class VidApiProvider extends BaseProvider { readonly IFRAME_URL = 'https://brightpathsignals.com'; readonly API_URL = 'https://streamdata.vaplayer.ru/api.php'; readonly HEADERS = { - 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", - "Referer": `${this.IFRAME_URL}/`, - "Origin": this.IFRAME_URL, - "Accept":"*/*" + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + Referer: `${this.IFRAME_URL}/`, + Origin: this.IFRAME_URL, + Accept: '*/*' }; readonly capabilities: ProviderCapabilities = { @@ -72,8 +73,8 @@ export class VidApiProvider extends BaseProvider { const data = json.data; const diagnostics: ProviderResult['diagnostics'] = []; - const sources: Source[] = (data.stream_urls ?? []) - .map((streamUrl: string): Source => { + const sources: Source[] = (data.stream_urls ?? []).map( + (streamUrl: string): Source => { const sourceType: SourceType = streamUrl.includes('mp4') || streamUrl.includes('mkv') ? 'mp4' @@ -94,7 +95,8 @@ export class VidApiProvider extends BaseProvider { name: this.name } }; - }); + } + ); const subtitles: Subtitle[] = (json.default_subs ?? []).map( (sub: { lang: string; From aa3ceaffbed4a736161be250612ddf20029ef0a0 Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 20:47:02 +0200 Subject: [PATCH 4/6] chore: general improvement. Every source returned is playable! --- package-lock.json | 12 ++++++------ src/providers/vidnest/vidnest.ts | 2 +- src/providers/vixsrc/vixsrc.ts | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d7e755..9a9adb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -601,9 +601,9 @@ "license": "MIT" }, "node_modules/@omss/framework": { - "version": "1.1.24", - "resolved": "https://registry.npmjs.org/@omss/framework/-/framework-1.1.24.tgz", - "integrity": "sha512-Xr+uhh8LMimF/J+31GDf4az3BZyzKLr1NCepJW2RHF6BzOuheoGDhxh4gAyjXcpr32HUcWKhkrpIXlh0c79d8Q==", + "version": "1.1.26", + "resolved": "https://registry.npmjs.org/@omss/framework/-/framework-1.1.26.tgz", + "integrity": "sha512-LiUjydmOmTnrlUKfJmOS8WNzV9quz/MAPg42nobeLdnjmxgCYsvL7E4CrEfCobV9Cu/BNKwXw9yDx3fvWpoTxw==", "license": "MIT", "dependencies": { "@fastify/cors": "^11.2.0", @@ -1548,9 +1548,9 @@ "license": "BSD-3-Clause" }, "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/src/providers/vidnest/vidnest.ts b/src/providers/vidnest/vidnest.ts index f4c2c00..7232284 100644 --- a/src/providers/vidnest/vidnest.ts +++ b/src/providers/vidnest/vidnest.ts @@ -168,7 +168,7 @@ export class VidNestProvider extends BaseProvider { root.sources.map((s) => ({ url: this.createProxyUrl(s.url), type: this.inferSourceType(s.format, s.url), - quality: s.name, + quality: this.inferQuality(s.name), audioTracks: [{ language: 'French', label: 'fr' }], provider: { id: this.id, name: this.name } })), diff --git a/src/providers/vixsrc/vixsrc.ts b/src/providers/vixsrc/vixsrc.ts index 3711022..116e087 100644 --- a/src/providers/vixsrc/vixsrc.ts +++ b/src/providers/vixsrc/vixsrc.ts @@ -299,6 +299,9 @@ export class VixSrcProvider extends BaseProvider { */ private parseSubtitles(content: string, pageUrl: string): Subtitle[] { const subtitles: Subtitle[] = []; + + /* Doesn't work.. + // TODO: Fix subtitles for vixsrc const lines = content.split('\n'); for (const line of lines) { @@ -318,6 +321,7 @@ export class VixSrcProvider extends BaseProvider { format: 'vtt' }); } + */ return subtitles; } From 60e64d01fdf81b16bafb7a7b31c6ec7476e8d556 Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 20:50:40 +0200 Subject: [PATCH 5/6] chore: add new docs --- .env.example | 1 + .github/CONTRIBUTING.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 0dfbab5..a3c9834 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ NODE_ENV=development # 'development' | 'production'. Development debuggs, produc STREMIO_ADDON=false # Set to 'true' to enable Stremio addon functionality. Usage: host:port/stremio/manifest.json CORS_ORIGIN="*" # CORS origin for the server. Set to '*' to allow all origins, or a specific origin (e.g., 'http://example.com') MCP_ENABLED=false # Set to 'true' to enable MCP functionality +INTERNAL_DEBUG=false # Set to 'true' to get non-playable sources too. recommended: false # TMDB Configuration TMDB_API_KEY=your_tmdb_api_key_here diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9c3a6b6..692dd94 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -79,9 +79,11 @@ const resp = await prov.getMovieSources(mediaObj) console.log(resp) ``` -Then run it with `npx tsx src/providers/example/test.ts`. +Then run it with `npx tsx src/providers/example/test.ts`. A full testing suite will be added in the future, but for now, this is the best way to test a single provider without having to run the entire server and make API calls to it. +When testing the whole setup, make sure to set `INTERNAL_DEBUG` to `true` in your `.env` file to get ALL sources (also non-playable ones) and detailed diagnostics in the response. This will help you identify any issues with your provider. + 3. **Test your changes**: - Test with multiple TMDB IDs (movies and TV shows) - Verify error handling works correctly From fad564a75f324d3f058534a07360f6f1e69e5bcb Mon Sep 17 00:00:00 2001 From: Anon <206556099+An0n-01@users.noreply.github.com> Date: Thu, 21 May 2026 21:12:23 +0200 Subject: [PATCH 6/6] chore: formatting --- src/providers/vixsrc/vixsrc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/vixsrc/vixsrc.ts b/src/providers/vixsrc/vixsrc.ts index 116e087..e4c6967 100644 --- a/src/providers/vixsrc/vixsrc.ts +++ b/src/providers/vixsrc/vixsrc.ts @@ -299,7 +299,7 @@ export class VixSrcProvider extends BaseProvider { */ private parseSubtitles(content: string, pageUrl: string): Subtitle[] { const subtitles: Subtitle[] = []; - + /* Doesn't work.. // TODO: Fix subtitles for vixsrc const lines = content.split('\n');