[MadaraParser] auto fix url manga on

fix query ( withoutAjax
[MangaPark] fix close #1152
close #1145
[AdultWebtoon] fix close #1139
[ComicExtra] change type close #1130
[liliana] fix and add ManhuaPlusorg, Raw1001, MangaSect, MangaKoma01, ManhuaGold
master
devi 2 years ago
parent 3e102e9d69
commit 72564f5449

@ -1 +1 @@
total: 1122 total: 1127

@ -82,6 +82,7 @@ public class Manga(
@InternalParsersApi @InternalParsersApi
public fun copy( public fun copy(
url: String = this.url,
title: String = this.title, title: String = this.title,
altTitle: String? = this.altTitle, altTitle: String? = this.altTitle,
publicUrl: String = this.publicUrl, publicUrl: String = this.publicUrl,

@ -236,18 +236,24 @@ internal class MangaPark(context: MangaLoaderContext) :
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
return when { return when {
WordSet("second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis WordSet("second")
WordSet("minute", "minutes", "mins", "min").anyWordIn(date) -> cal.apply { .anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
add(
Calendar.MINUTE, WordSet("minute", "minutes", "mins", "min")
-number, .anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
)
}.timeInMillis WordSet("hour", "hours")
.anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
WordSet("day", "days")
.anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
WordSet("month", "months")
.anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
WordSet("year")
.anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
WordSet("hour", "hours").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
WordSet("day", "days").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
else -> 0 else -> 0
} }
} }
@ -255,18 +261,23 @@ internal class MangaPark(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
val script = if (doc.selectFirst("script:containsData(comic-)") != null) { val id = chapter.url.removeSuffix('/').substringAfterLast('/').substringBefore('-')
doc.selectFirstOrThrow("script:containsData(comic-)").data() val s = doc.selectFirstOrThrow("script:containsData($id)").data()
.substringAfterLast("\"comic-")
val script = if (s.contains("\"comic-")) {
s.substringAfterLast("\"comic-")
} else { } else {
doc.selectFirstOrThrow("script:containsData(manga-)").data() s.substringAfterLast("\"manga-")
.substringAfterLast("\"manga-")
} }
return Regex("\"(https?:.+?)\"") return Regex("\"(https?:.+?)\"")
.findAll(script) .findAll(script)
.mapNotNullTo(ArrayList()) { .mapIndexedNotNullTo(ArrayList()) { i, it ->
val url = it.groupValues.getOrNull(1) ?: return@mapNotNullTo null val url = it.groupValues.getOrNull(1) ?: return@mapIndexedNotNullTo null
if (url.contains("/comic/") || url.contains("/manga/") || url.contains("/image/mpup/")) { if (url.contains(".jpg") || url.contains(".jpeg") || url.contains(".jfif") || url.contains(".pjpeg") ||
url.contains(".pjp") || url.contains(".png") || url.contains(".webp") || url.contains(".avif") ||
url.contains(".gif")
) {
MangaPage( MangaPage(
id = generateUid(url), id = generateUid(url),
url = url, url = url,
@ -274,7 +285,7 @@ internal class MangaPark(context: MangaLoaderContext) :
source = source, source = source,
) )
} else { } else {
return@mapNotNullTo null return@mapIndexedNotNullTo null
} }
} }
} }

@ -11,7 +11,7 @@ import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@MangaSourceParser("COMICEXTRA", "ComicExtra", "en") @MangaSourceParser("COMICEXTRA", "ComicExtra", "en", ContentType.COMICS)
internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.COMICEXTRA, 25) { internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.COMICEXTRA, 25) {
override val configKeyDomain = ConfigKey.Domain("comixextra.com") override val configKeyDomain = ConfigKey.Domain("comixextra.com")

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.parsers.site.liliana package org.koitharu.kotatsu.parsers.site.liliana
import androidx.collection.scatterSetOf
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -27,16 +28,26 @@ internal abstract class LilianaParser(
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED, SortOrder.UPDATED,
SortOrder.POPULARITY, SortOrder.POPULARITY,
SortOrder.POPULARITY_MONTH,
SortOrder.POPULARITY_WEEK,
SortOrder.POPULARITY_TODAY,
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
SortOrder.ALPHABETICAL_DESC,
SortOrder.NEWEST, SortOrder.NEWEST,
SortOrder.RATING_ASC, SortOrder.NEWEST_ASC,
SortOrder.RATING,
)
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions(
availableTags = getAvailableTags(),
availableStates = setOf(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED),
) )
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true, isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true,
) )
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
@ -45,32 +56,36 @@ internal abstract class LilianaParser(
append(domain) append(domain)
when { when {
!filter.query.isNullOrEmpty() -> { !filter.query.isNullOrEmpty() -> {
append("/search") append("/search/")
append("?keyword=") append(page)
append("/?keyword=")
append(filter.query.urlEncoded()) append(filter.query.urlEncoded())
} }
else -> { else -> {
append("/filter") append("/filter/")
}
}
append("/")
append(page) append(page)
append("/")
append("/?sort=")
when (order) { when (order) {
SortOrder.UPDATED -> append("?sort=latest-updated") SortOrder.UPDATED -> append("latest-updated")
SortOrder.POPULARITY -> append("?sort=views") SortOrder.POPULARITY -> append("views")
SortOrder.ALPHABETICAL -> append("?sort=az") SortOrder.POPULARITY_MONTH -> append("views_month")
SortOrder.NEWEST -> append("?sort=new") SortOrder.POPULARITY_WEEK -> append("views_week")
SortOrder.RATING_ASC -> append("?sort=score") SortOrder.POPULARITY_TODAY -> append("views_day")
else -> append("?sort=default") SortOrder.ALPHABETICAL -> append("az")
SortOrder.ALPHABETICAL_DESC -> append("za")
SortOrder.NEWEST -> append("new")
SortOrder.NEWEST_ASC -> append("old")
SortOrder.RATING -> append("score")
else -> append("latest-updated")
} }
filter.tags.forEach { tag ->
append("&genres=") append("&genres=")
append(tag.key) filter.tags.joinTo(this, ",") { it.key }
}
append("&notGenres=")
filter.tagsExclude.joinTo(this, ",") { it.key }
if (filter.states.isNotEmpty()) { if (filter.states.isNotEmpty()) {
append("&status=") append("&status=")
@ -85,6 +100,8 @@ internal abstract class LilianaParser(
) )
} }
} }
}
}
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
return doc.select("div#main div.grid > div").map { parseSearchManga(it) } return doc.select("div#main div.grid > div").map { parseSearchManga(it) }
@ -104,10 +121,30 @@ internal abstract class LilianaParser(
author = null, author = null,
state = null, state = null,
source = source, source = source,
isNsfw = false, isNsfw = isNsfwSource,
) )
} }
@JvmField
protected val ongoing = scatterSetOf(
"on-going", "đang tiến hành", "進行中",
)
@JvmField
protected val finished = scatterSetOf(
"completed", "hoàn thành", "完了",
)
@JvmField
protected val abandoned = scatterSetOf(
"canceled", "đã huỷ bỏ", "キャンセル",
)
@JvmField
protected val paused = scatterSetOf(
"on-hold", "tạm dừng", "一時停止",
)
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
@ -123,26 +160,27 @@ internal abstract class LilianaParser(
author = doc.selectFirst("div.y6x11p i.fas.fa-user + span.dt")?.text()?.takeUnless { author = doc.selectFirst("div.y6x11p i.fas.fa-user + span.dt")?.text()?.takeUnless {
it.equals("updating", true) it.equals("updating", true)
}, },
state = when (doc.selectFirst("div.y6x11p i.fas.fa-rss + span.dt")?.text()?.lowercase()) { state = when (doc.selectFirst("div.y6x11p i.fas.fa-rss + span.dt")?.text()?.lowercase().orEmpty()) {
"on-going", "đang tiến hành", "進行中" -> MangaState.ONGOING in ongoing -> MangaState.ONGOING
"completed", "hoàn thành", "完了" -> MangaState.FINISHED in finished -> MangaState.FINISHED
"on-hold", "tạm dừng", "一時停止" -> MangaState.PAUSED in paused -> MangaState.PAUSED
"canceled", "đã huỷ bỏ", "キャンセル" -> MangaState.ABANDONED in abandoned -> MangaState.ABANDONED
else -> null else -> null
}, },
chapters = doc.select("ul > li.chapter").mapChapters(reversed = true) { i, element -> chapters = doc.select("ul > li.chapter").mapChapters(reversed = true) { i, element ->
val href = element.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = element.selectFirstOrThrow("a").attrAsRelativeUrl("href")
MangaChapter( MangaChapter(
id = generateUid(href), id = generateUid(href),
name = element.selectFirst("a")?.text().orEmpty(), name = element.selectFirst("a")?.text() ?: "Chapter : ${i + 1f}",
number = i + 1f, number = i + 1f,
volume = 0,
url = href, url = href,
scanlator = null, scanlator = null,
uploadDate = element.selectFirst("time[datetime]")?.attr("datetime")?.toLongOrNull()?.times(1000) uploadDate = element.selectFirst("time[datetime]")?.attr("datetime")?.toLongOrNull()?.times(1000)
?: 0L, ?: 0L,
branch = null, branch = null,
source = source, source = source,
volume = 0,
) )
}, },
) )
@ -170,13 +208,10 @@ internal abstract class LilianaParser(
throw Exception(responseJson.optString("msg")) throw Exception(responseJson.optString("msg"))
} }
val pageListHtml = responseJson.getString("html") val pageListDoc = responseJson.getString("html").let(Jsoup::parse)
val pageListDoc = Jsoup.parse(pageListHtml)
return pageListDoc.select("div.iv-card").mapIndexed { index, div ->
val img = div.selectFirst("img")
val url = img?.attr("data-src") ?: img?.attr("src") ?: throw Exception("Failed to get image url")
return pageListDoc.select("div").map {
val url = it.selectFirstOrThrow("img").attr("src")
MangaPage( MangaPage(
id = generateUid(url), id = generateUid(url),
url = url, url = url,
@ -196,9 +231,4 @@ internal abstract class LilianaParser(
) )
} }
} }
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions(
availableTags = getAvailableTags(),
availableStates = setOf(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED),
)
} }

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.liliana.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
@MangaSourceParser("MANGASECT", "MangaSect", "en")
internal class MangaSect(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.MANGASECT, "mangasect.net")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.liliana.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
@MangaSourceParser("MANHUAGOLD", "ManhuaGold", "en")
internal class ManhuaGold(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.MANHUAGOLD, "manhuagold.top")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.liliana.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
@MangaSourceParser("MANHUAPLUSORG", "ManhuaPlus.org", "en")
internal class ManhuaPlusOrg(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.MANHUAPLUSORG, "manhuaplus.org")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.liliana.ja
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
@MangaSourceParser("MANGAKOMA01", "MangaKoma01", "ja")
internal class MangaKoma01(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.MANGAKOMA01, "mangakoma01.net")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.liliana.ja
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
@MangaSourceParser("RAW1001", "Raw1001", "ja")
internal class Raw1001(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.RAW1001, "raw1001.net")

@ -233,7 +233,9 @@ internal abstract class MadaraParser(
} }
append("/?s=") append("/?s=")
append(filter.query?.urlEncoded()) filter.query?.let {
append(filter.query.urlEncoded())
}
append("&post_type=wp-manga") append("&post_type=wp-manga")
@ -536,10 +538,10 @@ internal abstract class MadaraParser(
val fullUrl = manga.url.toAbsoluteUrl(domain) val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
val href = doc.selectFirst("head meta[property='og:url']")?.attr("content")?.toRelativeUrl(domain) ?: manga.url
val testCheckAsync = doc.select(selectTestAsync) val testCheckAsync = doc.select(selectTestAsync)
val chaptersDeferred = if (testCheckAsync.isNullOrEmpty()) { val chaptersDeferred = if (testCheckAsync.isNullOrEmpty()) {
async { loadChapters(manga.url, doc) } async { loadChapters(href, doc) }
} else { } else {
async { getChapters(manga, doc) } async { getChapters(manga, doc) }
} }
@ -561,6 +563,8 @@ internal abstract class MadaraParser(
val alt = doc.body().select(selectAlt).firstOrNull()?.tableValue()?.text()?.trim() val alt = doc.body().select(selectAlt).firstOrNull()?.tableValue()?.text()?.trim()
manga.copy( manga.copy(
url = href,
publicUrl = href.toAbsoluteUrl(domain),
tags = doc.body().select(selectGenre).mapNotNullToSet { a -> tags = doc.body().select(selectGenre).mapNotNullToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'), key = a.attr("href").removeSuffix("/").substringAfterLast('/'),

@ -5,13 +5,17 @@ import okhttp3.Headers
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Base64
@MangaSourceParser("ADULT_WEBTOON", "AdultWebtoon", "en", ContentType.HENTAI) @MangaSourceParser("ADULT_WEBTOON", "AdultWebtoon", "en", ContentType.HENTAI)
internal class AdultWebtoon(context: MangaLoaderContext) : internal class AdultWebtoon(context: MangaLoaderContext) :
@ -150,6 +154,68 @@ internal class AdultWebtoon(context: MangaLoaderContext) :
} }
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val chapterProtector = doc.getElementById("chapter-protector-data")
if (chapterProtector == null) {
throw if (doc.selectFirst(selectRequiredLogin) != null) {
AuthRequiredException(source)
} else {
val root = doc.body().selectFirst(selectBodyPage) ?: throw ParseException(
"No image found, try to log in",
fullUrl,
)
return root.select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
MangaPage(
id = generateUid(url),
url = url.replace("http:", "https:"),
preview = null,
source = source,
)
}
}
} else {
val chapterProtectorHtml = chapterProtector.attr("src")
.takeIf { it.startsWith("data:text/javascript;base64,") }
?.substringAfter("data:text/javascript;base64,")
?.let {
Base64.getDecoder().decode(it).decodeToString()
}
?: chapterProtector.html()
val password = chapterProtectorHtml.substringAfter("wpmangaprotectornonce='").substringBefore("';")
val chapterData = JSONObject(
chapterProtectorHtml.substringAfter("chapter_data='").substringBefore("';").replace("\\/", "/"),
)
val unsaltedCiphertext = context.decodeBase64(chapterData.getString("ct"))
val salt = chapterData.getString("s").toString().decodeHex()
val ciphertext = "Salted__".toByteArray(Charsets.UTF_8) + salt + unsaltedCiphertext
val rawImgArray = CryptoAES(context).decrypt(context.encodeBase64(ciphertext), password)
val imgArrayString = rawImgArray.filterNot { c -> c == '[' || c == ']' || c == '\\' || c == '"' }
return imgArrayString.split(",").map { url ->
MangaPage(
id = generateUid(url),
url = url.replace("http:", "https:"),
preview = null,
source = source,
)
}
}
}
private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
private suspend fun makeRequest(url: String, payload: RequestBody, headers: Headers): Document { private suspend fun makeRequest(url: String, payload: RequestBody, headers: Headers): Document {
var retryCount = 0 var retryCount = 0
val backoffDelay = 2000L // Initial delay (milliseconds) val backoffDelay = 2000L // Initial delay (milliseconds)

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.vi package org.koitharu.kotatsu.parsers.site.vi
import androidx.collection.ArrayMap
import org.json.JSONArray import org.json.JSONArray
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -53,7 +52,8 @@ internal class BlogTruyenParser(context: MangaLoaderContext) :
filter.tags.isNotEmpty() -> { filter.tags.isNotEmpty() -> {
filter.tags.oneOrThrowIfMany()?.let { tag -> filter.tags.oneOrThrowIfMany()?.let { tag ->
val categoryAjax = "https://${domain}/ajax/Category/AjaxLoadMangaByCategory?id=${tag.key}&orderBy=5&p=$page" val categoryAjax =
"https://${domain}/ajax/Category/AjaxLoadMangaByCategory?id=${tag.key}&orderBy=5&p=$page"
val listContent = webClient.httpGet(categoryAjax).parseHtml().selectFirst("div.list") val listContent = webClient.httpGet(categoryAjax).parseHtml().selectFirst("div.list")
parseMangaList(listContent) ?: emptyList() parseMangaList(listContent) ?: emptyList()
} ?: emptyList() } ?: emptyList()
@ -248,6 +248,6 @@ internal class BlogTruyenParser(context: MangaLoaderContext) :
MangaTag("Soft Yuri", "36", source), MangaTag("Soft Yuri", "36", source),
MangaTag("Live action", "16", source), MangaTag("Live action", "16", source),
MangaTag("Tu chân - tu tiên", "66", source), MangaTag("Tu chân - tu tiên", "66", source),
MangaTag("Ngôn tình", "65", source) MangaTag("Ngôn tình", "65", source),
) )
} }

@ -17,7 +17,7 @@ import org.koitharu.kotatsu.parsers.Broken
@Broken @Broken
@MangaSourceParser("BLOGTRUYENVN", "BlogTruyenVN", "vi") @MangaSourceParser("BLOGTRUYENVN", "BlogTruyenVN", "vi")
internal class BlogTruyenVNParser(context: MangaLoaderContext) : internal class BlogTruyenVN(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.BLOGTRUYENVN, pageSize = 20) { PagedMangaParser(context, MangaParserSource.BLOGTRUYENVN, pageSize = 20) {
override val configKeyDomain: ConfigKey.Domain override val configKeyDomain: ConfigKey.Domain

Loading…
Cancel
Save