[Hentai18VN] Add source

master
Draken 1 year ago
parent f10e6f021c
commit fcd6e92dba

@ -1,17 +1,21 @@
package org.koitharu.kotatsu.parsers.site.vi
import androidx.collection.arraySetOf
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONObject
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("HENTAI18VN", "Hentai18VN", "vi", type = ContentType.HENTAI)
internal class Hentai18VN(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.HENTAI18VN, 60) {
internal class Hentai18VN(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.HENTAI18VN, 30) {
override val configKeyDomain = ConfigKey.Domain("hentai18vn.art")
@ -20,18 +24,220 @@ internal class Hentai18VN(context: MangaLoaderContext) : PagedMangaParser(contex
keys.add(userAgentKey)
}
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
isSearchWithFiltersSupported = false,
isSearchWithFiltersSupported = false
)
override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(),
override suspend fun getFilterOptions() = MangaListFilterOptions(availableTags = fetchAvailableTags())
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST,
SortOrder.POPULARITY,
SortOrder.UPDATED
)
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
return when {
!filter.query.isNullOrEmpty() -> {
if (page > 1) {
return emptyList()
}
val url = "https://$domain/search/html/1".toHttpUrl()
val body = JSONObject().apply {
put("keyword", filter.query.urlEncoded())
}
val headers = Headers.Builder()
.add("X-Requested-With", "XMLHttpRequest")
.build()
val response = webClient.httpPost(url, body, headers).parseHtml()
parseMangaSearch(response)
}
!filter.tags.isNullOrEmpty() -> {
val tag = filter.tags.first()
val url = buildString {
append("https://")
append(domain)
append("/the-loai/")
append(tag.key)
if (page > 1) {
append("?page=")
append(page)
}
}
val response = webClient.httpGet(url).parseHtml()
parseMangaList(response)
}
else -> {
val url = buildString {
append("https://")
append(domain)
append("/")
append(
when (order) {
SortOrder.NEWEST -> "danh-sach/truyen-hentai-moi"
SortOrder.POPULARITY -> "danh-sach/truyen-hentai-hot"
SortOrder.UPDATED -> "danh-sach/truyen-hentai-hoan-thanh"
else -> "danh-sach/truyen-hentai-hay"
}
)
if (page > 1) {
append("?page=")
append(page)
}
}
val response = webClient.httpGet(url).parseHtml()
parseMangaList(response)
}
}
}
private fun parseMangaSearch(doc: Document): List<Manga> {
return doc.select("ul li a.item").map { a ->
val href = a.attr("href")
val img = a.selectFirst("img")!!
Manga(
id = generateUid(href),
url = href,
publicUrl = href.toAbsoluteUrl(domain),
title = img.attr("alt"),
altTitle = null,
author = null,
tags = emptySet(),
rating = RATING_UNKNOWN,
state = null,
coverUrl = img.attr("src"),
isNsfw = isNsfwSource,
source = source
)
}
}
private fun parseMangaList(doc: Document): List<Manga> {
return doc.select("div.visual").map { div ->
val a = div.selectFirst("div.main_text h3.title a")!!
val img = div.selectFirst("div.hentai-cover img")!!
val mangaUrl = a.attr("href")
Manga(
id = generateUid(mangaUrl),
publicUrl = mangaUrl,
url = mangaUrl.removePrefix("https://$domain"),
title = a.text(),
altTitle = null,
author = null,
description = null,
tags = emptySet(),
rating = RATING_UNKNOWN,
state = null,
coverUrl = img.attr("data-original").takeIf { it.isNotEmpty() } ?: img.attr("src"),
isNsfw = isNsfwSource,
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val tags = doc.select("div.hentai-info .line-content a.item-tag")
.mapNotNull { a ->
MangaTag(
title = a.text(),
key = a.attr("href").substringAfterLast("/"),
source = source
)
}.toSet()
val chapters = doc.select("ul#chapter-list li.citem").map { li ->
val a = li.selectFirst("a")!!
val number = a.text().substringAfter("Chap ").toFloatOrNull() ?: 0f
MangaChapter(
id = generateUid(a.attr("href")),
name = a.text(),
number = number,
url = a.attr("href").removePrefix("https://$domain"),
uploadDate = parseChapterDate(li.selectFirst(".time")?.text()),
source = source,
scanlator = null,
branch = null,
volume = 0
)
}
val altTitle = doc.selectFirst("h2.alternative")?.text()
val author = doc.selectFirst("div.hentai-info .line:contains(Tác giả) .line-content")?.text()
val state = when(doc.selectFirst("div.hentai-info .line:contains(Tình trạng) .line-content")?.text()) {
"Đang cập nhật" -> MangaState.ONGOING
"Hoàn thành" -> MangaState.FINISHED
else -> null
}
return manga.copy(
tags = tags,
author = author?.takeUnless { it == "Đang cập nhật" },
altTitle = altTitle,
state = state,
chapters = chapters,
description = doc.select("div.about").text()
)
}
private fun parseChapterDate(date: String?): Long {
if (date == null) return 0
return try {
val now = SimpleDateFormat("dd/MM/yyyy", Locale.US)
now.parse(date)?.time ?: 0L
} catch (e: Exception) {
0L
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return doc.select("div.chapter-content div.item-photo img").mapNotNull { img ->
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source
)
}
}
private suspend fun fetchAvailableTags(): Set<MangaTag> {
val tags = arraySetOf<MangaTag>()
val firstPage = webClient.httpGet("https://$domain/tim-the-loai").parseHtml()
val lastPage = firstPage.selectFirst("a[aria-label=Last]")
?.attr("href")
?.substringAfter("page=")
?.toIntOrNull() ?: 1
for (page in 1..lastPage) {
val doc = if (page == 1) {
firstPage
} else {
webClient.httpGet("https://$domain/tim-the-loai?page=$page").parseHtml()
}
doc.select("ul.list-tags li").forEach { li ->
val a = li.selectFirst("a") ?: return@forEach
val title = a.selectFirst("h3.tag-name")?.text()?.trim() ?: return@forEach
val url = a.attr("href")
tags.add(
MangaTag(
title = title,
key = url.substringAfterLast("/"),
source = source
)
)
}
}
return tags
}
}
Loading…
Cancel
Save