Rm duniakomik close #952
Fix DoujinKu close #951 Fix DoujinDesu.tv close Add, fix some sources #963 change url close #965 ( the copy was added anyway )
This commit is contained in:
@@ -24,7 +24,13 @@ private const val MAX_RETRY_COUNT = 5
|
||||
internal class ReaperComics(context: MangaLoaderContext) :
|
||||
PagedMangaParser(context, MangaParserSource.REAPERCOMICS, pageSize = 20) {
|
||||
|
||||
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL_DESC)
|
||||
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.ALPHABETICAL,
|
||||
SortOrder.POPULARITY,
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.ALPHABETICAL_DESC,
|
||||
)
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain("reaperscans.com")
|
||||
|
||||
@@ -46,7 +52,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
|
||||
if(page > 1) return emptyList()
|
||||
if (page > 1) return emptyList()
|
||||
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
@@ -154,7 +160,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
|
||||
MangaTag(
|
||||
key = id,
|
||||
title = name.toTitleCase(sourceLocale),
|
||||
source = source
|
||||
source = source,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
@@ -168,6 +174,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
|
||||
super.onCreateConfig(keys)
|
||||
keys.add(userAgentKey)
|
||||
}
|
||||
|
||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", sourceLocale)
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
@@ -177,7 +184,8 @@ internal class ReaperComics(context: MangaLoaderContext) :
|
||||
val data = response.getJSONArray("data")
|
||||
return manga.copy(
|
||||
chapters = data.mapJSONIndexed { index, it ->
|
||||
val chapterUrl = "/series/${it.getJSONObject("series").getString("series_slug")}/${it.getString("chapter_slug")}"
|
||||
val chapterUrl =
|
||||
"/series/${it.getJSONObject("series").getString("series_slug")}/${it.getString("chapter_slug")}"
|
||||
MangaChapter(
|
||||
id = it.getLong("id"),
|
||||
name = it.getString("chapter_name"),
|
||||
@@ -189,7 +197,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -76,15 +76,14 @@ class DoujinDesuParser(context: MangaLoaderContext) :
|
||||
.requireElementById("archives")
|
||||
.selectFirstOrThrow("div.entries")
|
||||
.select(".entry")
|
||||
.map {
|
||||
val titleTag = it.selectFirstOrThrow(".metadata > a")
|
||||
val relativeUrl = titleTag.attrAsRelativeUrl("href")
|
||||
.mapNotNull {
|
||||
val href = it.selectFirst(".metadata > a")?.attr("href") ?: return@mapNotNull null
|
||||
Manga(
|
||||
id = generateUid(relativeUrl),
|
||||
title = titleTag.attr("title"),
|
||||
id = generateUid(href),
|
||||
title = it.selectFirst(".metadata > a")?.attr("title").orEmpty(),
|
||||
altTitle = null,
|
||||
url = relativeUrl,
|
||||
publicUrl = relativeUrl.toAbsoluteUrl(domain),
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = true,
|
||||
coverUrl = it.selectFirst(".thumbnail > img")?.src().orEmpty(),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||
|
||||
import org.json.JSONObject
|
||||
import org.jsoup.nodes.Document
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
@@ -10,7 +9,7 @@ import org.koitharu.kotatsu.parsers.util.*
|
||||
|
||||
@MangaSourceParser("FIRESCANS", "FireScans", "en")
|
||||
internal class FireScans(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.FIRESCANS, "firescans.xyz", 10) {
|
||||
MadaraParser(context, MangaParserSource.FIRESCANS, "firecomics.org", 10) {
|
||||
|
||||
override fun parseMangaList(doc: Document): List<Manga> {
|
||||
return doc.select("div.row.c-tabs-item__content").ifEmpty {
|
||||
@@ -53,37 +52,15 @@ internal class FireScans(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.requireElementById("chapter-protector-data")
|
||||
val chapterProtectorHtml =
|
||||
context.decodeBase64(chapterProtector.attr("src").removePrefix("data:text/javascript;base64,"))
|
||||
.toString(Charsets.UTF_8)
|
||||
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,
|
||||
preview = null,
|
||||
override suspend fun getAvailableTags(): Set<MangaTag> {
|
||||
val doc = webClient.httpGet("https://$domain/?s=&post_type=wp-manga").parseHtml()
|
||||
return doc.select("form.search-advanced-form div.form-group div.checkbox ").mapNotNullToSet { div ->
|
||||
val key = div.selectFirst("input")?.attr("value") ?: return@mapNotNullToSet null
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = div.selectFirst("label")?.text() ?: key,
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||
|
||||
import org.koitharu.kotatsu.parsers.Broken
|
||||
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.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("IMMORTALUPDATES", "Immortal Updates", "en")
|
||||
@Broken // Redirect to @MortalsGroove
|
||||
@MangaSourceParser("IMMORTALUPDATES", "ImmortalUpdates", "en")
|
||||
internal class ImmortalUpdates(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.IMMORTALUPDATES, "immortalupdates.com") {
|
||||
override val listUrl = "mangas/"
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("MORTALSGROOVE", "Mortals Groove", "en")
|
||||
@MangaSourceParser("MORTALSGROOVE", "MortalsGroove", "en")
|
||||
internal class MortalsGroove(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.MORTALSGROOVE, "mortalsgroove.com") {
|
||||
override val postReq = true
|
||||
|
||||
@@ -1,10 +1,98 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.jsoup.nodes.Document
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.exception.ParseException
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
@MangaSourceParser("MANGASNOSEKAI", "MangasNoSekai", "es")
|
||||
internal class MangasNoSekai(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.MANGASNOSEKAI, "mangasnosekai.com")
|
||||
MadaraParser(context, MangaParserSource.MANGASNOSEKAI, "mangasnosekai.com") {
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||
val body = doc.body()
|
||||
val chaptersDeferred = async { loadChapters(manga.url, doc) }
|
||||
manga.copy(
|
||||
tags = doc.body().select("#section-sinopsis a[href*=genre]").mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
|
||||
title = a.text().toTitleCase(),
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
author = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Autor)) p a")?.text()
|
||||
.orEmpty(),
|
||||
description = body.selectFirst("#section-sinopsis p")?.text().orEmpty(),
|
||||
altTitle = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Otros nombres)) p")?.text()
|
||||
.orEmpty(),
|
||||
state = body.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Estado)) p")
|
||||
?.let {
|
||||
when (it.text()) {
|
||||
in ongoing -> MangaState.ONGOING
|
||||
in finished -> MangaState.FINISHED
|
||||
in abandoned -> MangaState.ABANDONED
|
||||
in paused -> MangaState.PAUSED
|
||||
else -> null
|
||||
}
|
||||
},
|
||||
chapters = chaptersDeferred.await(),
|
||||
)
|
||||
}
|
||||
|
||||
// todo take other pages
|
||||
override suspend fun loadChapters(mangaUrl: String, document: Document): List<MangaChapter> {
|
||||
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
|
||||
return document.select("div.container-capitulos div.contenedor-capitulo-miniatura")
|
||||
.mapChapters(reversed = true) { i, div ->
|
||||
val a = div.selectFirst("a")
|
||||
val href = a?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link is missing")
|
||||
val link = href + stylePage
|
||||
val dateText = div.selectFirst("a div.chapter-text")?.text()
|
||||
val name = div.selectFirst("a div.text-sm")?.text() ?: a.ownText()
|
||||
MangaChapter(
|
||||
id = generateUid(href),
|
||||
url = link,
|
||||
name = name,
|
||||
number = i + 1f,
|
||||
volume = 0,
|
||||
branch = null,
|
||||
uploadDate = parseChapterDate(
|
||||
dateFormat,
|
||||
dateText,
|
||||
),
|
||||
scanlator = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
||||
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||
val root = doc.body().selectFirst("div.reading-content")
|
||||
?: 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,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.id
|
||||
|
||||
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.madara.MadaraParser
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("LUMOSKOMIK", "LumosKomik", "id")
|
||||
internal class LumosKomik(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.LUMOSKOMIK, "lumoskomik.com") {
|
||||
override val tagPrefix = "genre/"
|
||||
override val listUrl = "komik/"
|
||||
override val datePattern = "dd MMMM yyyy"
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
}
|
||||
@@ -1,9 +1,22 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.ja
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrlOrNull
|
||||
import org.koitharu.kotatsu.parsers.util.domain
|
||||
import org.koitharu.kotatsu.parsers.util.generateUid
|
||||
import org.koitharu.kotatsu.parsers.util.host
|
||||
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
||||
import org.koitharu.kotatsu.parsers.util.parseFailed
|
||||
import org.koitharu.kotatsu.parsers.util.removeSuffix
|
||||
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
|
||||
import org.koitharu.kotatsu.parsers.util.toTitleCase
|
||||
import java.util.Locale
|
||||
|
||||
@MangaSourceParser("MANGAFENXI", "MangaFenxi", "ja")
|
||||
@@ -11,4 +24,46 @@ internal class MangaFenxi(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.MANGAFENXI, "mangafenxi.net", 40) {
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
override val tagPrefix = "genres/"
|
||||
|
||||
override fun parseMangaList(doc: Document): List<Manga> {
|
||||
return doc.select("div.row.c-tabs-item__content").ifEmpty {
|
||||
doc.select("div.page-item-detail")
|
||||
}.map { div ->
|
||||
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
|
||||
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||
coverUrl = div.selectFirst("img")?.attr("src")?.replace("-193x278", "").orEmpty(),
|
||||
title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4")
|
||||
?: div.selectFirst(".manga-name") ?: div.selectFirst(".post-title"))?.text().orEmpty(),
|
||||
altTitle = null,
|
||||
rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||
tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
|
||||
title = a.text().ifEmpty { return@mapNotNullToSet null }.toTitleCase(),
|
||||
source = source,
|
||||
)
|
||||
}.orEmpty(),
|
||||
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
|
||||
state = when (
|
||||
summary?.selectFirst(".mg_status")
|
||||
?.selectFirst(".summary-content")
|
||||
?.ownText()
|
||||
.orEmpty()
|
||||
) {
|
||||
in ongoing -> MangaState.ONGOING
|
||||
in finished -> MangaState.FINISHED
|
||||
in abandoned -> MangaState.ABANDONED
|
||||
in paused -> MangaState.PAUSED
|
||||
in upcoming -> MangaState.UPCOMING
|
||||
else -> null
|
||||
},
|
||||
source = source,
|
||||
isNsfw = isNsfwSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.pt
|
||||
|
||||
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.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("LIMITEDTIMEPOJECT", "LimitedTimePoject", "pt")
|
||||
internal class LimitedTimePoject(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.LIMITEDTIMEPOJECT, "limitedtimeproject.com", 10) {
|
||||
override val listUrl = "manhwa/"
|
||||
override val tagPrefix = "manhwa-genero/"
|
||||
override val datePattern = "dd 'de' MMMMM 'de' yyyy"
|
||||
}
|
||||
@@ -2,9 +2,10 @@ package org.koitharu.kotatsu.parsers.site.madara.pt
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("DEMONSECT", "DemonSect", "pt")
|
||||
internal class DemonSect(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.DEMONSECT, "dsectcomics.org", 10)
|
||||
@MangaSourceParser("PUSSYSUSSYTOONS", "PussySussyToons", "pt", ContentType.HENTAI)
|
||||
internal class PussySussyToons(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.PUSSYSUSSYTOONS, "pussy.sussytoons.com")
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.tr
|
||||
|
||||
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.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("MANGAOKUSANA", "MangaOkusana", "tr")
|
||||
internal class MangaOkusana(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaParserSource.MANGAOKUSANA, "mangaokusana.com") {
|
||||
override val datePattern = "dd MMMM yyyy"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("ALTAYSCANS", "AltayScans", "en")
|
||||
internal class AltayScans(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.ALTAYSCANS, "altayscans.com", pageSize = 20, searchPageSize = 10) {
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("LUACOMIC_COM", "luaComic.com", "en")
|
||||
internal class LuaComicCom(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.LUACOMIC_COM, "luacomic.com", pageSize = 20, searchPageSize = 10) {
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("LUASCANS", "LuaScans", "en")
|
||||
@MangaSourceParser("LUASCANS", "luaComic.net", "en")
|
||||
internal class LuaScans(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.LUASCANS, "luacomic.com", pageSize = 20, searchPageSize = 10) {
|
||||
MangaReaderParser(context, MangaParserSource.LUASCANS, "luacomic.net", pageSize = 20, searchPageSize = 10) {
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("DOUJINKU", "DoujinKu", "id", ContentType.HENTAI)
|
||||
internal class DoujinKu(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.DOUJINKU, "doujinku.org", pageSize = 20, searchPageSize = 10) {
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.id
|
||||
|
||||
import org.koitharu.kotatsu.parsers.Broken
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
|
||||
@Broken
|
||||
@MangaSourceParser("DUNIAKOMIK", "DuniaKomik", "id", ContentType.HENTAI)
|
||||
internal class Duniakomik(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.DUNIAKOMIK, "duniakomik.org", pageSize = 12, searchPageSize = 12) {
|
||||
|
||||
@@ -7,6 +7,6 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("KIRYUU", "Kiryuu", "id")
|
||||
internal class KiryuuParser(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.KIRYUU, "kiryuu.id", pageSize = 30, searchPageSize = 10) {
|
||||
MangaReaderParser(context, MangaParserSource.KIRYUU, "kiryuu.org", pageSize = 30, searchPageSize = 10) {
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.id
|
||||
|
||||
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.mangareader.MangaReaderParser
|
||||
import java.util.Locale
|
||||
|
||||
@MangaSourceParser("KOMIKINDO_MOE", "KomikIndo.moe", "id")
|
||||
internal class KomikIndo(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.KOMIKINDO_MOE, "komikindo.moe", pageSize = 20, searchPageSize = 10) {
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.id
|
||||
|
||||
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.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("MANHWAKU", "Manhwaku", "id")
|
||||
internal class Manhwaku(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.MANHWAKU, "manhwaku.id", pageSize = 20, searchPageSize = 10)
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.pt
|
||||
|
||||
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.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("DEMONSECT", "DemonSect", "pt")
|
||||
internal class DemonSect(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.DEMONSECT, "dsectcomics.org", pageSize = 20, searchPageSize = 10) {
|
||||
override val listUrl = "/comics"
|
||||
override val isTagsExclusionSupported = false
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.tr
|
||||
|
||||
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.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("CULTURESUBS", "CultureSubs", "tr")
|
||||
internal class CultureSubs(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaParserSource.CULTURESUBS, "culturesubs.com", pageSize = 20, searchPageSize = 10) {
|
||||
override val isMultipleTagsSupported = false
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import java.util.*
|
||||
|
||||
@MangaSourceParser("MANGA_SCAN", "MangaScan", "fr")
|
||||
internal class MangaScan(context: MangaLoaderContext) :
|
||||
MmrcmsParser(context, MangaParserSource.MANGA_SCAN, "mangascan-fr.co") {
|
||||
MmrcmsParser(context, MangaParserSource.MANGA_SCAN, "mangascan-fr.net") {
|
||||
override val imgUpdated = ".jpg"
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mmrcms.fr
|
||||
|
||||
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.mmrcms.MmrcmsParser
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("MANGASCANFR", "MangaScanFr", "fr")
|
||||
internal class MangaScanFr(context: MangaLoaderContext) :
|
||||
MmrcmsParser(context, MangaParserSource.MANGASCANFR, "mangascan-fr.net") {
|
||||
override val sourceLocale: Locale = Locale.ENGLISH
|
||||
override val imgUpdated = ".jpg"
|
||||
}
|
||||
@@ -141,15 +141,17 @@ internal abstract class WpComicsParser(
|
||||
|
||||
protected open fun parseMangaList(doc: Document, tagMap: ArrayMap<String, MangaTag>): List<Manga> {
|
||||
return doc.select("div.items div.item").mapNotNull { item ->
|
||||
val tooltipElement = item.selectFirst("div.box_tootip") ?: return@mapNotNull null
|
||||
val tooltipElement = item.selectFirst("div.box_tootip")
|
||||
val absUrl = item.selectFirst("div.image > a")?.attrAsAbsoluteUrlOrNull("href") ?: return@mapNotNull null
|
||||
val slug = absUrl.substringAfterLast('/')
|
||||
val mangaState = when (tooltipElement.selectFirst("div.message_main > p:contains(Tình trạng)")?.ownText()) {
|
||||
in ongoing -> MangaState.ONGOING
|
||||
in finished -> MangaState.FINISHED
|
||||
else -> null
|
||||
}
|
||||
val tagsElement = tooltipElement.selectFirst("div.message_main > p:contains(Thể loại)")?.ownText().orEmpty()
|
||||
val mangaState =
|
||||
when (tooltipElement?.selectFirst("div.message_main > p:contains(Tình trạng)")?.ownText()) {
|
||||
in ongoing -> MangaState.ONGOING
|
||||
in finished -> MangaState.FINISHED
|
||||
else -> null
|
||||
}
|
||||
val tagsElement =
|
||||
tooltipElement?.selectFirst("div.message_main > p:contains(Thể loại)")?.ownText().orEmpty()
|
||||
val mangaTags = tagsElement.split(',').mapNotNullToSet { tagMap[it.trim()] }
|
||||
Manga(
|
||||
id = generateUid(slug),
|
||||
@@ -163,8 +165,8 @@ internal abstract class WpComicsParser(
|
||||
largeCoverUrl = null,
|
||||
tags = mangaTags,
|
||||
state = mangaState,
|
||||
author = tooltipElement.selectFirst("div.message_main > p:contains(Tác giả)")?.ownText(),
|
||||
description = tooltipElement.selectFirst("div.box_text")?.text(),
|
||||
author = tooltipElement?.selectFirst("div.message_main > p:contains(Tác giả)")?.ownText(),
|
||||
description = tooltipElement?.selectFirst("div.box_text")?.text(),
|
||||
chapters = null,
|
||||
source = source,
|
||||
)
|
||||
@@ -180,8 +182,8 @@ internal abstract class WpComicsParser(
|
||||
return tagSet
|
||||
}
|
||||
|
||||
private val mutex = Mutex()
|
||||
private var tagCache: ArrayMap<String, MangaTag>? = null
|
||||
protected open val mutex = Mutex()
|
||||
protected open var tagCache: ArrayMap<String, MangaTag>? = null
|
||||
|
||||
protected open suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock {
|
||||
tagCache?.let { return@withLock it }
|
||||
|
||||
@@ -8,8 +8,8 @@ import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
|
||||
@MangaSourceParser("NETTRUYEN", "NetTruyen", "vi")
|
||||
internal class NetTruyen(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaParserSource.NETTRUYEN, "nettruyenaa.com") {
|
||||
WpComicsParser(context, MangaParserSource.NETTRUYEN, "www.nettruyenupp.com", 44) {
|
||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
||||
"nettruyenssr.com",
|
||||
"www.nettruyenupp.com", "nettruyenaa.com", "nettruyenx.com",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package org.koitharu.kotatsu.parsers.site.wpcomics.vi
|
||||
|
||||
import androidx.collection.ArrayMap
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaListFilter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.util.EnumSet
|
||||
|
||||
@MangaSourceParser("NETTRUYENSSR", "NetTruyenSSR", "vi")
|
||||
internal class NetTruyenSSR(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaParserSource.NETTRUYENSSR, "nettruyenssr.com", 20) {
|
||||
|
||||
override val isMultipleTagsSupported = true
|
||||
override val isTagsExclusionSupported = true
|
||||
override val listUrl = "/tim-kiem-nang-cao"
|
||||
override val availableStates: Set<MangaState> =
|
||||
EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED)
|
||||
override val availableSortOrders: Set<SortOrder> = EnumSet.allOf(SortOrder::class.java)
|
||||
|
||||
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
|
||||
val response =
|
||||
when (filter) {
|
||||
is MangaListFilter.Search -> {
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append("/search")
|
||||
append('/')
|
||||
append(page.toString())
|
||||
append('/')
|
||||
append("?keyword=")
|
||||
append(filter.query.urlEncoded())
|
||||
}
|
||||
|
||||
val result = runCatchingCancellable { webClient.httpGet(url) }
|
||||
val exception = result.exceptionOrNull()
|
||||
if (exception is NotFoundException) {
|
||||
return emptyList()
|
||||
}
|
||||
result.getOrThrow()
|
||||
}
|
||||
|
||||
is MangaListFilter.Advanced -> {
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append(listUrl)
|
||||
|
||||
append('/')
|
||||
append(page.toString())
|
||||
append('/')
|
||||
|
||||
val tagQuery = filter.tags.joinToString(",") { it.key }
|
||||
append("?genres=")
|
||||
append(tagQuery)
|
||||
|
||||
val tagQueryExclude = filter.tagsExclude.joinToString(",") { it.key }
|
||||
append("¬Genres=")
|
||||
append(tagQueryExclude)
|
||||
|
||||
append("&sex=All")
|
||||
|
||||
filter.states.oneOrThrowIfMany()?.let {
|
||||
append("&status=")
|
||||
append(
|
||||
when (it) {
|
||||
MangaState.ONGOING -> "on-going"
|
||||
MangaState.FINISHED -> "completed"
|
||||
MangaState.PAUSED -> "on-hold"
|
||||
MangaState.ABANDONED -> "canceled"
|
||||
else -> "-1"
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
append("&chapter_count=0")
|
||||
|
||||
append("&sort=")
|
||||
append(
|
||||
when (filter.sortOrder) {
|
||||
SortOrder.UPDATED -> "latest-updated"
|
||||
SortOrder.POPULARITY -> "views"
|
||||
SortOrder.NEWEST -> "new"
|
||||
SortOrder.RATING -> "score"
|
||||
SortOrder.ALPHABETICAL -> "az"
|
||||
SortOrder.ALPHABETICAL_DESC -> "za"
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
webClient.httpGet(url)
|
||||
}
|
||||
|
||||
null -> {
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append(listUrl)
|
||||
append('/')
|
||||
append(page.toString())
|
||||
append('/')
|
||||
append("?genres=¬Genres=&sex=All&status=&chapter_count=0&sort=latest-updated")
|
||||
}
|
||||
webClient.httpGet(url)
|
||||
}
|
||||
}
|
||||
|
||||
val tagMap = getOrCreateTagMap()
|
||||
return parseMangaList(response.parseHtml(), tagMap)
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock {
|
||||
tagCache?.let { return@withLock it }
|
||||
val doc = webClient.httpGet(listUrl.toAbsoluteUrl(domain)).parseHtml()
|
||||
val tagItems = doc.select("div.genre-item")
|
||||
val result = ArrayMap<String, MangaTag>(tagItems.size)
|
||||
for (item in tagItems) {
|
||||
val title = item.text()
|
||||
val key = item.selectFirstOrThrow("span").attr("data-id")
|
||||
if (key.isNotEmpty() && title.isNotEmpty()) {
|
||||
result[title] = MangaTag(title = title, key = key, source = source)
|
||||
}
|
||||
}
|
||||
tagCache = result
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,14 @@ package org.koitharu.kotatsu.parsers.site.wpcomics.vi
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
|
||||
@MangaSourceParser("NHATTRUYENVN", "NhatTruyenVN", "vi")
|
||||
internal class NhattruyenVN(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaParserSource.NHATTRUYENVN, "nhattruyenvn.com")
|
||||
internal class NhatTruyenVN(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaParserSource.NHATTRUYENVN, "nhattruyenvn.com", 36) {
|
||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
||||
"nhattruyenvn.com", "www.nhattruyenss.net",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ internal abstract class ZeistMangaParser(
|
||||
?.replace("""/s.+?-c-rw/""".toRegex(), "/w600/")
|
||||
?.replace("""=s(?!.*=s).+?-c-rw$""".toRegex(), "=w600")
|
||||
} else {
|
||||
Jsoup.parse(j.getJSONObject("content").getString("\$t")).selectFirstOrThrow("img").attr("src")
|
||||
Jsoup.parse(j.getJSONObject("content").getString("\$t")).selectFirst("img")?.attr("src")
|
||||
}
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.koitharu.kotatsu.parsers.site.zeistmanga.id
|
||||
|
||||
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.zeistmanga.ZeistMangaParser
|
||||
|
||||
|
||||
@MangaSourceParser("MAGERIN", "Magerin", "id")
|
||||
internal class Magerin(context: MangaLoaderContext) :
|
||||
ZeistMangaParser(context, MangaParserSource.MAGERIN, "www.magerin.com")
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.koitharu.kotatsu.parsers.site.zeistmanga.tr
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
|
||||
import org.koitharu.kotatsu.parsers.util.domain
|
||||
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
||||
import org.koitharu.kotatsu.parsers.util.parseHtml
|
||||
import org.koitharu.kotatsu.parsers.util.requireElementById
|
||||
|
||||
@MangaSourceParser("HYPERIONSCANS", "HyperionScans", "tr")
|
||||
internal class HyperionScans(context: MangaLoaderContext) :
|
||||
ZeistMangaParser(context, MangaParserSource.HYPERIONSCANS, "www.hyperionscans.site") {
|
||||
override val sateOngoing: String = "Devam Ediyor"
|
||||
override val sateFinished: String = "Tamamlandı"
|
||||
override val sateAbandoned: String = "Güncel"
|
||||
|
||||
override suspend fun getAvailableTags(): Set<MangaTag> {
|
||||
val doc = webClient.httpGet("https://$domain").parseHtml()
|
||||
return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet {
|
||||
MangaTag(
|
||||
key = it.attr("href").substringBefore("?").substringAfterLast('/'),
|
||||
title = it.text(),
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user