Add ZeistMangaParser and sources

add urldecode
ul VfScan
devi 2 years ago
parent 4d02dc0d12
commit a35c2f3394

@ -35,13 +35,14 @@ import java.util.EnumSet
import java.util.Locale
@MangaSourceParser("NINENINENINEHENTAI", "999Hentai", type = ContentType.HENTAI)
internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.NINENINENINEHENTAI, size), Interceptor {
internal class NineNineNineHentaiParser(context: MangaLoaderContext) :
PagedMangaParser(context, MangaSource.NINENINENINEHENTAI, size), Interceptor {
override val configKeyDomain = ConfigKey.Domain("999hentai.net")
override val availableSortOrders: EnumSet<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
SortOrder.NEWEST
SortOrder.NEWEST,
)
override val isMultipleTagsSupported = false
@ -50,7 +51,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
Locale.ENGLISH,
Locale.CHINESE,
Locale.JAPANESE,
Locale("es")
Locale("es"),
)
private fun Locale?.getSiteLang(): String {
@ -107,7 +108,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
MangaTag(
title = name.toCamelCase(),
key = name,
source = source
source = source,
)
}
}
@ -121,9 +122,11 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
getSearchList(page, null, filter.locale, filter.tags, filter.sortOrder)
}
}
is MangaListFilter.Search -> {
getSearchList(page, filter.query, null, null, filter.sortOrder)
}
else -> {
getPopularList(page, null)
}
@ -132,7 +135,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
private suspend fun getPopularList(
page: Int,
locale: Locale?
locale: Locale?,
): List<Manga> {
val query = """
queryPopularChapters(
@ -268,7 +271,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
val tags = entry.optJSONArray("tags")?.mapJSON {
SiteTag(
name = it.getString("tagName"),
type = it.getStringOrNull("tagType")
type = it.getStringOrNull("tagType"),
)
}
return manga.copy(
@ -282,7 +285,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
MangaTag(
title = it.name.toCamelCase(),
key = it.name,
source = source
source = source,
)
}.orEmpty(),
state = null,
@ -307,15 +310,15 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
return@let locale.getDisplayLanguage(locale)
},
scanlator = when(entry.getStringOrNull("format")) {
scanlator = when (entry.getStringOrNull("format")) {
"artistcg" -> "ArtistCG"
"gamecg" -> "GameCG"
"imageset" -> "ImageSet"
else -> entry.getStringOrNull("format")?.toCamelCase()
},
source = source
)
)
source = source,
),
),
)
}
@ -383,7 +386,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang
id = generateUid(img),
url = cdn + img,
preview = cdn + imgS,
source = source
source = source,
)
}
}

@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("VFSCAN", "VfScan", "fr")
internal class VfScan(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.VFSCAN, "www.vfscan.com", pageSize = 18, searchPageSize = 18)
MangaReaderParser(context, MangaSource.VFSCAN, "www.vfscan.cc", pageSize = 18, searchPageSize = 18)

@ -0,0 +1,10 @@
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.MangaSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("MANGAKINGS", "MangaKings", "tr")
internal class MangaKings(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.MANGAKINGS, "mangakings.com.tr", pageSize = 20, searchPageSize = 10)

@ -0,0 +1,331 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import org.json.JSONArray
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.ErrorMessages
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.toJSONList
import java.text.SimpleDateFormat
import java.util.*
internal abstract class ZeistMangaParser(
context: MangaLoaderContext,
source: MangaSource,
domain: String,
pageSize: Int = 12,
) : PagedMangaParser(context, source, pageSize) {
override val configKeyDomain = ConfigKey.Domain(domain)
override val isMultipleTagsSupported = false
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED)
override val availableStates: Set<MangaState> =
EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED)
protected open val datePattern = "yyyy-MM-dd"
@JvmField
protected val ongoing: Set<String> = hashSetOf(
"ongoing",
"en curso",
"ativo",
"lançando",
"مستمر",
"devam ediyor",
"güncel",
)
@JvmField
protected val finished: Set<String> = hashSetOf(
"completed",
"completo",
"tamamlandı",
)
@JvmField
protected val abandoned: Set<String> = hashSetOf(
"cancelled",
"dropped",
"dropado",
"abandonado",
"cancelado",
)
@JvmField
protected val paused: Set<String> = hashSetOf(
"hiatus",
)
protected open val sateOngoing: String = "Ongoing"
protected open val sateFinished: String = "Completed"
protected open val sateAbandoned: String = "Cancelled"
protected open val maxMangaResults: Int = 20
protected open val mangaCategory: String = "Series"
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
val startIndex = maxMangaResults * (page - 1) + 1
val url = buildString {
append("https://")
append(domain)
append("/feeds/posts/default/-/")
when (filter) {
is MangaListFilter.Search -> {
append(mangaCategory)
append("?alt=json&orderby=published&max-results=")
append((maxMangaResults + 1).toString())
append("&start-index=")
append(startIndex.toString())
append("&q=label:")
append(mangaCategory)
append("+")
append(filter.query.urlEncoded())
}
is MangaListFilter.Advanced -> {
if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) {
throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED)
}
if (filter.tags.isNotEmpty()) {
append(filter.tags.oneOrThrowIfMany()?.key.orEmpty())
} else if (filter.states.isNotEmpty()) {
append(
filter.states.oneOrThrowIfMany().let {
when (it) {
MangaState.ONGOING -> sateOngoing
MangaState.FINISHED -> sateFinished
MangaState.ABANDONED -> sateAbandoned
else -> mangaCategory
}
},
)
} else {
append(mangaCategory)
}
append("?alt=json&orderby=published&max-results=")
append((maxMangaResults + 1).toString())
append("&start-index=")
append(startIndex.toString())
}
null -> {
append(mangaCategory)
append("?alt=json&orderby=published&max-results=")
append((maxMangaResults + 1).toString())
append("&start-index=")
append(startIndex.toString())
}
}
}
val json = webClient.httpGet(url).parseJson().getJSONObject("feed")
return if (json.toString().contains("\"entry\":")) {
parseMangaList(json.getJSONArray("entry"))
} else {
emptyList()
}
}
protected open fun parseMangaList(json: JSONArray): List<Manga> {
return json.toJSONList().map { j ->
val name = j.getJSONObject("title").getString("\$t")
val href =
j.getJSONArray("link").toJSONList().first { it.getString("rel") == "alternate" }.getString("href")
val urlImg = if (j.toString().contains("media\$thumbnail")) {
j.getJSONObject("media\$thumbnail").getStringOrNull("url")
?.replace("""/s.+?-c/""".toRegex(), "/w600/")
?.replace("""=s(?!.*=s).+?-c$""".toRegex(), "=w600")
?.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")
}
Manga(
id = generateUid(href),
url = href,
publicUrl = href,
coverUrl = urlImg.orEmpty(),
title = name,
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
state = null,
source = source,
isNsfw = isNsfwSource,
)
}
}
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain").parseHtml()
return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet {
MangaTag(
key = it.selectFirstOrThrow("input").attr("value"),
title = it.selectFirstOrThrow("label").text(),
source = source,
)
}
}
protected open val selectTags = "article div.mt-15 a, .info-genre a"
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val state =
doc.selectFirst("div.y6x11p:contains(Status) .dt")
?: doc.selectFirst("div.y6x11p:contains(Estado) .dt")
?: doc.selectFirst("ul.infonime li:contains(Status) span")
?: doc.selectFirst("ul.infonime li:contains(Estado) span")
?: doc.selectFirst("span.status-novel")
val mangaState = state?.text()?.lowercase().let {
when (it) {
in ongoing -> MangaState.ONGOING
in finished -> MangaState.FINISHED
in abandoned -> MangaState.ABANDONED
in paused -> MangaState.PAUSED
else -> null
}
}
val desc = doc.getElementById("synopsis") ?: doc.getElementById("Sinopse") ?: doc.getElementById("sinopas")
val chaptersDeferred = async { loadChapters(manga.url, doc) }
manga.copy(
tags = doc.select(selectTags).mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").substringAfterLast("label/").substringBefore("?"),
title = a.text().toTitleCase(),
source = source,
)
},
description = desc?.text().orEmpty(),
state = mangaState,
chapters = chaptersDeferred.await(),
)
}
protected open suspend fun loadChapters(mangaUrl: String, doc: Document): List<MangaChapter> {
val feed = if (doc.getElementById("myUL") != null) {
doc.requireElementById("myUL").selectFirstOrThrow("script").attr("src")
.substringAfterLast("/-/").substringBefore("?").urlDecode()
} else if (doc.selectFirst("#latest > script") != null) {
val chapterRegex = """label\s*=\s*'([^']+)'""".toRegex()
val scriptSelector = "#latest > script"
val script = doc.selectFirstOrThrow(scriptSelector)
chapterRegex
.find(script.html())
?.groupValues?.get(1)
?: throw Exception("Failed to find chapter feed")
} else if (doc.selectFirst("#clwd > script") != null) {
val chapterRegex = """clwd\.run\('([^']+)'""".toRegex()
val scriptSelector = "#clwd > script"
val script = doc.selectFirstOrThrow(scriptSelector)
chapterRegex
.find(script.html())
?.groupValues?.get(1)
?: throw Exception("Failed to find chapter feed")
} else {
doc.selectFirstOrThrow("script:containsData(var label_chapter)").data()
.substringAfter("label_chapter = \"").substringBefore("\"")
}
val url = buildString {
append("https://")
append(domain)
append("/feeds/posts/default/-/")
append(feed)
append("?alt=json&orderby=published&max-results=9999")
}
val json =
webClient.httpGet(url).parseJson().getJSONObject("feed").getJSONArray("entry").toJSONList().reversed()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
return json.mapIndexedNotNull { i, j ->
val name = j.getJSONObject("title").getString("\$t")
val href =
j.getJSONArray("link").toJSONList().first { it.getString("rel") == "alternate" }.getString("href")
val dateText = j.getJSONObject("published").getString("\$t").substringBefore("T")
val slug = mangaUrl.substringAfterLast('/')
val slugChapter = href.substringAfterLast('/')
if (slug == slugChapter) {
return@mapIndexedNotNull null
}
MangaChapter(
id = generateUid(href),
url = href,
name = name,
number = i + 1,
branch = null,
uploadDate = dateFormat.tryParse(dateText),
scanlator = null,
source = source,
)
}
}
protected open val selectPage = "div.check-box img, article#reader .separator img, article.container .separator img"
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return if (doc.selectFirst("script:containsData(chapterImage =)") != null) {
doc.selectFirstOrThrow("script:containsData(chapterImage =)").data()
.substringAfter("[").substringBefore("]")
.replace(" ", "").replace("\"", "")
.split(",").map { url ->
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
} else if (doc.selectFirst("script:containsData(const content = )") != null) {
doc.selectFirstOrThrow("script:containsData(const content = )").data()
.substringAfter("`").substringBefore("`;").split("src=\"").drop(1)
.map { img ->
val url = img.substringBefore("\"")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
} else {
doc.select(selectPage).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
}
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("HIJALA", "Hijala", "ar")
internal class Hijala(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.HIJALA, "hijala.blogspot.com")

@ -0,0 +1,14 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("LONERTL", "LonerTranslations", "ar")
internal class LonerTl(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.LONERTL, "loner-tl.blogspot.com") {
override val sateOngoing: String = "مستمرة"
override val sateFinished: String = "مكتملة"
override val sateAbandoned: String = "متوقفة"
}

@ -0,0 +1,30 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
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("MANGAAILAND", "MangaAiLand", "ar")
internal class MangaAiLand(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.MANGAAILAND, "manga-ai-land.blogspot.com") {
override val sateOngoing: String = "مستمر"
override val sateFinished: String = "مكتملة"
override val sateAbandoned: String = "متوقفة"
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,
)
}
}
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("MANGASOUL", "MangaSoul", "ar")
internal class MangaSoul(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.MANGASOUL, "www.manga-soul.com")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("ROCKSMANGA_COM", "RocksManga.com", "ar")
internal class RocksManga(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.ROCKSMANGA_COM, "www.rocks-manga.com")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.ar
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("YOKAITEAM", "YokaiTeam", "ar")
internal class YokaiTeam(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.YOKAITEAM, "yokai-team.blogspot.com")

