commit
794a737b6d
@ -1 +1 @@
|
||||
total: 1178
|
||||
total: 1179
|
||||
@ -0,0 +1,295 @@
|
||||
package org.koitharu.kotatsu.parsers.site.vi
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.network.UserAgents
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("GOCTRUYENTRANH", "Góc Truyện Tranh", "vi")
|
||||
internal class GocTruyenTranh(context: MangaLoaderContext) :
|
||||
PagedMangaParser(context, MangaParserSource.GOCTRUYENTRANH, 30) {
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain("goctruyentranh.net")
|
||||
|
||||
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
|
||||
super.onCreateConfig(keys)
|
||||
keys.add(userAgentKey)
|
||||
}
|
||||
|
||||
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
|
||||
|
||||
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.NEWEST_ASC,
|
||||
SortOrder.ALPHABETICAL,
|
||||
SortOrder.RATING,
|
||||
SortOrder.POPULARITY
|
||||
)
|
||||
|
||||
override val filterCapabilities: MangaListFilterCapabilities
|
||||
get() = MangaListFilterCapabilities(
|
||||
isMultipleTagsSupported = true,
|
||||
isTagsExclusionSupported = false,
|
||||
isSearchSupported = true,
|
||||
isSearchWithFiltersSupported = true,
|
||||
)
|
||||
|
||||
override suspend fun getFilterOptions() = MangaListFilterOptions(
|
||||
availableTags = availableTags(),
|
||||
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
|
||||
availableContentTypes = EnumSet.of(
|
||||
ContentType.MANGA,
|
||||
ContentType.MANHWA,
|
||||
ContentType.MANHUA,
|
||||
ContentType.OTHER,
|
||||
),
|
||||
)
|
||||
|
||||
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append("/baseapi/comics/filterComic")
|
||||
append("?keyword=")
|
||||
append(filter.query?.urlEncoded() ?: "")
|
||||
|
||||
if (filter.tags.isNotEmpty()) {
|
||||
append("&categories=")
|
||||
append(filter.tags.joinToString(",") { tag ->
|
||||
availableTags().find { it.title == tag.title }?.key ?: tag.key
|
||||
})
|
||||
}
|
||||
|
||||
append("&status=")
|
||||
when {
|
||||
filter.states.isEmpty() -> append("")
|
||||
filter.states.size > 1 -> append("")
|
||||
else -> append(
|
||||
when (filter.states.first()) {
|
||||
MangaState.ONGOING -> "0"
|
||||
MangaState.FINISHED -> "1"
|
||||
else -> ""
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
append("&sort=")
|
||||
append(
|
||||
when (order) {
|
||||
SortOrder.UPDATED -> "recently_updated"
|
||||
SortOrder.NEWEST -> "latest"
|
||||
SortOrder.NEWEST_ASC -> "oldest"
|
||||
SortOrder.RATING -> "rating"
|
||||
SortOrder.ALPHABETICAL -> "alphabet"
|
||||
SortOrder.POPULARITY -> "mostView"
|
||||
else -> "recently_updated"
|
||||
}
|
||||
)
|
||||
|
||||
if (filter.types.isNotEmpty()) {
|
||||
append("&country=")
|
||||
append(
|
||||
filter.types.joinToString(",") {
|
||||
when (it) {
|
||||
ContentType.MANGA -> "manga"
|
||||
ContentType.MANHWA -> "manhwa"
|
||||
ContentType.MANHUA -> "manhua"
|
||||
ContentType.OTHER -> "other"
|
||||
else -> "manga"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
append("&page=")
|
||||
append(page)
|
||||
}
|
||||
|
||||
val json = webClient.httpGet(url).parseJson()
|
||||
val data = json.getJSONObject("comics").getJSONArray("data")
|
||||
|
||||
return List(data.length()) { i ->
|
||||
val item = data.getJSONObject(i)
|
||||
val slug = item.getString("slug")
|
||||
val mangaUrl = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append("/")
|
||||
append(slug)
|
||||
}
|
||||
|
||||
val categories = item.optJSONArray("categories")
|
||||
val tags = if (categories != null) {
|
||||
List(categories.length()) { j ->
|
||||
val category = categories.getJSONObject(j)
|
||||
MangaTag(
|
||||
key = category.getString("id"),
|
||||
title = category.getString("name").toTitleCase(sourceLocale),
|
||||
source = source,
|
||||
)
|
||||
}.toSet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
Manga(
|
||||
id = generateUid(mangaUrl),
|
||||
url = "/$slug",
|
||||
publicUrl = mangaUrl,
|
||||
title = item.getString("name"),
|
||||
altTitle = item.optString("origin_name").takeIf { it.isNotEmpty() },
|
||||
description = item.optString("content"),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = isNsfwSource,
|
||||
coverUrl = item.optString("thumbnail"),
|
||||
tags = tags,
|
||||
state = when (item.optString("status")) {
|
||||
"0" -> MangaState.ONGOING
|
||||
"1" -> MangaState.FINISHED
|
||||
else -> null
|
||||
},
|
||||
source = source,
|
||||
author = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
return manga.copy(
|
||||
rating = doc.selectFirst("div > span.leading-none")?.text()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
|
||||
author = doc.selectFirst("div:contains(Tác giả) + div")?.text()?.takeIf { it.isNotEmpty() },
|
||||
chapters = doc.select("ul[itemtype='https://schema.org/ItemList'] li").mapChapters(reversed = true) { i, li ->
|
||||
val a = li.selectFirstOrThrow("a")
|
||||
val href = a.attrAsRelativeUrl("href")
|
||||
val name = li.selectFirst("div.w-\\[50\\%\\].truncate.flex")?.text() ?: ""
|
||||
val dateText = li.selectFirst("div.w-\\[50\\%\\].truncate.text-center")?.text()
|
||||
MangaChapter(
|
||||
id = generateUid(href),
|
||||
name = name,
|
||||
number = i + 1f,
|
||||
volume = 0,
|
||||
url = href,
|
||||
scanlator = null,
|
||||
uploadDate = parseChapterDate(dateText),
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
||||
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||
return doc.select("img.lozad[data-src]").map { img ->
|
||||
val url = img.attr("data-src")
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseChapterDate(dateText: String?): Long {
|
||||
if (dateText == null) return 0
|
||||
|
||||
val number = dateText.filter { it.isDigit() }.toIntOrNull() ?: return 0
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
return when {
|
||||
dateText.contains("phút trước") -> {
|
||||
now - (number * 60 * 1000L)
|
||||
}
|
||||
dateText.contains("giờ trước") -> {
|
||||
now - (number * 60 * 60 * 1000L)
|
||||
}
|
||||
dateText.contains("ngày trước") -> {
|
||||
now - (number * 24 * 60 * 60 * 1000L)
|
||||
}
|
||||
else -> 0L
|
||||
}
|
||||
}
|
||||
|
||||
private fun availableTags() = arraySetOf(
|
||||
MangaTag("Action", "1", source),
|
||||
MangaTag("Adventure", "2", source),
|
||||
MangaTag("Fantasy", "3", source),
|
||||
MangaTag("Manhua", "4", source),
|
||||
MangaTag("Chuyển Sinh", "5", source),
|
||||
MangaTag("Truyện Màu", "6", source),
|
||||
MangaTag("Xuyên Không", "7", source),
|
||||
MangaTag("Manhwa", "8", source),
|
||||
MangaTag("Drama", "9", source),
|
||||
MangaTag("Historical", "10", source),
|
||||
MangaTag("Manga", "11", source),
|
||||
MangaTag("Seinen", "12", source),
|
||||
MangaTag("Comedy", "13", source),
|
||||
MangaTag("Martial Arts", "14", source),
|
||||
MangaTag("Mystery", "15", source),
|
||||
MangaTag("Romance", "16", source),
|
||||
MangaTag("Shounen", "17", source),
|
||||
MangaTag("Sports", "18", source),
|
||||
MangaTag("Supernatural", "19", source),
|
||||
MangaTag("Harem", "20", source),
|
||||
MangaTag("Webtoon", "21", source),
|
||||
MangaTag("School Life", "22", source),
|
||||
MangaTag("Psychological", "23", source),
|
||||
MangaTag("Cổ Đại", "24", source),
|
||||
MangaTag("Ecchi", "25", source),
|
||||
MangaTag("Gender Bender", "26", source),
|
||||
MangaTag("Shoujo", "27", source),
|
||||
MangaTag("Slice of Life", "28", source),
|
||||
MangaTag("Ngôn Tình", "29", source),
|
||||
MangaTag("Horror", "30", source),
|
||||
MangaTag("Sci-fi", "31", source),
|
||||
MangaTag("Tragedy", "32", source),
|
||||
MangaTag("Mecha", "33", source),
|
||||
MangaTag("Comic", "34", source),
|
||||
MangaTag("One shot", "35", source),
|
||||
MangaTag("Shoujo Ai", "36", source),
|
||||
MangaTag("Anime", "37", source),
|
||||
MangaTag("Josei", "38", source),
|
||||
MangaTag("Smut", "39", source),
|
||||
MangaTag("Shounen Ai", "40", source),
|
||||
MangaTag("Mature", "41", source),
|
||||
MangaTag("Soft Yuri", "42", source),
|
||||
MangaTag("Adult", "43", source),
|
||||
MangaTag("Doujinshi", "44", source),
|
||||
MangaTag("Live action", "45", source),
|
||||
MangaTag("Trinh Thám", "46", source),
|
||||
MangaTag("Việt Nam", "47", source),
|
||||
MangaTag("Truyện scan", "48", source),
|
||||
MangaTag("Cooking", "49", source),
|
||||
MangaTag("Tạp chí truyện tranh", "50", source),
|
||||
MangaTag("16+", "51", source),
|
||||
MangaTag("Thiếu Nhi", "52", source),
|
||||
MangaTag("Soft Yaoi", "53", source),
|
||||
MangaTag("Đam Mỹ", "54", source),
|
||||
MangaTag("BoyLove", "55", source),
|
||||
MangaTag("Yaoi", "56", source),
|
||||
MangaTag("18+", "57", source),
|
||||
MangaTag("Người Thú", "58", source),
|
||||
MangaTag("ABO", "59", source),
|
||||
MangaTag("Mafia", "60", source),
|
||||
MangaTag("Isekai", "61", source),
|
||||
MangaTag("Hệ Thống", "62", source),
|
||||
MangaTag("NTR", "63", source),
|
||||
MangaTag("Yuri", "64", source),
|
||||
MangaTag("Girl Love", "65", source),
|
||||
MangaTag("Demons", "66", source),
|
||||
MangaTag("Huyền Huyễn", "67", source),
|
||||
MangaTag("Detective", "68", source),
|
||||
MangaTag("Trọng Sinh", "69", source),
|
||||
MangaTag("Magic", "70", source),
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue