From eb364ada8421d4be012e791cd64cd13b61bfd284 Mon Sep 17 00:00:00 2001 From: beerpsi <92439990+beerpiss@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:56:47 +0700 Subject: [PATCH] Add HentaiNexus (#719) * Add HentaiNexus * icons * add parody filter * Apply reviews --- src/en/hentainexus/AndroidManifest.xml | 21 ++ src/en/hentainexus/build.gradle | 8 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2591 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1819 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3501 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 5665 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9007 bytes .../extension/en/hentainexus/HentaiNexus.kt | 181 ++++++++++++++++++ .../en/hentainexus/HentaiNexusActivity.kt | 38 ++++ .../en/hentainexus/HentaiNexusFilters.kt | 44 +++++ .../en/hentainexus/HentaiNexusUtils.kt | 59 ++++++ 11 files changed, 351 insertions(+) create mode 100644 src/en/hentainexus/AndroidManifest.xml create mode 100644 src/en/hentainexus/build.gradle create mode 100644 src/en/hentainexus/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/en/hentainexus/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/en/hentainexus/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/en/hentainexus/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/en/hentainexus/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexus.kt create mode 100644 src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusActivity.kt create mode 100644 src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusFilters.kt create mode 100644 src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusUtils.kt diff --git a/src/en/hentainexus/AndroidManifest.xml b/src/en/hentainexus/AndroidManifest.xml new file mode 100644 index 000000000..64db527dc --- /dev/null +++ b/src/en/hentainexus/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/en/hentainexus/build.gradle b/src/en/hentainexus/build.gradle new file mode 100644 index 000000000..8a01920cc --- /dev/null +++ b/src/en/hentainexus/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = "HentaiNexus" + extClass = ".HentaiNexus" + extVersionCode = 5 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/hentainexus/res/mipmap-hdpi/ic_launcher.png b/src/en/hentainexus/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..ce0177c2d77564f951784fda1620299919540e0e GIT binary patch literal 2591 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 bCm60DwYWO<5nmV*VS%MELoa%6cmR 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 literal 0 HcmV?d00001 diff --git a/src/en/hentainexus/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/hentainexus/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..3d95cadfae380714b5cb3b73ed2602875a48b80b GIT binary patch literal 9007 zcma)CWmr^Ex8B3hIY(mXp?Y zo!U;t^Q0U*`td8J{NtPw7J;%7+2@!?UzG7P1KH$T6v#1|QfmvcHp3KQAsI><8X7Hl z!`Xb&LmVgAX&IawT5{K#+VTCT*E?IP#xjGYzh<{?uX;+X^2R+We{1!}b60qEj@M;l z?3u;?>&ma?Oz3gHn-G>zsY!LDQ5cyXm~+>eO*>Ov=&Cy;4#j_=5J@+-IaOJhWm4xH z*4io~e}1%0b$WU_Jn*#F&_%m(6$b|=)_HUM2{|2|d)y^{$$}$Gmx9BTh<+Zir zZ*UZF-@B(v!XTtR*5u~(b$wVpoBQqk0mmAHMolZT&w z44&>$Wj!P_oXBHTKK>?Q_uFKZ%}6JOz#{3#LD_euIC*mo9=7gBTlvarYI=*ugmNT= z!T8y~CwWk)QYKXeS_X!%0hxv2VPT&h^75Lb@Z0|JrFip$fC#kjw$G2LC@Do(b$54v zeJ3~6J7GKgSy|iyUK;LBOG|rbj%-~xOcixG!on|B?+YNH7;SJr)U;?46T30je6Nkk zAS*jN!KBtnW4XQJ&Ye5=tn1f)H?+33RjTi{_~TjiLwj|N=4)B;nAU$^S(-md!(~wGl>g-LmsR?UB3Q!iIq!fNFH-a2v(AX2xZ6)Qd=Z@5z;C7*XR(~a+>v|^ zESA$*e0#{o)_TPf9XUTgZ$^$p<^}W)qaBf3n=omwi!Fas3HO=rud`q87e+CPRvmXn z(j)Jg)XH&l%iJ@qS8BREnihQV;)TOK{Zey*H-65gj6(pOM3x-PqO zChP0#`K9#uD*+Qfekj6_xwti&B~d4Ryc`@HG?657%N|W<8-?`6#x-v$TiwiRye~y; z8TBSxeuqhU9zlmmOiU#0@9QJ6o2#!;OqcNTB-atLC|n=SkDD4BJ521#R%lnsWV?A& z4o7x^?bldl^)W8UB%$N89xgViTZG*2+*qQlH|c(OT%&Uv%gkX}bSXSr+_=ekdrGHE zeouI7rFFBUNrD-7{`0%dai+hZmHs=6@_4H(XLeRr&q066#^tRGCy~^L0s^{R zG!6ic7AO7v?FXK;oB}q^=;Lbp=_=gJs8S9|S9nQ%y;#zNXPsXx z9AYo?$2L;n&24S6p`Z2mI;l2DNQ%swym8CV_kziouUcs4O%{!Mo(5Tczb)oB3EAJq zMnOTr@yB~cdZ+EFF^aTiVP0OXz`;688P-#@udnajoAv#fnVC#obN}b=OG`^L3piK^+*@mhXR5M6#BR%wllidBAtP5ygL7&TO62Xe?=}V6h}x+rDFa$g zyh?Y6i;M(Gwp1-flpnH#N0O3~cQC$t9;~bc9aa2F3^12>qcq2FcLy}>YhJ})U+((k zDMr%ie2R;s@cYE_T{f6>XYyqX0~fiklBEsBeRHvp8==L?lf!K%mx}~95a-8o#z$NI zsiJD3Y0N8cb_br8sVmv(m6%?N$?T|OE+S!QzGq@@|2%$|bkD?&SJ3?PW7smXEa~m@ z=bw?r>av)tz8C}YLSosX%P6%lnR}*)myLP-fc34Ukw@@_Po8l7c>9N`?7F zOs4~+JO3*BZW&QfP-M&Q?glfkV4ZKz&dhXDB9Y9Ly!B5XC&A3iD)1&NZ6`dG{KEJ5 z_iq4c*#~S+yA8)8nH0J@+zr*1C)s~7n9+cMfVH;PRxI<2vcWnhk=b~gkz7Urf#jm} zjwRrEvG;?ydBFpY9-Wz)K}jv(kS+Kjj3482+ToywXJ<|P+uIn0rBD$~cks6gCpNA6 zk{>K{!keG+08%Eg6ISpYS>|)QHN7)1Fwk4c&t(7LV9eXwdt5Ex1sI$bZGpS8;^D)< zox$@M_)mG6eOl^R12z_<+jLng}C=piHmLtGIh94dh(j`NR>YASRXxaMpEozLK z%$%&!qcX?4w04yh{&z@`nQ?2nx&*KwgP5aGoQUqyxj!HTp#;zfPGVMd>Y1o9SoHm+ zf$L*luTme$?Wvk@=iM3QI+yKQ`Ho%ZMVHt{BYvvuYL@|~yVDv9W?rW{ZVuMnRq3K0 zi4tOt&7V_69fri~oJWp|rq|j-DX8sbb$%Z#r;oh1pC+03%NS>?t!#9?;lA^$;bTvg zLtY$*T4H)|4NEd`#0O%-P{4?}qOq;bO-)Vv0D^{Q&3{gkfxuA!xOPneA zam6E$YIg`i<0+G*lnI`2-np`T5?<896C1zbMwEuk|g0dtQB6;!?M&g!5H zTh0qP(s`5mYRr}5^5DQSw40LdhD=<#t6x9X#lh-;9z6(<3f`$%O{*nnPUsE73;y#n zMcx|jR=c5`_qvvSOfM_9$;HJvp|S2=cw)s_2XehDhW)u_$}5Tb&fUqz7hL?LHga?f zZGGK2!vZN)7GniEPM9@~@QOu;l9DoigQ^t+&E=CF>vYpi!!VubtqHf^0G ze+$e~w)kjD3`obvDWHK9haPIB#PoN6z^eBwc*ipfQ#X8cw@)VT-N}d= zEl>d{AJfp$Zy`imMK0A}&K5dD(}Zt73=4{)QQc*IUUm~qBc^`{ypMm+8nIF`g$Ndi zpn1^x)c(t(v^R{^PvXlX7$p20vu}Z)4T(z^0g6frN`mh;w9f{7uG(0mvjKvh>PZKC zI1g#_ktnRW)&5JEapQAhUMsrRpJx({)G+fYj14h58-~hq4WJThrVb-15dn#IvN{Lg zb;N4oy<36-HjS^CR^UYfV1x$XN`X>0BKcrNZ36Tp9gymW%Pq}T$#cEYEa)C+3x}Bl zZg{hlKd}HoKoRl7tm@MJHT-)8L$5P zOa4<N`}&vpT6td-lh`!5;w?>VanZ0ZOh6zF?+dYC=yE50 zkZfNU99m^T7YGRB0yZ+l4?}&Cba8Y8aIAR`LumPf3xnFSCofA(mp!MC9s*+Y7@#qg z1zl~KS$+pG!MJgc;^ke>P~Zqit=GG~qXtG{o()#w{B2*$@&E0nd&7^u$}Ieg{&eBNs5LQjdVc%n4fp9z4t0G#wK|!yKOj zwrm~|qRlSQWihz*;Tv%%$^+8zJs3;1*K(79dUmthm<~e%+5<>M=aBBC7_pl5nHpRd+1?DjEaYSV z35i*Wi?;1NPTiet2##H*=+Y^=oQ4E32QNv0VDWr(|Mi5KPfiOQ?@_k2kIN-3z-@j7 z5WS41wXQ>KZq1_{9C~-i%mEGh5tzgRc7nurt55ToeLgd*rRQ#*7KEkyv`G)xATt~H9z?aseo@rWJHw2q ztwRv3c--x0+JgfJ3Jb^=O1S_onQMsQ^A{tzeIIY)uS9o#lGCrx?HOn@a#tDH?#cdl zykbBjfq6i+rwL{V)`W?*7%C4x(4#VvH?%Z^uA}`RzYTlbCQ|ARCf{h`Ye~OMXs)~$8dha(q zmc01u|DofxUhgXPMrd`Hb}VEF>O`)02($%|ikQ^D0mzWz{3^;hZnth>pu#+{B_?%| z{v8=~3Yc-5E${c-yHmtlD5DPt4_+|`3Wyy|qNb`Hm`wzp&1$%jS{xUf#M69~66q`Y zJ@miM?Q-(Clpi3n1)gp-s^s-oyk1r~J4`a$Ik{LV$Jfx}TghxmXw?1)E4=}TR8(?^ z`av=tT7S0h$>w8&bz02K!GKDf;h1n9?MN=Y$W7d4UGUQi6cC_MKX1R@i?v`9e)IXl zVW)yy#`C)8_x8u%TP8%Gzu46(6ila$ z4X?+IoQ{6dervq0-4jqtIXU5hJP(~C%aY9+h7=I8tsL&4M*fPuo3ZaWuHpIEt_CkU za$*sNQVk#g?PVSA*X#Lx*1z5k3<;X~*z3~VBnwXb`Qfb!+fSh14eyfIS3T5T3H$g( z%a$kC9dni9P8dcU#d$yQO9J?QNxSjHeTm=3tqH4Z!_b9h;$HSD^yR)U^MTSEsd&}$1zHn+c$)pC@!uB6k z)8bc@Yny7vN1KYf@d-l?O_!F=2JP!3lEWsm?$J}9x&2_JxH7y2MMtlj`Hab1k}j-PhsLh#6MFbRf-=4;Mnju~3az;0;ssbS+84{zf5YIZ86+ z_xh_IczoDClv7G$Lu^^y3$^l8mDv$c4^kM*B76Hii5EXG zgaX{oWayIDbL<>-!bQfFJs11d&(in~LpdDiUcLbOTg)Jm_ttH`Oj_QwtO1GhuZAzk zd(*rUjVo1$QH3RV5C~ocApyPo^uYzwd!oBW28@rZou4 zp=hY51&JSFU%K(0-oh^N6_1^>-3nN~&F@uW4dt;{` zpmdT4{BFy9Bx}&V|GPJ5(M(IWWG z4|NRri}AA=U&%(frZjw^+m=+zU8^+AXFb@yPu!ASZ;?v|{#s|@Il z2LQ3>F7ufCNixb;;%qH@UtoUS6CFJ#n>)#ehmTh%U$dXJB$sfxh2mxKCB{yH&3i3vC}Lv(AypT9sQNkRpWjOjUGQ06{; zjAn;~P;iwNeYa-q?N+guoW)BkX3mnskvEv4XKDcoxb`E({AG-OqPFvxA_;Nj8^et4x<8D9xtlRh2 znkaSy66OIIiwP@~3BkL+VW3adX@8tm>~zvaZu3j(ZI3_WIGiS(;uT%G)JS@Ys$vuq zzlV^vd;ZY{4(9IytJ6Xo**bn>@62EESe;w~jTCPw3c z(mzuf`|9SNPQ;N6{F;VtS-K7EUkL&J;qYd@?756#^(&LpUd#?38Gd^^O6h>6Tg5l7N}FF{<6jMAB`+~?9jgj7 zKoqyR;C?sJx3{22FWfQquCU4@m496uzvab0h{X=@$q=;Es_csdeLPW?5DxkXn_Qs* z@%{^o$|m7q*3b;iGT|RsM7LqFkOf;>Sw7-=(OcO00K9{tyVmxR&qtw96m{x?-}uOv zBmDM{QlX_$?W05(3XTpGpuv3su8jGMHQEsFD~pPsm8IYIu+ad;kND3(k4kn!n zF{zA|@y9?gKmd;=49023ud2xEOS0yB&YV&1(zCYI5iUUH{}10!fe2m-kjYf+-qYKc zU0G6+lOB10%n}G;7aW4*Vro`X#X84D8IjX5y-(N^Hv!S3M>+J}V3HIJrLBc@@>y=t zgp1$$0fj3m3|;^7Q&TnB;nuIX9_Z@Lw*O7=Zx01nv|czO+&J`>>Q58?_s}62sKEo~ z=wZH?M-Vzd#!tTyiidsS0d~&DpuOOABz>U9UdU4s#|5cV2+BiHm=-L)$3xpp*HXR9 ziF9asvf7{e&48vIBm)**=V#z2Fdtoe30tuJN=oI{OvI(DbbsU-Su+^r0nvCjIc^U{ zeBjMVTK{zy!m?`vIpp?OXny9_vjH*sGC-=>oe)Em(FozY-Ny6cCy=4hQ&|5+_=Di2)=kMmeKWCW z?NHEE(Du+8N^XwE;PY=y;@xYL3TtvNx1|7iQ?}NBiiV^0`S0Bo^XrUYdcy2=!U~Q4 zLLsGIF`>7Osx|DE3*abX08KxB6oY>i(o1bOt-*S;${qt@MSXtar+^03VKx6Pt>>+@ zOJi?%ynDXe_)lHhtk_exK}Z1=_+C&k{4DnA*!OJ3_l`Gy0__&~=uN<`Xg~>EtWqo7 zF@u@cUv$%2!RQ09^b!Vqis=?r8HtE&X3q|F|3%760w7vtj|YY-u|V6`Qr;K2IU~b4 zDn*7+12!rGKG_n0d+`W*MXlV&xoyE@NzA>&rcgWg1jy-v=4{}{=`E8_%9AI$lSjWVKs7k?DO z+e?0~U`WSfRQ1?UJSEQD7UtM(jt#X~G42mFqI2{_l?#zZk7hp^D;udP$1IWuDY6|K zQltAIk8JV2(ma>HZ{|jCOl*%&q)3YTIt9A6nM77B*1uyF?=Ra#zC1d7!)g^6)+$Rk zGzm3@I?F3nhBvTr!JFBWgxy`AbOmJ8=kv3}?8INf@d;_tP?;O9s=eFDk!?Wtd-eAa z1i0?;T(iQ}i>l~7EXs;4yJu>g9OfHGb9D+IO~0B7BB0%kw8}vr)4JdhGcZD z(*`$VTLx{c?yCEXoE)v*Pt=;TvFO9rKq6u^Kls9lN5+_yH1Rc)ic4>}M~BO%BaA9h z!*hNgX_`SoFEBVU?`fa#=ts6<)Ua(y!|kGCmiKx#^ojTLs^{FpRnx=_iUgABmVUim z{rqc9r%;!+_}3#Mv5MRZtg?My6Yt*yOOJ5QYE~m%Fo)!hL6Jn z`iFJiw?+s0evq)@?Kj_Z=K{?3^h7ZT2?-192|~JdO-#77#NW|+Pw#+v zXG9xcIchr9Ke43-j<%;=zCdL&s=|I+pc~Zv4JD7!1qm&-j%+G5)=?P+CJQzNDmUN_x&7x0H{?0?Iz^kG&nA{ zM%vien82j&iAfVj%rl?YdS9N|#Ky%z$<2uvCe;>1(lB%$%!d8QchALP_;WXr}anhMpcBCN^ zq^fd&zjFAa2*gb`>ZlHd!AiLQ0Gr#AKt%n{srZ|ft~;2kgTWe7UuT=?D*K0T&n@afYhJ_!j4_Z8SxkZ(p8gyF42EkjC50aT>|a%jMBr)F*42sniXCTUWJpON@YDu1U12oW zYonv1xA23*ySKis;D-O$8g_tOm$s$gm9X}17BU1XmaTBdO9<;BH?(^DDZ*FHiw7PP@2DVPaH zS$i59PVx4o@EZmHwdOFM^f7Z^&tZ;Ymh}E`E|l1GgXZc(AQ6&D+OA20o&DJ>sIN;8 zXdzQ5na4pq@5z=-6?yl@7Gl{(Ak35ik&?0QJ_(O>1_ZEaJR^tu`uYegqU)NPd{{t; zuO%D%ZUo92P3|WzEG%pbQ3wLQe_$nQlYHcN4`X zR=qtv{f(!8K3-S994ol)OP9P3-LKg zaot)^Qioa!A{oo=TbU`ySfm%0ma>kHj+}A8TqGU;4cROLbcVd0ot>W4Aamy6BnE@| zWxaJ^jiI;xtFS^~1lsRK*E3T#ksTkws+dD~Y*UBeo!j2+M;;c;qJtL2!Cv(tb6&I( zAA?tp(o16$%}*N4F*RLxC}M~Lu&^XO?``<-b&1|GlH=1H7|)7sKpPf~ySus7;Nan% z2T4UE1f?hA;P@giEOJtDLj2od`lOL&5uGJq=>D^FLe(Aq%t1xIVAnw&z{&f665dO>P>2wOHgHT(!gxjP2#hO zO*6W7XwP&u(@()kPM9A}BqTfQ30?9+luS%aMEEJJ4_-x<<~@^Yfu_GOYjZWxJcHLH#;37HS_zicn(|32;t*DUVo&)u-b2 z^l<&-DXdx8Zr+#!ox22W$ZWYQ)S3AT4r?p#LOWYVj8M0=G2%Zm69vX7F0SG``B_Yo z`QpNZ&Q4Ovj%F=5L@wrNtFnAAzd&?zYg(ikb&i;p9R`;!K)zq-4x_4ElzJB<$1)DW z6Tx=6aMLT(`|CTE*VmWkCylM7f4?DRnA9vVC|AaMG}n@M-o=F{i39~}RJjaKifo7` z)15cY&bn+jhrid=*}?+HM@B{n=9+ODnt;p?!(+{u>EU5xf05^~dCLA~ zb=|Sy%6^sTaoT8%>`ff5!qB@f=pPhh<=*~oCz|lHH7h!t5X!&*=f%x+O_uy!Vy-P% Sbrrf11}Mv`$`#9)`~44CT%>FO literal 0 HcmV?d00001 diff --git a/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexus.kt b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexus.kt new file mode 100644 index 000000000..85e467c7d --- /dev/null +++ b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexus.kt @@ -0,0 +1,181 @@ +package eu.kanade.tachiyomi.extension.en.hentainexus + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import uy.kohesive.injekt.injectLazy + +class HentaiNexus : ParsedHttpSource() { + + override val name = "HentaiNexus" + + override val lang = "en" + + override val baseUrl = "https://hentainexus.com" + + override val supportsLatest = false + + // Images on this site goes through the free Jetpack Photon CDN. + override val client = network.cloudflareClient.newBuilder() + .rateLimitHost(baseUrl.toHttpUrl(), 1) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + private val json: Json by injectLazy() + + override fun popularMangaRequest(page: Int) = GET( + baseUrl + (if (page > 1) "/page/$page" else ""), + headers, + ) + + override fun popularMangaSelector() = ".container .column" + + override fun popularMangaFromElement(element: Element) = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href")) + title = element.selectFirst(".card-header-title")!!.text() + thumbnail_url = element.selectFirst(".card-image img")?.absUrl("src") + } + + override fun popularMangaNextPageSelector() = "a.pagination-next[href]" + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesSelector() = throw UnsupportedOperationException() + + override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException() + + override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException() + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return if (query.startsWith(PREFIX_ID_SEARCH)) { + val id = query.removePrefix(PREFIX_ID_SEARCH) + client.newCall(GET("$baseUrl/view/$id", headers)).asObservableSuccess() + .map { MangasPage(listOf(mangaDetailsParse(it).apply { url = "/view/$id" }), false) } + } else { + super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = baseUrl.toHttpUrl().newBuilder().apply { + val actualPage = page + (filters.filterIsInstance().firstOrNull()?.state?.toIntOrNull() ?: 0) + if (actualPage > 1) { + addPathSegments("page/$actualPage") + } + + addQueryParameter("q", (combineQuery(filters) + query).trim()) + }.build() + + return GET(url, headers) + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + private val tagCountRegex = Regex("""\s*\([\d,]+\)$""") + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + val table = document.selectFirst(".view-page-details")!! + + title = document.selectFirst("h1.title")!!.text() + artist = table.select("td.viewcolumn:contains(Artist) + td a").joinToString { it.ownText() } + author = table.select("td.viewcolumn:contains(Author) + td a").joinToString { it.ownText() } + description = buildString { + listOf("Circle", "Event", "Magazine", "Parody", "Publisher", "Pages", "Favorites").forEach { key -> + val cell = table.selectFirst("td.viewcolumn:contains($key) + td") + + cell + ?.ownText() + ?.ifEmpty { cell.selectFirst("a")!!.ownText() } + ?.let { appendLine("$key: $it") } + } + appendLine() + + table.selectFirst("td.viewcolumn:contains(Description) + td")?.text()?.let { + appendLine(it) + } + } + genre = table.select("span.tag a").joinToString { + it.text().replace(tagCountRegex, "") + } + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + } + + override fun fetchChapterList(manga: SManga): Observable> { + val id = manga.url.split("/").last() + + return Observable.just( + listOf( + SChapter.create().apply { + url = "/read/$id" + name = "Chapter" + }, + ), + ) + } + + override fun chapterListSelector() = throw UnsupportedOperationException() + + override fun chapterFromElement(element: Element) = throw UnsupportedOperationException() + + override fun pageListParse(document: Document): List { + val script = document.selectFirst("script:containsData(initReader)")?.data() + ?: throw Exception("Could not find chapter data") + val encoded = script.substringAfter("initReader(\"").substringBefore("\",") + val data = HentaiNexusUtils.decryptData(encoded) + + return json.parseToJsonElement(data).jsonArray.mapIndexed { i, it -> + Page(i, imageUrl = it.jsonObject["image"]!!.jsonPrimitive.content) + } + } + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() + + override fun getFilterList() = FilterList( + Filter.Header( + """ + Separate items with commas (,) + Prepend with dash (-) to exclude + For items with multiple words, surround them with double quotes (") + """.trimIndent(), + ), + TagFilter(), + ArtistFilter(), + AuthorFilter(), + CircleFilter(), + EventFilter(), + ParodyFilter(), + MagazineFilter(), + PublisherFilter(), + + Filter.Separator(), + OffsetPageFilter(), + ) + + companion object { + const val PREFIX_ID_SEARCH = "id:" + } +} diff --git a/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusActivity.kt b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusActivity.kt new file mode 100644 index 000000000..5ef34cb8b --- /dev/null +++ b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusActivity.kt @@ -0,0 +1,38 @@ +package eu.kanade.tachiyomi.extension.en.hentainexus + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +/** + * Springboard that accepts https://hentainexus.com/view/xxxx intents + * and redirects them to the main Tachiyomi process. + */ +class HentaiNexusActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + val id = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${HentaiNexus.PREFIX_ID_SEARCH}$id") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("HentaiNexusActivity", e.toString()) + } + } else { + Log.e("HentaiNexusActivity", "Could not parse URI from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusFilters.kt b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusFilters.kt new file mode 100644 index 000000000..4043f16df --- /dev/null +++ b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusFilters.kt @@ -0,0 +1,44 @@ +package eu.kanade.tachiyomi.extension.en.hentainexus + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +class OffsetPageFilter : Filter.Text("Offset results by # pages") + +class TagFilter : AdvSearchEntryFilter("Tags") +class ArtistFilter : AdvSearchEntryFilter("Artists") +class AuthorFilter : AdvSearchEntryFilter("Authors") +class CircleFilter : AdvSearchEntryFilter("Circles") +class EventFilter : AdvSearchEntryFilter("Events") +class ParodyFilter : AdvSearchEntryFilter("Parodies", "parody") +class MagazineFilter : AdvSearchEntryFilter("Magazines") +class PublisherFilter : AdvSearchEntryFilter("Publishers") +open class AdvSearchEntryFilter( + name: String, + val key: String = name.lowercase().removeSuffix("s"), +) : Filter.Text(name) + +data class AdvSearchEntry(val key: String, val text: String, val exclude: Boolean) + +internal fun combineQuery(filters: FilterList): String { + val advSearch = filters.filterIsInstance().flatMap { filter -> + val splitState = filter.state.split(",").map(String::trim).filterNot(String::isBlank) + + splitState.map { + AdvSearchEntry(filter.key, it.removePrefix("-"), it.startsWith("-")) + } + } + + return buildString { + advSearch.forEach { entry -> + if (entry.exclude) { + append("-") + } + + append(entry.key) + append(":") + append(entry.text) + append(" ") + } + } +} diff --git a/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusUtils.kt b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusUtils.kt new file mode 100644 index 000000000..ecb9547b8 --- /dev/null +++ b/src/en/hentainexus/src/eu/kanade/tachiyomi/extension/en/hentainexus/HentaiNexusUtils.kt @@ -0,0 +1,59 @@ +package eu.kanade.tachiyomi.extension.en.hentainexus + +import android.util.Base64 + +object HentaiNexusUtils { + fun decryptData(data: String): String = decryptData(Base64.decode(data, Base64.DEFAULT)) + + private val primeNumbers = listOf(2, 3, 5, 7, 11, 13, 17) + + private fun decryptData(data: ByteArray): String { + val keyStream = data.slice(0 until 64).map { it.toUByte().toInt() } + val ciphertext = data.slice(64 until data.size).map { it.toUByte().toInt() } + val digest = (0..255).toMutableList() + + var primeIdx = 0 + for (i in 0 until 64) { + primeIdx = primeIdx xor keyStream[i] + + for (j in 0 until 8) { + primeIdx = if (primeIdx and 1 != 0) { + primeIdx ushr 1 xor 12 + } else { + primeIdx ushr 1 + } + } + } + primeIdx = primeIdx and 7 + + var temp: Int + var key = 0 + for (i in 0..255) { + key = (key + digest[i] + keyStream[i % 64]) % 256 + + temp = digest[i] + digest[i] = digest[key] + digest[key] = temp + } + + val q = primeNumbers[primeIdx] + var k = 0 + var n = 0 + var p = 0 + var xorKey = 0 + return buildString(ciphertext.size) { + for (i in ciphertext.indices) { + k = (k + q) % 256 + n = (p + digest[(n + digest[k]) % 256]) % 256 + p = (p + k + digest[k]) % 256 + + temp = digest[k] + digest[k] = digest[n] + digest[n] = temp + + xorKey = digest[(n + digest[(k + digest[(xorKey + p) % 256]) % 256]) % 256] + append((ciphertext[i].toUByte().toInt() xor xorKey).toChar()) + } + } + } +}