commit
1a0d623c02
@ -0,0 +1,144 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.foolslide.en
|
||||||
|
|
||||||
|
|
||||||
|
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.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("ASSORTEDSCANS", "AssortedScans", "en")
|
||||||
|
internal class AssortedScans(context: MangaLoaderContext) :
|
||||||
|
FoolSlideParser(context, MangaSource.ASSORTEDSCANS, "assortedscans.com", 56) {
|
||||||
|
|
||||||
|
override val listUrl = "reader/"
|
||||||
|
override val pagination = false
|
||||||
|
override val selectInfo = "div.#series-info"
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
|
||||||
|
val doc = if (!query.isNullOrEmpty()) {
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append('/')
|
||||||
|
append(searchUrl)
|
||||||
|
append("?q=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
}
|
||||||
|
webClient.httpGet(url).parseHtml()
|
||||||
|
} else {
|
||||||
|
val url = buildString {
|
||||||
|
append("https://$domain/$listUrl")
|
||||||
|
// For some sites that don't have enough manga and page 2 links to page 1
|
||||||
|
if (!pagination) {
|
||||||
|
if (page > 1) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
append(page.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webClient.httpGet(url).parseHtml()
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc.select("section.series, tr.result").map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),// in search no img
|
||||||
|
title = div.selectFirstOrThrow("a").text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = RATING_UNKNOWN,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val testAdultPage = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
|
||||||
|
val doc = if (testAdultPage.selectFirst("div.info form") != null) {
|
||||||
|
webClient.httpPost(fullUrl, "adult=true").parseHtml()
|
||||||
|
} else {
|
||||||
|
testAdultPage
|
||||||
|
}
|
||||||
|
val chapters = getChapters(manga, doc)
|
||||||
|
|
||||||
|
val desc = doc.getElementById("series-desc")?.selectFirst("div")?.html()
|
||||||
|
val alt = doc.getElementById("series-aliases")?.selectFirst("div.alias")?.text()
|
||||||
|
val author = doc.getElementById("series-authors")?.selectFirst("div.author")?.text()
|
||||||
|
val state = doc.getElementById("series-status")?.selectFirst("span")?.text()
|
||||||
|
manga.copy(
|
||||||
|
coverUrl = doc.selectFirst(".cover")?.src() ?: manga.coverUrl,
|
||||||
|
description = desc,
|
||||||
|
altTitle = alt,
|
||||||
|
author = author,
|
||||||
|
state = when (state) {
|
||||||
|
"Ongoing" -> MangaState.ONGOING
|
||||||
|
"Completed" -> MangaState.FINISHED
|
||||||
|
"Canceled" -> MangaState.ABANDONED
|
||||||
|
else -> null
|
||||||
|
},
|
||||||
|
chapters = chapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getChapters(manga: Manga, doc: Document): List<MangaChapter> {
|
||||||
|
return doc.body().select("div.chapter").mapChapters(reversed = true) { i, div ->
|
||||||
|
val a = div.selectFirstOrThrow("a")
|
||||||
|
val href = a.attrAsRelativeUrl("href")
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(href),
|
||||||
|
name = a.text(),
|
||||||
|
number = i + 1,
|
||||||
|
url = href,
|
||||||
|
uploadDate = 0,
|
||||||
|
source = source,
|
||||||
|
scanlator = null,
|
||||||
|
branch = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
|
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
|
||||||
|
return doc.body().select(".page-list .dropdown-list li a").map { a ->
|
||||||
|
val url = a.attr("href").toRelativeUrl(domain)
|
||||||
|
MangaPage(
|
||||||
|
id = generateUid(url),
|
||||||
|
url = url,
|
||||||
|
preview = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPageUrl(page: MangaPage): String {
|
||||||
|
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
|
||||||
|
val root = doc.body()
|
||||||
|
return root.requireElementById("page-image").attr("src") ?: doc.parseFailed("Page image not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.foolslide.en
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("SEINAGI", "Seinagi", "en")
|
||||||
|
internal class Seinagi(context: MangaLoaderContext) :
|
||||||
|
FoolSlideParser(context, MangaSource.SEINAGI, "reader.seinagi.org.es") {
|
||||||
|
|
||||||
|
override val pagination = false
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val testAdultPage = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
|
||||||
|
val doc = if (testAdultPage.selectFirst("div.info form") != null) {
|
||||||
|
webClient.httpPost(fullUrl, "adult=true").parseHtml()
|
||||||
|
} else {
|
||||||
|
testAdultPage
|
||||||
|
}
|
||||||
|
val chapters = getChapters(manga, doc)
|
||||||
|
|
||||||
|
val desc = if (doc.selectFirstOrThrow(selectInfo).html().contains("Description")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Description: ").substringBefore("Readings")
|
||||||
|
} else {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
val author = if (doc.selectFirstOrThrow(selectInfo).html().contains("Author")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Author: ").substringBefore("Art")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
manga.copy(
|
||||||
|
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
|
||||||
|
description = desc,
|
||||||
|
altTitle = null,
|
||||||
|
author = author,
|
||||||
|
state = null,
|
||||||
|
chapters = chapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.foolslide.es
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("PZYKOSIS666HFANSUB", "Pzykosis666h Fansub", "es", ContentType.HENTAI)
|
||||||
|
internal class Pzykosis666hFansub(context: MangaLoaderContext) :
|
||||||
|
FoolSlideParser(context, MangaSource.PZYKOSIS666HFANSUB, "lector.pzykosis666hfansub.com") {
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val testAdultPage = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
|
||||||
|
val doc = if (testAdultPage.selectFirst("div.info form") != null) {
|
||||||
|
webClient.httpPost(fullUrl, "adult=true").parseHtml()
|
||||||
|
} else {
|
||||||
|
testAdultPage
|
||||||
|
}
|
||||||
|
val chapters = getChapters(manga, doc)
|
||||||
|
|
||||||
|
val desc = if (doc.selectFirstOrThrow(selectInfo).html().contains("Descripción")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Descripción: ").substringBefore("Lecturas")
|
||||||
|
} else {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
val author = if (doc.selectFirstOrThrow(selectInfo).html().contains("Author")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Author: ").substringBefore("Art")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
manga.copy(
|
||||||
|
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
|
||||||
|
description = desc,
|
||||||
|
altTitle = null,
|
||||||
|
author = author,
|
||||||
|
state = null,
|
||||||
|
chapters = chapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.foolslide.es
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
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.Manga
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.domain
|
||||||
|
import org.koitharu.kotatsu.parsers.util.parseHtml
|
||||||
|
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
|
||||||
|
import org.koitharu.kotatsu.parsers.util.src
|
||||||
|
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("SEINAGIADULTO", "Seinagi Adulto", "es", ContentType.HENTAI)
|
||||||
|
internal class SeinagiAdulto(context: MangaLoaderContext) :
|
||||||
|
FoolSlideParser(context, MangaSource.SEINAGIADULTO, "adulto.seinagi.org.es") {
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val testAdultPage = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
|
||||||
|
val doc = if (testAdultPage.selectFirst("div.info form") != null) {
|
||||||
|
webClient.httpPost(fullUrl, "adult=true").parseHtml()
|
||||||
|
} else {
|
||||||
|
testAdultPage
|
||||||
|
}
|
||||||
|
val chapters = getChapters(manga, doc)
|
||||||
|
|
||||||
|
val desc = if (doc.selectFirstOrThrow(selectInfo).html().contains("Descripción")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Descripción: ").substringBefore("Lecturas")
|
||||||
|
} else {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
val author = if (doc.selectFirstOrThrow(selectInfo).html().contains("Author")) {
|
||||||
|
doc.selectFirstOrThrow(selectInfo).text().substringAfter("Author: ").substringBefore("Art")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
manga.copy(
|
||||||
|
coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search
|
||||||
|
description = desc,
|
||||||
|
altTitle = null,
|
||||||
|
author = author,
|
||||||
|
state = null,
|
||||||
|
chapters = chapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.all
|
||||||
|
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.util.EnumSet
|
||||||
|
import java.util.HashSet
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGA18FX", "Manga18Fx", "", ContentType.HENTAI)
|
||||||
|
internal class Manga18Fx(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGA18FX, "manga18fx.com") {
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
override val datePattern = "dd MMM yy"
|
||||||
|
override val sortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED)
|
||||||
|
override val listUrl = ""
|
||||||
|
override val selectTestAsync = "ul.row-content-chapter"
|
||||||
|
override val selectDate = "span.chapter-time"
|
||||||
|
override val selectChapter = "li.a-h"
|
||||||
|
override val selectBodyPage = "div.read-content"
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
val pages = page + 1
|
||||||
|
when {
|
||||||
|
!query.isNullOrEmpty() -> {
|
||||||
|
|
||||||
|
append("/search?q=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
append("&page=")
|
||||||
|
append(pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
!tags.isNullOrEmpty() -> {
|
||||||
|
append("/$tagPrefix")
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
if (pages > 1) {
|
||||||
|
append("/")
|
||||||
|
append(pages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
if (pages > 1) {
|
||||||
|
append("/page/")
|
||||||
|
append(pages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
|
||||||
|
return doc.select("div.listupd div.page-item").map { div ->
|
||||||
|
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
|
title = div.selectFirstOrThrow("h3").text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = div.selectFirst("div.item-rate span")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
|
val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml()
|
||||||
|
val list = doc.body().selectFirstOrThrow("div.genre-menu").select("ul li").orEmpty()
|
||||||
|
val keySet = HashSet<String>(list.size)
|
||||||
|
return list.mapNotNullToSet { li ->
|
||||||
|
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
|
||||||
|
val href = a.attr("href").removeSuffix("/").substringAfterLast(tagPrefix, "")
|
||||||
|
if (href.isEmpty() || !keySet.add(href)) {
|
||||||
|
return@mapNotNullToSet null
|
||||||
|
}
|
||||||
|
MangaTag(
|
||||||
|
key = href,
|
||||||
|
title = a.ownText().trim().ifEmpty {
|
||||||
|
a.selectFirst(".menu-image-title")?.text()?.trim() ?: return@mapNotNullToSet null
|
||||||
|
}.toTitleCase(),
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.all
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.util.HashSet
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("MANHWA18CC", "Manhwa 18 Cc", "", ContentType.HENTAI)
|
||||||
|
internal class Manhwa18Cc(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANHWA18CC, "manhwa18.cc", 24) {
|
||||||
|
override val datePattern = "dd MMM yyyy"
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
override val listUrl = "webtoons/"
|
||||||
|
override val tagPrefix = "webtoon-genre/"
|
||||||
|
override val withoutAjax = true
|
||||||
|
override val selectTestAsync = "ul.row-content-chapter"
|
||||||
|
override val selectDate = "span.chapter-time"
|
||||||
|
override val selectChapter = "li.a-h"
|
||||||
|
override val selectBodyPage = "div.read-content"
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
val pages = page + 1
|
||||||
|
when {
|
||||||
|
!query.isNullOrEmpty() -> {
|
||||||
|
append("/page/")
|
||||||
|
append(pages.toString())
|
||||||
|
append("/?s=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
append("&post_type=wp-manga&")
|
||||||
|
}
|
||||||
|
|
||||||
|
!tags.isNullOrEmpty() -> {
|
||||||
|
append("/$tagPrefix")
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
if (pages > 1) {
|
||||||
|
append("/page/")
|
||||||
|
append(pages.toString())
|
||||||
|
}
|
||||||
|
append("?")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
|
||||||
|
append("/$listUrl")
|
||||||
|
if (pages > 1) {
|
||||||
|
append("page/")
|
||||||
|
append(pages)
|
||||||
|
}
|
||||||
|
append("?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
append("m_orderby=")
|
||||||
|
when (sortOrder) {
|
||||||
|
SortOrder.POPULARITY -> append("trending")
|
||||||
|
SortOrder.UPDATED -> append("latest")
|
||||||
|
SortOrder.ALPHABETICAL -> append("alphabet")
|
||||||
|
SortOrder.RATING -> append("rating")
|
||||||
|
else -> append("latest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
|
||||||
|
return doc.select("div.manga-lists div.manga-item").map { div ->
|
||||||
|
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
|
title = div.selectFirstOrThrow("h3").text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = div.selectFirst(".item-rate span")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
|
val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml()
|
||||||
|
val list = doc.body().selectFirstOrThrow("div.sub-menu").select("ul li").orEmpty()
|
||||||
|
val keySet = HashSet<String>(list.size)
|
||||||
|
return list.mapNotNullToSet { li ->
|
||||||
|
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
|
||||||
|
val href = a.attr("href").removeSuffix("/").substringAfterLast(tagPrefix, "")
|
||||||
|
if (href.isEmpty() || !keySet.add(href)) {
|
||||||
|
return@mapNotNullToSet null
|
||||||
|
}
|
||||||
|
MangaTag(
|
||||||
|
key = href,
|
||||||
|
title = a.ownText().trim().ifEmpty {
|
||||||
|
a.selectFirst(".menu-image-title")?.text()?.trim() ?: return@mapNotNullToSet null
|
||||||
|
}.toTitleCase(),
|
||||||
|
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().selectFirstOrThrow(selectBodyPage)
|
||||||
|
return root.select("img").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,18 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.all
|
||||||
|
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("MANYTOON_CLUB", "Many Toon Club", "", ContentType.HENTAI)
|
||||||
|
internal class ManyToonClub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANYTOON_CLUB, "manytoon.club") {
|
||||||
|
override val postreq = true
|
||||||
|
override val listUrl = "manhwa-raw/"
|
||||||
|
override val tagPrefix = "manhwa-raw-genre/"
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.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.madara.MadaraParser
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("AKUMANGA", "Aku Manga", "ar")
|
||||||
|
internal class AkuManga(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.AKUMANGA, "akumanga.com") {
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("COMICARAB", "ComicArab", "ar")
|
||||||
|
internal class ComicArab(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.COMICARAB, "comicarab.com", pageSize = 24) {
|
||||||
|
override val datePattern = "d MMMM، yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGALEK_ORG", "MangaLek Org", "ar")
|
||||||
|
internal class MangaLekOrg(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGALEK_ORG, "ww.mangalek.org", pageSize = 10) {
|
||||||
|
override val listUrl = "comics/"
|
||||||
|
override val datePattern = "dd-MM-yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGASTARZCOM", "MangaStarz Com", "ar")
|
||||||
|
internal class MangaStarzCom(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGASTARZCOM, "mangastarz.com", 10) {
|
||||||
|
override val datePattern = "d MMMM، yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("COMICSVALLEY", "Comics Valley", "en", ContentType.HENTAI)
|
||||||
|
internal class ComicsValley(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.COMICSVALLEY, "comicsvalley.com") {
|
||||||
|
override val listUrl = "adult-comics/"
|
||||||
|
override val tagPrefix = "comic-genre/"
|
||||||
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("FREECOMICONLINE", "Free Comic Online", "en", ContentType.HENTAI)
|
||||||
|
internal class FreeComicOnline(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.FREECOMICONLINE, "freecomiconline.me") {
|
||||||
|
override val postreq = true
|
||||||
|
override val listUrl = "comic/"
|
||||||
|
override val tagPrefix = "comic-genre/"
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("HUNTERSSCANEN", "Hunters Scan En", "en")
|
||||||
|
internal class HuntersScanEn(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.HUNTERSSCANEN, "en.huntersscan.xyz") {
|
||||||
|
|
||||||
|
override val withoutAjax = true
|
||||||
|
override val datePattern = "MM/dd/yyyy"
|
||||||
|
override val tagPrefix = "series-genre/"
|
||||||
|
override val listUrl = "series/"
|
||||||
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.EnumSet
|
||||||
|
|
||||||
|
@MangaSourceParser("INSTAMANHWA", "Insta Manhwa", "en", ContentType.HENTAI)
|
||||||
|
internal class InstaManhwa(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.INSTAMANHWA, "www.instamanhwa.com", 15) {
|
||||||
|
override val tagPrefix = "genre/"
|
||||||
|
override val listUrl = "latest/"
|
||||||
|
override val postreq = true
|
||||||
|
override val datePattern = "d MMMM, yyyy"
|
||||||
|
|
||||||
|
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||||
|
SortOrder.ALPHABETICAL,
|
||||||
|
SortOrder.UPDATED,
|
||||||
|
SortOrder.NEWEST,
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
val pages = page + 1
|
||||||
|
|
||||||
|
when {
|
||||||
|
!query.isNullOrEmpty() -> {
|
||||||
|
|
||||||
|
append("/?search=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
append("&page=")
|
||||||
|
append(pages.toString())
|
||||||
|
append("&post_type=wp-manga&post_type=wp-manga")
|
||||||
|
}
|
||||||
|
|
||||||
|
!tags.isNullOrEmpty() -> {
|
||||||
|
append("/genre/")
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
append("?page=")
|
||||||
|
append(pages.toString())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
|
||||||
|
when (sortOrder) {
|
||||||
|
SortOrder.UPDATED -> append("/latest")
|
||||||
|
SortOrder.NEWEST -> append("/new")
|
||||||
|
SortOrder.ALPHABETICAL -> append("/alphabet")
|
||||||
|
else -> append("/latest")
|
||||||
|
}
|
||||||
|
append("?page=")
|
||||||
|
append(pages.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
return doc.select("div.page-listing-item div.page-item-detail").ifEmpty {
|
||||||
|
doc.select("div.page-item-detail.manga")
|
||||||
|
}.map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
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")?.src().orEmpty(),
|
||||||
|
title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4"))?.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()?.trim()
|
||||||
|
?.lowercase()) {
|
||||||
|
"ongoing" -> MangaState.ONGOING
|
||||||
|
"completed " -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
},
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadChapters(mangaUrl: String, document: Document): List<MangaChapter> {
|
||||||
|
|
||||||
|
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
|
||||||
|
val token = document.select("meta")[2].attr("content")
|
||||||
|
val url = "https://$domain/ajax"
|
||||||
|
val postdata = "_token=$token&action=manga_get_chapters&manga=$mangaId"
|
||||||
|
val doc = webClient.httpPost(url, postdata).parseHtml()
|
||||||
|
|
||||||
|
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
|
||||||
|
|
||||||
|
return doc.select(selectChapter).mapChapters(reversed = true) { i, li ->
|
||||||
|
val a = li.selectFirst("a")
|
||||||
|
val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing")
|
||||||
|
val link = href + stylepage
|
||||||
|
val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text()
|
||||||
|
val name = a.selectFirst("p")?.text() ?: a.ownText()
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = link,
|
||||||
|
name = name,
|
||||||
|
number = i + 1,
|
||||||
|
branch = null,
|
||||||
|
uploadDate = parseChapterDate(
|
||||||
|
dateFormat,
|
||||||
|
dateText,
|
||||||
|
),
|
||||||
|
scanlator = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("LHTRANSLATION", "Lh Translation", "en")
|
||||||
|
internal class LhTranslation(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.LHTRANSLATION, "lhtranslation.net")
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAECLIPSE", "Manga Eclipse", "en")
|
||||||
|
internal class MangaEclipse(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGAECLIPSE, "mangaeclipse.com")
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAKIK", "Manga Kik", "en")
|
||||||
|
internal class MangaKik(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGAKIK, "mangakik.biz", 10)
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGASY", "Mangasy", "en")
|
||||||
|
internal class Mangasy(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGASY, "www.mangasy.com") {
|
||||||
|
override val tagPrefix = "manhua-genre/"
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANHUAUSS", "Manhuauss", "en")
|
||||||
|
internal class Manhuauss(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANHUAUSS, "manhuauss.com") {
|
||||||
|
override val withoutAjax = true
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANHWARAW_COM", "ManhwaRaw Com", "en", ContentType.HENTAI)
|
||||||
|
internal class ManhwaRawCom(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANHWARAW_COM, "manhwaraw.com") {
|
||||||
|
override val postreq = true
|
||||||
|
override val listUrl = "manhwa-raw/"
|
||||||
|
override val tagPrefix = "manhwa-raw-genre/"
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("PORNCOMIXONLINE_NET", "Porn Comix Online Net", "en", ContentType.HENTAI)
|
||||||
|
internal class PornComixOnlineNet(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.PORNCOMIXONLINE_NET, "www.porncomixonline.net") {
|
||||||
|
|
||||||
|
override val listUrl = "m-comic/"
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("RESETSCANS", "Reset Scans", "en")
|
||||||
|
internal class ResetScans(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.RESETSCANS, "reset-scans.com", 18) {
|
||||||
|
override val datePattern = "MMM dd"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("SETSUSCANS", "Setsu Scans", "en")
|
||||||
|
internal class SetsuScans(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.SETSUSCANS, "setsuscans.com")
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.en
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("TOONGOD", "Toon God", "en", ContentType.HENTAI)
|
||||||
|
internal class ToonGod(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.TOONGOD, "www.toongod.org", 18) {
|
||||||
|
override val listUrl = "webtoon/"
|
||||||
|
override val tagPrefix = "webtoon-genre/"
|
||||||
|
override val datePattern = "d MMM yyyy"
|
||||||
|
override val withoutAjax = true
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("KNIGHTNOSCANLATION", "Knightno Scanlation", "es")
|
||||||
|
internal class KnightnoScanlation(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.KNIGHTNOSCANLATION, "knightnoscanlation.com") {
|
||||||
|
|
||||||
|
override val listUrl = "sr/"
|
||||||
|
override val tagPrefix = "generos/"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAXICO", "Mangaxico", "es", ContentType.HENTAI)
|
||||||
|
internal class Mangaxico(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGAXICO, "mangaxico.com", 24)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||||
|
|
||||||
|
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.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("STICKHORSE", "Stickhorse", "es")
|
||||||
|
internal class Stickhorse(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.STICKHORSE, "www.stickhorse.cl") {
|
||||||
|
|
||||||
|
override val postreq = true
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.util.EnumSet
|
||||||
|
|
||||||
|
@MangaSourceParser("TMOMANGA", "Tmo Manga", "es")
|
||||||
|
internal class TmoManga(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.TMOMANGA, "tmomanga.com") {
|
||||||
|
|
||||||
|
override val tagPrefix = "genero/"
|
||||||
|
override val listUrl = "biblioteca/"
|
||||||
|
override val selectGenre = "div.summary-content a.tags_manga"
|
||||||
|
override val withoutAjax = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
paginator.firstPage = 1
|
||||||
|
searchPaginator.firstPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override val sortOrders: Set<SortOrder> = EnumSet.of(SortOrder.POPULARITY)
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
val url = buildString {
|
||||||
|
append("https://$domain")
|
||||||
|
when {
|
||||||
|
!query.isNullOrEmpty() -> {
|
||||||
|
append("/$listUrl")
|
||||||
|
append("?search=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
if (page > 1) {
|
||||||
|
append("&page=")
|
||||||
|
append(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
!tags.isNullOrEmpty() -> {
|
||||||
|
append("/$tagPrefix")
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
if (page > 1) {
|
||||||
|
append("?page=")
|
||||||
|
append(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
append("/$listUrl")
|
||||||
|
if (page > 1) {
|
||||||
|
append("?page=")
|
||||||
|
append(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
|
||||||
|
return doc.select("div.page-item-detail").map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
|
title = div.selectFirstOrThrow("h3").text(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getChapters(manga: Manga, doc: Document): List<MangaChapter> {
|
||||||
|
return doc.body().select(selectChapter).mapChapters(reversed = true) { i, li ->
|
||||||
|
val a = li.selectFirstOrThrow("a")
|
||||||
|
val href = a.attrAsRelativeUrl("href")
|
||||||
|
val link = href + stylepage
|
||||||
|
val name = a.selectFirst("p")?.text() ?: a.ownText()
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(href),
|
||||||
|
name = name,
|
||||||
|
number = i + 1,
|
||||||
|
url = link,
|
||||||
|
uploadDate = 0,
|
||||||
|
source = source,
|
||||||
|
scanlator = null,
|
||||||
|
branch = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
|
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
||||||
|
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
val root = doc.body().requireElementById("images_chapter")
|
||||||
|
return root.select("img").map { img ->
|
||||||
|
val url = img.src()?.toRelativeUrl(domain).orEmpty()
|
||||||
|
MangaPage(
|
||||||
|
id = generateUid(url),
|
||||||
|
url = url,
|
||||||
|
preview = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.es
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("TOPCOMICPORNO", "TopComicPorno", "es", ContentType.HENTAI)
|
||||||
|
internal class TopComicPorno(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.TOPCOMICPORNO, "topcomicporno.com", 18) {
|
||||||
|
override val datePattern = "MMM dd, yy"
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.fr
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("KATAITAKE", "Kataitake", "fr", ContentType.HENTAI)
|
||||||
|
internal class Kataitake(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.KATAITAKE, "www.kataitake.fr", 10) {
|
||||||
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
override val tagPrefix = "genre/"
|
||||||
|
override val postreq = true
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("KOMIKTO", "Komikto", "id")
|
||||||
|
internal class Komikto(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.KOMIKTO, "komikto.com", 10) {
|
||||||
|
|
||||||
|
override val tagPrefix = "grafis/"
|
||||||
|
override val listUrl = "comic/"
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("AKUMANOTENSHI", "AkumanoTenshi", "pt")
|
||||||
|
internal class AkumanoTenshi(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.AKUMANOTENSHI, "akumanotenshi.com", 48) {
|
||||||
|
|
||||||
|
override val listUrl = "series/"
|
||||||
|
override val tagPrefix = "series-genre/"
|
||||||
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
override val postreq = true
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("HUNTERSSCAN", "Hunters Scan", "pt")
|
||||||
|
internal class HuntersScan(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.HUNTERSSCAN, "huntersscan.xyz", pageSize = 50) {
|
||||||
|
|
||||||
|
override val withoutAjax = true
|
||||||
|
override val datePattern = "MM/dd/yyyy"
|
||||||
|
override val tagPrefix = "series-genre/"
|
||||||
|
override val listUrl = "series/"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("YANPFANSUB", "Yanp Fansub", "pt")
|
||||||
|
internal class YanpFansub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.YANPFANSUB, "yanpfansub.com")
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.th
|
||||||
|
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("DOUJINZA", "Doujinza", "th", ContentType.HENTAI)
|
||||||
|
internal class Doujinza(context: MangaLoaderContext) : MadaraParser(context, MangaSource.DOUJINZA, "doujinza.com", 24) {
|
||||||
|
|
||||||
|
override val withoutAjax = true
|
||||||
|
override val datePattern = "MMMM dd, yyyy"
|
||||||
|
override val listUrl = "doujin/"
|
||||||
|
override val tagPrefix = "doujin-genre/"
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("CLOVERMANGA", "Clover Manga", "tr")
|
||||||
|
internal class CloverManga(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.CLOVERMANGA, "clover-manga.com", 20)
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("GHOSTFANSUB", "Ghost Fansub", "tr")
|
||||||
|
internal class GhostFansub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.GHOSTFANSUB, "ghostfansub.biz", 18)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.ContentType
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("LAVINIAFANSUB", "Lavinia Fansub", "tr", ContentType.HENTAI)
|
||||||
|
internal class LaviniaFansub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.LAVINIAFANSUB, "laviniafansub.com", 18) {
|
||||||
|
|
||||||
|
override val datePattern = "dd/MM/yyyy"
|
||||||
|
}
|
||||||
@ -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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAWOW", "Manga Wow", "tr")
|
||||||
|
internal class MangaWow(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.MANGAWOW, "mangawow.com", 18) {
|
||||||
|
override val datePattern = "d MMMM yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("PIEDPIPERFANSUB", "Piedpiper Fansub", "tr")
|
||||||
|
internal class PiedpiperFansub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.PIEDPIPERFANSUB, "piedpiperfansub.me", 18) {
|
||||||
|
|
||||||
|
override val datePattern = "d MMMM yyyy"
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("REAPERSCANSTR", "Reaper Scans Tr", "tr")
|
||||||
|
internal class ReaperScansTr(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.REAPERSCANSTR, "reaperscanstr.com", 5) {
|
||||||
|
override val listUrl = "seri/"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
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.ContentType
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||||
|
|
||||||
|
@MangaSourceParser("STRAYFANSUB", "Stray Fansub", "tr", ContentType.HENTAI)
|
||||||
|
internal class StrayFansub(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.STRAYFANSUB, "strayfansub.homes")
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madara.tr
|
||||||
|
|
||||||
|
|
||||||
|
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.site.madara.MadaraParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("TITANMANGA", "Titan Manga", "tr")
|
||||||
|
internal class TitanManga(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.TITANMANGA, "titanmanga.com") {
|
||||||
|
|
||||||
|
override suspend fun loadChapters(mangaUrl: String, document: Document): List<MangaChapter> {
|
||||||
|
val url = mangaUrl.toAbsoluteUrl(domain).removeSuffix('/') + "/ajax/chapters/"
|
||||||
|
val doc = webClient.httpPost(url, emptyMap()).parseHtml()
|
||||||
|
return doc.select(selectChapter).mapChapters(reversed = true) { i, li ->
|
||||||
|
val a = li.selectFirstOrThrow("a")
|
||||||
|
val href = a.attrAsRelativeUrl("href")
|
||||||
|
val link = href + stylepage
|
||||||
|
val name = a.selectFirst("p")?.text() ?: a.ownText()
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = link,
|
||||||
|
name = name,
|
||||||
|
number = i + 1,
|
||||||
|
branch = null,
|
||||||
|
uploadDate = 0,
|
||||||
|
scanlator = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.madtheme.all
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.madtheme.MadthemeParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("MANHUASCAN", "ManhuaScan", "")
|
||||||
|
internal class ManhuaScan(context: MangaLoaderContext) :
|
||||||
|
MadthemeParser(context, MangaSource.MANHUASCAN, "manhuascan.io") {
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
override val listUrl = "search"
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append('/')
|
||||||
|
append(listUrl)
|
||||||
|
append("?sort=")
|
||||||
|
when (sortOrder) {
|
||||||
|
SortOrder.POPULARITY -> append("views")
|
||||||
|
SortOrder.UPDATED -> append("updated_at")
|
||||||
|
SortOrder.ALPHABETICAL -> append("name")
|
||||||
|
SortOrder.NEWEST -> append("created_at")
|
||||||
|
SortOrder.RATING -> append("rating")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.isNullOrEmpty()) {
|
||||||
|
append("&q=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tags.isNullOrEmpty()) {
|
||||||
|
for (tag in tags) {
|
||||||
|
append("&")
|
||||||
|
append("include[]".urlEncoded())
|
||||||
|
append("=")
|
||||||
|
append(tag.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append("&page=")
|
||||||
|
append(page.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
|
||||||
|
return doc.select("div.book-item").map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
|
title = div.selectFirstOrThrow("div.meta").selectFirst("div.title")?.text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = div.selectFirstOrThrow("div.meta span.score").ownText().toFloatOrNull()?.div(5f)
|
||||||
|
?: RATING_UNKNOWN,
|
||||||
|
tags = doc.body().select("div.meta div.genres span").mapNotNullToSet { span ->
|
||||||
|
MangaTag(
|
||||||
|
key = span.attr("class"),
|
||||||
|
title = span.text().toTitleCase(),
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
|
val chapterUrl = chapter.url.toAbsoluteUrl(domain)
|
||||||
|
val docs = webClient.httpGet(chapterUrl).parseHtml()
|
||||||
|
|
||||||
|
val script = docs.selectFirstOrThrow("script:containsData(var chapImages)")
|
||||||
|
val images = script.data().substringAfter("= \"").substringBefore("\";").split(",")
|
||||||
|
return images.map {
|
||||||
|
MangaPage(
|
||||||
|
id = generateUid(it),
|
||||||
|
url = it,
|
||||||
|
preview = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("GALAXYACTION", "Galaxy Action", "ar")
|
||||||
|
internal class GalaxyAction(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.GALAXYACTION, "galaxyaction.site", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("SPIDERSCANS", "Spider Scans", "ar")
|
||||||
|
internal class SpiderScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.SPIDERSCANS, "spiderscans.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
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.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("CONSTELLARCOMIC", "Constellarcomic", "en", ContentType.HENTAI)
|
||||||
|
internal class Constellarcomic(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.CONSTELLARCOMIC, "constellarcomic.com", pageSize = 30, searchPageSize = 18) {
|
||||||
|
override val selectTestScript = "script:containsData(ts_rea_der_._run)"
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga {
|
||||||
|
val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||||
|
val chapters = docs.select(selectChapter).mapChapters(reversed = true) { index, element ->
|
||||||
|
val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(url),
|
||||||
|
name = element.selectFirst(".chapternum")?.textOrNull() ?: "Chapter ${index + 1}",
|
||||||
|
url = url,
|
||||||
|
number = index + 1,
|
||||||
|
scanlator = null,
|
||||||
|
uploadDate = 0,
|
||||||
|
branch = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return parseInfo(docs, manga, chapters)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("ENRYUMANGA", "Enryu Manga", "en")
|
||||||
|
internal class EnryuManga(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.ENRYUMANGA, "enryumanga.com", pageSize = 30, searchPageSize = 10)
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("LUMINOUSSCANS", "Luminous Scans", "en")
|
||||||
|
internal class LuminousScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.LUMINOUSSCANS, "luminousscans.com", pageSize = 20, searchPageSize = 10) {
|
||||||
|
|
||||||
|
override val listUrl = "/series"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("LYRASCANS", "Lyra Scans", "en")
|
||||||
|
internal class LyraScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.LYRASCANS, "lyrascans.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -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.ContentType
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAGENKI", "Manga Genki", "en", ContentType.HENTAI)
|
||||||
|
internal class MangaGenki(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.MANGAGENKI, "mangagenki.com", pageSize = 45, searchPageSize = 30)
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("XCALIBRSCANS", "XCalibr Scans", "en")
|
||||||
|
internal class XCalibrScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.XCALIBRSCANS, "xcalibrscans.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -1,10 +1,10 @@
|
|||||||
package org.koitharu.kotatsu.parsers.site.madara.es
|
package org.koitharu.kotatsu.parsers.site.mangareader.es
|
||||||
|
|
||||||
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.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
@MangaSourceParser("TECNOSCANN", "TecnoScann", "es")
|
@MangaSourceParser("TECNOSCANN", "TecnoScann", "es")
|
||||||
internal class TecnoScann(context: MangaLoaderContext) :
|
internal class TecnoScann(context: MangaLoaderContext) :
|
||||||
MadaraParser(context, MangaSource.TECNOSCANN, "tecnoscann.com", 24)
|
MangaReaderParser(context, MangaSource.TECNOSCANN, "tecnoscann.com", 20, 10)
|
||||||
@ -0,0 +1,190 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mangareader.es
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.EnumSet
|
||||||
|
|
||||||
|
@MangaSourceParser("TU_MANHWAS", "TuManhwas", "es")
|
||||||
|
internal class TuManhwas(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.TU_MANHWAS, "tumanhwas.com", 20, 20) {
|
||||||
|
override val listUrl = "/biblioteca"
|
||||||
|
override val selectPage = "div#readerarea img"
|
||||||
|
|
||||||
|
|
||||||
|
override val sortOrders: Set<SortOrder>
|
||||||
|
get() = EnumSet.of(SortOrder.NEWEST)
|
||||||
|
|
||||||
|
override suspend fun getTags(): Set<MangaTag> = emptySet()
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append(listUrl)
|
||||||
|
append("?page=")
|
||||||
|
append(page)
|
||||||
|
if (!tags.isNullOrEmpty()) {
|
||||||
|
append("&genero=")
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
}
|
||||||
|
if (!query.isNullOrEmpty()) {
|
||||||
|
append("&search=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseMangaList(webClient.httpGet(url).parseHtml())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga {
|
||||||
|
val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||||
|
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
|
||||||
|
val chapters = docs.select(selectChapter).mapChapters(reversed = true) { index, element ->
|
||||||
|
val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null
|
||||||
|
MangaChapter(
|
||||||
|
id = generateUid(url),
|
||||||
|
name = element.selectFirst(".chapternum")?.textOrNull() ?: "Chapter ${index + 1}",
|
||||||
|
url = url,
|
||||||
|
number = index + 1,
|
||||||
|
scanlator = null,
|
||||||
|
uploadDate = parseChapterDate(
|
||||||
|
dateFormat,
|
||||||
|
element.selectFirst(".chapterdate")?.text(),
|
||||||
|
),
|
||||||
|
branch = null,
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return parseInfo(docs, manga, chapters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List<MangaChapter>): Manga {
|
||||||
|
|
||||||
|
val stateSelect = docs.selectFirst(".tsinfo div:contains(Estado)")
|
||||||
|
val state = stateSelect?.lastElementChild()
|
||||||
|
val mangaState = state?.let {
|
||||||
|
when (it.text().lowercase()) {
|
||||||
|
"publishing" -> MangaState.ONGOING
|
||||||
|
"terminado" -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val nsfw = docs.selectFirst(".restrictcontainer") != null
|
||||||
|
|| docs.selectFirst(".info-right .alr") != null
|
||||||
|
|| docs.selectFirst(".postbody .alr") != null
|
||||||
|
|
||||||
|
return manga.copy(
|
||||||
|
description = docs.selectFirst("div.entry-content")?.text(),
|
||||||
|
state = mangaState,
|
||||||
|
author = null,
|
||||||
|
isNsfw = manga.isNsfw || nsfw,
|
||||||
|
tags = docs.select(".wd-full .mgen > a").mapNotNullToSet { a ->
|
||||||
|
MangaTag(
|
||||||
|
key = a.attr("href").substringAfterLast('='),
|
||||||
|
title = a.text().toTitleCase(),
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
chapters = chapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
|
val chapterUrl = chapter.url.toAbsoluteUrl(domain)
|
||||||
|
val docs = webClient.httpGet(chapterUrl).parseHtml()
|
||||||
|
val pages = ArrayList<MangaPage>()
|
||||||
|
docs.select(selectPage).map { img ->
|
||||||
|
val url = img.src()?.toRelativeUrl(domain)
|
||||||
|
if (!url.isNullOrEmpty()) {
|
||||||
|
pages.add(
|
||||||
|
MangaPage(
|
||||||
|
id = generateUid(url),
|
||||||
|
url = url,
|
||||||
|
preview = null,
|
||||||
|
source = source,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
|
||||||
|
// Clean date (e.g. 5th December 2019 to 5 December 2019) before parsing it
|
||||||
|
val d = date?.lowercase() ?: return 0
|
||||||
|
return when {
|
||||||
|
d.startsWith("hace") -> parseRelativeDate(date)
|
||||||
|
|
||||||
|
else -> dateFormat.tryParse(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses dates in this form:
|
||||||
|
// 21 hours ago
|
||||||
|
private fun parseRelativeDate(date: String): Long {
|
||||||
|
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
|
||||||
|
return when {
|
||||||
|
WordSet(
|
||||||
|
"días",
|
||||||
|
"día",
|
||||||
|
).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||||
|
|
||||||
|
WordSet(
|
||||||
|
"hora",
|
||||||
|
"horas",
|
||||||
|
).anyWordIn(date) -> cal.apply {
|
||||||
|
add(
|
||||||
|
Calendar.HOUR,
|
||||||
|
-number,
|
||||||
|
)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
WordSet(
|
||||||
|
"minutos",
|
||||||
|
"minuto",
|
||||||
|
).anyWordIn(date) -> cal.apply {
|
||||||
|
add(
|
||||||
|
Calendar.MINUTE,
|
||||||
|
-number,
|
||||||
|
)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
WordSet("segundo").anyWordIn(date) -> cal.apply {
|
||||||
|
add(
|
||||||
|
Calendar.SECOND,
|
||||||
|
-number,
|
||||||
|
)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
WordSet(
|
||||||
|
"semana",
|
||||||
|
).anyWordIn(date) -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
|
||||||
|
|
||||||
|
WordSet("mes").anyWordIn(date) -> cal.apply {
|
||||||
|
add(
|
||||||
|
Calendar.MONTH,
|
||||||
|
-number,
|
||||||
|
)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
WordSet("año").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
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.Manga
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.domain
|
||||||
|
import org.koitharu.kotatsu.parsers.util.parseHtml
|
||||||
|
import org.koitharu.kotatsu.parsers.util.urlEncoded
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("COSMIC_SCANS", "Cosmic Scans", "id")
|
||||||
|
internal class CosmicScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.COSMIC_SCANS, "cosmicscans.id", pageSize = 30, searchPageSize = 30) {
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
override val listUrl = "/semua-komik"
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
if (!query.isNullOrEmpty()) {
|
||||||
|
if (page > lastSearchPage) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append("/page/")
|
||||||
|
append(page)
|
||||||
|
append("/?s=")
|
||||||
|
append(query.urlEncoded())
|
||||||
|
}
|
||||||
|
|
||||||
|
val docs = webClient.httpGet(url).parseHtml()
|
||||||
|
lastSearchPage = docs.selectFirst(".pagination .next")
|
||||||
|
?.previousElementSibling()
|
||||||
|
?.text()?.toIntOrNull() ?: 1
|
||||||
|
return parseMangaList(docs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
val sortQuery = when (sortOrder) {
|
||||||
|
SortOrder.ALPHABETICAL -> "title"
|
||||||
|
SortOrder.NEWEST -> "latest"
|
||||||
|
SortOrder.POPULARITY -> "popular"
|
||||||
|
SortOrder.UPDATED -> "update"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
val tagKey = "genre[]".urlEncoded()
|
||||||
|
val tagQuery =
|
||||||
|
if (tags.isNullOrEmpty()) "" else tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" }
|
||||||
|
val url = buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append(listUrl)
|
||||||
|
append("/?order=")
|
||||||
|
append(sortQuery)
|
||||||
|
append(tagQuery)
|
||||||
|
append("&page=")
|
||||||
|
append(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseMangaList(webClient.httpGet(url).parseHtml())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("FUTARI", "Futari", "id")
|
||||||
|
internal class Futari(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.FUTARI, "futari.info", pageSize = 25, searchPageSize = 10)
|
||||||
@ -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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("MISCANS", "MiScans", "id")
|
||||||
|
internal class MiScans(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.MISCANS, "miscans.my.id", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("SHIRAKAMI", "Shirakami", "id")
|
||||||
|
internal class Shirakami(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.SHIRAKAMI, "shirakami.xyz", pageSize = 10, 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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("SIRENKOMIK", "Siren Komik", "id")
|
||||||
|
internal class SirenKomik(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.SIRENKOMIK, "sirenkomik.my.id", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.ContentType
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("SUGARLAB", "Sugar Lab", "id", ContentType.HENTAI)
|
||||||
|
internal class SugarLab(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.SUGARLAB, "sugarlab.my.id", 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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("YUMEKOMIK", "Yume Komik", "id")
|
||||||
|
internal class YumeKomik(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.YUMEKOMIK, "yumekomik.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mangareader.ja
|
||||||
|
|
||||||
|
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
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@MangaSourceParser("MANGAJP", "Manga Jp", "ja")
|
||||||
|
internal class MangaJp(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.MANGAJP, "mangajp.top", pageSize = 54, searchPageSize = 10) {
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
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.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("DISKUSSCAN", "DiskusScan", "pt")
|
||||||
|
internal class DiskusScan(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.DISKUSSCAN, "diskusscan.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mangareader.th
|
||||||
|
|
||||||
|
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.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("TOOMTAMMANGA", "Toomtam Manga", "th", ContentType.HENTAI)
|
||||||
|
internal class ToomtamManga(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.TOOMTAMMANGA, "toomtam-manga.com", pageSize = 30, searchPageSize = 28)
|
||||||
@ -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("ADONISFANSUB", "Adonis Fansub", "tr")
|
||||||
|
internal class AdonisFansub(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.ADONISFANSUB, "manga.adonisfansub.com", pageSize = 20, searchPageSize = 20)
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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.ContentType
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||||
|
|
||||||
|
@MangaSourceParser("ARCURAFANSUB", "Arcura Fansub", "tr", ContentType.HENTAI)
|
||||||
|
internal class ArcuraFansub(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.ARCURAFANSUB, "arcurafansub.com", pageSize = 20, searchPageSize = 10) {
|
||||||
|
|
||||||
|
override val listUrl = "/seri"
|
||||||
|
}
|
||||||
@ -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("NIRVANAMANGA", "Nirvana Manga", "tr")
|
||||||
|
internal class NirvanaManga(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(context, MangaSource.NIRVANAMANGA, "nirvanamanga.com", pageSize = 20, searchPageSize = 10)
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
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("RAINDROPTEAMFAN", "Raindropteamfan", "tr")
|
||||||
|
internal class Raindropteamfan(context: MangaLoaderContext) :
|
||||||
|
MangaReaderParser(
|
||||||
|
context,
|
||||||
|
MangaSource.RAINDROPTEAMFAN,
|
||||||
|
"www.raindropteamfan.com",
|
||||||
|
pageSize = 25,
|
||||||
|
searchPageSize = 10,
|
||||||
|
)
|
||||||
|
|
||||||
@ -0,0 +1,151 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mmrcms.ar
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||||
|
import org.koitharu.kotatsu.parsers.model.*
|
||||||
|
import org.koitharu.kotatsu.parsers.site.mmrcms.MmrcmsParser
|
||||||
|
import org.koitharu.kotatsu.parsers.util.*
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("ONMA", "Onma", "es")
|
||||||
|
internal class Onma(context: MangaLoaderContext) :
|
||||||
|
MmrcmsParser(context, MangaSource.ONMA, "onma.me") {
|
||||||
|
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
|
||||||
|
override val selectState = "h3:contains(الحالة) .text"
|
||||||
|
override val selectAlt = "h3:contains(أسماء أخرى) .text"
|
||||||
|
override val selectAut = "h3:contains(المؤلف) .text"
|
||||||
|
override val selectTag = "h3:contains(التصنيفات) .text"
|
||||||
|
|
||||||
|
override suspend fun getListPage(
|
||||||
|
page: Int,
|
||||||
|
query: String?,
|
||||||
|
tags: Set<MangaTag>?,
|
||||||
|
sortOrder: SortOrder,
|
||||||
|
): List<Manga> {
|
||||||
|
val tag = tags.oneOrThrowIfMany()
|
||||||
|
val url = if (sortOrder == SortOrder.UPDATED) {
|
||||||
|
buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append("/latest-release")
|
||||||
|
append("?page=")
|
||||||
|
append(page.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildString {
|
||||||
|
append("https://")
|
||||||
|
append(domain)
|
||||||
|
append("/$listUrl/")
|
||||||
|
append("?page=")
|
||||||
|
append(page.toString())
|
||||||
|
append("&asc=true&author=&tag=")
|
||||||
|
append("&alpha=")
|
||||||
|
|
||||||
|
if (!query.isNullOrEmpty()) {
|
||||||
|
append(query.urlEncoded())
|
||||||
|
}
|
||||||
|
|
||||||
|
append("&cat=")
|
||||||
|
if (!tags.isNullOrEmpty()) {
|
||||||
|
append(tag?.key.orEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
append("&sortBy=")
|
||||||
|
when (sortOrder) {
|
||||||
|
SortOrder.POPULARITY -> append("views")
|
||||||
|
SortOrder.ALPHABETICAL -> append("name")
|
||||||
|
else -> append("views")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val doc = webClient.httpGet(url).parseHtml()
|
||||||
|
|
||||||
|
if (sortOrder == SortOrder.UPDATED) {
|
||||||
|
|
||||||
|
return doc.select("div.manga-item").map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
val deeplink = href.substringAfterLast('/')
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = "https://$domain/uploads/manga/$deeplink$imgUpdated",
|
||||||
|
title = div.selectFirstOrThrow("div.content-left a").text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = RATING_UNKNOWN,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return doc.select("div.chapter-container").map { div ->
|
||||||
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||||
|
Manga(
|
||||||
|
id = generateUid(href),
|
||||||
|
url = href,
|
||||||
|
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
|
||||||
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
|
title = div.selectFirstOrThrow("h5.media-heading").text().orEmpty(),
|
||||||
|
altTitle = null,
|
||||||
|
rating = div.selectFirstOrThrow("span").ownText().toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
|
||||||
|
tags = emptySet(),
|
||||||
|
author = null,
|
||||||
|
state = null,
|
||||||
|
source = source,
|
||||||
|
isNsfw = isNsfwSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
||||||
|
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||||
|
val body = doc.body().selectFirstOrThrow("div.panel-body")
|
||||||
|
|
||||||
|
val chaptersDeferred = async { getChapters(manga, doc) }
|
||||||
|
|
||||||
|
val desc = doc.selectFirst(selectDesc)?.text().orEmpty()
|
||||||
|
|
||||||
|
val stateDiv = body.selectFirst(selectState)
|
||||||
|
|
||||||
|
val state = stateDiv?.let {
|
||||||
|
when (it.text()) {
|
||||||
|
in ongoing -> MangaState.ONGOING
|
||||||
|
in finished -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val alt = doc.body().selectFirst(selectAlt)?.text()
|
||||||
|
val auth = doc.body().selectFirst(selectAut)?.text()
|
||||||
|
|
||||||
|
val tags = doc.body().selectFirst(selectTag)?.select("a") ?: emptySet()
|
||||||
|
|
||||||
|
manga.copy(
|
||||||
|
tags = tags.mapNotNullToSet { a ->
|
||||||
|
MangaTag(
|
||||||
|
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
|
||||||
|
title = a.text().toTitleCase(),
|
||||||
|
source = source,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
author = auth,
|
||||||
|
description = desc,
|
||||||
|
altTitle = alt,
|
||||||
|
state = state,
|
||||||
|
chapters = chaptersDeferred.await(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package org.koitharu.kotatsu.parsers.site.mmrcms.es
|
||||||
|
|
||||||
|
|
||||||
|
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.mmrcms.MmrcmsParser
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
|
@MangaSourceParser("ANZMANGASHD", "Anz Mangas Hd", "es")
|
||||||
|
internal class AnzMangasHd(context: MangaLoaderContext) :
|
||||||
|
MmrcmsParser(context, MangaSource.ANZMANGASHD, "www.anzmangashd.com") {
|
||||||
|
|
||||||
|
|
||||||
|
override val sourceLocale: Locale = Locale.ENGLISH
|
||||||
|
|
||||||
|
override val selectState = "dt:contains(Estado)"
|
||||||
|
override val selectAlt = "dt:contains(Otros nombres)"
|
||||||
|
override val selectAut = "dt:contains(Autor(es))"
|
||||||
|
override val selectTag = "dt:contains(Categorías)"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue