From 5d5f804c768cd2db698ff225cb79c088555c63d5 Mon Sep 17 00:00:00 2001 From: devi Date: Wed, 30 Aug 2023 13:44:25 +0200 Subject: [PATCH 01/15] add sources and fix --- .../kotatsu/parsers/site/ar/TeamXNovel.kt | 184 ++++++++++++++ .../kotatsu/parsers/site/en/MangaGeko.kt | 147 +++++++++++ .../parsers/site/madara/MadaraParser.kt | 17 +- .../kotatsu/parsers/site/madara/all/Ero18x.kt | 56 +++++ .../parsers/site/madara/all/ManhwaRaw.kt | 57 +++++ .../parsers/site/madara/en/GoodGirls.kt | 12 + .../parsers/site/madara/en/HiperDex.kt | 11 + .../en/{MangaGreatOrg.kt => MangaDinoTop.kt} | 8 +- .../parsers/site/madara/en/MangaGreat.kt | 2 +- .../parsers/site/madara/en/MangaOwlBlog.kt | 13 + .../parsers/site/madara/en/MangaRawInfo.kt | 12 + .../parsers/site/madara/en/MangaUpdatesTop.kt | 12 + .../parsers/site/madara/en/ManhuaScanInfo.kt | 12 + .../parsers/site/madara/en/StkissMangaBlog.kt | 12 + .../parsers/site/madara/es/ManhwaEs.kt | 55 ++++ .../parsers/site/madara/es/UniToonOficial.kt | 15 ++ .../parsers/site/madara/fr/Hentaizone.kt | 7 +- .../parsers/site/madara/fr/Readergen.kt | 10 + .../parsers/site/madara/id/ManhwaHub.kt | 121 +++++++++ .../site/madara/it/Beyondtheataraxia.kt | 12 + .../parsers/site/madara/pt/CeriseScans.kt | 2 +- .../parsers/site/madara/pt/GhostScan.kt | 13 + .../parsers/site/madara/tr/AlliedFansub.kt | 13 + .../parsers/site/madara/tr/SarcasmScans.kt | 11 + .../parsers/site/madara/vi/Mi2Manga.kt | 2 +- .../site/mangareader/MangaReaderParser.kt | 2 +- .../parsers/site/mangareader/ar/AresManga.kt | 12 + .../site/mangareader/ar/EnAresManga.kt | 12 + .../parsers/site/mangareader/ar/MangaProtm.kt | 4 +- .../parsers/site/mangareader/ar/SwaTeam.kt | 2 +- .../parsers/site/mangareader/ar/VexManga.kt | 98 ++++++++ .../site/mangareader/en/AsuraScansParser.kt | 2 +- .../parsers/site/mangareader/en/FreakComic.kt | 10 + .../site/mangareader/en/ManhwaFreak.kt | 237 +++++++++++++++++ .../site/mangareader/es/MangaShiina.kt | 10 + .../site/mangareader/fr/ManhwaFreakFr.kt | 238 ++++++++++++++++++ .../site/mangareader/id/MasterKomik.kt | 5 +- .../parsers/site/mangareader/id/OmKomik.kt | 11 + .../parsers/site/mangareader/pt/Mangaschan.kt | 4 +- .../parsers/site/mangareader/tr/Lshistoria.kt | 10 + .../site/mangareader/tr/MoonDaisyScans.kt | 11 + .../parsers/site/mangareader/tr/SummerToon.kt | 11 + .../parsers/site/mmrcms/MmrcmsParser.kt | 2 + .../parsers/site/mmrcms/tr/MangaDenizi.kt | 20 ++ .../kotatsu/parsers/site/tr/SadScans.kt | 95 +++++++ 45 files changed, 1589 insertions(+), 23 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/GoodGirls.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HiperDex.kt rename src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/{MangaGreatOrg.kt => MangaDinoTop.kt} (55%) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaOwlBlog.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaRawInfo.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaUpdatesTop.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhuaScanInfo.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/StkissMangaBlog.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/UniToonOficial.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Readergen.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/it/Beyondtheataraxia.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GhostScan.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/AlliedFansub.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/SarcasmScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/EnAresManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaShiina.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/tr/MangaDenizi.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt new file mode 100644 index 00000000..3dbd5406 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt @@ -0,0 +1,184 @@ +package org.koitharu.kotatsu.parsers.site.ar + +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import org.jsoup.nodes.Element +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("TEAMXNOVEL", "TeamXNovel", "ar") +internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.TEAMXNOVEL, 10) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override val configKeyDomain = ConfigKey.Domain("teamxnovel.com") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + val tag = tags.oneOrThrowIfMany() + + val url = buildString { + append("https://$domain") + if (!tags.isNullOrEmpty()) { + append("/series?genre=") + append(tag?.key.orEmpty()) + if (page > 1) { + append("&page=") + append(page) + } + } else if (!query.isNullOrEmpty()) { + append("/series?search=") + append(query.urlEncoded()) + if (page > 1) { + append("&page=") + append(page) + } + } else { + when (sortOrder) { + SortOrder.POPULARITY -> append("/series") + SortOrder.UPDATED -> append("/") + else -> append("/") + } + if (page > 1) { + append("?page=") + append(page) + } + } + } + + val doc = webClient.httpGet(url).parseHtml() + + return doc.select("div.listupd .bs .bsx").ifEmpty { + doc.select("div.post-body .box") + }.map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.select(".tt, h3").text(), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = div.selectFirstOrThrow("img").src().orEmpty(), + tags = emptySet(), + state = when (div.selectFirst(".status")?.text()) { + "مستمرة" -> MangaState.ONGOING + "متوقف", "مكتمل" -> MangaState.FINISHED + else -> null + }, + author = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/series").parseHtml() + return doc.requireElementById("select_genre").select("option").mapNotNullToSet { + MangaTag( + key = it.attr("value"), + title = it.text(), + source = source, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val mangaUrl = manga.url.toAbsoluteUrl(domain) + + val maxPageChapterSelect = doc.select(".pagination .page-item a") + var maxPageChapter = 1 + if (!maxPageChapterSelect.isNullOrEmpty()) { + maxPageChapterSelect.map { + val i = it.attr("href").substringAfterLast("=").toInt() + if (i > maxPageChapter) { + maxPageChapter = i + } + } + } + + return manga.copy( + altTitle = null, + state = when (doc.selectFirstOrThrow(".full-list-info:contains(الحالة:) a").text()) { + "مستمرة" -> MangaState.ONGOING + "متوقف", "مكتمل" -> MangaState.FINISHED + else -> null + }, + tags = doc.select(".review-author-info a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").substringAfterLast("="), + title = a.text(), + source = source, + ) + }, + author = null, + description = doc.selectFirstOrThrow(".review-content").text(), + chapters = run { + if (maxPageChapter == 1) { + parseChapters(doc) + } else { + coroutineScope { + val result = ArrayList(parseChapters(doc)) + result.ensureCapacity(result.size * maxPageChapter) + (2..maxPageChapter).map { i -> + async { + loadChapters(mangaUrl, i) + } + }.awaitAll() + .flattenTo(result) + result + } + } + }.reversed(), + ) + } + + private suspend fun loadChapters(baseUrl: String, page: Int): List { + return parseChapters(webClient.httpGet("$baseUrl?page=$page").parseHtml().body()) + } + + private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", sourceLocale) + + private fun parseChapters(root: Element): List { + return root.requireElementById("chapter-contact").select(".eplister ul li") + .map { li -> + val url = li.selectFirstOrThrow("a").attrAsRelativeUrl("href") + MangaChapter( + id = generateUid(url), + name = li.selectFirstOrThrow(".epl-title").text(), + number = url.substringAfterLast('/').toIntOrNull() ?: 0, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(li.selectFirstOrThrow(".epl-date").text()), + branch = null, + source = source, + ) + } + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select(".image_list img").map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt new file mode 100644 index 00000000..d79f7bae --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt @@ -0,0 +1,147 @@ +package org.koitharu.kotatsu.parsers.site.en + +import okhttp3.Headers +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.network.UserAgents +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("MANGAGEKO", "MangaGeko", "en") +internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.MANGAGEKO, 30) { + + override val sortOrders: Set = EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST) + + override val configKeyDomain = ConfigKey.Domain("www.mangageko.com") + + override val headers: Headers = Headers.Builder() + .add("User-Agent", UserAgents.CHROME_DESKTOP) + .build() + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + val tag = tags.oneOrThrowIfMany() + + val url = if (!query.isNullOrEmpty()) { + if (page > 1) { + return emptyList() + } + buildString { + append("https://$domain/search/?search=") + append(query.urlEncoded()) + } + } else { + buildString { + append("https://$domain/browse-comics/?results=") + append(page) + append("&filter=") + when (sortOrder) { + SortOrder.POPULARITY -> append("views") + SortOrder.UPDATED -> append("Updated") + SortOrder.NEWEST -> append("New") + else -> append("Updated") + } + if (!tags.isNullOrEmpty()) { + append("&genre=") + append(tag?.key.orEmpty()) + } + } + } + + val doc = webClient.httpGet(url).parseHtml() + + return doc.select("li.novel-item").map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.selectFirstOrThrow("h4").text(), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = div.selectFirstOrThrow("img").src().orEmpty(), + tags = emptySet(), + state = null, + author = div.selectFirstOrThrow("h6").text().removePrefix("Author(S): "), + source = source, + ) + } + } + + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml() + return doc.select("label.checkbox-inline").mapNotNullToSet { label -> + MangaTag( + key = label.selectFirstOrThrow("input").attr("value"), + title = label.text(), + source = source, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("MMM dd, yyyy", sourceLocale) + return manga.copy( + altTitle = doc.selectFirstOrThrow(".alternative-title").text(), + state = when (doc.selectFirstOrThrow(".header-stats span:contains(Status) strong").text()) { + "Ongoing" -> MangaState.ONGOING + "Completed" -> MangaState.FINISHED + else -> null + }, + tags = doc.select(".categories ul li a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").substringAfterLast("="), + title = a.text(), + source = source, + ) + }, + author = doc.selectFirstOrThrow(".author").text(), + description = doc.selectFirstOrThrow(".description").html(), + chapters = doc.requireElementById("chapters").select("ul.chapter-list li") + .mapChapters(reversed = true) { i, li -> + val a = li.selectFirstOrThrow("a") + val url = a.attrAsRelativeUrl("href") + val name = li.selectFirstOrThrow(".chapter-title").text() + val dateText = li.select(".chapter-update").attr("datetime").substringBeforeLast(",") + .replace(".", "").replace("Sept", "Sep") + MangaChapter( + id = generateUid(url), + name = name, + number = i + 1, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(dateText), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + + return doc.requireElementById("chapter-reader").select("img").map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + +} 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 e3a71e68..4632d2dd 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 @@ -155,7 +155,7 @@ internal abstract class MadaraParser( !tags.isNullOrEmpty() -> { append("/$tagPrefix") append(tag?.key.orEmpty()) - if (page > 1) { + if (pages > 1) { append("/page/") append(pages.toString()) } @@ -165,7 +165,7 @@ internal abstract class MadaraParser( else -> { append("/$listUrl") - if (page > 1) { + if (pages > 1) { append("page/") append(pages) } @@ -278,9 +278,9 @@ internal abstract class MadaraParser( val doc = webClient.httpGet(fullUrl).parseHtml() val body = doc.body() - val testchekasync = body.select(selectTestAsync) + val testCheckAsync = body.select(selectTestAsync) - val chaptersDeferred = if (testchekasync.isNullOrEmpty()) { + val chaptersDeferred = if (testCheckAsync.isNullOrEmpty()) { async { loadChapters(manga.url, doc) } } else { async { getChapters(manga, doc) } @@ -373,6 +373,7 @@ internal abstract class MadaraParser( val url = mangaUrl.toAbsoluteUrl(domain).removeSuffix('/') + "/ajax/chapters/" webClient.httpPost(url, emptyMap()).parseHtml() } + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) return doc.select(selectChapter).mapChapters(reversed = true) { i, li -> @@ -563,7 +564,13 @@ internal abstract class MadaraParser( ) }.timeInMillis - WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("month", "months", "أشهر").anyWordIn(date) -> cal.apply { + add( + Calendar.MONTH, + -number, + ) + }.timeInMillis + WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis else -> 0 } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt new file mode 100644 index 00000000..89d26a74 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt @@ -0,0 +1,56 @@ +package org.koitharu.kotatsu.parsers.site.madara.all + +import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.Manga +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.attrAsRelativeUrlOrNull +import org.koitharu.kotatsu.parsers.util.generateUid +import org.koitharu.kotatsu.parsers.util.mapChapters +import org.koitharu.kotatsu.parsers.util.parseFailed +import java.text.SimpleDateFormat +import java.util.Locale + +@MangaSourceParser("ERO18X", "Ero18x", "", ContentType.HENTAI) +internal class Ero18x(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.ERO18X, "ero18x.com", 10) { + override val datePattern = "MMMM d" + override val sourceLocale: Locale = Locale.ENGLISH + override val withoutAjax = true + + override suspend fun getChapters(manga: Manga, doc: Document): List { + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + 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 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), + name = name, + number = i + 1, + url = link, + uploadDate = if (dateText == "Newly Published!") { + parseChapterDate( + dateFormat, + "today", + ) + } else { + parseChapterDate( + dateFormat, + dateText, + ) + }, + source = source, + scanlator = null, + branch = null, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt new file mode 100644 index 00000000..4241fed7 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt @@ -0,0 +1,57 @@ +package org.koitharu.kotatsu.parsers.site.madara.all + +import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.Manga +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.attrAsRelativeUrlOrNull +import org.koitharu.kotatsu.parsers.util.generateUid +import org.koitharu.kotatsu.parsers.util.mapChapters +import org.koitharu.kotatsu.parsers.util.parseFailed +import java.text.SimpleDateFormat +import java.util.Locale + +@MangaSourceParser("MANHWARAW", "Manhwa Raw", "", ContentType.HENTAI) +internal class ManhwaRaw(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANHWARAW, "manhwa-raw.com", 10) { + override val datePattern = "MMMM d" + override val sourceLocale: Locale = Locale.ENGLISH + override val withoutAjax = true + + + override suspend fun getChapters(manga: Manga, doc: Document): List { + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + 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 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), + name = name, + number = i + 1, + url = link, + uploadDate = if (dateText == "Newly Published!") { + parseChapterDate( + dateFormat, + "today", + ) + } else { + parseChapterDate( + dateFormat, + dateText, + ) + }, + source = source, + scanlator = null, + branch = null, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/GoodGirls.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/GoodGirls.kt new file mode 100644 index 00000000..381ccb65 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/GoodGirls.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("GOODGIRLS", "GoodGirls", "en") +internal class GoodGirls(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.GOODGIRLS, "goodgirls.moe", 10) { + override val selectDesc = "div.post-content_item:contains(Synopsis) div.summary-content" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HiperDex.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HiperDex.kt new file mode 100644 index 00000000..bd5b7a47 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HiperDex.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.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("HIPERDEX", "HiperDex", "en", ContentType.HENTAI) +internal class HiperDex(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.HIPERDEX, "hiperdex.com", 36) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreatOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDinoTop.kt similarity index 55% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreatOrg.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDinoTop.kt index 6a28fb12..2b6a9c0f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreatOrg.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDinoTop.kt @@ -5,6 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -@MangaSourceParser("MANGAGREAT_ORG", "MangaGreat Org", "en") -internal class MangaGreatOrg(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MANGAGREAT_ORG, "mangagreat.org") +@MangaSourceParser("MANGADINOTOP", "MangaDino Top", "en") +internal class MangaDinoTop(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGADINOTOP, "mangadino.top", 10) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreat.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreat.kt index 5fd20b23..6917c62b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreat.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaGreat.kt @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("MANGAGREAT", "MangaGreat", "en") internal class MangaGreat(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MANGAGREAT, "mangagreat.com") + MadaraParser(context, MangaSource.MANGAGREAT, "mangagreat.org") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaOwlBlog.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaOwlBlog.kt new file mode 100644 index 00000000..7376d452 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaOwlBlog.kt @@ -0,0 +1,13 @@ +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("MANGAOWLBLOG", "MangaOwl Blog (unoriginal)", "en", ContentType.HENTAI) +internal class MangaOwlBlog(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGAOWLBLOG, "mangaowl.blog", 20) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaRawInfo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaRawInfo.kt new file mode 100644 index 00000000..9fd1279b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaRawInfo.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("MANGARAWINFO", "Manga-Raw Info (unoriginal)", "en") +internal class MangaRawInfo(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGARAWINFO, "manga-raw.info", 20) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaUpdatesTop.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaUpdatesTop.kt new file mode 100644 index 00000000..0067e287 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaUpdatesTop.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("MANGAUPDATESTOP", "MangaUpdates Top (unoriginal)", "en") +internal class MangaUpdatesTop(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGAUPDATESTOP, "mangaupdates.top", 10) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhuaScanInfo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhuaScanInfo.kt new file mode 100644 index 00000000..db645bf3 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhuaScanInfo.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("MANHUASCANINFO", "ManhuaScan Info (unoriginal)", "en") +internal class ManhuaScanInfo(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANHUASCANINFO, "manhuascan.info", 10) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/StkissMangaBlog.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/StkissMangaBlog.kt new file mode 100644 index 00000000..0d46de08 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/StkissMangaBlog.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("STKISSMANGABLOG", "StkissManga Blog", "en") +internal class StkissMangaBlog(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.STKISSMANGABLOG, "1stkissmanga.blog", 10) { + override val postreq = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt new file mode 100644 index 00000000..1c58527c --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt @@ -0,0 +1,55 @@ +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.Manga +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.attrAsRelativeUrlOrNull +import org.koitharu.kotatsu.parsers.util.generateUid +import org.koitharu.kotatsu.parsers.util.mapChapters +import org.koitharu.kotatsu.parsers.util.parseFailed +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow +import java.text.SimpleDateFormat + +@MangaSourceParser("MANHWA_ES", "Manhwa Es", "es") +internal class ManhwaEs(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANHWA_ES, "manhwa-es.com", 10) { + + override val withoutAjax = true + override val datePattern = "d 'de' MMMM" + + override suspend fun getChapters(manga: Manga, doc: Document): List { + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + 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 dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text() + + val name = li.selectFirstOrThrow(".mini-letters a").text() + MangaChapter( + id = generateUid(href), + name = name, + number = i + 1, + url = link, + uploadDate = if (dateText == "¡Recién publicado!") { + parseChapterDate( + dateFormat, + "today", + ) + } else { + parseChapterDate( + dateFormat, + dateText, + ) + }, + source = source, + scanlator = null, + branch = null, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/UniToonOficial.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/UniToonOficial.kt new file mode 100644 index 00000000..72a94576 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/UniToonOficial.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("UNITOONOFICIAL", "UniToonOficial", "es") +internal class UniToonOficial(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.UNITOONOFICIAL, "unitoonoficial.com") { + + override val datePattern = "dd/MM/yyyy" + override val tagPrefix = "generos/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt index 8b84945e..b2fc38da 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt @@ -31,11 +31,12 @@ internal class Hentaizone(context: MangaLoaderContext) : // correct parse date missing a "." val dateOrg = li.selectFirst("span.chapter-release-date i")?.text() ?: "janv 1, 2000" val dateCorrectParse = dateOrg - .replace("janv", "janv.") - .replace("févr", "févr.") + .replace("Jan", "janv.") + .replace("Fév", "févr.") + .replace("Mar", "mars") .replace("avr", "avr.") .replace("juil", "juil.") - .replace("sept", "sept.") + .replace("Sep", "sept.") .replace("nov", "nov.") .replace("oct", "oct.") .replace("déc", "déc.") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Readergen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Readergen.kt new file mode 100644 index 00000000..ce7c32b2 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Readergen.kt @@ -0,0 +1,10 @@ +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.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("READERGEN", "Readergen", "fr") +internal class Readergen(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.READERGEN, "fr.readergen.fr", 18) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt new file mode 100644 index 00000000..88742d71 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt @@ -0,0 +1,121 @@ +package org.koitharu.kotatsu.parsers.site.madara.id + + +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.madara.MadaraParser +import org.koitharu.kotatsu.parsers.util.* +import java.util.* + +@MangaSourceParser("MANHWAHUB", "ManhwaHub", "id", ContentType.HENTAI) +internal class ManhwaHub(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANHWAHUB, "manhwahub.net", 40) { + + override val tagPrefix = "genre/" + override val datePattern = "MMMM d, yyyy" + override val sourceLocale: Locale = Locale.ENGLISH + override val withoutAjax = true + override val listUrl = "genre/manhwa" + override val selectTestAsync = "ul.box-list-chapter" + 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?s=") + append(query.urlEncoded()) + append("&page=") + append(pages) + } + + !tags.isNullOrEmpty() -> { + append("/$tagPrefix") + append(tag?.key.orEmpty()) + append("?page=") + append(pages) + append("&") + } + + else -> { + + append("/$listUrl") + append("?page=") + append(pages) + append("&") + } + + } + + append("m_orderby=") + when (sortOrder) { + SortOrder.POPULARITY -> append("views") + SortOrder.UPDATED -> append("latest") + SortOrder.NEWEST -> append("new-manga") + SortOrder.ALPHABETICAL -> append("alphabet") + SortOrder.RATING -> append("rating") + } + } + val doc = webClient.httpGet(url).parseHtml() + + return doc.select("div.row.c-tabs-item__content").ifEmpty { + doc.select("div.page-item-detail") + }.map { div -> + val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found") + val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary") + Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(div.host ?: domain), + coverUrl = div.selectFirst("img")?.src().orEmpty(), + title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4") + ?: div.selectFirst("h5.series-title"))?.text().orEmpty(), + altTitle = null, + rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f, + tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix('/').substringAfterLast('/'), + title = a.text().ifEmpty { return@mapNotNullToSet null }.toTitleCase(), + source = source, + ) + }.orEmpty(), + author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(), + state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText() + ?.lowercase()) { + in ongoing -> MangaState.ONGOING + in finished -> MangaState.FINISHED + else -> null + }, + source = source, + isNsfw = isNsfwSource, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.select("div.genres li").mapNotNullToSet { li -> + val a = li.selectFirst("a") ?: return@mapNotNullToSet null + val href = a.attr("href").removeSuffix("/").substringAfterLast(tagPrefix, "") + 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/it/Beyondtheataraxia.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/it/Beyondtheataraxia.kt new file mode 100644 index 00000000..0e501837 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/it/Beyondtheataraxia.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.parsers.site.madara.it + +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("BEYONDTHEATARAXIA", "Beyondtheataraxia", "it") +internal class Beyondtheataraxia(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.BEYONDTHEATARAXIA, "www.beyondtheataraxia.com", 10) { + override val datePattern = "d MMMM yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/CeriseScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/CeriseScans.kt index fdf881b8..6e136927 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/CeriseScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/CeriseScans.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("CERISE_SCANS", "Cerise Scans", "pt") internal class CeriseScans(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.CERISE_SCANS, "cerisescans.com") { + MadaraParser(context, MangaSource.CERISE_SCANS, "cerisescan.com/") { override val datePattern: String = "dd 'de' MMMMM 'de' yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GhostScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GhostScan.kt new file mode 100644 index 00000000..766b4755 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GhostScan.kt @@ -0,0 +1,13 @@ +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("GHOSTSCAN", "GhostScan", "pt") +internal class GhostScan(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.GHOSTSCAN, "ghostscan.com.br", 24) { + + override val datePattern: String = "dd 'de' MMMMM 'de' yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/AlliedFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/AlliedFansub.kt new file mode 100644 index 00000000..4bee57fc --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/AlliedFansub.kt @@ -0,0 +1,13 @@ +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("ALLIED_FANSUB", "Allied Fansub", "tr", ContentType.HENTAI) +internal class AlliedFansub(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.ALLIED_FANSUB, "alliedfansub.online", 20) { + override val datePattern = "dd/MM/yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/SarcasmScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/SarcasmScans.kt new file mode 100644 index 00000000..3ba78e00 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/SarcasmScans.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("SARCASMSCANS", "Sarcasm Scans", "tr", ContentType.HENTAI) +internal class SarcasmScans(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.SARCASMSCANS, "sarcasmscans.com", 16) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Mi2Manga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Mi2Manga.kt index 6124ae06..d835e1aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Mi2Manga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Mi2Manga.kt @@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("MI2MANGA", "Mi2Manga", "vi") internal class Mi2Manga(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MI2MANGA, "www.mi2manga2.com"){ + MadaraParser(context, MangaSource.MI2MANGA, "www.mi2manga2.com") { override val listUrl = "truyen-tranh/" override val tagPrefix = "the-loai/" override val datePattern = "d MMMM, yyyy" 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 7a9dfdf9..5fed39c7 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 @@ -204,7 +204,7 @@ internal abstract class MangaReaderParser( Manga( id = generateUid(relativeUrl), url = relativeUrl, - title = it.selectFirstOrThrow(selectMangaListTitle).text() ?: a.attr("title"), + title = it.selectFirst(selectMangaListTitle)?.text() ?: a.attr("title"), altTitle = null, publicUrl = a.attrAsAbsoluteUrl("href"), rating = rating, 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 new file mode 100644 index 00000000..9da0c5e0 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AresManga.kt @@ -0,0 +1,12 @@ +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("ARESMANGA", "AresManga", "ar") +internal class AresManga(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.ARESMANGA, "aresmanga.org", pageSize = 20, searchPageSize = 10) { + override val listUrl = "/series" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/EnAresManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/EnAresManga.kt new file mode 100644 index 00000000..0f3ed836 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/EnAresManga.kt @@ -0,0 +1,12 @@ +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("ENARESMANGA", "EnAresManga", "ar") +internal class EnAresManga(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.ENARESMANGA, "en-aresmanga.com", pageSize = 20, searchPageSize = 10) { + override val listUrl = "/series" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaProtm.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaProtm.kt index 1bb01818..6fc55787 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaProtm.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaProtm.kt @@ -5,9 +5,9 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("MANGAPROTM", "MangaProtm", "ar") +@MangaSourceParser("MANGAPROTM", "Manga Pro", "ar") internal class MangaProtm(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MANGAPROTM, "mangaprotm.com", pageSize = 20, searchPageSize = 20) { + MangaReaderParser(context, MangaSource.MANGAPROTM, "mangapro.co", pageSize = 20, searchPageSize = 20) { override val listUrl = "/series" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt index f370055f..61de14c6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt @@ -10,7 +10,7 @@ import java.text.SimpleDateFormat @MangaSourceParser("SWATEAM", "Swa Team", "ar") internal class SwaTeam(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.SWATEAM, "swatop.club", pageSize = 42, searchPageSize = 39) { + MangaReaderParser(context, MangaSource.SWATEAM, "stmgs.com", pageSize = 42, searchPageSize = 39) { override val datePattern = "MMMM dd, yyyy" override val selectMangalist = ".listupd .bs .bsx" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt new file mode 100644 index 00000000..11949c15 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt @@ -0,0 +1,98 @@ +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.* +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.Calendar + +@MangaSourceParser("VEXMANGA", "Vex Manga", "ar") +internal class VexManga(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.VEXMANGA, "vexmanga.net", pageSize = 10, searchPageSize = 10) { + override val selectMangalist = ".listupd .latest-series" + override val selectChapter = ".ulChapterList > a" + + 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) + } + + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + val d = date?.lowercase() ?: return 0 + return when { + d.startsWith("منذ") -> parseRelativeDate(date) + d.startsWith("جديد") -> Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + }.timeInMillis + + else -> dateFormat.tryParse(date) + } + } + + private fun parseRelativeDate(date: String): Long { + val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + + return when { + WordSet( + "أيام", + ).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + + WordSet( + "أسابيع", + ).anyWordIn(date) -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis + + WordSet( + "ساعة", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.HOUR, + -number, + ) + }.timeInMillis + + WordSet( + "دقائق", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.MINUTE, + -number, + ) + }.timeInMillis + + WordSet("ثوان").anyWordIn(date) -> cal.apply { + add( + Calendar.SECOND, + -number, + ) + }.timeInMillis + + WordSet("أشهر").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + + else -> 0 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt index 6d897ac3..22ae237d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ASURASCANS", "Asura Scans", "en") internal class AsuraScansParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.ASURASCANS, "asura.nacm.xyz", pageSize = 20, searchPageSize = 10) { + MangaReaderParser(context, MangaSource.ASURASCANS, "asuracomics.com", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" override val selectPage = "div#readerarea p img" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt new file mode 100644 index 00000000..fbbb4cf5 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.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("FREAKCOMIC", "FreakComic", "en") +internal class FreakComic(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.FREAKCOMIC, "freakcomic.com", pageSize = 20, searchPageSize = 10) 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 new file mode 100644 index 00000000..0b040ab6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaFreak.kt @@ -0,0 +1,237 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser +import org.koitharu.kotatsu.parsers.util.* +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.Calendar + + +@MangaSourceParser("MANHWA_FREAK", "Manhwa Freak", "en") +internal class ManhwaFreak(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANHWA_FREAK, "manhwa-freak.com", pageSize = 0, searchPageSize = 10) { + + override val selectMangalist = ".listupd .lastest-serie" + override val selectMangaListImg = "img" + + + 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 (!tags.isNullOrEmpty()) { + + if (page > 1) { + return emptyList() + } + + val tag = tags.oneOrThrowIfMany() + val url = buildString { + append("https://") + append(domain) + append("/genres/?genre=") + append(tag?.key.orEmpty()) + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + if (page > 1) { + return emptyList() + } + val sortQuery = when (sortOrder) { + SortOrder.ALPHABETICAL -> "az" + SortOrder.NEWEST -> "new" + SortOrder.POPULARITY -> "views" + SortOrder.UPDATED -> "" + else -> "" + } + + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("/?order=") + append(sortQuery) + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/genres/").parseHtml() + + return doc.select("ul.genre-list li a").mapNotNullToSet { a -> + val href = a.attr("href").substringAfterLast("=") + MangaTag( + key = href, + title = a.text(), + source = source, + ) + } + } + + 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("div.chapter-li a").mapChapters(reversed = true) { index, a -> + val url = a.attrAsRelativeUrl("href") + val dateText = a.selectFirst(".chapter-info p.new")?.text() ?: a.select(".chapter-info p")[1].text() + MangaChapter( + id = generateUid(url), + name = a.selectFirst(".chapter-info p:contains(Chapter)")?.text() ?: "Chapter ${index + 1}", + url = url, + number = index + 1, + scanlator = null, + uploadDate = if (dateText == "NEW") { + parseChapterDate( + dateFormat, + "today", + ) + } else { + parseChapterDate( + dateFormat, + dateText, + ) + }, + branch = null, + source = source, + ) + } + return parseInfo(docs, manga, chapters) + } + + override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List): Manga { + + val tagMap = getOrCreateTagMap() + val selectTag = docs.requireElementById("info").select("div:contains(Genre) > p:last-child").text().split(",") + val tags = selectTag.mapNotNullToSet { tagMap[it] } + + val mangaState = docs.requireElementById("info").select("div:contains(Status) > p:last-child").text().let { + when (it) { + "Ongoing" -> MangaState.ONGOING + "Completed" -> MangaState.FINISHED + else -> null + } + } + val author = docs.requireElementById("info").select("div:contains(Author(s)) > p:last-child").text() + + return manga.copy( + altTitle = docs.requireElementById("info").select("div:contains(Alternative) > p:last-child").text(), + description = docs.requireElementById("summary").html(), + state = mangaState, + author = author, + isNsfw = manga.isNsfw, + tags = tags, + chapters = chapters, + ) + } + + protected 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.endsWith(" ago") -> parseRelativeDate(date) + // Handle 'yesterday' and 'today', using midnight + d.startsWith("year") -> Calendar.getInstance().apply { + add(Calendar.DAY_OF_MONTH, -1) // yesterday + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + }.timeInMillis + + d.startsWith("today") -> Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + }.timeInMillis + + date.contains(Regex("""\d(st|nd|rd|th)""")) -> date.split(" ").map { + if (it.contains(Regex("""\d\D\D"""))) { + it.replace(Regex("""\D"""), "") + } else { + it + } + }.let { dateFormat.tryParse(it.joinToString(" ")) } + + 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( + "day", + "days", + "d", + ).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + + WordSet( + "hour", + "hours", + "h", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.HOUR, + -number, + ) + }.timeInMillis + + WordSet( + "minute", + "minutes", + "mins", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.MINUTE, + -number, + ) + }.timeInMillis + + WordSet("second").anyWordIn(date) -> cal.apply { + add( + Calendar.SECOND, + -number, + ) + }.timeInMillis + + WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis + else -> 0 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaShiina.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaShiina.kt new file mode 100644 index 00000000..6f155061 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaShiina.kt @@ -0,0 +1,10 @@ +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.mangareader.MangaReaderParser + +@MangaSourceParser("MANGASHIINA", "Manga Shiina", "es") +internal class MangaShiina(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGASHIINA, "mangashiina.com", pageSize = 20, searchPageSize = 10) 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 new file mode 100644 index 00000000..6f6313b7 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/ManhwaFreakFr.kt @@ -0,0 +1,238 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser +import org.koitharu.kotatsu.parsers.util.* +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + + +@MangaSourceParser("MANHWA_FREAK_FR", "Manhwa Freak Fr", "fr") +internal class ManhwaFreakFr(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANHWA_FREAK_FR, "manhwafreak.fr", pageSize = 0, searchPageSize = 10) { + + override val selectMangalist = ".listupd .lastest-serie" + override val selectMangaListImg = "img" + override val sourceLocale: Locale = Locale.ENGLISH + + 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 (!tags.isNullOrEmpty()) { + + if (page > 1) { + return emptyList() + } + + val tag = tags.oneOrThrowIfMany() + val url = buildString { + append("https://") + append(domain) + append("/genres/?genre=") + append(tag?.key.orEmpty()) + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + if (page > 1) { + return emptyList() + } + val sortQuery = when (sortOrder) { + SortOrder.ALPHABETICAL -> "az" + SortOrder.NEWEST -> "new" + SortOrder.POPULARITY -> "views" + SortOrder.UPDATED -> "" + else -> "" + } + + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("/?order=") + append(sortQuery) + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/genres/").parseHtml() + + return doc.select("ul.genre-list li a").mapNotNullToSet { a -> + val href = a.attr("href").substringAfterLast("=") + MangaTag( + key = href, + title = a.text(), + source = source, + ) + } + } + + 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("div.chapter-li a").mapChapters(reversed = true) { index, a -> + val url = a.attrAsRelativeUrl("href") + val dateText = a.selectFirst(".chapter-info p.new")?.text() ?: a.select(".chapter-info p")[1].text() + MangaChapter( + id = generateUid(url), + name = a.selectFirst(".chapter-info p:contains(Chapter)")?.text() ?: "Chapter ${index + 1}", + url = url, + number = index + 1, + scanlator = null, + uploadDate = if (dateText == "NEW") { + parseChapterDate( + dateFormat, + "today", + ) + } else { + parseChapterDate( + dateFormat, + dateText, + ) + }, + branch = null, + source = source, + ) + } + return parseInfo(docs, manga, chapters) + } + + override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List): Manga { + + val tagMap = getOrCreateTagMap() + val selectTag = docs.requireElementById("info").select("div:contains(Genre) > p:last-child").text().split(",") + val tags = selectTag.mapNotNullToSet { tagMap[it] } + + val mangaState = docs.requireElementById("info").select("div:contains(Status) > p:last-child").text().let { + when (it) { + "Ongoing" -> MangaState.ONGOING + "Completed" -> MangaState.FINISHED + else -> null + } + } + val author = docs.requireElementById("info").select("div:contains(Author(s)) > p:last-child").text() + + return manga.copy( + altTitle = docs.requireElementById("info").select("div:contains(Alternative) > p:last-child").text(), + description = docs.requireElementById("summary").html(), + state = mangaState, + author = author, + isNsfw = manga.isNsfw, + tags = tags, + chapters = chapters, + ) + } + + protected 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.endsWith(" ago") -> parseRelativeDate(date) + // Handle 'yesterday' and 'today', using midnight + d.startsWith("year") -> Calendar.getInstance().apply { + add(Calendar.DAY_OF_MONTH, -1) // yesterday + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + }.timeInMillis + + d.startsWith("today") -> Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + }.timeInMillis + + date.contains(Regex("""\d(st|nd|rd|th)""")) -> date.split(" ").map { + if (it.contains(Regex("""\d\D\D"""))) { + it.replace(Regex("""\D"""), "") + } else { + it + } + }.let { dateFormat.tryParse(it.joinToString(" ")) } + + 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( + "day", + "days", + "d", + ).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + + WordSet( + "hour", + "hours", + "h", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.HOUR, + -number, + ) + }.timeInMillis + + WordSet( + "minute", + "minutes", + "mins", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.MINUTE, + -number, + ) + }.timeInMillis + + WordSet("second").anyWordIn(date) -> cal.apply { + add( + Calendar.SECOND, + -number, + ) + }.timeInMillis + + WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis + else -> 0 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MasterKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MasterKomik.kt index b546c6a0..73e0e890 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MasterKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MasterKomik.kt @@ -6,9 +6,10 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("MASTERKOMIK", "MasterKomik", "id") +@MangaSourceParser("MASTERKOMIK", "Tenshi ( MasterKomik )", "id") internal class MasterKomik(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MASTERKOMIK, "masterkomik.com", pageSize = 20, searchPageSize = 20) { + MangaReaderParser(context, MangaSource.MASTERKOMIK, "tenshi.id", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" + override val listUrl = "/komik" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt new file mode 100644 index 00000000..ceb2261e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt @@ -0,0 +1,11 @@ +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("OMKOMIK", "OmKomik", "id") +internal class OmKomik(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.OMKOMIK, "omkomik.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Mangaschan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Mangaschan.kt index c07c77b2..b1098628 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Mangaschan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Mangaschan.kt @@ -7,8 +7,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGASCHAN", "Mangaschan", "pt") internal class Mangaschan(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MANGASCHAN, "mangaschan.com", pageSize = 20, searchPageSize = 20) { + MangaReaderParser(context, MangaSource.MANGASCHAN, "mangaschan.net", pageSize = 20, searchPageSize = 20) { - override val datePattern = "MMM d, yyyy" + override val datePattern = "MMMM d, yyyy" } 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 new file mode 100644 index 00000000..22d59298 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Lshistoria.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("LSHISTORIA", "Lshistoria", "tr") +internal class Lshistoria(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.LSHISTORIA, "omkomik.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt new file mode 100644 index 00000000..7c694c77 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt @@ -0,0 +1,11 @@ +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("MOONDAISY_SCANS", "MoonDaisyScans", "tr", ContentType.HENTAI) +internal class MoonDaisyScans(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MOONDAISY_SCANS, "moondaisyscans.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt new file mode 100644 index 00000000..bb34e46f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt @@ -0,0 +1,11 @@ +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("SUMMERTOON", "Summer Toon", "tr") +internal class SummerToon(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.SUMMERTOON, "summertoon.com", pageSize = 10, 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 aad06724..d2a5125b 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 @@ -43,6 +43,7 @@ internal abstract class MmrcmsParser( "Ongoing", "En cours", "En curso", + "DEVAM EDİYOR", ) @JvmField @@ -51,6 +52,7 @@ internal abstract class MmrcmsParser( "Completo", "Complete", "Terminé", + "TAMAMLANDI", ) protected open val imgUpdated = "/cover/cover_250x350.jpg" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/tr/MangaDenizi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/tr/MangaDenizi.kt new file mode 100644 index 00000000..5716cd8b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/tr/MangaDenizi.kt @@ -0,0 +1,20 @@ +package org.koitharu.kotatsu.parsers.site.mmrcms.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.mmrcms.MmrcmsParser +import java.util.Locale + + +@MangaSourceParser("MANGA_DENIZI", "MangaDenizi", "tr") +internal class MangaDenizi(context: MangaLoaderContext) : + MmrcmsParser(context, MangaSource.MANGA_DENIZI, "www.mangadenizi.net") { + override val selectState = "dt:contains(Durum)" + override val selectAlt = "dt:contains(Diğer Adları)" + override val selectAut = "dt:contains(Yazar & Çizer)" + override val selectTag = "dt:contains(Kategoriler)" + override val sourceLocale: Locale = Locale.ENGLISH + override val datePattern = "dd.MM.yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt new file mode 100644 index 00000000..c1bc6fb1 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt @@ -0,0 +1,95 @@ +package org.koitharu.kotatsu.parsers.site.tr + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaParser +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("SADSCANS", "SadScans", "tr") +internal class SadScans(context: MangaLoaderContext) : MangaParser(context, MangaSource.SADSCANS) { + + override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("sadscans.com") + + override suspend fun getList(offset: Int, query: String?, tags: Set?, sortOrder: SortOrder): List { + if (offset > 0) { + return emptyList() + } + val url = buildString { + append("https://$domain/series") + if (!query.isNullOrEmpty()) { + append("?search=") + append(query.urlEncoded()) + } + } + val doc = webClient.httpGet(url).parseHtml() + return doc.select(".series-list").map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.selectFirstOrThrow("h2").text(), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = div.selectFirstOrThrow("img").src()?.toAbsoluteUrl(domain).orEmpty(), + tags = emptySet(), + state = null, + author = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set = emptySet() + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH) + return manga.copy( + altTitle = null, + state = when (doc.select(".status span").last()?.text()) { + "Devam ediyor" -> MangaState.ONGOING + "Tamamlandı" -> MangaState.FINISHED + else -> null + }, + tags = emptySet(), + author = doc.select(".author span").last()?.text(), + description = doc.selectFirstOrThrow(".summary").text(), + chapters = doc.select(".chap-section .chap") + .mapChapters(reversed = true) { i, div -> + val a = div.selectFirstOrThrow("a") + val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) + MangaChapter( + id = generateUid(url), + name = a.text(), + number = i + 1, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(div.select(".detail span").last()?.text()), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select(".swiper-slide img").map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} From c4bf7b2ae0c04dbd656597ccf9d81d91d61831b2 Mon Sep 17 00:00:00 2001 From: devi Date: Wed, 30 Aug 2023 13:45:40 +0200 Subject: [PATCH 02/15] add sources and fix --- .../org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt | 3 --- .../koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt | 1 - 2 files changed, 4 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt index 88742d71..368b4d40 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt @@ -1,8 +1,5 @@ package org.koitharu.kotatsu.parsers.site.madara.id - -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.* diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt index ceb2261e..1b2a8ff2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/OmKomik.kt @@ -4,7 +4,6 @@ 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("OMKOMIK", "OmKomik", "id") internal class OmKomik(context: MangaLoaderContext) : From 4d4c126327c5bb92f69f0030e7268d59581979a8 Mon Sep 17 00:00:00 2001 From: devi Date: Wed, 30 Aug 2023 17:50:26 +0200 Subject: [PATCH 03/15] remove dead source --- .../kotatsu/parsers/site/madara/pt/NinjaScan.kt | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt deleted file mode 100644 index 6729886d..00000000 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt +++ /dev/null @@ -1,13 +0,0 @@ -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("NINJASCAN", "Ninja Scan", "pt") -internal class NinjaScan(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.NINJASCAN, "ninjascan.site") { - - override val datePattern = "dd 'de' MMMMM 'de' yyyy" -} From a3afbafdccc9ef0939e5880fe09c10541d92d2ea Mon Sep 17 00:00:00 2001 From: devi Date: Wed, 30 Aug 2023 18:03:45 +0200 Subject: [PATCH 04/15] remove small code --- .../org/koitharu/kotatsu/parsers/site/madara/fr/BlueSolo.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/BlueSolo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/BlueSolo.kt index f9dccb8a..0886bc5e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/BlueSolo.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/BlueSolo.kt @@ -4,12 +4,10 @@ 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.* @MangaSourceParser("BLUESOLO", "Blue Solo", "fr") internal class BlueSolo(context: MangaLoaderContext) : MadaraParser(context, MangaSource.BLUESOLO, "www1.bluesolo.org", 10) { override val datePattern = "d MMMM yyyy" - override val sourceLocale: Locale = Locale.FRENCH } From e0249accf8c35721927ab2fc593358b7c84f3512 Mon Sep 17 00:00:00 2001 From: devi Date: Thu, 31 Aug 2023 09:46:19 +0200 Subject: [PATCH 05/15] fix domain Mgkomik --- .../kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt | 2 +- .../org/koitharu/kotatsu/parsers/site/madara/id/Komiksay.kt | 2 +- .../org/koitharu/kotatsu/parsers/site/madara/id/Mgkomik.kt | 5 +++-- .../koitharu/kotatsu/parsers/site/madara/id/PojokManga.kt | 2 +- .../org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt | 2 +- .../koitharu/kotatsu/parsers/site/madara/id/Worldmanhwas.kt | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt index 2338cb6d..1602a973 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt @@ -4,7 +4,7 @@ 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.* +import java.util.Locale @MangaSourceParser("IMMORTALUPDATESID", "Immortal Updates Id", "id") internal class ImmortalUpdatesId(context: MangaLoaderContext) : diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komiksay.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komiksay.kt index c6649665..84960de9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komiksay.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Komiksay.kt @@ -4,7 +4,7 @@ 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.* +import java.util.Locale @MangaSourceParser("KOMIKSA", "Komiksay", "id") internal class Komiksay(context: MangaLoaderContext) : diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Mgkomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Mgkomik.kt index 4551cff7..bf2d8fb2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Mgkomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Mgkomik.kt @@ -4,14 +4,15 @@ 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.* +import java.util.Locale @MangaSourceParser("MGKOMIK", "Mgkomik", "id") internal class Mgkomik(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MGKOMIK, "mgkomik.com", 20) { + MadaraParser(context, MangaSource.MGKOMIK, "mgkomik.id", 20) { override val tagPrefix = "genres/" override val listUrl = "komik/" override val datePattern = "dd MMM yy" + override val stylepage = "" override val sourceLocale: Locale = Locale.ENGLISH } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/PojokManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/PojokManga.kt index 1f7954a5..3ad8ee2e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/PojokManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/PojokManga.kt @@ -4,7 +4,7 @@ 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.* +import java.util.Locale @MangaSourceParser("POJOKMANGA", "PojokManga", "id") internal class PojokManga(context: MangaLoaderContext) : diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt index 36104878..a740b37d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt @@ -4,7 +4,7 @@ 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.* +import java.util.Locale @MangaSourceParser("SHINIGAMI", "Shinigami", "id") internal class Shinigami(context: MangaLoaderContext) : diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Worldmanhwas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Worldmanhwas.kt index 7ce482e5..891fdde3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Worldmanhwas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Worldmanhwas.kt @@ -4,7 +4,7 @@ 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.* +import java.util.Locale @MangaSourceParser("WORLDMANHWAS", "Worldmanhwas", "id") internal class Worldmanhwas(context: MangaLoaderContext) : From 03581add21fe0630993896b999dcba705f983707 Mon Sep 17 00:00:00 2001 From: devi Date: Thu, 31 Aug 2023 12:26:13 +0200 Subject: [PATCH 06/15] add Perf Scan and fix Legacy Scans --- .../kotatsu/parsers/site/fr/FuryoSociety.kt | 12 +- .../parsers/site/fr/LegacyScansParser.kt | 169 ++++++++++++++++++ .../kotatsu/parsers/site/fr/PerfScan.kt | 163 +++++++++++++++++ .../site/mangareader/fr/LegacyScansParser.kt | 10 -- 4 files changed, 338 insertions(+), 16 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt delete mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LegacyScansParser.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt index 13a41710..09cecbd1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt @@ -131,12 +131,12 @@ internal class FuryoSociety(context: MangaLoaderContext) : val d = date?.lowercase() ?: return 0 return when { d.startsWith("il y a") || // Handle translated 'ago' in French. - d.endsWith(" an") || d.endsWith(" ans") || - d.endsWith(" mois") || - d.endsWith(" jour") || d.endsWith(" jours") || - d.endsWith(" heure") || d.endsWith(" heures") || - d.endsWith(" seconde") || d.endsWith(" secondes") || - d.endsWith(" minute") || d.endsWith(" minutes") -> parseRelativeDate(date) + d.endsWith(" an") || d.endsWith(" ans") || + d.endsWith(" mois") || + d.endsWith(" jour") || d.endsWith(" jours") || + d.endsWith(" heure") || d.endsWith(" heures") || + d.endsWith(" seconde") || d.endsWith(" secondes") || + d.endsWith(" minute") || d.endsWith(" minutes") -> parseRelativeDate(date) else -> dateFormat.tryParse(date) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt new file mode 100644 index 00000000..8a581d79 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -0,0 +1,169 @@ +package org.koitharu.kotatsu.parsers.site.fr + +import okhttp3.Headers +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.network.UserAgents +import org.koitharu.kotatsu.parsers.util.* +import org.koitharu.kotatsu.parsers.util.json.mapJSON +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("LEGACY_SCANS", "Legacy Scans", "fr") +internal class LegacyScansParser(context: MangaLoaderContext) : + PagedMangaParser(context, MangaSource.LEGACY_SCANS, 18) { + + override val sortOrders: Set = EnumSet.of( + SortOrder.ALPHABETICAL, + ) + + override val configKeyDomain = ConfigKey.Domain("legacy-scans.com") + + override val headers: Headers = Headers.Builder() + .add("User-Agent", UserAgents.CHROME_MOBILE) + .build() + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + + val end = page * pageSize + val start = end - (pageSize - 1) + val url = if (!query.isNullOrEmpty()) { + if (page > 1) { + return emptyList() + } + buildString { + append("https://api.$domain/misc/home/search?title=") + append(query.urlEncoded()) + } + } else { + buildString { + append("https://api.$domain/misc/comic/search/query?status=&order=&genreNames=") + if (!tags.isNullOrEmpty()) { + for (tag in tags) { + append(tag.key) + append(",") + } + } + + append("&type=&start=") + append(start) + append("&end=") + append(end) + } + + } + val json = webClient.httpGet(url).parseJson() + return if (!query.isNullOrEmpty()) { + json.getJSONArray("results").mapJSON { j -> + val slug = j.getString("slug") + val urlManga = "https://$domain/comics/$slug" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga, + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = "", + tags = setOf(), + state = null, + author = null, + source = source, + ) + } + } else { + json.getJSONArray("comics").mapJSON { j -> + val slug = j.getString("slug") + val urlManga = "https://$domain/comics/$slug" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga, + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = "https://api.$domain/" + j.getString("cover"), + tags = setOf(), + state = null, + author = null, + source = source, + ) + } + } + + } + + override suspend fun getDetails(manga: Manga): Manga { + val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH) + + return manga.copy( + altTitle = null, + tags = root.select("div.serieGenre span").mapNotNullToSet { span -> + MangaTag( + key = span.text(), + title = span.text(), + source = source, + ) + }, + coverUrl = root.selectFirstOrThrow("div.serieImg img").attr("src"), + author = root.select("div.serieAdd p:contains(Auteur:) strong").text(), + description = root.selectFirst("div.serieDescription div")?.html(), + chapters = root.select("div.chapterList a") + .mapChapters(reversed = true) { i, a -> + val href = a.attrAsRelativeUrl("href") + val name = a.selectFirstOrThrow("span").text() + val dateText = a.selectLast("span")?.text() ?: "0" + MangaChapter( + id = generateUid(href), + name = name, + number = i + 1, + url = href, + scanlator = null, + uploadDate = dateFormat.tryParse(dateText), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select("div.readerComics img").map { img -> + val url = img.src() ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/comics").parseHtml() + val script = doc.requireElementById("__NUXT_DATA__").data() + .substringAfterLast("\"genres\"").substringBeforeLast("\"comics\"") + .split("\",\"").drop(1) + + return script.mapNotNullToSet { tag -> + MangaTag( + key = tag.substringBeforeLast("\",{"), + title = tag.substringBeforeLast("\",{"), + source = source, + ) + } + } +} 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 new file mode 100644 index 00000000..44713f57 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt @@ -0,0 +1,163 @@ +package org.koitharu.kotatsu.parsers.site.fr + +import okhttp3.Headers +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.network.UserAgents +import org.koitharu.kotatsu.parsers.util.* +import org.koitharu.kotatsu.parsers.util.json.mapJSON +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("PERF_SCAN", "Perf Scan", "fr") +internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.PERF_SCAN, 12) { + + override val sortOrders: Set = EnumSet.of( + SortOrder.ALPHABETICAL, + SortOrder.UPDATED, + SortOrder.NEWEST, + SortOrder.POPULARITY, + ) + + override val configKeyDomain = ConfigKey.Domain("perf-scan.fr") + + override val headers: Headers = Headers.Builder() + .add("User-Agent", UserAgents.CHROME_DESKTOP) + .build() + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + + val url = buildString { + append("https://api.$domain/query?query_string=") + + if (!query.isNullOrEmpty()) { + append(query.urlEncoded()) + } + + append("&series_status=All&order=desc&orderBy=") + when (sortOrder) { + SortOrder.POPULARITY -> append("total_views") + SortOrder.UPDATED -> append("latest") + SortOrder.NEWEST -> append("created_at") + SortOrder.ALPHABETICAL -> append("title") + else -> append("latest") + } + + append("&series_type=Comic&page=") + append(page) + append("&perPage=12&tags_ids=") + append("[]".urlEncoded()) + } + val json = webClient.httpGet(url).parseJson() + return json.getJSONArray("data").mapJSON { j -> + val slug = j.getString("series_slug") + val urlManga = "https://$domain/series/$slug" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga, + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = j.getString("thumbnail"), + tags = setOf(), + state = when (j.getString("status")) { + "Ongoing" -> MangaState.ONGOING + "Completed" -> MangaState.FINISHED + else -> null + }, + author = null, + source = source, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga { + val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("MM/DD/yyyy", Locale.ENGLISH) + + return manga.copy( + altTitle = root.selectFirstOrThrow("p.text-center.text-gray-400").text(), + tags = emptySet(), + author = root.select("div.flex.flex-col.gap-y-2 p:contains(Autor:) strong").text(), + description = root.selectFirst(".datas_synopsis")?.html(), + chapters = root.select("ul.grid a") + .mapChapters(reversed = true) { i, a -> + + val href = a.attrAsRelativeUrl("href") + val name = a.selectFirstOrThrow("span").text() + val dateText = a.selectLast("span")?.text() ?: "0" + MangaChapter( + id = generateUid(href), + name = name, + number = i + 1, + url = href, + scanlator = null, + uploadDate = parseChapterDate( + dateFormat, + dateText, + ), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select("p.flex img").map { img -> + val url = img.src() ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set = emptySet() + + protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + val d = date?.lowercase() ?: return 0 + return when { + d.endsWith(" ago") -> parseRelativeDate(date) + + else -> dateFormat.tryParse(date) + } + } + + private fun parseRelativeDate(date: String): Long { + val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + + return when { + WordSet("day", "days").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + WordSet("hour", "hours").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis + WordSet("minute", "minutes").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis + WordSet("second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis + WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis + WordSet("week").anyWordIn(date) -> cal.apply { + add( + Calendar.WEEK_OF_MONTH, + -number, + ) + }.timeInMillis + + else -> 0 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LegacyScansParser.kt deleted file mode 100644 index 38fe6b33..00000000 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LegacyScansParser.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.koitharu.kotatsu.parsers.site.mangareader.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.mangareader.MangaReaderParser - -@MangaSourceParser("LEGACY_SCANS", "Legacy Scans", "fr") -internal class LegacyScansParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.LEGACY_SCANS, "legacy-scans.com", pageSize = 20, searchPageSize = 10) From 0395f29503c67ea66d1a5bf8c44bd510f5d7b725 Mon Sep 17 00:00:00 2001 From: devi Date: Thu, 31 Aug 2023 12:31:28 +0200 Subject: [PATCH 07/15] small change --- .../org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt index 8a581d79..46cfb5fa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -17,7 +17,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.LEGACY_SCANS, 18) { override val sortOrders: Set = EnumSet.of( - SortOrder.ALPHABETICAL, + SortOrder.POPULARITY, ) override val configKeyDomain = ConfigKey.Domain("legacy-scans.com") From 0dc5032a7ae7f0f871cb39d2ae9948b4e31d0a71 Mon Sep 17 00:00:00 2001 From: devi Date: Thu, 31 Aug 2023 19:22:51 +0200 Subject: [PATCH 08/15] change url Shinigami --- .../org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt index a740b37d..bcc267b1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Shinigami.kt @@ -8,7 +8,7 @@ import java.util.Locale @MangaSourceParser("SHINIGAMI", "Shinigami", "id") internal class Shinigami(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.SHINIGAMI, "shinigami.id", 10) { + MadaraParser(context, MangaSource.SHINIGAMI, "shinigami.ae", 10) { override val tagPrefix = "genre/" override val listUrl = "series/" From fce952d240c55575a1bdd8b0bac3ae2e83245989 Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 1 Sep 2023 12:13:57 +0200 Subject: [PATCH 09/15] change request and change url Nettruyenmax & NetTruyen --- .../kotatsu/parsers/site/en/MangaGeko.kt | 2 +- .../kotatsu/parsers/site/fr/LegacyScansParser.kt | 3 +-- .../kotatsu/parsers/site/madara/all/Ero18x.kt | 14 ++++---------- .../kotatsu/parsers/site/madara/es/ManhwaEs.kt | 14 ++++---------- .../kotatsu/parsers/site/madara/id/ManhwaHub.kt | 16 ++++++++++------ .../kotatsu/parsers/site/vi/NetTruyenParser.kt | 2 ++ .../parsers/site/wpcomics/vi/Nettruyenmax.kt | 2 +- 7 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt index d79f7bae..9ef23aa7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt @@ -113,7 +113,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context val a = li.selectFirstOrThrow("a") val url = a.attrAsRelativeUrl("href") val name = li.selectFirstOrThrow(".chapter-title").text() - val dateText = li.select(".chapter-update").attr("datetime").substringBeforeLast(",") + val dateText = li.select(".chapter-update").attr("datetime").substringBeforeLast(',') .replace(".", "").replace("Sept", "Sep") MangaChapter( id = generateUid(url), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt index 46cfb5fa..64ed1cbd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -49,10 +49,9 @@ internal class LegacyScansParser(context: MangaLoaderContext) : if (!tags.isNullOrEmpty()) { for (tag in tags) { append(tag.key) - append(",") + append(',') } } - append("&type=&start=") append(start) append("&end=") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt index 89d26a74..37a796f2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt @@ -3,15 +3,9 @@ package org.koitharu.kotatsu.parsers.site.madara.all import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaSource +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.generateUid -import org.koitharu.kotatsu.parsers.util.mapChapters -import org.koitharu.kotatsu.parsers.util.parseFailed +import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.Locale @@ -25,8 +19,8 @@ internal class Ero18x(context: MangaLoaderContext) : override suspend fun getChapters(manga: Manga, doc: Document): List { val dateFormat = SimpleDateFormat(datePattern, sourceLocale) 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 a = li.selectFirstOrThrow("a") + val href = a.attrAsRelativeUrl("href") val link = href + stylepage val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt index 1c58527c..124b13ad 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt @@ -3,15 +3,9 @@ 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.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaSource +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.generateUid -import org.koitharu.kotatsu.parsers.util.mapChapters -import org.koitharu.kotatsu.parsers.util.parseFailed -import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow +import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @MangaSourceParser("MANHWA_ES", "Manhwa Es", "es") @@ -24,8 +18,8 @@ internal class ManhwaEs(context: MangaLoaderContext) : override suspend fun getChapters(manga: Manga, doc: Document): List { val dateFormat = SimpleDateFormat(datePattern, sourceLocale) 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 a = li.selectFirstOrThrow("a") + val href = a.attrAsRelativeUrl("href") val link = href + stylepage val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt index 368b4d40..3820600b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt @@ -17,6 +17,11 @@ internal class ManhwaHub(context: MangaLoaderContext) : override val withoutAjax = true override val listUrl = "genre/manhwa" override val selectTestAsync = "ul.box-list-chapter" + + init { + paginator.firstPage = 1 + searchPaginator.firstPage = 1 + } override suspend fun getListPage( page: Int, query: String?, @@ -27,21 +32,20 @@ internal class ManhwaHub(context: MangaLoaderContext) : val url = buildString { append("https://") append(domain) - val pages = page + 1 - when { !query.isNullOrEmpty() -> { append("/search?s=") append(query.urlEncoded()) append("&page=") - append(pages) + append(page) + append("&") } !tags.isNullOrEmpty() -> { append("/$tagPrefix") append(tag?.key.orEmpty()) append("?page=") - append(pages) + append(page) append("&") } @@ -49,7 +53,7 @@ internal class ManhwaHub(context: MangaLoaderContext) : append("/$listUrl") append("?page=") - append(pages) + append(page) append("&") } @@ -69,7 +73,7 @@ internal class ManhwaHub(context: MangaLoaderContext) : return doc.select("div.row.c-tabs-item__content").ifEmpty { doc.select("div.page-item-detail") }.map { div -> - val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary") Manga( id = generateUid(href), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/NetTruyenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/NetTruyenParser.kt index 63e0ac4f..a7ed93da 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/NetTruyenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/NetTruyenParser.kt @@ -19,6 +19,8 @@ class NetTruyenParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.NETTRUYEN, pageSize = 36) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain( + "www.nettruyenlive.com", + "www.nettruyenio.com", "www.nettruyento.com", "nettruyento.com", "nettruyenin.com", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/Nettruyenmax.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/Nettruyenmax.kt index a43e6597..17abbfaa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/Nettruyenmax.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/Nettruyenmax.kt @@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser @MangaSourceParser("NETTRUYENMAX", "Nettruyenmax", "vi") internal class Nettruyenmax(context: MangaLoaderContext) : - WpComicsParser(context, MangaSource.NETTRUYENMAX, "www.nettruyenmax.com", 35) { + WpComicsParser(context, MangaSource.NETTRUYENMAX, "www.nettruyenus.com", 36) { override val listUrl = "/tim-truyen" } From ad51271c40db61bee6e4822fb15571926452128e Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 1 Sep 2023 12:14:32 +0200 Subject: [PATCH 10/15] format code --- .../parsers/site/fmreader/FmreaderParser.kt | 8 +-- .../parsers/site/ja/NicovideoSeigaParser.kt | 2 +- .../parsers/site/madara/MadaraParser.kt | 52 +++++++++---------- .../parsers/site/madara/id/ManhwaHub.kt | 1 + .../parsers/site/madtheme/MadthemeParser.kt | 8 +-- .../parsers/site/mangabox/MangaboxParser.kt | 8 +-- .../site/mangareader/MangaReaderParser.kt | 4 +- .../parsers/site/mangareader/ar/SwaTeam.kt | 4 +- .../parsers/site/mangareader/en/RealmScans.kt | 4 +- .../otakusanctuary/OtakuSanctuaryParser.kt | 38 +++++++------- .../parsers/site/wpcomics/WpComicsParser.kt | 2 +- .../parsers/site/zmanga/ZMangaParser.kt | 8 +-- 12 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt index 2bd01f6c..7e6864ee 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt @@ -210,10 +210,10 @@ internal abstract class FmreaderParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || - // short Hours - d.endsWith(" h") || - // short Day - d.endsWith(" d") -> parseRelativeDate(date) + // short Hours + d.endsWith(" h") || + // short Day + d.endsWith(" d") -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight d.startsWith("year") -> Calendar.getInstance().apply { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt index 1eee2f15..79c25bdf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt @@ -48,7 +48,7 @@ class NicovideoSeigaParser(context: MangaLoaderContext) : !query.isNullOrEmpty() -> return if (offset == 0) getSearchList(query, page) else emptyList() tags.isNullOrEmpty() -> "https://$domain/manga/list?page=$page&sort=${getSortKey(sortOrder)}" tags.size == 1 -> "https://$domain/manga/list?category=${tags.first().key}&page=$page" + - "&sort=${getSortKey(sortOrder)}" + "&sort=${getSortKey(sortOrder)}" tags.size > 1 -> throw IllegalArgumentException("This source supports only 1 category") else -> "https://$domain/manga/list?page=$page&sort=${getSortKey(sortOrder)}" 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 4632d2dd..8c9ad3d8 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 @@ -448,32 +448,32 @@ internal abstract class MadaraParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || d.endsWith(" atrás") || // Handle translated 'ago' in Portuguese. - d.startsWith("há ") || // other translated 'ago' in Portuguese. - d.endsWith(" hace") || // other translated 'ago' in Spanish - d.endsWith(" назад") || // other translated 'ago' in Russian - d.endsWith(" önce") || // Handle translated 'ago' in Turkish. - d.endsWith(" trước") || // Handle translated 'ago' in Viêt Nam. - d.endsWith("مضت") || // Handle translated 'ago' in Arabic - d.startsWith("il y a") || // Handle translated 'ago' in French. - //If there is no ago but just a motion of time - // short Hours - d.endsWith(" h") || - // short Day - d.endsWith(" d") || - // Day in Portuguese - d.endsWith(" días") || d.endsWith(" día") || - // Day in French - d.endsWith(" jour") || d.endsWith(" jours") || - // Hours in Portuguese - d.endsWith(" horas") || d.endsWith(" hora") || - // Hours in french - d.endsWith(" heure") || d.endsWith(" heures") || - // Minutes in English - d.endsWith(" mins") || - // Minutes in Portuguese - d.endsWith(" minutos") || d.endsWith(" minuto") || - //Minutes in French - d.endsWith(" minute") || d.endsWith(" minutes") -> parseRelativeDate(date) + d.startsWith("há ") || // other translated 'ago' in Portuguese. + d.endsWith(" hace") || // other translated 'ago' in Spanish + d.endsWith(" назад") || // other translated 'ago' in Russian + d.endsWith(" önce") || // Handle translated 'ago' in Turkish. + d.endsWith(" trước") || // Handle translated 'ago' in Viêt Nam. + d.endsWith("مضت") || // Handle translated 'ago' in Arabic + d.startsWith("il y a") || // Handle translated 'ago' in French. + //If there is no ago but just a motion of time + // short Hours + d.endsWith(" h") || + // short Day + d.endsWith(" d") || + // Day in Portuguese + d.endsWith(" días") || d.endsWith(" día") || + // Day in French + d.endsWith(" jour") || d.endsWith(" jours") || + // Hours in Portuguese + d.endsWith(" horas") || d.endsWith(" hora") || + // Hours in french + d.endsWith(" heure") || d.endsWith(" heures") || + // Minutes in English + d.endsWith(" mins") || + // Minutes in Portuguese + d.endsWith(" minutos") || d.endsWith(" minuto") || + //Minutes in French + d.endsWith(" minute") || d.endsWith(" minutes") -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight d.startsWith("year") -> Calendar.getInstance().apply { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt index 3820600b..adb23962 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt @@ -22,6 +22,7 @@ internal class ManhwaHub(context: MangaLoaderContext) : paginator.firstPage = 1 searchPaginator.firstPage = 1 } + override suspend fun getListPage( page: Int, query: String?, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt index bbe9552b..39a4ca87 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt @@ -219,10 +219,10 @@ internal abstract class MadthemeParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || - // short Hours - d.endsWith(" h") || - // short Day - d.endsWith(" d") -> parseRelativeDate(date) + // short Hours + d.endsWith(" h") || + // short Day + d.endsWith(" d") -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight d.startsWith("year") -> Calendar.getInstance().apply { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt index f4f738fe..5cfee1d9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt @@ -233,10 +233,10 @@ internal abstract class MangaboxParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || - // short Hours - d.endsWith(" h") || - // short Day - d.endsWith(" d") -> parseRelativeDate(date) + // short Hours + d.endsWith(" h") || + // short Day + d.endsWith(" d") -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight d.startsWith("year") -> Calendar.getInstance().apply { 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 5fed39c7..2035be22 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 @@ -125,8 +125,8 @@ internal abstract class MangaReaderParser( ?: docs.selectFirst(".tsinfo div:contains(Durum)")?.lastElementChild()?.text() val nsfw = docs.selectFirst(".restrictcontainer") != null - || docs.selectFirst(".info-right .alr") != null - || docs.selectFirst(".postbody .alr") != null + || docs.selectFirst(".info-right .alr") != null + || docs.selectFirst(".postbody .alr") != null return manga.copy( description = docs.selectFirst("div.entry-content")?.text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt index 61de14c6..1cc02226 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt @@ -111,8 +111,8 @@ internal class SwaTeam(context: MangaLoaderContext) : val author = docs.selectFirst("span.author i")?.text() val nsfw = docs.selectFirst(".restrictcontainer") != null - || docs.selectFirst(".info-right .alr") != null - || docs.selectFirst(".postbody .alr") != null + || docs.selectFirst(".info-right .alr") != null + || docs.selectFirst(".postbody .alr") != null return manga.copy( description = docs.selectFirst("span.desc")?.html(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RealmScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RealmScans.kt index adfa4484..82596e84 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RealmScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RealmScans.kt @@ -76,8 +76,8 @@ internal class RealmScans(context: MangaLoaderContext) : } val author = docs.selectFirst(".tsinfo div:contains(Author)")?.lastElementChild()?.text() val nsfw = docs.selectFirst(".restrictcontainer") != null - || docs.selectFirst(".info-right .alr") != null - || docs.selectFirst(".postbody .alr") != null + || docs.selectFirst(".info-right .alr") != null + || docs.selectFirst(".postbody .alr") != null // Description in markdown renders it unattractive and unclear on the synopsis // val desc = docs.selectFirstOrThrow("script:containsData(var description)").data().substringAfter("var description = \"").substringBefore("\";") 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 7a860000..5ff7ede8 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 @@ -392,26 +392,26 @@ internal abstract class OtakuSanctuaryParser( }.build().toString() } else if ( ( - url.contains("merakiscans") || - url.contains("mangazuki") || - url.contains("ninjascans") || - url.contains("anyacg.co") || - url.contains("mangakatana") || - url.contains("zeroscans") || - url.contains("mangapark") || - url.contains("mangadex") || - url.contains("uptruyen") || - url.contains("hocvientruyentranh") || - url.contains("ntruyen.info") || - url.contains("chancanvas") || - url.contains("bato.to") - ) && + url.contains("merakiscans") || + url.contains("mangazuki") || + url.contains("ninjascans") || + url.contains("anyacg.co") || + url.contains("mangakatana") || + url.contains("zeroscans") || + url.contains("mangapark") || + url.contains("mangadex") || + url.contains("uptruyen") || + url.contains("hocvientruyentranh") || + url.contains("ntruyen.info") || + url.contains("chancanvas") || + url.contains("bato.to") + ) && ( - !url.contains("googleusercontent") && - !url.contains("otakusan") && - !url.contains("otakuscan") && - !url.contains("shopotaku") - ) + !url.contains("googleusercontent") && + !url.contains("otakusan") && + !url.contains("otakuscan") && + !url.contains("shopotaku") + ) ) { url = "https://images2-focus-opensocial.googleusercontent.com/gadgets/proxy?container=focus&gadget=a&no_expand=1&resize_h=0&rewriteMime=image%2F*".toHttpUrl() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt index b5eb2e1c..cbf902aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt @@ -216,7 +216,7 @@ internal abstract class WpComicsParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || - d.endsWith(" trước") // Handle translated 'ago' in Viêt Nam. + d.endsWith(" trước") // Handle translated 'ago' in Viêt Nam. -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt index 196a0a58..bbe8e178 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt @@ -223,10 +223,10 @@ internal abstract class ZMangaParser( val d = date?.lowercase() ?: return 0 return when { d.endsWith(" ago") || - // short Hours - d.endsWith(" h") || - // short Day - d.endsWith(" d") -> parseRelativeDate(date) + // short Hours + d.endsWith(" h") || + // short Day + d.endsWith(" d") -> parseRelativeDate(date) // Handle 'yesterday' and 'today', using midnight d.startsWith("year") -> Calendar.getInstance().apply { From c0bf3ad56c1cbad6944ea90ea1c7c255a1f5140e Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 1 Sep 2023 12:28:10 +0200 Subject: [PATCH 11/15] add RackusReads --- .../kotatsu/parsers/site/madara/en/RackusReads.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/RackusReads.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/RackusReads.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/RackusReads.kt new file mode 100644 index 00000000..af830441 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/RackusReads.kt @@ -0,0 +1,13 @@ +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("RACKUSREADS", "RackusReads", "en") +internal class RackusReads(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.RACKUSREADS, "rackusreads.com", 20) { + + override val datePattern = "MM/dd/yyyy" +} From c6c4b7190a95b662f40fbf80cb8e6fdf55709ed0 Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 2 Sep 2023 10:09:33 +0200 Subject: [PATCH 12/15] fix url Random Scans --- .../org/koitharu/kotatsu/parsers/site/madara/pt/RandomScans.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/RandomScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/RandomScans.kt index c503249c..b9bbed47 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/RandomScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/RandomScans.kt @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("RANDOMSCANS", "Random Scans", "pt") internal class RandomScans(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.RANDOMSCANS, "randomscans.com") + MadaraParser(context, MangaSource.RANDOMSCANS, "randomscanlators.net") From 4faaa9fc13b4c473f11fcdf101648b12cf93be50 Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 2 Sep 2023 13:35:27 +0200 Subject: [PATCH 13/15] fix url Manhwalist --- .../kotatsu/parsers/site/mangareader/id/ManhwalistParser.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwalistParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwalistParser.kt index d12e1f95..156fd70e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwalistParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwalistParser.kt @@ -8,7 +8,7 @@ import java.util.* @MangaSourceParser("MANHWALIST", "Manhwalist", "id") internal class ManhwalistParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MANHWALIST, "manhwalist.xyz", pageSize = 24, searchPageSize = 10) { + MangaReaderParser(context, MangaSource.MANHWALIST, "manhwalist.com", pageSize = 24, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH } From 1498541b450adcefb407a28e52e5ced80fcffc84 Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 2 Sep 2023 16:27:24 +0200 Subject: [PATCH 14/15] fix url Nivera Fansub --- .../koitharu/kotatsu/parsers/site/madara/tr/NiveraFansub.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 d411d33f..f2358661 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 @@ -10,7 +10,8 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("NIVERAFANSUB", "Nivera Fansub", "tr", ContentType.HENTAI) internal class NiveraFansub(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.NIVERAFANSUB, "niverafansub.com") { + MadaraParser(context, MangaSource.NIVERAFANSUB, "niverafansub.co") { override val datePattern = "d MMMM yyyy" + override val selectPage = "div.page-break, div.login-required" } From 18125ea9829970e9c6ef3a93fbdd7301e5779b5c Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 3 Sep 2023 09:52:55 +0200 Subject: [PATCH 15/15] fix url Ozulscans --- .../koitharu/kotatsu/parsers/site/mangareader/ar/Ozulscans.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Ozulscans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Ozulscans.kt index 2e8ebbe4..aeb95fbd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Ozulscans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Ozulscans.kt @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("OZULSCANS", "Ozulscans", "ar") internal class Ozulscans(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.OZULSCANS, "ozulmanga.com", pageSize = 30, searchPageSize = 30) + MangaReaderParser(context, MangaSource.OZULSCANS, "ozulscans.xyz", pageSize = 30, searchPageSize = 30)