@ -0,0 +1,10 @@
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.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("ASUPANKOMIK", "AsupanKomik", "id")
internal class AsupanKomik(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.ASUPANKOMIK, "www.asupankomik.my.id")

@ -0,0 +1,28 @@
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.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
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("KLMANHUA", "KlManhua", "id", ContentType.HENTAI)
internal class KlManhua(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.KLMANHUA, "klmanhua.blogspot.com") {
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,
)
}
}
}

@ -0,0 +1,10 @@
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.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("KOMIKREALM", "KomikRealm", "id")
internal class KomikRealm(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.KOMIKREALM, "www.komikrealm.my.id")

@ -0,0 +1,29 @@
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.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
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
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
@MangaSourceParser("MIKOROKU", "Mikoroku", "id", ContentType.HENTAI)
internal class Mikoroku(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.MIKOROKU, "www.mikoroku.web.id") {
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain").parseHtml()
return doc.requireElementById("Genre").select("div.items-center").mapNotNullToSet {
MangaTag(
key = it.selectFirstOrThrow("input").attr("value"),
title = it.selectFirstOrThrow("label").text().substringBefore('('),
source = source,
)
}
}
}

@ -0,0 +1,29 @@
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.MangaSource
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
@MangaSourceParser("SHIYURASUB", "ShiyuraSub", "id")
internal class ShiyuraSub(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.SHIYURASUB, "shiyurasub.blogspot.com") {
override val selectTags = ".leading-8 div.my-5.gap-2 a"
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain").parseHtml()
return doc.select("div.list-label-widget-content ul li a").mapNotNullToSet {
MangaTag(
key = it.attr("href").removeSuffix("/").substringAfterLast('/'),
title = it.html().substringBefore("<span"),
source = source,
)
}
}
}

