GocTruyenTranhVui + KuroNeko: Fixes (#2165)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
This commit is contained in:
@@ -14,30 +14,29 @@ import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.parsers.Broken
|
||||
|
||||
@Broken("Need to clean code + Testing")
|
||||
@MangaSourceParser("GOCTRUYENTRANHVUI", "Góc Truyện Tranh Vui", "vi")
|
||||
internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
PagedMangaParser(context, MangaParserSource.GOCTRUYENTRANHVUI, 50) {
|
||||
internal class GocTruyenTranhVui(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.GOCTRUYENTRANHVUI, 50) {
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain("goctruyentranhvui17.com")
|
||||
private val apiUrl by lazy { "https://$domain/api/v2" }
|
||||
|
||||
private val apiUrl = "https://$domain/api/v2"
|
||||
private val requestMutex = Mutex()
|
||||
private var lastRequestTime = 0L
|
||||
|
||||
override fun getRequestHeaders(): Headers = Headers.Builder()
|
||||
private val apiHeaders by lazy {
|
||||
Headers.Builder()
|
||||
.add("Authorization", TOKEN_KEY)
|
||||
.add("Referer", "https://$domain/")
|
||||
.add("X-Requested-With", "XMLHttpRequest")
|
||||
.build()
|
||||
}
|
||||
|
||||
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.POPULARITY,
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.RATING,
|
||||
SortOrder.RATING
|
||||
)
|
||||
|
||||
override val filterCapabilities = MangaListFilterCapabilities(
|
||||
@@ -47,18 +46,14 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
|
||||
override suspend fun getFilterOptions() = MangaListFilterOptions(
|
||||
availableTags = availableTags(),
|
||||
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
|
||||
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED)
|
||||
)
|
||||
|
||||
init {
|
||||
setFirstPage(0)
|
||||
}
|
||||
|
||||
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
|
||||
enforceRateLimit()
|
||||
val url = buildString {
|
||||
append(apiUrl)
|
||||
append("/search?p=$page")
|
||||
append("/search?p=${page - 1}")
|
||||
if (!filter.query.isNullOrBlank()) {
|
||||
append("&searchValue=${filter.query.urlEncoded()}")
|
||||
}
|
||||
@@ -83,7 +78,7 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
}
|
||||
}
|
||||
|
||||
val json = webClient.httpGet(url).parseJson()
|
||||
val json = webClient.httpGet(url, extraHeaders = apiHeaders).parseJson()
|
||||
val result = json.optJSONObject("result") ?: return emptyList()
|
||||
val data = result.optJSONArray("data") ?: return emptyList()
|
||||
|
||||
@@ -129,7 +124,7 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
val chapters = try {
|
||||
enforceRateLimit()
|
||||
val chapterApiUrl = "https://$domain/api/comic/$comicId/chapter?limit=-1"
|
||||
val chapterJson = webClient.httpGet(chapterApiUrl).parseJson()
|
||||
val chapterJson = webClient.httpGet(chapterApiUrl, extraHeaders = apiHeaders).parseJson()
|
||||
val chaptersData = chapterJson.getJSONObject("result").getJSONArray("chapters")
|
||||
|
||||
List(chaptersData.length()) { i ->
|
||||
@@ -205,19 +200,14 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
"nameEn" to nameEn
|
||||
)
|
||||
val authApiUrl = "$apiUrl/chapter/auth".toHttpUrl()
|
||||
val authResponse = webClient.httpPost(url = authApiUrl, form = formBody).parseJson()
|
||||
val authResponse = webClient.httpPost(url = authApiUrl, form = formBody, extraHeaders = apiHeaders).parseJson()
|
||||
val data = authResponse.getJSONObject("result").getJSONArray("data")
|
||||
imageUrls = List(data.length()) { i -> data.getString(i) }
|
||||
}
|
||||
|
||||
return imageUrls.map { url ->
|
||||
val finalUrl = if (url.startsWith("/image/")) "https://$domain$url" else url
|
||||
MangaPage(
|
||||
id = generateUid(finalUrl),
|
||||
url = finalUrl,
|
||||
preview = null,
|
||||
source = source
|
||||
)
|
||||
MangaPage(id = generateUid(finalUrl), url = finalUrl, preview = null, source = source)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +215,7 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
requestMutex.withLock {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val timeSinceLastRequest = currentTime - lastRequestTime
|
||||
if (timeSinceLastRequest < REQUEST_DELAY_MS) {
|
||||
if (timeSinceLastRequest < REQUEST_DELAY_MS) { // Vẫn truy cập được REQUEST_DELAY_MS
|
||||
delay(REQUEST_DELAY_MS - timeSinceLastRequest)
|
||||
}
|
||||
lastRequestTime = System.currentTimeMillis()
|
||||
@@ -277,7 +267,7 @@ internal class GocTruyenTranhVui(context: MangaLoaderContext):
|
||||
MangaTag("Gender Bender", "GDB", source),
|
||||
MangaTag("Murim", "MRR", source),
|
||||
MangaTag("Leo Tháp", "LTT", source),
|
||||
MangaTag("Nấu Ăn", "COO", source),
|
||||
MangaTag("Nấu Ăn", "COO", source)
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.koitharu.kotatsu.parsers.site.vi
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
@@ -10,11 +12,13 @@ import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("KURONEKO", "Kuro Neko / vi-Hentai", "vi", type = ContentType.HENTAI)
|
||||
internal class KuroNeko(context: MangaLoaderContext):
|
||||
PagedMangaParser(context, MangaParserSource.KURONEKO, 30) {
|
||||
internal class KuroNeko(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.KURONEKO, 30) {
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain("vi-hentai.moe")
|
||||
|
||||
private val pagesRequestMutex = Mutex()
|
||||
private var lastPagesRequestTime = 0L
|
||||
|
||||
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
|
||||
super.onCreateConfig(keys)
|
||||
keys.add(userAgentKey)
|
||||
@@ -129,9 +133,9 @@ internal class KuroNeko(context: MangaLoaderContext):
|
||||
}
|
||||
}
|
||||
|
||||
val doc = parseHttp(url) { it }
|
||||
|
||||
return doc.select("div.grid div.relative").map { div ->
|
||||
val doc = webClient.httpGet(url).parseHtml()
|
||||
return doc.select("div.grid div.relative")
|
||||
.map { div ->
|
||||
val href = div.selectFirst("a[href^=/truyen/]")?.attrOrNull("href")
|
||||
?: div.parseFailed("Không thể tìm thấy nguồn ảnh của Manga này!")
|
||||
val coverUrl = div.selectFirst("div.cover")?.attr("style")
|
||||
@@ -155,7 +159,7 @@ internal class KuroNeko(context: MangaLoaderContext):
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val root = parseHttp(manga.url.toAbsoluteUrl(domain)) { it }
|
||||
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
val author = root.selectFirst("div.mt-2:contains(Tác giả) span a")?.textOrNull()
|
||||
|
||||
return manga.copy(
|
||||
@@ -180,7 +184,6 @@ internal class KuroNeko(context: MangaLoaderContext):
|
||||
val name = a.selectFirst("span.text-ellipsis")?.text().orEmpty()
|
||||
val dateText = a.parent()?.selectFirst("span.timeago")?.attr("datetime").orEmpty()
|
||||
val scanlator = root.selectFirst("div.mt-2:contains(Nhóm dịch) span a")?.textOrNull()
|
||||
|
||||
MangaChapter(
|
||||
id = generateUid(href),
|
||||
title = name,
|
||||
@@ -197,12 +200,18 @@ internal class KuroNeko(context: MangaLoaderContext):
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = parseHttp(chapter.url.toAbsoluteUrl(domain)) { it }
|
||||
return doc.select("div.text-center img.lazy").mapNotNull { img ->
|
||||
val url = img.attr("src").takeIf { it.isNotBlank() }
|
||||
?: img.attr("data-src").takeIf { it.isNotBlank() }
|
||||
?: return@mapNotNull null
|
||||
pagesRequestMutex.withLock {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val timeSinceLastRequest = currentTime - lastPagesRequestTime
|
||||
if (timeSinceLastRequest < PAGES_REQUEST_DELAY_MS) {
|
||||
delay(PAGES_REQUEST_DELAY_MS - timeSinceLastRequest)
|
||||
}
|
||||
lastPagesRequestTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
return doc.select("div.text-center img").mapNotNull { img ->
|
||||
val url = img.requireSrc()
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
@@ -243,15 +252,8 @@ internal class KuroNeko(context: MangaLoaderContext):
|
||||
calendar.timeInMillis
|
||||
}.getOrDefault(0L)
|
||||
|
||||
private suspend fun <T> parseHttp(url: String, block: (Document) -> T): T {
|
||||
// 15 reqs / minute
|
||||
kotlinx.coroutines.delay(4000L)
|
||||
val doc = webClient.httpGet(url).parseHtml()
|
||||
return block(doc)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val PAGES_REQUEST_DELAY_MS = 5000L
|
||||
const val PATH = "AxsAEQdJWk4YDUkHDgcVEwxaBQoHShIXHwYbD1seHAwHOwAKCAYFFw==\n"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user