11package org.dokiteam.doki.parsers.site.vi
22
3- import org.jsoup.nodes.Document
43import org.dokiteam.doki.parsers.MangaLoaderContext
54import org.dokiteam.doki.parsers.MangaSourceParser
65import org.dokiteam.doki.parsers.config.ConfigKey
76import org.dokiteam.doki.parsers.core.LegacyPagedMangaParser
87import org.dokiteam.doki.parsers.model.*
9- import org.dokiteam.doki.parsers.network.UserAgents
108import org.dokiteam.doki.parsers.util.*
119import java.util.*
1210
1311@MangaSourceParser(" HENTAIVNBUZZ" , " HentaiVn.buzz" , " vi" , type = ContentType .HENTAI )
1412internal class HentaiVnBuzz (context : MangaLoaderContext ) :
15- LegacyPagedMangaParser (context, MangaParserSource .HENTAIVNBUZZ , 24 ) {
13+ LegacyPagedMangaParser (context, MangaParserSource .HENTAIVNBUZZ , 60 ) {
1614
17- override val configKeyDomain = ConfigKey .Domain (" hentaivn.email " )
15+ override val configKeyDomain = ConfigKey .Domain (" hentaivn.beer " )
1816
19- override fun onCreateConfig (keys : MutableCollection <ConfigKey <* >>) {
20- super .onCreateConfig(keys)
21- keys.add(userAgentKey)
22- }
23-
24- override val userAgentKey = ConfigKey .UserAgent (UserAgents .CHROME_DESKTOP )
25-
26- override val availableSortOrders: Set <SortOrder > = EnumSet .of(
27- SortOrder .NEWEST ,
28- SortOrder .POPULARITY ,
29- SortOrder .UPDATED ,
30- )
17+ override val availableSortOrders: Set <SortOrder > =
18+ EnumSet .of(
19+ SortOrder .UPDATED ,
20+ SortOrder .NEWEST ,
21+ SortOrder .POPULARITY ,
22+ SortOrder .RATING ,
23+ )
3124
3225 override val filterCapabilities: MangaListFilterCapabilities
3326 get() = MangaListFilterCapabilities (
27+ isMultipleTagsSupported = true ,
28+ isTagsExclusionSupported = true ,
3429 isSearchSupported = true ,
3530 )
3631
3732 override suspend fun getFilterOptions () = MangaListFilterOptions (
38- availableTags = fetchTags (),
33+ availableTags = fetchAvailableTags (),
3934 availableStates = EnumSet .of(MangaState .ONGOING , MangaState .FINISHED ),
4035 )
4136
4237 override suspend fun getListPage (page : Int , order : SortOrder , filter : MangaListFilter ): List <Manga > {
43- val url = when {
44- ! filter.query.isNullOrEmpty() -> {
45- buildString {
46- append(" /tim-kiem?key_word=" )
47- append(filter.query.urlEncoded())
48- if (page > 1 ) {
49- append(" &page=" )
50- append(page)
51- }
52- }
38+ val url = buildString {
39+ append(" https://" )
40+ append(domain)
41+ append(" /tim-kiem-nang-cao" )
42+
43+ append(" ?page=" )
44+ append(page)
45+
46+ append(" &sort=" )
47+ append(
48+ when (order) {
49+ SortOrder .UPDATED -> " 0"
50+ SortOrder .NEWEST -> " 1"
51+ SortOrder .POPULARITY -> " 2"
52+ SortOrder .RATING -> " 6"
53+ else -> " 0"
54+ },
55+ )
56+
57+ append(" &status=" )
58+ append(
59+ when (filter.states.oneOrThrowIfMany()) {
60+ MangaState .ONGOING -> " 1"
61+ MangaState .FINISHED -> " 2"
62+ else -> " 0"
63+ },
64+ )
65+
66+ if (filter.tags.isNotEmpty()) {
67+ append(" &category=" )
68+ append(filter.tags.joinToString(" ," ) { it.key })
5369 }
5470
55- filter.tags.isNotEmpty() -> {
56- val tag = filter.tags.first()
57- buildString {
58- append(" /the-loai/" )
59- append(tag.key)
60- append(" ?" )
61- when (order) {
62- SortOrder .NEWEST -> append(" sort=0" )
63- SortOrder .UPDATED -> append(" sort=1" )
64- SortOrder .POPULARITY -> append(" sort=2" )
65- else -> append(" sort=0" )
66- }
67- if (filter.states.isNotEmpty()) {
68- filter.states.forEach {
69- when (it) {
70- MangaState .ONGOING -> append(" &is_full=0" )
71- MangaState .FINISHED -> append(" &is_full=1" )
72- else -> append(" " )
73- }
74- }
75- }
76- if (page > 1 ) {
77- append(" &page=" )
78- append(page)
79- }
80- }
71+ if (filter.tagsExclude.isNotEmpty()) {
72+ append(" ¬category=" )
73+ append(filter.tagsExclude.joinToString(" ," ) { it.key })
8174 }
8275
83- else -> {
84- buildString {
85- append(" /danh-sach/truyen-moi?" )
86- when (order) {
87- SortOrder .NEWEST -> append(" sort=0" )
88- SortOrder .UPDATED -> append(" sort=1" )
89- SortOrder .POPULARITY -> append(" sort=2" )
90- else -> append(" sort=0" )
91- }
92- if (filter.states.isNotEmpty()) {
93- filter.states.forEach {
94- when (it) {
95- MangaState .ONGOING -> append(" &is_full=0" )
96- MangaState .FINISHED -> append(" &is_full=1" )
97- else -> append(" " )
98- }
99- }
100- }
101- if (page > 1 ) {
102- append(" &page=" )
103- append(page)
104- }
105- }
76+ if (! filter.query.isNullOrEmpty()) {
77+ clear()
78+
79+ append(" https://" )
80+ append(domain)
81+ append(" /tim-kiem?q=" )
82+ append(filter.query.urlEncoded())
83+
84+ return @buildString // end of buildString
10685 }
10786 }
10887
109- val fullUrl = url.toAbsoluteUrl(domain)
110- val doc = webClient.httpGet(fullUrl).parseHtml()
111- return when {
112- ! filter.query.isNullOrEmpty() -> parseSearchManga(doc)
113- filter.tags.isNotEmpty() -> parseSearchManga(doc)
114- else -> parseListManga(doc)
115- }
116- }
88+ val doc = webClient.httpGet(url).parseHtml()
89+ return doc.select(" ul.list_grid.grid > li" ).map { element ->
90+ val aTag = element.selectFirstOrThrow(" h3 a" )
91+ val tags = element.select(" .genre-item" ).mapToSet {
92+ MangaTag (
93+ key = it.attr(" href" ).substringAfterLast(' -' ).substringBeforeLast(' .' ),
94+ title = it.text().toTitleCase(sourceLocale),
95+ source = source,
96+ )
97+ }
98+
99+ val href = aTag.attrAsRelativeUrl(" href" )
117100
118- private fun parseSearchManga (doc : Document ): List <Manga > {
119- return doc.select(" .story-item-list.d-flex.align-items-center.position-relative.mb-1" ).map { div ->
120- val href = div.selectFirstOrThrow(" a.story-item-list__image" ).attrAsRelativeUrl(" href" )
121- val coverUrl = div.selectFirst(" img" )?.attr(" data-src" )
122- val title = div.selectFirst(" img" )?.attr(" alt" ).orEmpty()
123101 Manga (
124102 id = generateUid(href),
125- title = title ,
103+ title = aTag.text() ,
126104 altTitles = emptySet(),
127105 url = href,
128- publicUrl = href.toAbsoluteUrl(domain ),
106+ publicUrl = aTag.attrAsAbsoluteUrl( " href " ),
129107 rating = RATING_UNKNOWN ,
130- contentRating = if (isNsfwSource) ContentRating .ADULT else null ,
131- coverUrl = coverUrl ,
132- tags = emptySet() ,
108+ contentRating = ContentRating .ADULT ,
109+ coverUrl = element.selectFirst( " .book_avatar a img " )?.src() ,
110+ tags = tags ,
133111 state = null ,
134112 authors = emptySet(),
135113 source = source,
136114 )
137115 }
138116 }
139117
140- private fun parseListManga (doc : Document ): List <Manga > {
141- return doc.select(" .story-item-list.d-flex.align-items-center.position-relative.mb-1" ).map { div ->
142- val href = div.selectFirstOrThrow(" a.story-item-list__image" ).attrAsRelativeUrl(" href" )
143- val coverUrl = div.selectFirst(" img" )?.attr(" data-src" ).orEmpty()
144- val title = div.selectFirst(" img" )?.attr(" alt" ).orEmpty()
145- Manga (
146- id = generateUid(href),
147- title = title,
148- altTitles = emptySet(),
149- url = href,
150- publicUrl = href.toAbsoluteUrl(domain),
151- rating = RATING_UNKNOWN ,
152- contentRating = if (isNsfwSource) ContentRating .ADULT else null ,
153- coverUrl = coverUrl,
154- tags = emptySet(),
155- state = null ,
156- authors = emptySet(),
118+ override suspend fun getDetails (manga : Manga ): Manga {
119+ val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
120+ val tags = doc.select(" ul.list01 li" ).mapToSet {
121+ MangaTag (
122+ key = it.attr(" href" ).substringAfterLast(' -' ).substringBeforeLast(' .' ),
123+ title = it.text().toTitleCase(sourceLocale),
157124 source = source,
158125 )
159126 }
160- }
127+ val author = doc.selectFirst( " li.author a " )?.textOrNull()
161128
162- override suspend fun getDetails (manga : Manga ): Manga {
163- val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
164- val author = doc.select(" p:contains(Tác giả:) a" ).text().nullIfEmpty()
165129 return manga.copy(
130+ altTitles = setOfNotNull(doc.selectFirst(" h2.other-name" )?.textOrNull()),
166131 authors = setOfNotNull(author),
167- tags = doc.select(" div.mb-1 span a" ).mapToSet { element ->
168- MangaTag (
169- key = element.attr(" href" ).substringAfter(" /the-loai/" ),
170- title = element.text().substringBefore(' ,' ).trim(), // force trim before , symbol and space
171- source = source,
172- )
173- },
174- description = null ,
175- state = when (doc.select(" p:contains(Trạng thái:) span" ).text()) {
176- " Đang ra" -> MangaState .ONGOING
132+ tags = tags,
133+ description = doc.selectFirst(" div.story-detail-info" )?.html(),
134+ state = when (doc.selectFirst(" .status p.col-xs-9" )?.text()) {
135+ " Đang tiến hành" -> MangaState .ONGOING
177136 " Hoàn thành" -> MangaState .FINISHED
178137 else -> null
179138 },
180- chapters = doc.select(" div.story-detail__list- chapter--list ul.list-unstyled li a " )
181- .mapIndexed { i, element ->
182- val href = element .attrAsRelativeUrl(" href" )
183- val name = element .text().removePrefix( " - " )
184- MangaChapter (
185- id = generateUid(href),
186- title = name,
187- number = i + 1f ,
188- volume = 0 ,
189- url = href,
190- scanlator = null ,
191- uploadDate = 0 ,
192- branch = null ,
193- source = source,
194- )
195- },
139+ chapters = doc.select(" div.list_chapter div.works- chapter-item " ).mapChapters(reversed = true ) { i, div ->
140+ val a = div.selectFirstOrThrow( " a " )
141+ val href = a .attrAsRelativeUrl(" href" )
142+ val name = a .text()
143+ MangaChapter (
144+ id = generateUid(href),
145+ title = name,
146+ number = i + 1f ,
147+ volume = 0 ,
148+ url = href,
149+ scanlator = null ,
150+ uploadDate = 0L ,
151+ branch = null ,
152+ source = source,
153+ )
154+ },
196155 )
197156 }
198157
199158 override suspend fun getPages (chapter : MangaChapter ): List <MangaPage > {
200159 val fullUrl = chapter.url.toAbsoluteUrl(domain)
201160 val doc = webClient.httpGet(fullUrl).parseHtml()
202- val imageUrls = doc.select(" meta[property='og:image']" ).map { it.attr(" content" ) }
203- val finalUrls = imageUrls.drop(1 )
204- return finalUrls.map { url ->
161+ return doc.select(" .chapter_content img" ).map { img ->
162+ val url = img.requireSrc()
205163 MangaPage (
206164 id = generateUid(url),
207165 url = url,
@@ -211,18 +169,15 @@ internal class HentaiVnBuzz(context: MangaLoaderContext) :
211169 }
212170 }
213171
214- private suspend fun fetchTags (): Set <MangaTag > {
215- val doc = webClient.httpGet(" https://$domain /" ).parseHtml()
216- val list = doc.select(" ul.dropdown-menu.dropdown-menu-custom li a" )
217- return list.mapToSet { tags ->
218- val href = tags.attr(" href" )
219- val key = href.substringAfter(" /the-loai/" ).substringBefore(" /" )
220- val title = tags.text()
172+ private suspend fun fetchAvailableTags (): Set <MangaTag > {
173+ val doc = webClient.httpGet(" https://$domain /tim-kiem-nang-cao" ).parseHtml()
174+ val elements = doc.select(" .genre-item" )
175+ return elements.mapIndexed { i, element ->
221176 MangaTag (
222- key = key ,
223- title = title ,
177+ key = (i + 1 ).toString() ,
178+ title = element.text().toTitleCase(sourceLocale) ,
224179 source = source,
225180 )
226- }
181+ }.toSet()
227182 }
228183}
0 commit comments