@ -0,0 +1,10 @@
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.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("SOBATMANKU", "Sobatmanku", "id")
internal class Sobatmanku(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.SOBATMANKU, "www.sobatmanku19.site")

@ -0,0 +1,44 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.id
import org.jsoup.nodes.Document
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.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("TOONCUBUS", "ToonCubus", "id", ContentType.HENTAI)
internal class ToonCubus(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.TOONCUBUS, "www.tooncubus.top") {
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/p/genre-list.html").parseHtml()
return doc.select(".dzdes-genre ul li a").mapNotNullToSet {
MangaTag(
key = it.attr("href").removeSuffix("/").substringAfterLast("/"),
title = it.selectFirstOrThrow("span").text(),
source = source,
)
}
}
override suspend fun loadChapters(mangaUrl: String, doc: Document): List<MangaChapter> {
return doc.selectFirstOrThrow("ul.series-chapterlist").select("div.flexch-infoz")
.mapChapters(reversed = true) { i, div ->
val url = div.selectFirstOrThrow("a").attr("href")
val name = div.selectFirstOrThrow("a span").text()
MangaChapter(
id = generateUid(url),
url = url,
name = name,
number = i + 1,
branch = null,
uploadDate = 0,
scanlator = null,
source = source,
)
}
}
}

@ -0,0 +1,48 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.pt
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("ANIMEXNOVEL", "AnimeXNovel", "pt")
internal class AnimeXNovel(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.ANIMEXNOVEL, "www.animexnovel.com") {
override val sateOngoing: String = "Ativo"
override val sateFinished: String = "Completo"
override val sateAbandoned: String = "Dropado"
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").removeSuffix("/").substringAfterLast('/'),
title = it.text(),
source = source,
)
}
}
override suspend fun loadChapters(mangaUrl: String, doc: Document): List<MangaChapter> {
return doc.select("div:has(> .list-judul:contains(Lista de Capítulos)) div#latest ul > li, div.tab:has(> label:contains(Lista de Capítulos)) div.tab-content ul > li")
.mapChapters(reversed = true) { i, li ->
val url = li.selectFirstOrThrow("a").attr("href")
val name = li.selectFirstOrThrow("a span").text()
MangaChapter(
id = generateUid(url),
url = url,
name = name,
number = i + 1,
branch = null,
uploadDate = 0,
scanlator = null,
source = source,
)
}
}
}

