From 4c73cc5e757637eb98ec6d04f0316b5051cf921a Mon Sep 17 00:00:00 2001 From: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:38:17 +0700 Subject: [PATCH] Add Hangtruyen (#11497) * add HangTruyen * Update * refactor to ParseHttpSource * Using custom domain * Add validation for custom domain input in HangTruyen settings * Auto update custom domain when redirected * Add filters * Fix latest/popular * using commit to avoid race conditions * minor fix * Fix domain regex & dateTime parsing * Synchronize preference access * Refactor genre fetching logic to use atomic variables for thread safety * apply review * typo * remove all trim --------- Co-authored-by: siritami <102145692+FiorenMas@users.noreply.github.com> --- src/vi/hangtruyen/build.gradle | 8 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 4678 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2435 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 7015 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 13849 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 21265 bytes .../extension/vi/hangtruyen/Filters.kt | 119 +++++++ .../extension/vi/hangtruyen/HangTruyen.kt | 304 ++++++++++++++++++ 8 files changed, 431 insertions(+) create mode 100644 src/vi/hangtruyen/build.gradle create mode 100644 src/vi/hangtruyen/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/vi/hangtruyen/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/vi/hangtruyen/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/vi/hangtruyen/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/vi/hangtruyen/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/Filters.kt create mode 100644 src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/HangTruyen.kt diff --git a/src/vi/hangtruyen/build.gradle b/src/vi/hangtruyen/build.gradle new file mode 100644 index 000000000..502ddcb0d --- /dev/null +++ b/src/vi/hangtruyen/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'HangTruyen' + extClass = '.HangTruyen' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/vi/hangtruyen/res/mipmap-hdpi/ic_launcher.png b/src/vi/hangtruyen/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..59b28535970448660765199879975ebecc25fc5c GIT binary patch literal 4678 zcmV-M61nY(P)OQkJspVnz@>&(=#cl34}?^J8;=8Rhl zI<4cl7jUVdprRrwihxuC0$~dv5FiPVgmB;URr=T~DvJhsACAxW5aCYa!TSkPm#AO^By>)j*i#nB7t*!{gY{+>hI>2t@{ zD=)^N@RO61Ek}+V8DeZ~?9b!zEFMoE`{pcdZEcMjjpkNbTH5(pvu4#(FLZZdb=ska z4KemNgV@;EK}(h_*&z@JLI{pI{Z7#JTVvPecYSyL?5SyQZ&whc%ZZ7JG4toom(gY0 z9t7!OfDG2JUvIf;)v7&0p)gWEeb?_*2-2yoTemJ=xpHOQT}bzJ;6Z@c?*$TxWR91Y z*LFUiZ`t*(_e-nkwEH@pu0bl5zV7Mid5Cts9Jab;!YpBx+<*3xIgt^@jA z?WvW?WS=-XI&P$1x7^jE-`v2102$C;qN=K@b#``kf7JIaGxwse_@lbIdXv4q{YP|3 zWoN_eVSvoYg4U4*tASP90jVdwzh_W0qfr5 zY5mHAB>55?IeZw#LLovzLlH1!2uww$553d+x;mUsJC7STvS4jxjrj`~5L~;*Lx;}N zZveHlv>-VtnX>}hL4%N*k^(<}e@qOY1XI(_Ayvs_NI9JXl}d$4wBOY$SK#XIj^NtL;^#7kps z@W%4_up~GgjalN4M~-m+ogNhh6BCneJf_Zf?Q=j3LVkV$P8>UmH&(m>A!*RPjmi(W zeB~MvPp3n!QgacstDOZF%$x$Rdti1PQy`HbAwB`oFTKclwBEMI3#bPR*zg%<%$|o4L4Lg+W{($8Hy#&_ z2FM7`mV}$QNMie8%w&X)AIoV;ZyK|>?Xi21UdF>K3nLKXZ%@#Be$GAt#Nd?QQDRHt z&-hR3EqLf|VaEs2@b>oVoJ}_@lj%_6iBnj!GaWAeqwz`fXpHc-=`o}}0VG$bu`MwV z+mHT?h6Xvpooeybx)(t?4+n@1lfr^R)YsQRY+(W4!GrJ9Cd#r=TvWsrj-8#H;ppVZ z4NnH>YGxKl$DhsG;}>jhbJ*FK;I-GH;U5qHrZGPqKY@dXjzZZgKoeQu zf_YQn>}-#*6hX6?hy|Hj5>BDre-aY*Wxo0t&7J{~NQBI*S5aM4i@$%p2ccn;v1sNv{!@-e;qoEnT*>xNuo*c`ci-Ns_1AKgZD1VaC5Ke-1_)O$x)!?Upr(@PQH+=fm z1ekZziu4AESx;G26W;zb8EH2v5gaiT)1M1Qb5#k1`BxA<_Zbe*m5j?MmX>16t{?Hr z%geBM&Q#>x%tHf3$>YY4LksCs>gm&%GOX*HRxl(m5LD^J zwjY0j&E(l=)d_L<+q0-GlwteYNK6>v(k;Q(8=y8_I}%Pyux4`#G;|{i-iUyApeq_m zZ^Jl01Iy;lfEfYGx_%vMwFcj8{SLnV0a(6t5w2%t!zyv zE!%g)+tnV+mMr4*>sLVWNwAqT3j#AsTuRDD(w+-g_Ow6Ntq6meX&1EUZZ4@eK+Vk> zY>rFEH{V}|-xyy!`&tA{&5WU_E{87X9HM7Ug9Ul4>({QMjRM1sJNBVU$)nW69c{{b zyfix$Qz$8Iq1sRE_xo`1wgRfgdX!YwVnNtogolhI9dd??t1D@c6x)xVg>%G07*b?j zRv^QkO^NWbHpY(iv*6)EDN7fnNN<3uYMSug|C~h9g<{NkeL9AR2Ef3O54EZZva<BJe4OW(&_0L_#*BI3RD(oS5#xsIA?@~ z1Y^{wkuags$jMVFI8&~IYgiOrLjYA%GxmLT6ph6)Y+f6Mh%oOii?!YW6%^It)juD^ z?MfwHTDK6EHpXbzX~CoFq5O6pd~|f9^Ja6Y0Ts$|i6BVJWiS_;!`{Xcety2>&Yfxa zsga$XjiTZbC|k79wQ1nz<4GQDEW~0lm(hQ@b2kLTB4Fb^gwD^0rd5M8htqL6AsrvB znu2BXN01J6ty-x!Ksk9;Sn~Q0(3y(x{Hhs{m)ziD66c_?&}lU&J+%+>!bfBL_+T!U zVV1%+ra8>5=MWSo!mK7FA}e8W5z`yi7*w_?NrV3Vp99EI2;deH1w$ia7#W+Ojr1ft zEf+t29go#3CgQDE!{{3Q4AAxLax8o?4rUIvSoUAfLuf)Z9|F)+Q-KzR90FrgG+a+X zRLF3Q7(SGXi8_)|R&!>XQmN$Nu_76(G79K31E$qzP*q)nl(Xk?nF2$%33JfWBuCke zbT|c$hKbl3dFfd=y5kI1ygU(qc{hS8mt9*zpf?M;o?V88FMJD;gEf}EzYJzpVl-Bl zA@j($806s(|4FluJh{9{RxOlt-D_)&OfLK)C&j4i+py#7ElUGq9 z{JE#$=sOg3r4p!>3V4i~1Y>&2nr0Ogb(K)ml*6#S4T{p65GrMuG%*YYg5S<=S5;Qx zN>(0tHXaaIIT09Ji0w$D=p40lUL%n?9DIf|ngp{A1zANn^yLA(854%p?@sT37L8vtav@< zz8tEUQj+iX{{*4$1W2IR2cR*kuEH%!7G1=#jS!WEM6T0dp8?sn-%ng4RG^HDhi4#pyG+4t11P*R*FFm z7I1a5#|zJfBVy|0`v9>b+NRCl;9{-}6-8xeQc@P|;t#&539OB^G-VWs!6PTa*xZuy zVn6*W0lBH^_+;%|ys&tD|MOy1)eU&>ulsQi-2P-3N_}gl@xHxwTXz$+r_~BqG8pwL;YwC%Q z34BJ5g^9Tpnw#oKj}!=sSc>Y3ChY%u7kpgI5WDR)czM|Mzerlgao<5uEYOKSp3-K^Aq2SOf2o1$o@bqXWOr1LEzS=S?s%_aC3z>lr zOst$yc;ysqT)p8jd@>2TkSnCARgF-$sB!VcIb1xNgcqJ4k54{WL^B&GNB=tEa)2UBt`L^Wg2{ z%|*)0z02$6pf#4*e>4M5qvpfOCy4e}bI0)ol!*Qk4bYEvR7uOodUin5Sc~try@BwF zL%OU#>>PRn#4e^*YZ3Ra6nyaUe()&DogG6(u#r#Ejj^cqt|rjUsR>ePelB^Q1k^}# z;53Mj31K7Q;qDBrwhaJxP zNthWm0$aX(1s39m&-MRbYtkzq7U-3g*W&GWcOvCnE?fuu;^}2e$t`>QI)qqsT`Ma^ zX6i9Wf4)I3x|MP^HLWvkhM1e@;-R6AR_F5RM#%$;#FhvcGXX<_!(l4^Wrmeqr;#Qj zGgHstY+@oD2Z>1RV=!g%ux?q8-Ytk-u#FOPKKm4e6k=0RcLAk1HK{5t?f{DyATS&c_6?BG^&6v?ph^8?s_;2hK%9ejd@W9*bDxcd0fnjmuy zPCE%Q+m~4n(-#BE=GpJGl>IZy;`8`uZJ|U~*+fulaXT-Y%CA>ZUQ|fj+A(&FA3pvl z8X@C{P-5G&MOAv$pnC<7d7~0(6*hf-5C;yOrTH-xtZAuBP;eMoR45!=Jt%YkZD~}a zQB%;TA`x%pyd2BB*yq|B8Kn7nWIYnd%8I!fQE13e{Pq2%2n!twBcqcBgrGQ!*GnXH~4^+DehOmg1o5%2L$O zR$EmLX#q__QZ+ziD#X)K!C3j$Jd7Ug$IavSru8YH4oJ1NjW~ZH2XS%7X(dM;nt#3qETK)GaYxJpr1-bBa8^d(4n4KxNr*Q&JBa7hr?r8 ztJvp&I-V|zZ_3JQkeZr__=GfExR8z7I$D}c>w#EVmQR|)%BP$r-PNUbiq1t86$b`- z(FEieL`@%ypdfFmADQ$>kUO)qego)UP;98><`&`FwS45|6;WVViaWJ((jAh20%ayL zq5RB_R*|_=iFOzQ0%+RV(}{!ixLY3sda%D^@sd)>%B(H4NVA!X!aFL8Ojm?LBhFI9 zViDDrRm=P*Dte_mzQsL zadGL<%PeO3`T2kH_V!*w?zb1K0Q;le{L&|i_Szzw0-hAM}W5VxTc4CFPHik&*jopKiTS$0}SVVPRp;yLa#2NsGTH^j|yZdCzES z=>omb8z8h=Tu3dfT(4l;~AbXjPz0})fK&+E23uK|I`M7c8oMK{P!aO`Y2Ge}H z@JZjnlRSTWTbsIADwV{=#nE>Qq*dJ29%eo4-TC|4BfnUHj$lC8OA%~iR>i<|ZPMvU z-f7Ry&C1yf1hWCF@=N^5r0P6zx)fBX`Sf8y1U~qo-L+l<%Y(}!jUrllEGwJ}< z0SzC}C${}gTL+Z86G#ufi{I-|(c5zw0Q3md1Aqnq^=>r&FYbEr!8lcDi2wiq07*qo IM6N<$f(eN50ssI2 literal 0 HcmV?d00001 diff --git a/src/vi/hangtruyen/res/mipmap-mdpi/ic_launcher.png b/src/vi/hangtruyen/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a0844c0ed6136dfa72fd18bcdbe384512ac6bc15 GIT binary patch literal 2435 zcmV-}34Hd6P)DV#)fnRo zY_V1fih?!+Mg_F;aFO>zLdp6-mVhkmd)Z}szf}n+k41L1&h*aQ*`4{m@80t}=iGD7 zy_be}ywToq`SE{%w{5}REI{7@P+co4ttBMK^+Tg3!?r)~uguV$aJ1;?Xp>#LcI{>{ zSxeM(niX9?f@w9XTCHxB$zkWVcVk?zOo`HdZ`5Tf=t7+QX+XY0X;pF8mR(RP0l((2uX47ia{=7ta z@c@01Mx$AhQJOoihwcC@2tY%BWP4NZ5&&y5SJVF(fcB=*D?m|U5lV`S;U5qHyP5y+zK7lGa^P+nGshK73lYNkKLVlmDqCZPh3 z_*p#|h6Z%lDIdejOpLjq^I&9TgmY*Aj$a4(!^Xzu?E_HSO-f8eU{Dau%*-?b37Q4i z{8bz>cqH(sRS2JGhHV?d!C){n&ne)>#wPg9@O%3J5BLJ4rCh|iHQ}Jse~FWgjg8>f z^Fhq6@pqCKJA{?X<%mC%fYqxaU}*Tlji?0Md)1OW_rprjIK)^jjxmOoS~73v;82IW@v4h|iQ zlv?CpkF z4SYVn|2_jxMGO=ca}l*N43m8);|2+(1@AfHYHBTp8>kQ$8wiF`?_pIZ0F?Hl_g}~5 zoQL@IPYcmnk%Q%)j6rX7f5t@;Z^2fd+vH2C`;+ynH@5Zf>}K<0i^2-C$yAhxjj3 z5bS1&*bRQ5lc?2G(+NOUPA!&wo{UwymchZ<2BJnj>>uS}L1+j8D8RmN6VT9Rf_3wJ zuxR0L(AL(5{fCa@axM>EHpF_HqA=3Q35QRg1#QZFFihCEoq8Ai+)|{*{T57yp?(44 z|8W;P4`m`ECK&P-KCE3hXw1(*RHz^9?Cd}`Lr{=7pO%^iV`F1TrBav=F(+AiCW-lm zFflPfQDHI8ad{XvB@l)T7XDMh!{q}>xR(uw_&Pox8xBR0B-M_Z zoOD<_av-nC#T*}3c#L-ki^+t7XkIFjfKKWiQ&UroYly`X+#$Z7Uh){jW`yA9*A*&I3;>?|~Dr5N&c99v9yNTP1{z zkrX}?J&DlI5|mUb!QG+?)JY6LvvvmC(jLO5dYC&nLoODg@reKz4_+bwTl5P+2@{`i z3)^=dLvUm$n8r*vdd)yh;cZlwSD^Ji7tNEcVw`iC!&@~=Xu>BODJc!Qy8f;VRr#``Zq=7nnzR+SmKpuE?L_WV6OP22iw~$nsi2%vlh(3Lt7}Cc?Kvp^%e&V2pP1U)iQ0NsK{~ z7K3P>4qkONt|uo#Bxu5(-K+3vR0s*leIFVP0HAxzmRInwI`RNY%6V{~JPm%q3t(+O zq9guO8yAI4iZ*4N=J)3(k8r<`i{iX&NQsp`|7-zbw=DyU)pPb8Krz?%7LOX7$KxaR zkEfBGoCTK2Ah@_qz*sL|3?UhoK`bDbNgyOvc~Db{pGc^DRC^y5<|f!48;#FpxjAL>LG`9=zzYzeP=d0uT2kwmp|rGy6n0|GM&H)f5LW=urCW_lgp>GDnQOSr14DuKwffyhLq^^SY8 z?n*xvhr@A*i;LUo;^GoYwt=So0@3lyUQBlzYGQ7Mm04k>&lTO`{ zQ3smTE?bMMR*F-sWAQbbR+3KLiwQ`Vbao$VvYrH^ASl79G}(8NqHD3cjz{?xbu&qU zbol;@SG3TqHrm%KL6>{smuJ!jK(Epbbntf`;J+T=EN%Di(SQH|002ovPDHLkV1l2W Bd8_~c literal 0 HcmV?d00001 diff --git a/src/vi/hangtruyen/res/mipmap-xhdpi/ic_launcher.png b/src/vi/hangtruyen/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..c2983d2538d20606cf872b2f5db2f1fb622ce754 GIT binary patch literal 7015 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 literal 0 HcmV?d00001 diff --git a/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/Filters.kt b/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/Filters.kt new file mode 100644 index 000000000..a6289c6b7 --- /dev/null +++ b/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/Filters.kt @@ -0,0 +1,119 @@ +package eu.kanade.tachiyomi.extension.vi.hangtruyen + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.OkHttpClient +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +private val genresFetchAttempts = AtomicInteger(0) +private val genresFetched = AtomicBoolean(false) + +@Volatile +private var genresList: List = emptyList() + +private val genreRegex = Regex("""\s*#\s*(.*)\s*""") + +fun fetchMetadata(baseUrl: String, client: OkHttpClient) { + if (genresFetchAttempts.get() < 3 && !genresFetched.get()) { + try { + client.newCall(GET("$baseUrl/tim-kiem")) + .execute().asJsoup() + .let { document -> + genresList = document.select(".list-genres span") + .mapNotNull { + genreRegex.find(it.ownText()) + ?.groupValues?.getOrNull(1)?.trim() + ?.let { name -> FilterData(it.attr("data-value"), name) } + } + genresFetched.set(true) + } + } catch (_: Exception) { + } finally { + genresFetchAttempts.incrementAndGet() + } + } +} + +internal class SortFilter( + selection: Selection = Selection(0, false), + private val options: List = getSortFilter(), +) : Filter.Sort( + "Sắp xếp", + options.map { it.name }.toTypedArray(), + selection, +) { + val selected: SelectFilterOption + get() = state?.index?.let { options.getOrNull(it) } ?: options[0] + + fun toUriPart(): String { + val base = selected.value + val order = if (state?.ascending == true) "_asc" else "_desc" + return if (base.isNotEmpty()) base + order else "" + } +} + +private fun getSortFilter() = listOf( + SelectFilterOption("Liên quan", ""), + SelectFilterOption("Lượt xem", "view"), + SelectFilterOption("Ngày cập nhật", "udpated_at_date"), + SelectFilterOption("Ngày đăng", "created_at_date"), +) + +internal class SelectFilterOption(val name: String, val value: String) + +internal class GenresFilter( + genres: List = genresList, +) : UriPartMultiSelectFilter( + "Genres", + "genreIds", + genres.map { + MultiSelectOption(it.name, it.id) + }, +) + +internal class CategoriesFilter( + categories: List = getCategoriesList(), +) : UriPartMultiSelectFilter( + "Thể loại", + "categoryIds", + categories.map { + MultiSelectOption(it.name, it.id) + }, +) + +private fun getCategoriesList() = listOf( + FilterData("1", "Manga"), + FilterData("2", "Manhua"), + FilterData("3", "Manhwa"), + FilterData("4", "Marvel Comics"), + FilterData("5", "DC Comics"), +) + +internal class FilterData( + val id: String, + val name: String, +) + +internal open class MultiSelectOption(name: String, val id: String = name) : Filter.CheckBox(name, false) + +internal open class UriPartMultiSelectFilter( + name: String, + val param: String, + genres: List, +) : Filter.Group(name, genres), UriPartFilter { + override fun toUriPart(): String { + val whatToInclude = state.filter { it.state }.map { it.id } + + return if (whatToInclude.isNotEmpty()) { + whatToInclude.joinToString(",") + } else { + "" + } + } +} + +internal interface UriPartFilter { + fun toUriPart(): String +} diff --git a/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/HangTruyen.kt b/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/HangTruyen.kt new file mode 100644 index 000000000..93edff767 --- /dev/null +++ b/src/vi/hangtruyen/src/eu/kanade/tachiyomi/extension/vi/hangtruyen/HangTruyen.kt @@ -0,0 +1,304 @@ +package eu.kanade.tachiyomi.extension.vi.hangtruyen + +import android.text.Editable +import android.text.TextWatcher +import android.widget.Button +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.ConfigurableSource +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.online.ParsedHttpSource +import keiyoushi.utils.getPreferencesLazy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import java.util.TimeZone + +class HangTruyen : ParsedHttpSource(), ConfigurableSource { + override val name = "HangTruyen" + override val lang = "vi" + override val supportsLatest = true + + private val preferences by getPreferencesLazy() + private val prefsLock = Any() + + override val baseUrl: String + get() { + return getCustomDomain().ifBlank { "https://hangtruyen.top" } + } + + override val client = network.cloudflareClient.newBuilder() + .followRedirects(false) + .addInterceptor { chain -> + val maxRedirects = 5 + var request = chain.request() + var response = chain.proceed(request) + var redirectCount = 0 + + while (response.isRedirect && redirectCount < maxRedirects) { + val newUrl = response.header("Location") ?: break + val newUrlHttp = newUrl.toHttpUrl() + val redirectedDomain = newUrlHttp.run { "$scheme://$host" } + if (redirectedDomain != baseUrl) { + synchronized(prefsLock) { + preferences.edit().putString(CUSTOM_URL_PREF, redirectedDomain).commit() + } + } + response.close() + request = request.newBuilder() + .url(newUrlHttp) + .build() + response = chain.proceed(request) + redirectCount++ + } + if (redirectCount >= maxRedirects) { + response.close() + throw java.io.IOException("Too many redirects: $maxRedirects") + } + response + } + .build() + + // Popular + override fun fetchPopularManga(page: Int): Observable { + return super.fetchPopularManga(page) + .map { + if (page == 1) { + MangasPage(it.mangas, true) + } else { + it + } + } + } + + override fun popularMangaRequest(page: Int): Request { + return if (page == 1) { + GET("$baseUrl/hot-nhat?type=week") + } else { + searchMangaRequest(page - 1, "", FilterList(SortFilter(Filter.Sort.Selection(1, false)))) + } + } + + override fun popularMangaSelector() = searchMangaSelector() + override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() + override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element) + + // Latest + override fun latestUpdatesRequest(page: Int) = + searchMangaRequest(page, "", FilterList(SortFilter(Filter.Sort.Selection(2, false)))) + + override fun latestUpdatesSelector() = searchMangaSelector() + override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() + override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element) + + // Search + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val filterList = filters.ifEmpty { getFilterList() } + .filterNotNull() + val url = "$baseUrl/tim-kiem".toHttpUrl().newBuilder().apply { + addQueryParameter("page", page.toString()) + if (query.isNotBlank()) { + addQueryParameter("keyword", query) + } + filterList.forEach { filter -> + when (filter) { + is SortFilter -> { + val uriPart = filter.toUriPart() + if (uriPart.isNotEmpty()) { + addQueryParameter("orderBy", uriPart) + } + } + is UriPartMultiSelectFilter -> { + val uriPart = filter.toUriPart() + if (uriPart.isNotEmpty()) { + addQueryParameter(filter.param, uriPart) + } + } + else -> {} + } + } + } + + return GET(url.toString(), headers) + } + + override fun searchMangaSelector() = "div.search-result .m-post, div.list-managas .m-post" + override fun searchMangaNextPageSelector() = ".next-page" + + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + val a = element.selectFirst("a")!! + setUrlWithoutDomain(a.attr("abs:href")) + title = a.attr("title") + thumbnail_url = element.selectFirst("img")?.attr("abs:data-src") + } + + // Details + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.selectFirst("h1.title-detail a")!!.text() + author = document.selectFirst("div.author p")?.text() + description = document.selectFirst("div.sort-des div.line-clamp")?.text() + genre = document.select("div.kind a, div.m-tags a").joinToString { it.text() } + status = when (document.selectFirst("div.status p")?.text()) { + "Đang tiến hành" -> SManga.ONGOING + "Hoàn thành" -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + thumbnail_url = document.selectFirst("div.col-image img")?.attr("abs:src") + } + + // Chapters + override fun chapterListSelector() = "div.list-chapters div.l-chapter" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + val a = element.selectFirst("a.ll-chap")!! + setUrlWithoutDomain(a.attr("href")) + name = a.text() + date_upload = element.selectFirst("span.ll-update")?.text()?.toDate() ?: 0L + } + + // Pages + override fun pageListParse(document: Document): List { + return document.select("#read-chaps .mi-item img.reading-img").mapIndexed { index, element -> + val img = when { + element.hasAttr("data-src") -> element.attr("abs:data-src") + else -> element.attr("abs:src") + } + Page(index, imageUrl = img) + }.distinctBy { it.imageUrl } + } + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() + + private fun getCustomDomain(): String = synchronized(prefsLock) { + preferences.getString(CUSTOM_URL_PREF, "")!! + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + EditTextPreference(screen.context).apply { + key = CUSTOM_URL_PREF + title = CUSTOM_URL_PREF_TITLE + summary = "$CUSTOM_URL_PREF_SUMMARY${getCustomDomain()}" + setDefaultValue("") + dialogTitle = CUSTOM_URL_PREF_TITLE + + val validate = { str: String -> + if (str.isBlank()) { + true + } else { + runCatching { str.toHttpUrl() }.isSuccess && domainRegex.matchEntire(str) != null + } + } + + setOnBindEditTextListener { editText -> + editText.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {} + + override fun afterTextChanged(editable: Editable?) { + editable ?: return + val text = editable.toString() + val valid = validate(text) + editText.error = if (!valid) "https://example.com" else null + editText.rootView.findViewById