Merge pull request #1461 - dragonx943/site/vi/newtruyen
[NewTruyen] Add source + Update sources domain [vi]
This commit is contained in:
2
.github/summary.yaml
vendored
2
.github/summary.yaml
vendored
@@ -1 +1 @@
|
|||||||
total: 1185
|
total: 1186
|
||||||
@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.parsers.util.json.getBooleanOrDefault
|
|||||||
|
|
||||||
@MangaSourceParser("DOCTRUYEN5S", "DocTruyen5s", "vi")
|
@MangaSourceParser("DOCTRUYEN5S", "DocTruyen5s", "vi")
|
||||||
internal class DocTruyen5s(context: MangaLoaderContext) :
|
internal class DocTruyen5s(context: MangaLoaderContext) :
|
||||||
LilianaParser(context, MangaParserSource.DOCTRUYEN5S, "manga.io.vn", pageSize = 42) {
|
LilianaParser(context, MangaParserSource.DOCTRUYEN5S, "manga.io.vn", 42) {
|
||||||
|
|
||||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import org.koitharu.kotatsu.parsers.util.*
|
|||||||
|
|
||||||
@MangaSourceParser("HENTAICUBE", "CBHentai", "vi", ContentType.HENTAI)
|
@MangaSourceParser("HENTAICUBE", "CBHentai", "vi", ContentType.HENTAI)
|
||||||
internal class HentaiCube(context: MangaLoaderContext) :
|
internal class HentaiCube(context: MangaLoaderContext) :
|
||||||
MadaraParser(context, MangaParserSource.HENTAICUBE, "hentaicb.art") {
|
MadaraParser(context, MangaParserSource.HENTAICUBE, "hentaicb.love") {
|
||||||
|
|
||||||
override val configKeyDomain = ConfigKey.Domain("hentaicb.art", "hentaicube.xyz")
|
override val configKeyDomain = ConfigKey.Domain("hentaicb.love", "hentaicube.xyz")
|
||||||
|
|
||||||
override val datePattern = "dd/MM/yyyy"
|
override val datePattern = "dd/MM/yyyy"
|
||||||
override val tagPrefix = "the-loai/"
|
override val tagPrefix = "the-loai/"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
|||||||
|
|
||||||
@MangaSourceParser("HENTAIVNPLUS", "HentaiVN.plus", "vi", ContentType.HENTAI)
|
@MangaSourceParser("HENTAIVNPLUS", "HentaiVN.plus", "vi", ContentType.HENTAI)
|
||||||
internal class HentaiVnPlus(context: MangaLoaderContext) :
|
internal class HentaiVnPlus(context: MangaLoaderContext) :
|
||||||
MadaraParser(context, MangaParserSource.HENTAIVNPLUS, "hentaivn.tech", 24) {
|
MadaraParser(context, MangaParserSource.HENTAIVNPLUS, "hentaivn.men", 24) {
|
||||||
override val listUrl = "truyen-hentai/"
|
override val listUrl = "truyen-hentai/"
|
||||||
override val tagPrefix = "the-loai/"
|
override val tagPrefix = "the-loai/"
|
||||||
override val datePattern = "dd/MM/yyyy"
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
|||||||
|
|
||||||
@MangaSourceParser("TRUYENVN", "TruyenVn", "vi", ContentType.HENTAI)
|
@MangaSourceParser("TRUYENVN", "TruyenVn", "vi", ContentType.HENTAI)
|
||||||
internal class TruyenVn(context: MangaLoaderContext) :
|
internal class TruyenVn(context: MangaLoaderContext) :
|
||||||
MadaraParser(context, MangaParserSource.TRUYENVN, "truyenvn.li", 20) {
|
MadaraParser(context, MangaParserSource.TRUYENVN, "truyenvn.tech", 20) {
|
||||||
override val listUrl = "truyen-tranh/"
|
override val listUrl = "truyen-tranh/"
|
||||||
override val tagPrefix = "the-loai/"
|
override val tagPrefix = "the-loai/"
|
||||||
override val datePattern = "dd/MM/yyyy"
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ internal class DuaLeoTruyen(context: MangaLoaderContext) :
|
|||||||
PagedMangaParser(context, MangaParserSource.DUALEOTRUYEN, 60) {
|
PagedMangaParser(context, MangaParserSource.DUALEOTRUYEN, 60) {
|
||||||
|
|
||||||
override val configKeyDomain: ConfigKey.Domain
|
override val configKeyDomain: ConfigKey.Domain
|
||||||
get() = ConfigKey.Domain("dualeotruyenbeta.com")
|
get() = ConfigKey.Domain("dualeotruyenxxy.com")
|
||||||
|
|
||||||
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
|
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import javax.crypto.spec.SecretKeySpec
|
|||||||
internal class VcomycsParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.VCOMYCS, 36) {
|
internal class VcomycsParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.VCOMYCS, 36) {
|
||||||
|
|
||||||
override val configKeyDomain: ConfigKey.Domain
|
override val configKeyDomain: ConfigKey.Domain
|
||||||
get() = ConfigKey.Domain("vivicomi.store")
|
get() = ConfigKey.Domain("vivicomi.shop")
|
||||||
|
|
||||||
override val availableSortOrders: Set<SortOrder>
|
override val availableSortOrders: Set<SortOrder>
|
||||||
get() = EnumSet.of(SortOrder.UPDATED)
|
get() = EnumSet.of(SortOrder.UPDATED)
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ import java.util.*
|
|||||||
|
|
||||||
@MangaSourceParser("DOCTRUYEN3Q", "DocTruyen3Q", "vi")
|
@MangaSourceParser("DOCTRUYEN3Q", "DocTruyen3Q", "vi")
|
||||||
internal class DocTruyen3Q(context: MangaLoaderContext) :
|
internal class DocTruyen3Q(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.DOCTRUYEN3Q, "doctruyen3qui.com", 36) {
|
WpComicsParser(context, MangaParserSource.DOCTRUYEN3Q, "doctruyen3qui2.com", 36) {
|
||||||
|
|
||||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
||||||
"doctruyen3qui.com", // Main repo
|
"doctruyen3qui2.com", // Main domain
|
||||||
"doctruyen3qll.net",
|
"doctruyen3qll.net",
|
||||||
"doctruyen3q3.net",
|
"doctruyen3q3.net",
|
||||||
"doctruyen3qw.com",
|
"doctruyen3qw.com",
|
||||||
"doctruyen3qk.pro",
|
"doctruyen3qk.pro",
|
||||||
"doctruyen3qw.pro",
|
"doctruyen3qw.pro",
|
||||||
"doctruyen3qvip.com",
|
"doctruyen3qvip.com",
|
||||||
"truyen3qvip.com", // Main repo
|
"truyen3qvip.com", // Backup domain
|
||||||
)
|
)
|
||||||
|
|
||||||
override val datePattern = "dd/MM/yyyy"
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import java.text.SimpleDateFormat
|
|||||||
internal class NetTruyen(context: MangaLoaderContext) :
|
internal class NetTruyen(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.NETTRUYEN, "nettruyenrr.com", 36) {
|
WpComicsParser(context, MangaParserSource.NETTRUYEN, "nettruyenrr.com", 36) {
|
||||||
|
|
||||||
override val configKeyDomain: ConfigKey.Domain =
|
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
||||||
ConfigKey.Domain("nettruyenrr.com", "nettruyenww.com", "nettruyenx.com")
|
"nettruyenrr.com",
|
||||||
|
"nettruyenx.com"
|
||||||
|
)
|
||||||
|
|
||||||
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.EnumSet
|
|||||||
|
|
||||||
@MangaSourceParser("NETTRUYENFE", "NetTruyenFE", "vi")
|
@MangaSourceParser("NETTRUYENFE", "NetTruyenFE", "vi")
|
||||||
internal class NetTruyenFE(context: MangaLoaderContext) :
|
internal class NetTruyenFE(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.NETTRUYENFE, "nettruyentet.com", 20) {
|
WpComicsParser(context, MangaParserSource.NETTRUYENFE, "nettruyenedu.com", 20) {
|
||||||
|
|
||||||
override val listUrl = "/tim-kiem-nang-cao"
|
override val listUrl = "/tim-kiem-nang-cao"
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.EnumSet
|
|||||||
|
|
||||||
@MangaSourceParser("NETTRUYENLL", "NetTruyenLL", "vi")
|
@MangaSourceParser("NETTRUYENLL", "NetTruyenLL", "vi")
|
||||||
internal class NetTruyenLL(context: MangaLoaderContext) :
|
internal class NetTruyenLL(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.NETTRUYENLL, "nettruyentet.com", 20) {
|
WpComicsParser(context, MangaParserSource.NETTRUYENLL, "nettruyenedu.com", 20) {
|
||||||
|
|
||||||
override val listUrl = "/tim-kiem-nang-cao"
|
override val listUrl = "/tim-kiem-nang-cao"
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.EnumSet
|
|||||||
|
|
||||||
@MangaSourceParser("NETTRUYENSSR", "NetTruyenSSR", "vi")
|
@MangaSourceParser("NETTRUYENSSR", "NetTruyenSSR", "vi")
|
||||||
internal class NetTruyenSSR(context: MangaLoaderContext) :
|
internal class NetTruyenSSR(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.NETTRUYENSSR, "nettruyentet.com", 20) {
|
WpComicsParser(context, MangaParserSource.NETTRUYENSSR, "nettruyenedu.com", 20) {
|
||||||
|
|
||||||
override val listUrl = "/tim-kiem-nang-cao"
|
override val listUrl = "/tim-kiem-nang-cao"
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.*
|
|||||||
|
|
||||||
@MangaSourceParser("NETTRUYENUU", "NetTruyenUU", "vi")
|
@MangaSourceParser("NETTRUYENUU", "NetTruyenUU", "vi")
|
||||||
internal class NetTruyenUU(context: MangaLoaderContext) :
|
internal class NetTruyenUU(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.NETTRUYENUU, "nettruyentet.com", 20) {
|
WpComicsParser(context, MangaParserSource.NETTRUYENUU, "nettruyenedu.com", 20) {
|
||||||
|
|
||||||
override val listUrl = "/tim-kiem-nang-cao"
|
override val listUrl = "/tim-kiem-nang-cao"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.wpcomics.vi
|
||||||
|
|
||||||
|
import androidx.collection.ArraySet
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||||
|
import org.koitharu.kotatsu.parsers.exception.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@MangaSourceParser("NEWTRUYEN", "NewTruyen", "vi")
|
||||||
|
internal class NewTruyen(context: MangaLoaderContext) :
|
||||||
|
WpComicsParser(context, MangaParserSource.NEWTRUYEN, "newtruyen2.com", 36) {
|
||||||
|
|
||||||
|
override suspend fun getFilterOptions() = MangaListFilterOptions(
|
||||||
|
availableTags = getAvailableTags(),
|
||||||
|
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
val storyID = doc.selectFirst("input#storyID")?.attr("value")
|
||||||
|
?: throw ParseException("Story ID not found", fullUrl)
|
||||||
|
val chaptersDeferred = async { getChapterList(storyID) }
|
||||||
|
val tagsElement = doc.select("p.col-xs-12 a.tr-theloai")
|
||||||
|
val mangaTags = tagsElement.map {
|
||||||
|
MangaTag(
|
||||||
|
title = it.text(),
|
||||||
|
key = it.attr("href").substringAfterLast('/'),
|
||||||
|
source = source
|
||||||
|
)
|
||||||
|
}.toSet()
|
||||||
|
|
||||||
|
manga.copy(
|
||||||
|
description = doc.selectFirst(selectDesc)?.html(),
|
||||||
|
author = doc.body().select(selectAut).textOrNull(),
|
||||||
|
state = doc.selectFirst(selectState)?.let {
|
||||||
|
when (it.text()) {
|
||||||
|
in ongoing -> MangaState.ONGOING
|
||||||
|
in finished -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tags = mangaTags,
|
||||||
|
chapters = chaptersDeferred.await(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getChapterList(storyID: String): List<MangaChapter> {
|
||||||
|
val url = "/Story/ListChapterByStoryID?storyID=" + storyID
|
||||||
|
val fullUrl = url.toAbsoluteUrl(domain)
|
||||||
|
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
return doc.select("div.col-xs-5.chapter").mapChapters(reversed = true) { i, li ->
|
||||||
|
val a = li.selectFirstOrThrow("a")
|
||||||
|
val href = a.attrAsRelativeUrl("href")
|
||||||
|
val dateText = li.selectFirst("div.col-xs-4.text-center.small")?.text() // Broken, will fix it later
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(href),
|
||||||
|
name = a.text(),
|
||||||
|
number = i + 1f,
|
||||||
|
volume = 0,
|
||||||
|
url = href,
|
||||||
|
uploadDate = parseChapterDate(dateText), // Broken, will fix it later
|
||||||
|
source = source,
|
||||||
|
scanlator = null,
|
||||||
|
branch = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val relativeTimePattern = Regex("(\\d+)\\s*(phút|giờ|ngày|tháng) trước")
|
||||||
|
private val absoluteTimePattern = Regex("(\\d{2}-\\d{2}-\\d{4})")
|
||||||
|
|
||||||
|
private fun parseChapterDate(dateText: String?): Long { // Broken, will fix it later
|
||||||
|
if (dateText == null) return 0
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dateText.contains("phút trước") -> {
|
||||||
|
val match = relativeTimePattern.find(dateText)
|
||||||
|
val minutes = match?.groups?.get(1)?.value?.toIntOrNull() ?: 0
|
||||||
|
System.currentTimeMillis() - minutes * 60 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
dateText.contains("giờ trước") -> {
|
||||||
|
val match = relativeTimePattern.find(dateText)
|
||||||
|
val hours = match?.groups?.get(1)?.value?.toIntOrNull() ?: 0
|
||||||
|
System.currentTimeMillis() - hours * 3600 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
dateText.contains("ngày trước") -> {
|
||||||
|
val match = relativeTimePattern.find(dateText)
|
||||||
|
val days = match?.groups?.get(1)?.value?.toIntOrNull() ?: 0
|
||||||
|
System.currentTimeMillis() - days * 86400 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
dateText.contains("tháng trước") -> {
|
||||||
|
val match = relativeTimePattern.find(dateText)
|
||||||
|
val months = match?.groups?.get(1)?.value?.toIntOrNull() ?: 0
|
||||||
|
System.currentTimeMillis() - months * 30 * 86400 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
absoluteTimePattern.matches(dateText) -> {
|
||||||
|
val formatter = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
try {
|
||||||
|
val parsedDate = formatter.parse(dateText)
|
||||||
|
parsedDate?.time ?: 0L
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getAvailableTags(): Set<MangaTag> {
|
||||||
|
val doc = webClient.httpGet("https://$domain/").parseHtml()
|
||||||
|
val tagItems = doc.select("ul.dropdown-menu.megamenu li a")
|
||||||
|
val tagSet = ArraySet<MangaTag>(tagItems.size)
|
||||||
|
for (item in tagItems) {
|
||||||
|
val title = item.attr("title").toTitleCase(sourceLocale)
|
||||||
|
val key = item.attr("href").substringAfterLast('/').trim()
|
||||||
|
if (key.isNotEmpty() && title.isNotEmpty()) {
|
||||||
|
tagSet.add(MangaTag(title = title, key = key, source = source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tagSet
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,10 +12,10 @@ import java.util.*
|
|||||||
|
|
||||||
@MangaSourceParser("TOPTRUYEN", "TopTruyen", "vi")
|
@MangaSourceParser("TOPTRUYEN", "TopTruyen", "vi")
|
||||||
internal class TopTruyen(context: MangaLoaderContext) :
|
internal class TopTruyen(context: MangaLoaderContext) :
|
||||||
WpComicsParser(context, MangaParserSource.TOPTRUYEN, "www.toptruyentv.net", 36) {
|
WpComicsParser(context, MangaParserSource.TOPTRUYEN, "www.toptruyen369.net", 36) {
|
||||||
|
|
||||||
override val configKeyDomain = ConfigKey.Domain(
|
override val configKeyDomain = ConfigKey.Domain(
|
||||||
"www.toptruyentv.net", // Main repo
|
"www.toptruyen369.net", // Main domain
|
||||||
"www.toptruyen28.net",
|
"www.toptruyen28.net",
|
||||||
"www.toptruyento.pro",
|
"www.toptruyento.pro",
|
||||||
"www.toptruyenpro1.com",
|
"www.toptruyenpro1.com",
|
||||||
|
|||||||
Reference in New Issue
Block a user