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 )
master
devi 2 years ago
parent 5f771973a8
commit 50194df24d

@ -24,7 +24,13 @@ private const val MAX_RETRY_COUNT = 5
internal class ReaperComics(context: MangaLoaderContext) : internal class ReaperComics(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.REAPERCOMICS, pageSize = 20) { 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") 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> { override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
if(page > 1) return emptyList() if (page > 1) return emptyList()
val url = buildString { val url = buildString {
append("https://") append("https://")
@ -154,7 +160,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
MangaTag( MangaTag(
key = id, key = id,
title = name.toTitleCase(sourceLocale), title = name.toTitleCase(sourceLocale),
source = source source = source,
) )
} else { } else {
null null
@ -168,6 +174,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
super.onCreateConfig(keys) super.onCreateConfig(keys)
keys.add(userAgentKey) keys.add(userAgentKey)
} }
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", sourceLocale) private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", sourceLocale)
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
@ -177,7 +184,8 @@ internal class ReaperComics(context: MangaLoaderContext) :
val data = response.getJSONArray("data") val data = response.getJSONArray("data")
return manga.copy( return manga.copy(
chapters = data.mapJSONIndexed { index, it -> 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( MangaChapter(
id = it.getLong("id"), id = it.getLong("id"),
name = it.getString("chapter_name"), name = it.getString("chapter_name"),
@ -189,7 +197,7 @@ internal class ReaperComics(context: MangaLoaderContext) :
branch = null, branch = null,
source = source, source = source,
) )
} },
) )
} }

@ -76,15 +76,14 @@ class DoujinDesuParser(context: MangaLoaderContext) :
.requireElementById("archives") .requireElementById("archives")
.selectFirstOrThrow("div.entries") .selectFirstOrThrow("div.entries")
.select(".entry") .select(".entry")
.map { .mapNotNull {
val titleTag = it.selectFirstOrThrow(".metadata > a") val href = it.selectFirst(".metadata > a")?.attr("href") ?: return@mapNotNull null
val relativeUrl = titleTag.attrAsRelativeUrl("href")
Manga( Manga(
id = generateUid(relativeUrl), id = generateUid(href),
title = titleTag.attr("title"), title = it.selectFirst(".metadata > a")?.attr("title").orEmpty(),
altTitle = null, altTitle = null,
url = relativeUrl, url = href,
publicUrl = relativeUrl.toAbsoluteUrl(domain), publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN, rating = RATING_UNKNOWN,
isNsfw = true, isNsfw = true,
coverUrl = it.selectFirst(".thumbnail > img")?.src().orEmpty(), coverUrl = it.selectFirst(".thumbnail > img")?.src().orEmpty(),

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.madara.en package org.koitharu.kotatsu.parsers.site.madara.en
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
@ -10,7 +9,7 @@ import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("FIRESCANS", "FireScans", "en") @MangaSourceParser("FIRESCANS", "FireScans", "en")
internal class FireScans(context: MangaLoaderContext) : 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> { override fun parseMangaList(doc: Document): List<Manga> {
return doc.select("div.row.c-tabs-item__content").ifEmpty { 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> { override suspend fun getAvailableTags(): Set<MangaTag> {
val fullUrl = chapter.url.toAbsoluteUrl(domain) val doc = webClient.httpGet("https://$domain/?s=&post_type=wp-manga").parseHtml()
val doc = webClient.httpGet(fullUrl).parseHtml() return doc.select("form.search-advanced-form div.form-group div.checkbox ").mapNotNullToSet { div ->
val chapterProtector = doc.requireElementById("chapter-protector-data") val key = div.selectFirst("input")?.attr("value") ?: return@mapNotNullToSet null
val chapterProtectorHtml = MangaTag(
context.decodeBase64(chapterProtector.attr("src").removePrefix("data:text/javascript;base64,")) key = key,
.toString(Charsets.UTF_8) title = div.selectFirst("label")?.text() ?: key,
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,
source = source, 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 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.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser 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) : internal class ImmortalUpdates(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.IMMORTALUPDATES, "immortalupdates.com") { MadaraParser(context, MangaParserSource.IMMORTALUPDATES, "immortalupdates.com") {
override val listUrl = "mangas/" 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.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("MORTALSGROOVE", "Mortals Groove", "en") @MangaSourceParser("MORTALSGROOVE", "MortalsGroove", "en")
internal class MortalsGroove(context: MangaLoaderContext) : internal class MortalsGroove(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MORTALSGROOVE, "mortalsgroove.com") { MadaraParser(context, MangaParserSource.MORTALSGROOVE, "mortalsgroove.com") {
override val postReq = true override val postReq = true

@ -1,10 +1,98 @@
package org.koitharu.kotatsu.parsers.site.madara.es 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.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser 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.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.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
@MangaSourceParser("MANGASNOSEKAI", "MangasNoSekai", "es") @MangaSourceParser("MANGASNOSEKAI", "MangasNoSekai", "es")
internal class MangasNoSekai(context: MangaLoaderContext) : 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 package org.koitharu.kotatsu.parsers.site.madara.ja
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.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource 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.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 import java.util.Locale
@MangaSourceParser("MANGAFENXI", "MangaFenxi", "ja") @MangaSourceParser("MANGAFENXI", "MangaFenxi", "ja")
@ -11,4 +24,46 @@ internal class MangaFenxi(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MANGAFENXI, "mangafenxi.net", 40) { MadaraParser(context, MangaParserSource.MANGAFENXI, "mangafenxi.net", 40) {
override val sourceLocale: Locale = Locale.ENGLISH override val sourceLocale: Locale = Locale.ENGLISH
override val tagPrefix = "genres/" 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.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser 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.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("DEMONSECT", "DemonSect", "pt") @MangaSourceParser("PUSSYSUSSYTOONS", "PussySussyToons", "pt", ContentType.HENTAI)
internal class DemonSect(context: MangaLoaderContext) : internal class PussySussyToons(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.DEMONSECT, "dsectcomics.org", 10) 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.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("LUASCANS", "LuaScans", "en") @MangaSourceParser("LUASCANS", "luaComic.net", "en")
internal class LuaScans(context: MangaLoaderContext) : 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 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.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
import java.util.*
@MangaSourceParser("DOUJINKU", "DoujinKu", "id", ContentType.HENTAI) @MangaSourceParser("DOUJINKU", "DoujinKu", "id", ContentType.HENTAI)
internal class DoujinKu(context: MangaLoaderContext) : internal class DoujinKu(context: MangaLoaderContext) :
MangaReaderParser(context, MangaParserSource.DOUJINKU, "doujinku.org", pageSize = 20, searchPageSize = 10) { MangaReaderParser(context, MangaParserSource.DOUJINKU, "doujinku.org", pageSize = 20, searchPageSize = 10) {
override val sourceLocale: Locale = Locale.ENGLISH
override val isTagsExclusionSupported = false override val isTagsExclusionSupported = false
} }

@ -1,11 +1,13 @@
package org.koitharu.kotatsu.parsers.site.mangareader.id 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.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@Broken
@MangaSourceParser("DUNIAKOMIK", "DuniaKomik", "id", ContentType.HENTAI) @MangaSourceParser("DUNIAKOMIK", "DuniaKomik", "id", ContentType.HENTAI)
internal class Duniakomik(context: MangaLoaderContext) : internal class Duniakomik(context: MangaLoaderContext) :
MangaReaderParser(context, MangaParserSource.DUNIAKOMIK, "duniakomik.org", pageSize = 12, searchPageSize = 12) { 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") @MangaSourceParser("KIRYUU", "Kiryuu", "id")
internal class KiryuuParser(context: MangaLoaderContext) : 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 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") @MangaSourceParser("MANGA_SCAN", "MangaScan", "fr")
internal class MangaScan(context: MangaLoaderContext) : 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 imgUpdated = ".jpg"
override val sourceLocale: Locale = Locale.ENGLISH 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> { protected open fun parseMangaList(doc: Document, tagMap: ArrayMap<String, MangaTag>): List<Manga> {
return doc.select("div.items div.item").mapNotNull { item -> 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 absUrl = item.selectFirst("div.image > a")?.attrAsAbsoluteUrlOrNull("href") ?: return@mapNotNull null
val slug = absUrl.substringAfterLast('/') val slug = absUrl.substringAfterLast('/')
val mangaState = when (tooltipElement.selectFirst("div.message_main > p:contains(Tình trạng)")?.ownText()) { val mangaState =
in ongoing -> MangaState.ONGOING when (tooltipElement?.selectFirst("div.message_main > p:contains(Tình trạng)")?.ownText()) {
in finished -> MangaState.FINISHED in ongoing -> MangaState.ONGOING
else -> null in finished -> MangaState.FINISHED
} else -> null
val tagsElement = tooltipElement.selectFirst("div.message_main > p:contains(Thể loại)")?.ownText().orEmpty() }
val tagsElement =
tooltipElement?.selectFirst("div.message_main > p:contains(Thể loại)")?.ownText().orEmpty()
val mangaTags = tagsElement.split(',').mapNotNullToSet { tagMap[it.trim()] } val mangaTags = tagsElement.split(',').mapNotNullToSet { tagMap[it.trim()] }
Manga( Manga(
id = generateUid(slug), id = generateUid(slug),
@ -163,8 +165,8 @@ internal abstract class WpComicsParser(
largeCoverUrl = null, largeCoverUrl = null,
tags = mangaTags, tags = mangaTags,
state = mangaState, state = mangaState,
author = tooltipElement.selectFirst("div.message_main > p:contains(Tác giả)")?.ownText(), author = tooltipElement?.selectFirst("div.message_main > p:contains(Tác giả)")?.ownText(),
description = tooltipElement.selectFirst("div.box_text")?.text(), description = tooltipElement?.selectFirst("div.box_text")?.text(),
chapters = null, chapters = null,
source = source, source = source,
) )
@ -180,8 +182,8 @@ internal abstract class WpComicsParser(
return tagSet return tagSet
} }
private val mutex = Mutex() protected open val mutex = Mutex()
private var tagCache: ArrayMap<String, MangaTag>? = null protected open var tagCache: ArrayMap<String, MangaTag>? = null
protected open suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock { protected open suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock {
tagCache?.let { return@withLock it } tagCache?.let { return@withLock it }

@ -8,8 +8,8 @@ import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
@MangaSourceParser("NETTRUYEN", "NetTruyen", "vi") @MangaSourceParser("NETTRUYEN", "NetTruyen", "vi")
internal class NetTruyen(context: MangaLoaderContext) : 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( 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("&notGenres=")
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=&notGenres=&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.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser 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.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
@MangaSourceParser("NHATTRUYENVN", "NhatTruyenVN", "vi") @MangaSourceParser("NHATTRUYENVN", "NhatTruyenVN", "vi")
internal class NhattruyenVN(context: MangaLoaderContext) : internal class NhatTruyenVN(context: MangaLoaderContext) :
WpComicsParser(context, MangaParserSource.NHATTRUYENVN, "nhattruyenvn.com") 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.+?-c-rw/""".toRegex(), "/w600/")
?.replace("""=s(?!.*=s).+?-c-rw$""".toRegex(), "=w600") ?.replace("""=s(?!.*=s).+?-c-rw$""".toRegex(), "=w600")
} else { } else {
Jsoup.parse(j.getJSONObject("content").getString("\$t")).selectFirstOrThrow("img").attr("src") Jsoup.parse(j.getJSONObject("content").getString("\$t")).selectFirst("img")?.attr("src")
} }
Manga( Manga(
id = generateUid(href), 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,
)
}
}
}
Loading…
Cancel
Save