@ -0,0 +1,14 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.pt
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("ELEVENSCANLATOR", "ElevenScanlator", "pt")
internal class ElevenScanlator(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.ELEVENSCANLATOR, "elevenscanlator.blogspot.com") {
override val sateOngoing: String = "Lançando"
override val sateFinished: String = "Completo"
override val sateAbandoned: String = "Dropado"
}

@ -0,0 +1,15 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.pt
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("GALAXSCANS", "GalaxScans", "pt")
internal class GalaxScans(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.GALAXSCANS, "galaxscans.blogspot.com") {
override val mangaCategory = "Recentes"
override val sateOngoing: String = "Lançando"
override val sateFinished: String = "Completo"
override val sateAbandoned: String = "Dropado"
}

@ -0,0 +1,27 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.pt
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
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("GUILDATIERDRAW", "GuildaTierDraw", "pt")
internal class GuildaTierDraw(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.GUILDATIERDRAW, "www.guildatierdraw.com") {
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain").parseHtml()
return doc.requireElementById("LinkList2").select("ul li a").mapNotNullToSet {
MangaTag(
key = it.attr("href").substringBefore("?").substringAfterLast('/'),
title = it.text(),
source = source,
)
}
}
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.zeistmanga.pt
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser
@MangaSourceParser("TYRANTSCANS", "TyrantScans", "pt")
internal class TyrantScans(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.TYRANTSCANS, "www.tyrantscans.com")

