diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt index 7f515720..24e2c976 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt @@ -24,7 +24,7 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( private val chapterDateFormat = SimpleDateFormat("yyyy-MM-dd", sourceLocale) - override val sortOrders = EnumSet.of( + override val sortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, SortOrder.UPDATED, SortOrder.NEWEST, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt index 547e0152..c7e7696a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt @@ -72,7 +72,7 @@ internal abstract class FoolSlideParser( url = href, publicUrl = href.toAbsoluteUrl(div.host ?: domain), coverUrl = div.selectFirst("img")?.src().orEmpty(),// in search no img - title = div.selectFirstOrThrow(".title").text().orEmpty(), + title = div.selectFirstOrThrow(".title a").text().orEmpty(), altTitle = null, rating = RATING_UNKNOWN, tags = emptySet(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt new file mode 100644 index 00000000..e939cebc --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt @@ -0,0 +1,160 @@ +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.Manga +import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaState +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN +import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser +import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.generateUid +import org.koitharu.kotatsu.parsers.util.host +import org.koitharu.kotatsu.parsers.util.mapChapters +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow +import org.koitharu.kotatsu.parsers.util.src +import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl +import org.koitharu.kotatsu.parsers.util.tryParse +import org.koitharu.kotatsu.parsers.util.urlEncoded +import java.text.SimpleDateFormat +import java.util.ArrayList + + +@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?, + sortOrder: SortOrder, + ): List { + + val doc = if (!query.isNullOrEmpty()) { + + val url = buildString { + append("https://$domain/$searchUrl") + append("?q=") + append(query.urlEncoded()) + if (page > 1) { + return emptyList() + } + } + 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( + tags = emptySet(), + coverUrl = doc.selectFirst(".cover")?.src().orEmpty(),// for manga result on search + 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 { + 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 { + val chapterUrl = chapter.url.toAbsoluteUrl(domain) + val docs = webClient.httpGet(chapterUrl).parseHtml() + val max = docs.selectFirstOrThrow(".curr-page input").attr("data-max").toInt() + 1 + val pages = ArrayList(max) + for (i in 1 until max) { + val pagesUrl = chapterUrl + i + val page = webClient.httpGet(pagesUrl).parseHtml().requireElementById("page-image").attr("src") + pages.add( + MangaPage( + id = generateUid(page), + url = page, + preview = null, + source = source, + ), + ) + } + return pages + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/Seinagi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/Seinagi.kt new file mode 100644 index 00000000..f6a19fb3 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/Seinagi.kt @@ -0,0 +1,51 @@ +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( + tags = emptySet(), + coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search + description = desc, + altTitle = null, + author = author, + state = null, + chapters = chapters, + ) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/Pzykosis666hFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/Pzykosis666hFansub.kt new file mode 100644 index 00000000..8a2deaca --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/Pzykosis666hFansub.kt @@ -0,0 +1,49 @@ +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( + tags = emptySet(), + coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search + description = desc, + altTitle = null, + author = author, + state = null, + chapters = chapters, + ) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/SeinagiAdulto.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/SeinagiAdulto.kt new file mode 100644 index 00000000..57440d4e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/es/SeinagiAdulto.kt @@ -0,0 +1,56 @@ +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( + tags = emptySet(), + coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search + description = desc, + altTitle = null, + author = author, + state = null, + chapters = chapters, + ) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt index ee7a1f64..8239c5ae 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt @@ -178,7 +178,7 @@ internal class LugnicaScans(context: MangaLoaderContext) : PagedMangaParser(cont override suspend fun getTags(): Set = emptySet() - protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { val d = date?.lowercase() ?: return 0 return when { d.startsWith("il y a") -> parseRelativeDate(date) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt index 44713f57..e99d88f9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt @@ -130,7 +130,7 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, override suspend fun getTags(): Set = emptySet() - protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") -> parseRelativeDate(date) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt index 8c9ad3d8..44ee2bdc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt @@ -125,6 +125,19 @@ internal abstract class MadaraParser( "End", ) + @JvmField + protected val abandoned: Set = hashSetOf( + "Canceled", + "Cancelled", + "Cancelado", + "cancellato", + "Cancelados", + "Dropped", + "Discontinued", + "abandonné", + "Abandonné", + ) + // Change these values only if the site does not support manga listings via ajax protected open val withoutAjax = false @@ -234,6 +247,7 @@ internal abstract class MadaraParser( ?.lowercase()) { in ongoing -> MangaState.ONGOING in finished -> MangaState.FINISHED + in abandoned -> MangaState.ABANDONED else -> null }, source = source, @@ -310,6 +324,7 @@ internal abstract class MadaraParser( when (it.text()) { in ongoing -> MangaState.ONGOING in finished -> MangaState.FINISHED + in abandoned -> MangaState.ABANDONED else -> null } } @@ -399,7 +414,7 @@ internal abstract class MadaraParser( } protected open val selectBodyPage = "div.main-col-inner div.reading-content" - protected open val selectPage = "div.page-break" + protected open val selectPage = "div.page-break, div.login-required" override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt new file mode 100644 index 00000000..22b370a8 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt @@ -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 = 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?, + sortOrder: SortOrder, + ): List { + 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 { + val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() + val list = doc.body().selectFirstOrThrow("div.genre-menu").select("ul li").orEmpty() + val keySet = HashSet(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, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt new file mode 100644 index 00000000..f1e3077a --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt @@ -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?, + sortOrder: SortOrder, + ): List { + 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 { + val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() + val list = doc.body().selectFirstOrThrow("div.sub-menu").select("ul li").orEmpty() + val keySet = HashSet(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 { + 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().orEmpty() + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManyToonClub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManyToonClub.kt new file mode 100644 index 00000000..4b650edc --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManyToonClub.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/AkuManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/AkuManga.kt new file mode 100644 index 00000000..715cb613 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/AkuManga.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ComicArab.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ComicArab.kt new file mode 100644 index 00000000..3b06951e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ComicArab.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLekOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLekOrg.kt new file mode 100644 index 00000000..8deb25ec --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLekOrg.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarzCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarzCom.kt new file mode 100644 index 00000000..41d2a7a0 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarzCom.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ComicsValley.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ComicsValley.kt new file mode 100644 index 00000000..86118d29 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ComicsValley.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FreeComicOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FreeComicOnline.kt new file mode 100644 index 00000000..6c6dd117 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FreeComicOnline.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HuntersScanEn.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HuntersScanEn.kt new file mode 100644 index 00000000..af0a88be --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HuntersScanEn.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/InstaManhwa.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/InstaManhwa.kt new file mode 100644 index 00000000..90908052 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/InstaManhwa.kt @@ -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 = EnumSet.of( + SortOrder.ALPHABETICAL, + SortOrder.UPDATED, + SortOrder.NEWEST, + ) + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + 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 { + + 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, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LeviatanScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LeviatanScans.kt index 7002d2f7..6ba4378c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LeviatanScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LeviatanScans.kt @@ -5,6 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -@MangaSourceParser("LEVIATANSCANS", "Leviatan Scans", "en") +@MangaSourceParser("LEVIATANSCANS", "Ls Comic", "en") internal class LeviatanScans(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.LEVIATANSCANS, "en.leviatanscans.com", 10) + MadaraParser(context, MangaSource.LEVIATANSCANS, "lscomic.com", 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LhTranslation.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LhTranslation.kt new file mode 100644 index 00000000..786410db --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/LhTranslation.kt @@ -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") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEclipse.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEclipse.kt new file mode 100644 index 00000000..1fc75879 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEclipse.kt @@ -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") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaKik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaKik.kt new file mode 100644 index 00000000..b9a4b146 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaKik.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangasy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangasy.kt new file mode 100644 index 00000000..4fe251cc --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangasy.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt new file mode 100644 index 00000000..2880265c --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaRawCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaRawCom.kt new file mode 100644 index 00000000..4b11052b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaRawCom.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/PornComixOnlineNet.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/PornComixOnlineNet.kt new file mode 100644 index 00000000..3d590022 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/PornComixOnlineNet.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ResetScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ResetScans.kt new file mode 100644 index 00000000..2716dff6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ResetScans.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/SetsuScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/SetsuScans.kt new file mode 100644 index 00000000..5b13a869 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/SetsuScans.kt @@ -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") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt new file mode 100644 index 00000000..75ae8ed0 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/KnightnoScanlation.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/KnightnoScanlation.kt new file mode 100644 index 00000000..18f94633 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/KnightnoScanlation.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mangaxico.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mangaxico.kt new file mode 100644 index 00000000..c65509d9 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mangaxico.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Stickhorse.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Stickhorse.kt new file mode 100644 index 00000000..869311cf --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Stickhorse.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt new file mode 100644 index 00000000..4517d8df --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt @@ -0,0 +1,119 @@ +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.attrAsRelativeUrlOrNull +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 = EnumSet.of(SortOrder.POPULARITY) + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + 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.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(), + 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 { + return doc.body().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 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 { + 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, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TopComicPorno.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TopComicPorno.kt new file mode 100644 index 00000000..1e2c3f0b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TopComicPorno.kt @@ -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" + +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Vermanhwa.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Vermanhwa.kt index ccc5e3f4..95a0fbe0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Vermanhwa.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Vermanhwa.kt @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("VERMANHWA", "Vermanhwa", "es", ContentType.HENTAI) internal class Vermanhwa(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.VERMANHWA, "vermanhwa.es", 10) { + MadaraParser(context, MangaSource.VERMANHWA, "vermanhwa.com", 10) { override val withoutAjax = true } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Kataitake.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Kataitake.kt new file mode 100644 index 00000000..783e1dbf --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Kataitake.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komikto.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komikto.kt new file mode 100644 index 00000000..42a6476b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komikto.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ja/HachiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ja/HachiManga.kt index 2b7d38bf..d055028b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ja/HachiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ja/HachiManga.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.en +package org.koitharu.kotatsu.parsers.site.madara.ja import kotlinx.coroutines.async diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AkumanoTenshi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AkumanoTenshi.kt new file mode 100644 index 00000000..2ca72535 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AkumanoTenshi.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/HuntersScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/HuntersScan.kt new file mode 100644 index 00000000..c35ce080 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/HuntersScan.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/YanpFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/YanpFansub.kt new file mode 100644 index 00000000..92c415cf --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/YanpFansub.kt @@ -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") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt new file mode 100644 index 00000000..bed06a99 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/CloverManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/CloverManga.kt new file mode 100644 index 00000000..16fb78a8 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/CloverManga.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/GhostFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/GhostFansub.kt new file mode 100644 index 00000000..70c2dbe4 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/GhostFansub.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Hayalistic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Hayalistic.kt index 5f3257d0..6487258d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Hayalistic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Hayalistic.kt @@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("HAYALISTIC", "Hayalistic", "tr") internal class Hayalistic(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.HAYALISTIC, "hayalistic.com", 24) { + MadaraParser(context, MangaSource.HAYALISTIC, "hayalistic.com.tr", 24) { override val datePattern = "dd/MM/yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt new file mode 100644 index 00000000..f67727be --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/MangaWow.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/MangaWow.kt new file mode 100644 index 00000000..df2092d1 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/MangaWow.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangakeyfi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangakeyfi.kt index 44a7aa43..385af35a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangakeyfi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangakeyfi.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.pt +package org.koitharu.kotatsu.parsers.site.madara.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangasehri.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangasehri.kt index e2308e37..a7cd0595 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangasehri.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangasehri.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.pt +package org.koitharu.kotatsu.parsers.site.madara.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangawt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangawt.kt index 19fd1ecd..594e9575 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangawt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Mangawt.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.pt +package org.koitharu.kotatsu.parsers.site.madara.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/NiveraFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/NiveraFansub.kt index f2358661..68e52d89 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/NiveraFansub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/NiveraFansub.kt @@ -13,5 +13,4 @@ internal class NiveraFansub(context: MangaLoaderContext) : MadaraParser(context, MangaSource.NIVERAFANSUB, "niverafansub.co") { override val datePattern = "d MMMM yyyy" - override val selectPage = "div.page-break, div.login-required" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/PiedpiperFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/PiedpiperFansub.kt new file mode 100644 index 00000000..291e8c22 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/PiedpiperFansub.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/ReaperScansTr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/ReaperScansTr.kt new file mode 100644 index 00000000..cb1e4c4e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/ReaperScansTr.kt @@ -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/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/StrayFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/StrayFansub.kt new file mode 100644 index 00000000..0e05bec5 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/StrayFansub.kt @@ -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") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt new file mode 100644 index 00000000..754552a4 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt @@ -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 { + 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.selectFirst("a") + val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing") + 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, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonhatti.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonhatti.kt index 0bc63acc..8f5c7577 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonhatti.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonhatti.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.pt +package org.koitharu.kotatsu.parsers.site.madara.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -7,8 +7,9 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("WEBTOONHATTI", "Webtoonhatti", "tr") internal class Webtoonhatti(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.WEBTOONHATTI, "webtoonhatti.com", 20) { + MadaraParser(context, MangaSource.WEBTOONHATTI, "webtoonhatti.net", 20) { + override val listUrl = "webtoon/" override val tagPrefix = "webtoon-tur/" override val datePattern = "d MMMM" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoontr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoontr.kt index 12d0f6d6..533395f3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoontr.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoontr.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.pt +package org.koitharu.kotatsu.parsers.site.madara.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext @@ -8,8 +8,9 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("WEBTOONTR", "Webtoontr", "tr") internal class Webtoontr(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.WEBTOONTR, "webtoon-tr.com", 16) { + MadaraParser(context, MangaSource.WEBTOONTR, "webtoontr.net", 16) { override val tagPrefix = "webtoon-kategori/" + override val listUrl = "webtoon/" override val datePattern = "dd/MM/yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/all/ManhuaScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/all/ManhuaScan.kt new file mode 100644 index 00000000..b0f75f0d --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/all/ManhuaScan.kt @@ -0,0 +1,102 @@ +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.ArrayList +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?, + sortOrder: SortOrder, + ): List { + val url = buildString { + append("https://") + append(domain) + append("/$listUrl?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 { + 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(",") + + val pages = ArrayList() + images.map { + pages.add( + MangaPage( + id = generateUid(it), + url = it, + preview = null, + source = source, + ), + ) + } + return pages + } + +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/Manga18.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/Manga18.kt index 75714c1c..44d24c08 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/Manga18.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/Manga18.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.en +package org.koitharu.kotatsu.parsers.site.manga18.en import org.koitharu.kotatsu.parsers.MangaLoaderContext diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/PornComic18.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/PornComic18.kt index 617e9621..0fdb46a3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/PornComic18.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/en/PornComic18.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.en +package org.koitharu.kotatsu.parsers.site.manga18.en import org.koitharu.kotatsu.parsers.MangaLoaderContext diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/es/Tumanhwas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/es/Tumanhwas.kt index b9ebecfa..dc569bd9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/es/Tumanhwas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/es/Tumanhwas.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.madara.es +package org.koitharu.kotatsu.parsers.site.manga18.es import org.koitharu.kotatsu.parsers.MangaLoaderContext diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt index 2035be22..27a68c08 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt @@ -113,6 +113,9 @@ internal abstract class MangaReaderParser( "Завершено", "Finished", "Finalizado", "Completata", "One-Shot", "Bitti", "Tamat", "Completado", "Concluído", "Concluido", "已完结", "Bitmiş", -> MangaState.FINISHED + "Canceled", "Cancelled", "Cancelado", "cancellato", "Cancelados", "Dropped", "Discontinued", "abandonné", "Abandonné", + -> MangaState.ABANDONED + else -> null } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt index 9da0c5e0..5b43fdf0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt @@ -7,6 +7,6 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ARESMANGA", "AresManga", "ar") internal class AresManga(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.ARESMANGA, "aresmanga.org", pageSize = 20, searchPageSize = 10) { + MangaReaderParser(context, MangaSource.ARESMANGA, "aresnov.org", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/GalaxyAction.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/GalaxyAction.kt new file mode 100644 index 00000000..c7df38ce --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/GalaxyAction.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SpiderScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SpiderScans.kt new file mode 100644 index 00000000..f99517d0 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SpiderScans.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt new file mode 100644 index 00000000..d582c275 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt @@ -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")?.text() ?: "Chapter ${index + 1}", + url = url, + number = index + 1, + scanlator = null, + uploadDate = 0, + branch = null, + source = source, + ) + } + return parseInfo(docs, manga, chapters) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt new file mode 100644 index 00000000..c52cab45 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt new file mode 100644 index 00000000..274d06ac --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LyraScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LyraScans.kt new file mode 100644 index 00000000..100e4cb7 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LyraScans.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MangaGenki.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MangaGenki.kt new file mode 100644 index 00000000..6f8ece4f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MangaGenki.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt index 0b040ab6..d6d2236f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt @@ -154,7 +154,7 @@ internal class ManhwaFreak(context: MangaLoaderContext) : ) } - protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + 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 { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt new file mode 100644 index 00000000..39c7fbe9 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TecnoScann.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt similarity index 57% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TecnoScann.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt index 856b842c..1f2c6edd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TecnoScann.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt @@ -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.MangaSourceParser 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") internal class TecnoScann(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.TECNOSCANN, "tecnoscann.com", 24) + MangaReaderParser(context, MangaSource.TECNOSCANN, "tecnoscann.com", 20, 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt new file mode 100644 index 00000000..fae9588e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt @@ -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 + get() = EnumSet.of(SortOrder.NEWEST) + + override suspend fun getTags(): Set = emptySet() + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + 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")?.text() ?: "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): Manga { + + val stateSelect = docs.selectFirst(".tsinfo div:contains(Estado)") + val state = stateSelect?.lastElementChild() + val mangaState = state?.let { + when (it.text()) { + "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 { + val chapterUrl = chapter.url.toAbsoluteUrl(domain) + val docs = webClient.httpGet(chapterUrl).parseHtml() + val pages = ArrayList() + 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 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LelManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LelManga.kt index 97a37650..f7afcbe1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LelManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LelManga.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr -import org.jsoup.nodes.Element import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaChapter diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt index 6f6313b7..138d16d7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.mangareader.en +package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext @@ -155,7 +155,7 @@ internal class ManhwaFreakFr(context: MangaLoaderContext) : ) } - protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + 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 { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt new file mode 100644 index 00000000..b587d01b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt @@ -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?, + sortOrder: SortOrder, + ): List { + 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()) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt new file mode 100644 index 00000000..35009394 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt index dd51f88f..a79afba9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt @@ -9,4 +9,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KANZENIN", "Kanzenin", "id", ContentType.HENTAI) internal class Kanzenin(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.KANZENIN, "kanzenin.xyz", pageSize = 25, searchPageSize = 25) + MangaReaderParser(context, MangaSource.KANZENIN, "kanzenin.info", pageSize = 27, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt index 560332fd..17beb68c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KOMIKTAP", "KomikTap", "id") internal class KomikTapParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.KOMIKTAP, "komiktap.in", pageSize = 25, searchPageSize = 10) { + MangaReaderParser(context, MangaSource.KOMIKTAP, "92.87.6.124", pageSize = 25, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt index 40070c12..1d0f5a28 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt @@ -13,7 +13,7 @@ import java.util.* @MangaSourceParser("KOMIKCAST", "Komikcast", "id") internal class Komikcast(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.KOMIKCAST, "komikcast.io", pageSize = 60, searchPageSize = 28) { + MangaReaderParser(context, MangaSource.KOMIKCAST, "komikcast.vip", pageSize = 60, searchPageSize = 28) { override val listUrl = "/daftar-komik" override val datePattern = "MMM d, yyyy" @@ -63,7 +63,7 @@ internal class Komikcast(context: MangaLoaderContext) : append(listUrl) append("/page/") append(page) - append("/?order=") + append("/?orderby=") append(sortQuery) append(tagQuery) } @@ -75,7 +75,7 @@ internal class Komikcast(context: MangaLoaderContext) : val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat(datePattern, sourceLocale) val chapters = docs.select("#chapter-wrapper > li").mapChapters(reversed = true) { index, element -> - val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null + val url = element.selectFirst("a.chapter-link-item")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), name = element.selectFirst("a.chapter-link-item")?.ownText().orEmpty(), @@ -132,7 +132,7 @@ internal class Komikcast(context: MangaLoaderContext) : override fun parseMangaList(docs: Document): List { return docs.select("div.list-update_item").mapNotNull { - val a = it.selectFirst("a") ?: return@mapNotNull null + val a = it.selectFirstOrThrow("a.data-tooltip") val relativeUrl = a.attrAsRelativeUrl("href") val rating = it.selectFirst(".numscore")?.text()?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN @@ -191,7 +191,7 @@ internal class Komikcast(context: MangaLoaderContext) : } - protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { date ?: return 0 return when { date.endsWith(" ago", ignoreCase = true) -> { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiScans.kt new file mode 100644 index 00000000..14793171 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiScans.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt new file mode 100644 index 00000000..f395e654 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt @@ -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 +} + diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt new file mode 100644 index 00000000..b373c3f4 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SugarLab.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SugarLab.kt new file mode 100644 index 00000000..93192aa6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SugarLab.kt @@ -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 +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YumeKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YumeKomik.kt new file mode 100644 index 00000000..5befa9e1 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YumeKomik.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt new file mode 100644 index 00000000..614e4da2 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt @@ -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 + +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DiskusScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DiskusScan.kt new file mode 100644 index 00000000..936a9c45 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DiskusScan.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt new file mode 100644 index 00000000..5f062871 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt new file mode 100644 index 00000000..af5953ed --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt new file mode 100644 index 00000000..022301fc --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt @@ -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" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.kt index 22d59298..c6d63d39 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.kt @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("LSHISTORIA", "Lshistoria", "tr") internal class Lshistoria(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.LSHISTORIA, "omkomik.com", pageSize = 20, searchPageSize = 10) + MangaReaderParser(context, MangaSource.LSHISTORIA, "lshistoria.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt new file mode 100644 index 00000000..1ef2ff79 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt @@ -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) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt new file mode 100644 index 00000000..8b24bcd6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt @@ -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, + ) + diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt index d2a5125b..0d87807b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt @@ -44,6 +44,7 @@ internal abstract class MmrcmsParser( "En cours", "En curso", "DEVAM EDİYOR", + "مستمرة", ) @JvmField @@ -53,6 +54,7 @@ internal abstract class MmrcmsParser( "Complete", "Terminé", "TAMAMLANDI", + "مكتملة", ) protected open val imgUpdated = "/cover/cover_250x350.jpg" @@ -65,7 +67,6 @@ internal abstract class MmrcmsParser( ): List { val tag = tags.oneOrThrowIfMany() val url = if (sortOrder == SortOrder.UPDATED) { - //the Updated page doesn't really exist, we just use the home page to weight the latest chapters, so it doesn't include tag and page management. buildString { append("https://") append(domain) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/ar/Onma.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/ar/Onma.kt new file mode 100644 index 00000000..bf20b1df --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/ar/Onma.kt @@ -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?, + sortOrder: SortOrder, + ): List { + 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(), + ) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/es/AnzMangasHd.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/es/AnzMangasHd.kt new file mode 100644 index 00000000..5ae35519 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/es/AnzMangasHd.kt @@ -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)" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/ScanManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/ScanManga.kt new file mode 100644 index 00000000..213d6935 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/ScanManga.kt @@ -0,0 +1,17 @@ +package org.koitharu.kotatsu.parsers.site.mmrcms.fr + + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mmrcms.MmrcmsParser +import java.util.Locale + + +@MangaSourceParser("SCANMANGA", "Scan Manga", "fr") +internal class ScanManga(context: MangaLoaderContext) : + MmrcmsParser(context, MangaSource.SCANMANGA, "scan-manga.me") { + + override val imgUpdated = ".jpg" + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt index 5ff7ede8..18734698 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt @@ -290,7 +290,7 @@ internal abstract class OtakuSanctuaryParser( } - fun processUrl(url: String, vi: String = ""): String { + private fun processUrl(url: String, vi: String = ""): String { var url = url.replace("_h_", "http") .replace("_e_", "/extendContent/Manga") .replace("_r_", "/extendContent/MangaRaw") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/UnionMangasParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/UnionMangasParser.kt index de8baa37..4d6dd01f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/UnionMangasParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/UnionMangasParser.kt @@ -15,7 +15,7 @@ import java.util.* @MangaSourceParser("UNION_MANGAS", "Union Mangás", "pt") class UnionMangasParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.UNION_MANGAS, 40) { - override val sortOrders = EnumSet.of( + override val sortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, SortOrder.POPULARITY, )