@ -0,0 +1,33 @@
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.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
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.selectFirstOrThrow
@MangaSourceParser("MIKROKOSMOSFB", "Mikrokosmosfb", "tr", ContentType.HENTAI)
internal class Mikrokosmosfb(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.MIKROKOSMOSFB, "mikrokosmosfb.blogspot.com") {
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()
val tags = doc.selectFirstOrThrow("script:containsData(label: )").data()
.substringAfter("label: [").substringBefore("]").replace("\"", "").split(", ")
return tags.mapNotNullToSet {
MangaTag(
key = it,
title = it,
source = source,
)
}
}
}

@ -0,0 +1,33 @@
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.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
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.selectFirstOrThrow
@MangaSourceParser("SNSCOEURTURKEY", "SnscoeurTurkey", "tr", ContentType.HENTAI)
internal class SnscoeurTurkey(context: MangaLoaderContext) :
ZeistMangaParser(context, MangaSource.SNSCOEURTURKEY, "snscoeurturkey.blogspot.com") {
override val sateOngoing: String = "Güncel"
override val sateFinished: String = "Final"
override val sateAbandoned: String = "Düzenleniyor"
override val mangaCategory = "Seriler"
override suspend fun getAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/p/gelismis-arama.html").parseHtml()
return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet {
MangaTag(
key = it.selectFirstOrThrow("input").attr("value"),
title = it.selectFirstOrThrow("label").text(),
source = source,
)
}
}
}

@ -5,6 +5,7 @@ package org.koitharu.kotatsu.parsers.util
import androidx.annotation.FloatRange
import androidx.collection.arraySetOf
import java.math.BigInteger
import java.net.URLDecoder
import java.net.URLEncoder
import java.security.MessageDigest
import java.util.*
@ -98,6 +99,8 @@ fun String.splitTwoParts(delimiter: Char): Pair<String, String>? {
fun String.urlEncoded(): String = URLEncoder.encode(this, Charsets.UTF_8.name())
fun String.urlDecode(): String = URLDecoder.decode(this, Charsets.UTF_8.name())
fun ByteArray.byte2HexFormatted(): String {
val str = StringBuilder(size * 2)
for (i in indices) {

Loading…
Cancel
Save