From 55e14e4cb35c83c7dcdae7c039b8448c3d1b0aea Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 16 Sep 2023 17:07:22 +0200 Subject: [PATCH 01/13] add sources --- .../kotatsu/parsers/site/ar/TeamXNovel.kt | 2 +- .../kotatsu/parsers/site/en/Po2Scans.kt | 96 ++++++++ .../kotatsu/parsers/site/fr/FmTeam.kt | 150 ++++++++++++ .../kotatsu/parsers/site/fr/LireScan.kt | 144 +++++++++++ .../kotatsu/parsers/site/fr/LugnicaScans.kt | 230 ++++++++---------- .../{fr/PerfScan.kt => heancms/HeanCms.kt} | 134 +++++----- .../parsers/site/heancms/es/YugenMangasEs.kt | 11 + .../parsers/site/heancms/fr/PerfScan.kt | 10 + .../parsers/site/heancms/pt/ReaperScansPt.kt | 10 + .../parsers/site/heancmsalt/HeanCmsAlt.kt | 161 ++++++++++++ .../site/heancmsalt/es/CerberuSeries.kt | 10 + .../parsers/site/heancmsalt/es/MangaEsp.kt | 24 ++ .../parsers/site/madara/ar/MangaLikeOrg.kt | 10 + .../parsers/site/madara/ar/MangaStarz.kt | 2 +- .../parsers/site/madara/en/CreepyScans.kt | 12 + .../parsers/site/madara/en/HiperDex.kt | 2 +- .../parsers/site/madara/en/MurimScan.kt | 1 + .../kotatsu/parsers/site/madara/tr/YaoiTr.kt | 13 + .../site/mangareader/ar/EnAresManga.kt | 1 + .../parsers/site/mangareader/ar/OzulShojo.kt | 10 + .../mangareader/{id => en}/KomikLabParser.kt | 5 +- .../site/mangareader/en/OzulScansEn.kt | 12 + .../es/{LegionScans.kt => InariManga.kt} | 12 +- .../site/mangareader/id/DoujinDesuRip.kt | 5 +- .../parsers/site/mangareader/id/ManhwaLand.kt | 14 ++ .../site/mangareader/id/ManhwadesuParser.kt | 7 +- .../mangareader/pt/HentaiSsssscanlator.kt | 14 ++ .../parsers/site/mangareader/th/Manga168.kt | 11 + .../site/mangareader/tr/MoonDaisyScans.kt | 2 +- .../parsers/site/mangareader/tr/TarotScans.kt | 11 + .../parsers/site/mmrcms/fr/JpScanVf.kt | 16 ++ .../parsers/site/nepnep/NepnepParser.kt | 65 ++--- 32 files changed, 952 insertions(+), 255 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt rename src/main/kotlin/org/koitharu/kotatsu/parsers/site/{fr/PerfScan.kt => heancms/HeanCms.kt} (51%) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/es/YugenMangasEs.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/fr/PerfScan.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/pt/ReaperScansPt.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/CerberuSeries.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/MangaEsp.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLikeOrg.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/CreepyScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/YaoiTr.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/OzulShojo.kt rename src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/{id => en}/KomikLabParser.kt (74%) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/OzulScansEn.kt rename src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/{LegionScans.kt => InariManga.kt} (54%) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/JpScanVf.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 index 3dbd5406..f67529c1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt @@ -17,7 +17,7 @@ import java.util.* 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 val configKeyDomain = ConfigKey.Domain("team1x12.com") override suspend fun getListPage( page: Int, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt new file mode 100644 index 00000000..e8594e0f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt @@ -0,0 +1,96 @@ +package org.koitharu.kotatsu.parsers.site.en + +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("PO2SCANS", "Po2Scans", "en") +internal class Po2Scans(context: MangaLoaderContext) : MangaParser(context, MangaSource.PO2SCANS) { + + override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("po2scans.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()) { + "Ongoing" -> MangaState.ONGOING + "Done" -> 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()?.replace("./assets", "/assets")?.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/fr/FmTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt new file mode 100644 index 00000000..200d15e3 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt @@ -0,0 +1,150 @@ +package org.koitharu.kotatsu.parsers.site.fr + +import kotlinx.coroutines.coroutineScope +import org.json.JSONArray +import org.json.JSONObject +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 org.koitharu.kotatsu.parsers.util.json.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("FMTEAM", "FmTeam", "fr") +internal class FmTeam(context: MangaLoaderContext) : + PagedMangaParser(context, MangaSource.FMTEAM, 0) { + + override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("fmteam.fr") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + if (page > 1) { return emptyList() } + + val jsonManga = if(!query.isNullOrEmpty()) + { + //3 letters minimum + webClient.httpGet("https://$domain/api/search/${query.urlEncoded()}").parseJson().getJSONArray("comics") + + }else + { + webClient.httpGet("https://$domain/api/comics").parseJson().getJSONArray("comics") + } + + val manga = ArrayList(jsonManga.length()) + for (i in 0 until jsonManga.length()) { + val j = jsonManga.getJSONObject(i) + val href = "/api" + j.getString("url") + when { + !tags.isNullOrEmpty() -> { + val a = j.getJSONArray("genres").toString() + var found = true + tags.forEach { + if (!a.contains(it.key, ignoreCase = true)) { + found = false + } + } + if (found) { + manga.add( + addManga(href, j) + ) + } + } + + else -> { + manga.add( + addManga(href, j) + ) + } + } + } + return manga + } + + private fun addManga(href : String, j : JSONObject): Manga { + return Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(domain), + coverUrl = j.getString("thumbnail"), + title = j.getString("title"), + description = j.getString("description"), + altTitle = j.getJSONArray("alt_titles").toString() + .replace("[\"", "") + .replace("\"]", "") + .replace("\",\"", " , "), + rating = j.getString("rating").toFloatOrNull()?.div(10f) + ?: RATING_UNKNOWN, + tags = emptySet(), + author = j.getString("author"), + state = when (j.getString("status").lowercase()) { + "en cours" -> MangaState.ONGOING + "terminé" -> MangaState.FINISHED + else -> null + }, + source = source, + isNsfw = when (j.getString("adult").toInt()) { + 0 -> false + 1 -> true + else -> true + }, + ) + } + + + override suspend fun getTags(): Set = emptySet() + + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + val fullUrl = manga.url.toAbsoluteUrl(domain) + val json = webClient.httpGet(fullUrl).parseJson().getJSONObject("comic") + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US) + val chapters = JSONArray(json.getJSONArray("chapters").toJSONList().reversed()) + + manga.copy( + tags = json.getJSONArray("genres").toJSONList().mapNotNullToSet { + MangaTag( + key = it.getString("slug"), + title = it.getString("name"), + source = source, + ) + }, + chapters = chapters.mapJSONIndexed { i,j -> + val url = "/api" + j.getString("url").toRelativeUrl(domain) + val name = j.getString("full_title") + val date = j.getStringOrNull("updated_at") + MangaChapter( + id = generateUid(url), + name = name, + number = i + 1, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(date), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val jsonPages = webClient.httpGet(fullUrl).parseJson().getJSONObject("chapter").getJSONArray("pages").toString() + val pages = jsonPages.replace("[", "").replace("]", "") + .replace("\\", "").split("\",\"").drop(1) + return pages.map { url -> + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt new file mode 100644 index 00000000..91553f23 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt @@ -0,0 +1,144 @@ +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 java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("LIRESCAN", "Lire Scan", "fr") +internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.LIRESCAN, 20) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED) + + override val configKeyDomain = ConfigKey.Domain("lire-scan.me") + + 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 tag = tags.oneOrThrowIfMany() + val doc = + if (!query.isNullOrEmpty()) { // search only works with 4 or more letters + if (page > 1) { + return emptyList() + } + val q = query.urlEncoded().replace("%20", "+") + val post = "do=search&subaction=search&search_start=0&full_search=0&result_from=1&story=$q" + webClient.httpPost("https://$domain/index.php?do=search", post).parseHtml() + } else { + val url = buildString { + append("https://") + append(domain) + if (!tags.isNullOrEmpty()) { + append("/manga/") + append(tag?.key.orEmpty()) + } + if (page > 1) { + append("/page/") + append(page) + append('/') + } + } + webClient.httpGet(url).parseHtml() + } + + return doc.select("div.sect__content.grid-items div.item-poster").map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.select(".item-poster__title").text(), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = div.selectFirstOrThrow(".item__rating").ownText().toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN, + isNsfw = false, + coverUrl = div.selectFirstOrThrow("img").attrAsAbsoluteUrl("src"), + 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.FRANCE) + return manga.copy( + altTitle = root.select("ul.pmovie__list li:contains(Nom Alternatif:)").text() + .replace("Nom Alternatif:", ""), + state = when (root.select("ul.pmovie__list li:contains(Status:)").text()) { + "Status: OnGoing", "Status: En cours" -> MangaState.ONGOING + "Status: Fini" -> MangaState.FINISHED + else -> null + }, + tags = root.select("ul.pmovie__list li:contains(Genre:)").text() + .replace("Genre:", "").split(" / ").mapNotNullToSet { tag -> + MangaTag( + key = tag.lowercase(), + title = tag, + source = source, + ) + }, + author = root.select("ul.pmovie__list li:contains(Artist(s):)").text().replace("Artist(s):", ""), + description = root.selectFirst("div.pmovie__text")?.html(), + chapters = root.select("ul li div.chapter") + .mapChapters(reversed = true) { i, div -> + val a = div.selectFirstOrThrow("a") + val href = a.attrAsRelativeUrl("href") + val name = a.text() + val dateText = div.select("p").last()?.text() + MangaChapter( + id = generateUid(href), + name = name, + number = i, + 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() + val pages = doc.selectFirstOrThrow("script:containsData(const manga = )").data() + .substringAfter("chapter1: [\"").substringBefore("\"]") + .split("\",\"") + return pages.map { img -> + MangaPage( + id = generateUid(img), + url = img, + preview = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/").parseHtml() + return doc.select(".nav-menu li a").mapNotNullToSet { a -> + val key = a.attr("href").removeSuffix('/').substringAfterLast("manga/", "") + MangaTag( + key = key, + title = a.text(), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt index 8239c5ae..7ddd2cb4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt @@ -8,7 +8,7 @@ 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.DateFormat +import org.koitharu.kotatsu.parsers.util.json.mapJSON import java.text.SimpleDateFormat import java.util.* @@ -49,124 +49,130 @@ internal class LugnicaScans(context: MangaLoaderContext) : PagedMangaParser(cont sortOrder: SortOrder, ): List { - val url = buildString { - append("https://") - append(domain) - if (sortOrder == SortOrder.ALPHABETICAL) { - append("/mangas/") - // just to stop the search of the ALPHABETICAL page because it contains all the manga and has no page function ( to change if there is a better method to stop the search ) - if (page == 2) { - append(page.toString()) // juste for break - } + if (sortOrder == SortOrder.ALPHABETICAL) { + + if (page > 1) { + return emptyList() } - if (sortOrder == SortOrder.UPDATED) { - append("/api/manga/home/getlast/") - append(page.toString()) + val url = buildString { + append("https://") + append(domain) + append("/api/get/catalog?page=0&filter=all") + } + val json = webClient.httpGet(url).parseJsonArray() + + return json.mapJSON { j -> + val urlManga = "https://$domain/api/get/card/${j.getString("slug")}" + val img = "https://$domain/upload/min_cover/${j.getString("image")}" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga.toAbsoluteUrl(domain), + rating = j.getString("rate").toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN, + isNsfw = false, + coverUrl = img, + tags = setOf(), + state = when (j.getString("status")) { + "0" -> MangaState.ONGOING + "1" -> MangaState.FINISHED + "3" -> MangaState.ABANDONED + else -> null + }, + author = null, + source = source, + ) } - } - val doc = webClient.httpGet(url).parseHtml() - if (sortOrder == SortOrder.UPDATED) { - return doc.select(".last_chapters-element") - .map { div -> - val a = div.selectFirstOrThrow("a.last_chapters-title") - val href = a.attrAsAbsoluteUrl("href") - Manga( - id = generateUid(href), - title = a.text(), - altTitle = null, - url = href, - publicUrl = href.toAbsoluteUrl(domain), - rating = div.selectFirstOrThrow(".last_chapters-rate").ownText().toFloatOrNull()?.div(5f) - ?: -1f, - isNsfw = false, - coverUrl = div.selectFirstOrThrow(".last_chapters-image img").attrAsAbsoluteUrl("src"), - tags = setOf(), - state = null, - author = null, - source = source, - ) - } } else { - val root = doc.selectFirstOrThrow(".catalog") - return root.select("div.element") - .map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") - Manga( - id = generateUid(href), - title = div.select("a.title").text(), - altTitle = null, - url = href, - publicUrl = href.toAbsoluteUrl(domain), - rating = div.selectFirstOrThrow("div.stats").lastElementChild()?.ownText()?.toFloatOrNull() - ?.div(5f) ?: -1f, - isNsfw = false, - coverUrl = div.selectFirstOrThrow("img").attrAsAbsoluteUrl("src"), - tags = setOf(), - state = null, - author = null, - source = source, - ) - } - } + val url = buildString { + append("https://") + append(domain) + append("/api/get/homegrid/") + append(page) + } + val json = webClient.httpGet(url).parseJsonArray() + + return json.mapJSON { j -> + val urlManga = "https://$domain/api/get/card/${j.getString("manga_slug")}" + val img = "https://$domain/upload/min_cover/${j.getString("manga_image")}" + Manga( + id = generateUid(urlManga), + title = j.getString("manga_title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga.toAbsoluteUrl(domain), + rating = j.getString("manga_rate").toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN, + isNsfw = false, + coverUrl = img, + 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.FRANCE) + val json = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseJson() + + val jsonManga = json.getJSONObject("manga") + val chapters = json.getJSONObject("chapters").toString().split("{\"id\":").drop(1) // Possible improvement here + val slug = manga.url.substringAfterLast("/") + val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE) return manga.copy( altTitle = null, - state = when (root.select("div.manga-tags")[3].select("a").text()) { - "En Cours" -> MangaState.ONGOING - "Fini", "Abandonné", "Licencier" -> MangaState.FINISHED + state = when (jsonManga.getString("status")) { + "0" -> MangaState.ONGOING + "1" -> MangaState.FINISHED + "3" -> MangaState.ABANDONED else -> null }, - - // Lists the tags but there is no search on the site so it will just come back to the a-z or last list. - tags = root.select("div.manga-tags")[1].select("a").mapNotNullToSet { a -> - MangaTag( - key = a.text(), - title = a.text().toTitleCase(), + author = jsonManga.getString("author"), + description = jsonManga.getString("description"), + chapters = chapters.mapChapters(reversed = true) { i, it -> + val id = it.substringAfter("\"chapter\":").substringBefore(",") + val url = "https://$domain/api/get/chapter/$slug/$id" + val date = getDateString( + it.substringAfter("\"date\":\"").substringBefore("\",").toLong(), + ) // Possible improvement here + + MangaChapter( + id = generateUid(url), + name = "Chapitre : $id", + number = i, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(date), + branch = null, source = source, ) }, - author = root.select("div.manga-staff").text(), - description = root.selectFirst("div.manga-description div")?.text(), - chapters = root.select("div.manga-chapters_wrapper div.manga-chapter") - .mapChapters(reversed = true) { i, div -> - - val a = div.selectFirstOrThrow("a") - val href = a.attrAsRelativeUrl("href") - val name = a.text() - - val dateText = div.select("span").last()?.text() - MangaChapter( - id = generateUid(href), - name = name, - number = i, - url = href, - scanlator = null, - uploadDate = parseChapterDate( - dateFormat, - dateText, - ), - branch = null, - source = source, - ) - }, ) } + private val simpleDateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE) + private fun getDateString(time: Long): String = simpleDateFormat.format(time * 1000L) + + override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) - val doc = webClient.httpGet(fullUrl).parseHtml() - val root = doc.body().requireElementById("forgen_reader") - return root.select("img").map { img -> - val url = img.attrAsRelativeUrlOrNull("data-src") ?: img.attrAsRelativeUrlOrNull("src") - ?: img.parseFailed("Image src not found") + val jsonPage = webClient.httpGet(fullUrl).parseJson() + + val idManga = jsonPage.getJSONObject("manga").getString("id") + val slugChapter = chapter.url.substringAfterLast("/") + + val pages = jsonPage.getJSONObject("chapter").getJSONArray("files").toString() + .replace("[", "").replace("]", "").replace("\"", "") + .split(",") // Possible improvement here + + return pages.map { img -> + val url = "https://$domain/upload/chapitre/$idManga/$slugChapter/$img" MangaPage( id = generateUid(url), url = url, @@ -178,34 +184,4 @@ internal class LugnicaScans(context: MangaLoaderContext) : PagedMangaParser(cont override suspend fun getTags(): Set = emptySet() - private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { - val d = date?.lowercase() ?: return 0 - return when { - d.startsWith("il y a") -> 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("jour", "jours").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis - WordSet("heure", "heures").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis - WordSet("minute", "minutes").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis - WordSet("seconde", "secondes").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis - WordSet("mois").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis - WordSet("année", "années").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis - WordSet("semaine", "semaines").anyWordIn(date) -> cal.apply { - add( - Calendar.WEEK_OF_MONTH, - -number, - ) - }.timeInMillis - - else -> 0 - } - } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt similarity index 51% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt index e99d88f9..18a9e23b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/PerfScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt @@ -1,20 +1,24 @@ -package org.koitharu.kotatsu.parsers.site.fr +package org.koitharu.kotatsu.parsers.site.heancms 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) { +internal abstract class HeanCms( + context: MangaLoaderContext, + source: MangaSource, + domain: String, + pageSize: Int = 20, +) : PagedMangaParser(context, source, pageSize) { + + override val configKeyDomain = ConfigKey.Domain(domain) override val sortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, @@ -23,12 +27,11 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, SortOrder.POPULARITY, ) - override val configKeyDomain = ConfigKey.Domain("perf-scan.fr") - override val headers: Headers = Headers.Builder() .add("User-Agent", UserAgents.CHROME_DESKTOP) .build() + //For some sources, you need to send a json. For the moment, this part only works in Get. ( ex source need json gloriousscan.com , omegascans.org ) override suspend fun getListPage( page: Int, query: String?, @@ -36,8 +39,11 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, sortOrder: SortOrder, ): List { + var firstTag = false val url = buildString { - append("https://api.$domain/query?query_string=") + append("https://api.") + append(domain) + append("/query?query_string=") if (!query.isNullOrEmpty()) { append(query.urlEncoded()) @@ -55,12 +61,29 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, append("&series_type=Comic&page=") append(page) append("&perPage=12&tags_ids=") - append("[]".urlEncoded()) + append("[".urlEncoded()) + if (!tags.isNullOrEmpty()) { + for (tag in tags) { + // Just to make it fit [1,2,44] ect + if (!firstTag) { + firstTag = true + } else { + append(",") + } + append(tag.key) + } + } + 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" + val cover = if (j.getString("thumbnail").contains('/')) { + j.getString("thumbnail") + } else { + "https://api.$domain/${j.getString("thumbnail")}" + } Manga( id = generateUid(urlManga), title = j.getString("title"), @@ -69,11 +92,12 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, publicUrl = urlManga, rating = RATING_UNKNOWN, isNsfw = false, - coverUrl = j.getString("thumbnail"), + coverUrl = cover, tags = setOf(), state = when (j.getString("status")) { "Ongoing" -> MangaState.ONGOING "Completed" -> MangaState.FINISHED + "Dropped" -> MangaState.ABANDONED else -> null }, author = null, @@ -82,35 +106,40 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, } } + protected open val datePattern = "yyyy-MM-dd" override suspend fun getDetails(manga: Manga): Manga { val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val dateFormat = SimpleDateFormat("MM/DD/yyyy", Locale.ENGLISH) + val dateFormat = SimpleDateFormat(datePattern, Locale.ENGLISH) + + val slug = manga.url.substringAfterLast('/') + val chapter = root.selectFirstOrThrow("script:containsData(chapter_slug)").data() + .replace("\\", "") + .substringAfter("\"seasons\":") + .substringBefore("}]}],\"children\"") + .split("chapter_name") + .drop(1) 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, - ) - }, + description = root.selectFirst("h5:contains(Desc) + .bg-gray-800")?.html(), + chapters = chapter.mapChapters(reversed = true) { i, it -> + val slugChapter = it.substringAfter("chapter_slug\":\"").substringBefore("\",\"") + val url = "https://$domain/series/$slug/$slugChapter" + val date = it.substringAfter("created_at\":\"").substringBefore("\",\"").substringBefore("T") + val name = slugChapter.replace("-", " ") + MangaChapter( + id = generateUid(url), + name = name, + number = i + 1, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(date), + branch = null, + source = source, + ) + }, ) } @@ -128,36 +157,21 @@ internal class PerfScan(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getTags(): Set = emptySet() + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/comics").parseHtml() - private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { - val d = date?.lowercase() ?: return 0 - return when { - d.endsWith(" ago") -> parseRelativeDate(date) + val tags = doc.selectFirstOrThrow("script:containsData(Genres)").data() + .replace("\\", "") + .substringAfterLast("\"Genres\"") + .split("\",{\"") + .drop(1) - 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 + return tags.mapNotNullToSet { + MangaTag( + key = it.substringAfter("id\":").substringBefore(",\""), + title = it.substringAfter("name\":\"").substringBefore("\"}]"), + source = source, + ) } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/es/YugenMangasEs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/es/YugenMangasEs.kt new file mode 100644 index 00000000..f114ce94 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/es/YugenMangasEs.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.parsers.site.heancms.es + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.heancms.HeanCms + +@MangaSourceParser("YUGEN_MANGAS_ES", "Yugen Mangas Es", "es", ContentType.HENTAI) +internal class YugenMangasEs(context: MangaLoaderContext) : + HeanCms(context, MangaSource.YUGEN_MANGAS_ES, "yugenmangas.net") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/fr/PerfScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/fr/PerfScan.kt new file mode 100644 index 00000000..f220d87b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/fr/PerfScan.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.heancms.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.heancms.HeanCms + +@MangaSourceParser("PERF_SCAN", "Perf Scan", "fr") +internal class PerfScan(context: MangaLoaderContext) : + HeanCms(context, MangaSource.PERF_SCAN, "perf-scan.fr") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/pt/ReaperScansPt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/pt/ReaperScansPt.kt new file mode 100644 index 00000000..f58e7a2c --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/pt/ReaperScansPt.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.heancms.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.heancms.HeanCms + +@MangaSourceParser("REAPERSCANSPT", "ReaperScans Pt", "pt") +internal class ReaperScansPt(context: MangaLoaderContext) : + HeanCms(context, MangaSource.REAPERSCANSPT, "reaperscans.net") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt new file mode 100644 index 00000000..7fba394a --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt @@ -0,0 +1,161 @@ +package org.koitharu.kotatsu.parsers.site.heancmsalt + +import kotlinx.coroutines.coroutineScope +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +// Template similar to Heancms but with a different way of working + +internal abstract class HeanCmsAlt( + context: MangaLoaderContext, + source: MangaSource, + domain: String, + pageSize: Int = 18, +) : PagedMangaParser(context, source, pageSize) { + + override val configKeyDomain = ConfigKey.Domain(domain) + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED) + + protected open val listUrl = "/comics" + protected open val datePattern = "MMMM d, yyyy" + + + init { + paginator.firstPage = 1 + searchPaginator.firstPage = 1 + } + + protected open val selectManga = "div.grid.grid-cols-2 div:not([class]):contains(M)" + protected open val selectMangaTitle = "h5" + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + //No search or tag + if(!query.isNullOrEmpty()) + { + return emptyList() + } + val url = buildString { + append("https://") + append(domain) + append(listUrl) + if (page > 1) { + append("?page=") + append(page) + } + } + val doc = webClient.httpGet(url).parseHtml() + + return doc.select(selectManga).map { div -> + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") + Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(div.host ?: domain), + coverUrl = div.selectFirstOrThrow("img").src().orEmpty(), + title = div.selectFirstOrThrow(selectMangaTitle).text().orEmpty(), + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + author = null, + state = null, + source = source, + isNsfw = isNsfwSource, + ) + } + } + + override suspend fun getTags(): Set = emptySet() + + protected open val selectDesc = "div.description-container" + protected open val selectAlt = "div.series-alternative-names" + protected open val selectChapter = "ul.MuiList-root a" + protected open val selectChapterTitle = "div.MuiListItemText-multiline span" + protected open val selectChapterDate = "div.MuiListItemText-multiline p" + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + val fullUrl = manga.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + manga.copy( + altTitle = doc.selectFirst(selectAlt)?.text().orEmpty(), + description = doc.selectFirstOrThrow(selectDesc).html(), + chapters = doc.select(selectChapter) + .mapChapters(reversed = true) { i, a -> + val dateText = a.selectFirstOrThrow(selectChapterDate).text() + val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) + MangaChapter( + id = generateUid(url), + name = a.selectFirstOrThrow(selectChapterTitle).text(), + number = i + 1, + url = url, + scanlator = null, + uploadDate = parseChapterDate( + dateFormat, + dateText, + ), + branch = null, + source = source, + ) + }, + ) + } + + protected open val selectPage = "p.flex-col.items-center img" + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select(selectPage).map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + val d = date?.lowercase() ?: return 0 + return when { + d.startsWith("hace ") -> parseRelativeDate(date) + else -> dateFormat.tryParse(date) + } + } + + // Parses dates in this form: + // 21 hours ago + private fun parseRelativeDate(date: String): Long { + val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + + return when { + WordSet("segundo").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis + WordSet("minutos", "minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis + WordSet("hora", "horas").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis + WordSet("días", "día").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + WordSet("semana", "semanas").anyWordIn(date) -> cal.apply { + add( + Calendar.WEEK_OF_YEAR, + -number, + ) + }.timeInMillis + + WordSet("mes").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("año").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis + else -> 0 + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/CerberuSeries.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/CerberuSeries.kt new file mode 100644 index 00000000..2e06abcb --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/CerberuSeries.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.heancmsalt.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.heancmsalt.HeanCmsAlt + +@MangaSourceParser("LEGIONSCANS", "CerberuSeries", "es") +internal class CerberuSeries(context: MangaLoaderContext) : + HeanCmsAlt(context, MangaSource.LEGIONSCANS, "cerberuseries.xyz") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/MangaEsp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/MangaEsp.kt new file mode 100644 index 00000000..d4c95d28 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/MangaEsp.kt @@ -0,0 +1,24 @@ +package org.koitharu.kotatsu.parsers.site.heancmsalt.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.heancmsalt.HeanCmsAlt + +@MangaSourceParser("MANGAESP", "MangaEsp", "es") +internal class MangaEsp(context: MangaLoaderContext) : + HeanCmsAlt(context, MangaSource.MANGAESP, "mangaesp.co", 15) { + + override val listUrl = "/comic" + + override val selectManga = "div.contenedor div.grid-5 .p-relative:not(.portada-contenedor)" + override val selectMangaTitle = "div.titulo-contenedor" + + override val selectDesc = "div.project-sinopsis-contenido" + override val selectAlt = "div.project-info-opcion:contains(Altenativo) div.project-info-contenido" + override val selectChapter = "div.grid-capitulos div a" + override val selectChapterTitle = ".capitulo-info-titulo" + override val selectChapterDate = ".capitulo-info-fecha" + + override val selectPage = ".grid-center img" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLikeOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLikeOrg.kt new file mode 100644 index 00000000..4978b50e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaLikeOrg.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.madara.ar + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("MANGALIKE_ORG", "MangaLike Org", "ar") +internal class MangaLikeOrg(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGALIKE_ORG, "mangalike.org", pageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarz.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarz.kt index caee966b..f3ac84c8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarz.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/MangaStarz.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser @MangaSourceParser("MANGASTARZ", "Manga Starz", "ar") internal class MangaStarz(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MANGASTARZ, "mangalike.org", pageSize = 10) { + MadaraParser(context, MangaSource.MANGASTARZ, "mangastarz.com", pageSize = 10) { override val datePattern = "d MMMM، yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/CreepyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/CreepyScans.kt new file mode 100644 index 00000000..d144a140 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/CreepyScans.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("CREEPYSCANS", "CreepyScans", "en") +internal class CreepyScans(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.CREEPYSCANS, "creepyscans.com") { + override val stylepage = "" +} 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 index bd5b7a47..ba0e2106 100644 --- 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 @@ -8,4 +8,4 @@ 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) + MadaraParser(context, MangaSource.HIPERDEX, "hiperdex.xyz", 36) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MurimScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MurimScan.kt index ad895606..d4ea27f9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MurimScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MurimScan.kt @@ -9,5 +9,6 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser internal class MurimScan(context: MangaLoaderContext) : MadaraParser(context, MangaSource.MURIMSCAN, "murimscan.run", 100) { + override val withoutAjax = true override val postreq = true } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/YaoiTr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/YaoiTr.kt new file mode 100644 index 00000000..b8b82a34 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/YaoiTr.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.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("YAOITR", "Yaoi Tr", "tr") +internal class YaoiTr(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.YAOITR, "yaoitr.com", 16) { + + override val datePattern = "d MMMM yyyy" +} 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 index 0f3ed836..7ed82485 100644 --- 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 @@ -9,4 +9,5 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class EnAresManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.ENARESMANGA, "en-aresmanga.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" + override val encodedSrc = true } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/OzulShojo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/OzulShojo.kt new file mode 100644 index 00000000..38ad6245 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/OzulShojo.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.ar + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("OZULSHOJO", "OzulShojo", "ar") +internal class OzulShojo(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.OZULSHOJO, "ozulshojo.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikLabParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt similarity index 74% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikLabParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt index 38a75304..9aca54d9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikLabParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site.mangareader.id +package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -7,10 +7,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* -@MangaSourceParser("KOMIKLAB", "KomikLab", "id") +@MangaSourceParser("KOMIKLAB", "KomikLab", "en") internal class KomikLabParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.KOMIKLAB, "komiklab.com", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val sourceLocale: Locale = Locale.ENGLISH } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/OzulScansEn.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/OzulScansEn.kt new file mode 100644 index 00000000..5fd9e8b5 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/OzulScansEn.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.en + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("OZULSCANSEN", "OzulScans En", "en") +internal class OzulScansEn(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.OZULSCANSEN, "ozulscansen.com", pageSize = 30, searchPageSize = 10) { + override val listUrl = "/comics" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LegionScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt similarity index 54% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LegionScans.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt index dc16d89c..bd9c5603 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LegionScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt @@ -4,14 +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.mangareader.MangaReaderParser -import java.util.* +import java.util.Locale -@MangaSourceParser("LEGIONSCANS", "Legion Scans", "es") -internal class LegionScans(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.LEGIONSCANS, "legionscans.com", pageSize = 20, searchPageSize = 20) { - - override val datePattern = "MMM d, yyyy" +@MangaSourceParser("INARIMANGA", "Inari Manga", "es") +internal class InariManga(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.INARIMANGA, "inarimanga.com", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - } - diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt index 962ad755..007c38d4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt @@ -8,7 +8,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("DOUJINDESURIP", "Doujin Desu Rip", "id", ContentType.HENTAI) internal class DoujinDesuRip(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.DOUJINDESURIP, "doujindesu.rip", pageSize = 10, searchPageSize = 10) { - - override val datePattern = "MMM d, yyyy" -} + MangaReaderParser(context, MangaSource.DOUJINDESURIP, "doujindesu.cfd", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt new file mode 100644 index 00000000..15c8dce3 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.id + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser +import java.util.* + +@MangaSourceParser("MANHWALAND", "Manhwa Land", "id", ContentType.HENTAI) +internal class ManhwaLand(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANHWALAND, "manhwaland.lat", pageSize = 20, searchPageSize = 10) { + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt index 51e60de8..76678bd0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt @@ -2,13 +2,12 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("MANHWADESU", "ManhwaDesu", "id") +@MangaSourceParser("MANHWADESU", "ManhwaDesu", "id", ContentType.HENTAI) internal class ManhwadesuParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MANHWADESU, "manhwadesu.top", pageSize = 20, searchPageSize = 10) { - + MangaReaderParser(context, MangaSource.MANHWADESU, "manhwadesu.one", pageSize = 20, searchPageSize = 10) { override val listUrl = "/komik" - override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt new file mode 100644 index 00000000..37bf3c74 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.pt + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("HENTAISSSSSCANLATOR", "Sssscanlator Hentai", "pt", type = ContentType.HENTAI) +internal class HentaiSsssscanlator(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.HENTAISSSSSCANLATOR, "hentais.sssscanlator.com", pageSize = 20, searchPageSize = 10) { + + override val datePattern = "MMM d, yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt new file mode 100644 index 00000000..11df5344 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.th + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("MANGA168", "Manga 168", "th", ContentType.HENTAI) +internal class Manga168(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGA168, "manga168.com", pageSize = 40, searchPageSize = 30) 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 index 7c694c77..e2441238 100644 --- 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 @@ -8,4 +8,4 @@ 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) + MangaReaderParser(context, MangaSource.MOONDAISY_SCANS, "moondaisyscans.biz", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt new file mode 100644 index 00000000..7b2c9b7f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.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("TAROTSCANS", "Tarot Scans", "tr") +internal class TarotScans(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.TAROTSCANS, "www.tarotscans.com", pageSize = 20, searchPageSize = 10) + diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/JpScanVf.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/JpScanVf.kt new file mode 100644 index 00000000..c85d258b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/JpScanVf.kt @@ -0,0 +1,16 @@ +package org.koitharu.kotatsu.parsers.site.mmrcms.fr + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mmrcms.MmrcmsParser +import java.util.* + +@MangaSourceParser("JPSCANVF", "JpScanVf", "fr") +internal class JpScanVf(context: MangaLoaderContext) : + MmrcmsParser(context, MangaSource.JPSCANVF, "jpscan-vf.net") { + + //the search doesn't work on the source. + + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt index dbecf159..9386392a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt @@ -53,25 +53,10 @@ internal abstract class NepnepParser( !query.isNullOrEmpty() -> { if (m.getString("i").contains(query.urlEncoded(), ignoreCase = true)) { manga.add( - Manga( - id = generateUid(href), - title = m.getString("i").replace('-', ' '), - altTitle = null, - url = href, - publicUrl = href.toAbsoluteUrl(domain), - rating = RATING_UNKNOWN, - isNsfw = false, - coverUrl = imgUrl, - tags = emptySet(), - state = null, - author = null, - source = source, - ), + addManga(href, imgUrl, m) ) } - } - !tags.isNullOrEmpty() -> { val a = m.getJSONArray("g").toString() var found = true @@ -82,50 +67,40 @@ internal abstract class NepnepParser( } if (found) { manga.add( - Manga( - id = generateUid(href), - title = m.getString("i").replace('-', ' '), - altTitle = null, - url = href, - publicUrl = href.toAbsoluteUrl(domain), - rating = RATING_UNKNOWN, - isNsfw = false, - coverUrl = imgUrl, - tags = emptySet(), - state = null, - author = null, - source = source, - ), + addManga(href, imgUrl, m) ) } } else -> { manga.add( - Manga( - id = generateUid(href), - title = m.getString("i").replace('-', ' '), - altTitle = null, - url = href, - publicUrl = href.toAbsoluteUrl(domain), - rating = RATING_UNKNOWN, - isNsfw = false, - coverUrl = imgUrl, - tags = emptySet(), - state = null, - author = null, - source = source, - ), + addManga(href, imgUrl, m) ) } } } - return manga } + private fun addManga(href : String, imgUrl : String, m : JSONObject): Manga { + return Manga( + id = generateUid(href), + title = m.getString("i").replace('-', ' '), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = imgUrl, + tags = emptySet(), + state = null, + author = null, + source = source, + ) + } + override suspend fun getTags(): Set { val doc = webClient.httpGet("https://$domain/search/").parseHtml() val tags = doc.selectFirstOrThrow("script:containsData(vm.AvailableFilters)").data() From 7debe6d7e2cf25d1fb80e94c8c6fe4d9fe0dcd04 Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 16 Sep 2023 17:07:59 +0200 Subject: [PATCH 02/13] add sources --- .../kotatsu/parsers/site/mangareader/en/KomikLabParser.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt index 9aca54d9..15125800 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.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.* @MangaSourceParser("KOMIKLAB", "KomikLab", "en") From e25656c09003e63b060f78832abbd230cbb87430 Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 09:35:58 +0200 Subject: [PATCH 03/13] reformat --- .../kotatsu/parsers/site/fr/FmTeam.kt | 70 ++++---- .../parsers/site/heancmsalt/HeanCmsAlt.kt | 3 +- .../site/mangareader/MangaReaderParser.kt | 169 ++++++++---------- .../mangareader/pt/HentaiSsssscanlator.kt | 3 +- .../parsers/site/nepnep/NepnepParser.kt | 9 +- 5 files changed, 120 insertions(+), 134 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt index 200d15e3..cb66169c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FmTeam.kt @@ -26,15 +26,15 @@ internal class FmTeam(context: MangaLoaderContext) : tags: Set?, sortOrder: SortOrder, ): List { - if (page > 1) { return emptyList() } + if (page > 1) { + return emptyList() + } - val jsonManga = if(!query.isNullOrEmpty()) - { + val jsonManga = if (!query.isNullOrEmpty()) { //3 letters minimum webClient.httpGet("https://$domain/api/search/${query.urlEncoded()}").parseJson().getJSONArray("comics") - }else - { + } else { webClient.httpGet("https://$domain/api/comics").parseJson().getJSONArray("comics") } @@ -53,14 +53,14 @@ internal class FmTeam(context: MangaLoaderContext) : } if (found) { manga.add( - addManga(href, j) + addManga(href, j), ) } } else -> { manga.add( - addManga(href, j) + addManga(href, j), ) } } @@ -68,34 +68,34 @@ internal class FmTeam(context: MangaLoaderContext) : return manga } - private fun addManga(href : String, j : JSONObject): Manga { + private fun addManga(href: String, j: JSONObject): Manga { return Manga( - id = generateUid(href), - url = href, - publicUrl = href.toAbsoluteUrl(domain), - coverUrl = j.getString("thumbnail"), - title = j.getString("title"), - description = j.getString("description"), - altTitle = j.getJSONArray("alt_titles").toString() - .replace("[\"", "") - .replace("\"]", "") - .replace("\",\"", " , "), - rating = j.getString("rating").toFloatOrNull()?.div(10f) - ?: RATING_UNKNOWN, - tags = emptySet(), - author = j.getString("author"), - state = when (j.getString("status").lowercase()) { - "en cours" -> MangaState.ONGOING - "terminé" -> MangaState.FINISHED - else -> null - }, - source = source, - isNsfw = when (j.getString("adult").toInt()) { - 0 -> false - 1 -> true - else -> true - }, - ) + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(domain), + coverUrl = j.getString("thumbnail"), + title = j.getString("title"), + description = j.getString("description"), + altTitle = j.getJSONArray("alt_titles").toString() + .replace("[\"", "") + .replace("\"]", "") + .replace("\",\"", " , "), + rating = j.getString("rating").toFloatOrNull()?.div(10f) + ?: RATING_UNKNOWN, + tags = emptySet(), + author = j.getString("author"), + state = when (j.getString("status").lowercase()) { + "en cours" -> MangaState.ONGOING + "terminé" -> MangaState.FINISHED + else -> null + }, + source = source, + isNsfw = when (j.getString("adult").toInt()) { + 0 -> false + 1 -> true + else -> true + }, + ) } @@ -115,7 +115,7 @@ internal class FmTeam(context: MangaLoaderContext) : source = source, ) }, - chapters = chapters.mapJSONIndexed { i,j -> + chapters = chapters.mapJSONIndexed { i, j -> val url = "/api" + j.getString("url").toRelativeUrl(domain) val name = j.getString("full_title") val date = j.getStringOrNull("updated_at") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt index 7fba394a..ba3250b9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt @@ -42,8 +42,7 @@ internal abstract class HeanCmsAlt( sortOrder: SortOrder, ): List { //No search or tag - if(!query.isNullOrEmpty()) - { + if (!query.isNullOrEmpty()) { return emptyList() } val url = buildString { 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 27a68c08..24c70551 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 @@ -13,7 +13,6 @@ import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.* - internal abstract class MangaReaderParser( context: MangaLoaderContext, source: MangaSource, @@ -34,6 +33,84 @@ internal abstract class MangaReaderParser( private val mutex = Mutex() protected open var lastSearchPage = 1 + 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) + } + + val sortQuery = when (sortOrder) { + SortOrder.ALPHABETICAL -> "title" + SortOrder.NEWEST -> "latest" + SortOrder.POPULARITY -> "popular" + SortOrder.UPDATED -> "update" + else -> "" + } + val tagKey = "genre[]".urlEncoded() + val tagQuery = + if (tags.isNullOrEmpty()) "" else tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("/?order=") + append(sortQuery) + append(tagQuery) + append("&page=") + append(page) + } + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + protected open val selectMangalist = ".postbody .listupd .bs .bsx" + protected open val selectMangaListImg = "img.ts-post-image" + protected open val selectMangaListTitle = "div.tt" + + protected open fun parseMangaList(docs: Document): List { + return docs.select(selectMangalist).mapNotNull { + val a = it.selectFirst("a") ?: return@mapNotNull null + val relativeUrl = a.attrAsRelativeUrl("href") + val rating = it.selectFirst(".numscore")?.text() + ?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN + + Manga( + id = generateUid(relativeUrl), + url = relativeUrl, + title = it.selectFirst(selectMangaListTitle)?.text() ?: a.attr("title"), + altTitle = null, + publicUrl = a.attrAsAbsoluteUrl("href"), + rating = rating, + isNsfw = isNsfwSource, + coverUrl = it.selectFirst(selectMangaListImg)?.src().orEmpty(), + tags = emptySet(), + state = null, + author = null, + source = source, + ) + } + } + protected open val selectChapter = "#chapterlist > ul > li" override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() @@ -61,7 +138,6 @@ internal abstract class MangaReaderParser( docs.selectFirst("div.seriestucontent > div.seriestucontentr") ?: docs.selectFirst("div.seriestucontentr") ?: docs.selectFirst("div.seriestucon") - val tagMap = getOrCreateTagMap() val selectTag = if (tablemode != null) { @@ -72,7 +148,6 @@ internal abstract class MangaReaderParser( val tags = selectTag.mapNotNullToSet { tagMap[it.text()] } - val stateSelect = if (tablemode != null) { tablemode.selectFirst(".infotable td:contains(Status)") ?: tablemode.selectFirst(".infotable td:contains(Statut)") @@ -141,86 +216,6 @@ internal abstract class MangaReaderParser( ) } - - 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) - } - - val sortQuery = when (sortOrder) { - SortOrder.ALPHABETICAL -> "title" - SortOrder.NEWEST -> "latest" - SortOrder.POPULARITY -> "popular" - SortOrder.UPDATED -> "update" - else -> "" - } - val tagKey = "genre[]".urlEncoded() - val tagQuery = - if (tags.isNullOrEmpty()) "" else tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append("/?order=") - append(sortQuery) - append(tagQuery) - append("&page=") - append(page) - } - - return parseMangaList(webClient.httpGet(url).parseHtml()) - } - - protected open val selectMangalist = ".postbody .listupd .bs .bsx" - protected open val selectMangaListImg = "img.ts-post-image" - protected open val selectMangaListTitle = "div.tt" - - protected open fun parseMangaList(docs: Document): List { - return docs.select(selectMangalist).mapNotNull { - val a = it.selectFirst("a") ?: return@mapNotNull null - val relativeUrl = a.attrAsRelativeUrl("href") - val rating = it.selectFirst(".numscore")?.text() - ?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN - - Manga( - id = generateUid(relativeUrl), - url = relativeUrl, - title = it.selectFirst(selectMangaListTitle)?.text() ?: a.attr("title"), - altTitle = null, - publicUrl = a.attrAsAbsoluteUrl("href"), - rating = rating, - isNsfw = isNsfwSource, - coverUrl = it.selectFirst(selectMangaListImg)?.src().orEmpty(), - tags = emptySet(), - state = null, - author = null, - source = source, - ) - } - } - protected open val encodedSrc = false protected open val selectScript = "div.wrapper script" protected open val selectPage = "div#readerarea img" @@ -280,10 +275,7 @@ internal abstract class MangaReaderParser( } return pages - } - - } override suspend fun getTags(): Set { @@ -293,22 +285,17 @@ internal abstract class MangaReaderParser( protected suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() - val url = listUrl.toAbsoluteUrl(domain) val tagElements = webClient.httpGet(url).parseHtml().select("ul.genrez > li") for (el in tagElements) { if (el.text().isEmpty()) continue - tagMap[el.text()] = MangaTag( title = el.text(), key = el.selectFirst("input")?.attr("value") ?: continue, source = source, ) } - tagCache = tagMap return@withLock tagMap } - - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt index 37bf3c74..089d8601 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt @@ -8,7 +8,6 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("HENTAISSSSSCANLATOR", "Sssscanlator Hentai", "pt", type = ContentType.HENTAI) internal class HentaiSsssscanlator(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.HENTAISSSSSCANLATOR, "hentais.sssscanlator.com", pageSize = 20, searchPageSize = 10) { - + MangaReaderParser(context, MangaSource.HENTAISSSSSCANLATOR, "hentais.sssscanlator.com", pageSize = 20, searchPageSize = 10,) { override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt index 9386392a..f8ee646b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt @@ -53,10 +53,11 @@ internal abstract class NepnepParser( !query.isNullOrEmpty() -> { if (m.getString("i").contains(query.urlEncoded(), ignoreCase = true)) { manga.add( - addManga(href, imgUrl, m) + addManga(href, imgUrl, m), ) } } + !tags.isNullOrEmpty() -> { val a = m.getJSONArray("g").toString() var found = true @@ -67,14 +68,14 @@ internal abstract class NepnepParser( } if (found) { manga.add( - addManga(href, imgUrl, m) + addManga(href, imgUrl, m), ) } } else -> { manga.add( - addManga(href, imgUrl, m) + addManga(href, imgUrl, m), ) } } @@ -84,7 +85,7 @@ internal abstract class NepnepParser( return manga } - private fun addManga(href : String, imgUrl : String, m : JSONObject): Manga { + private fun addManga(href: String, imgUrl: String, m: JSONObject): Manga { return Manga( id = generateUid(href), title = m.getString("i").replace('-', ' '), From 712d829b5450e1c7050893744b5bef795f9f5536 Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 10:50:01 +0200 Subject: [PATCH 04/13] add sources --- .../kotatsu/parsers/site/madara/en/NvManga.kt | 14 ++++++++ .../kotatsu/parsers/site/madara/es/MhScans.kt | 12 +++++++ .../parsers/site/madara/pt/DianxiaTrads.kt | 13 ++++++++ .../parsers/site/madara/tr/Webtoonevreni.kt | 12 +++++++ .../parsers/site/manga18/zh/Hanman18.kt | 32 +++++++++++++++++++ .../parsers/site/mangabox/en/MangaNeloCom.kt | 14 ++++++++ 6 files changed, 97 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/NvManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MhScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/DianxiaTrads.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonevreni.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangaNeloCom.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/NvManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/NvManga.kt new file mode 100644 index 00000000..a5d20296 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/NvManga.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.madara.en + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("NVMANGA", "NvManga", "en") +internal class NvManga(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.NVMANGA, "nvmanga.com") { + override val datePattern = "dd/MM/yyyy" + override val tagPrefix = "genre/" + override val listUrl = "webtoon/" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MhScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MhScans.kt new file mode 100644 index 00000000..6eaf62d8 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MhScans.kt @@ -0,0 +1,12 @@ +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("MHSCANS", "MhScans", "es") +internal class MhScans(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MHSCANS, "mhscans.com") { + override val datePattern = "d 'de' MMMMM 'de' yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/DianxiaTrads.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/DianxiaTrads.kt new file mode 100644 index 00000000..452e090b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/DianxiaTrads.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("DIANXIATRADS", "Dianxia Trads", "pt") +internal class DianxiaTrads(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.DIANXIATRADS, "dianxiatrads.com", 10) { + + override val datePattern: String = "dd/MM/yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonevreni.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonevreni.kt new file mode 100644 index 00000000..f92ee182 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Webtoonevreni.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.parsers.site.madara.tr + + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + + +@MangaSourceParser("WEBTOONEVRENI", "Webtoonevreni", "tr") +internal class Webtoonevreni(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.WEBTOONEVRENI, "webtoonevreni.net", 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt new file mode 100644 index 00000000..b96bec34 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt @@ -0,0 +1,32 @@ +package org.koitharu.kotatsu.parsers.site.manga18.zh + +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.manga18.Manga18Parser +import org.koitharu.kotatsu.parsers.util.* + +@MangaSourceParser("HANMAN18", "Hanman18", "zh", ContentType.HENTAI) +internal class Hanman18(context: MangaLoaderContext) : + Manga18Parser(context, MangaSource.HANMAN18, "hanman18.com") { + + override suspend fun getChapters(manga: Manga, doc: Document): List { + return doc.body().select(selectChapter).mapChapters(reversed = true) { i, li -> + val a = li.selectFirstOrThrow("a") + val href = a.attrAsRelativeUrl("href") + MangaChapter( + id = generateUid(href), + name = a.text(), + number = i + 1, + url = href, + uploadDate = 0, + source = source, + scanlator = null, + branch = null, + ) + } + } + + override suspend fun getTags(): Set = emptySet() // search by tag does not work +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangaNeloCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangaNeloCom.kt new file mode 100644 index 00000000..c761418f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangaNeloCom.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.mangabox.en + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.site.mangabox.MangaboxParser + +@MangaSourceParser("MANGANELO_COM", "MangaNelo Com", "en") +internal class MangaNeloCom(context: MangaLoaderContext) : + MangaboxParser(context, MangaSource.MANGANELO_COM) { + override val configKeyDomain = ConfigKey.Domain("m.manganelo.com", "chapmanganelo.com") + override val otherDomain = "chapmanganelo.com" +} From 611aaf0d38e72fde17c31e3a1fdbf830428128f5 Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 10:52:27 +0200 Subject: [PATCH 05/13] remove mirrordesu --- .../parsers/site/mangareader/id/Mirrordesu.kt | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mirrordesu.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mirrordesu.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mirrordesu.kt deleted file mode 100644 index 264186a2..00000000 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mirrordesu.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.koitharu.kotatsu.parsers.site.mangareader.id - -import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser - -@MangaSourceParser("MIRRORDESU", "Mirrordesu", "id", ContentType.HENTAI) -internal class Mirrordesu(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.MIRRORDESU, "mirrordesu.ink", pageSize = 20, searchPageSize = 20) { - - override val listUrl = "/komik" - override val datePattern = "MMM d, yyyy" - -} From 366cc862bad7bfc419cc4d5d377c0af50b60585b Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 13:12:35 +0200 Subject: [PATCH 06/13] remove 1stkissmanga.me and add likemanga --- .../kotatsu/parsers/site/en/LikeManga.kt | 221 ++++++++++++++++++ .../parsers/site/madara/en/Stkissmanga.kt | 13 -- 2 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt delete mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Stkissmanga.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt new file mode 100644 index 00000000..264c1ab9 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt @@ -0,0 +1,221 @@ +package org.koitharu.kotatsu.parsers.site.en + +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.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("LIKEMANGA", "LikeManga", "en") +internal class LikeManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.LIKEMANGA, 36) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST) + override val configKeyDomain = ConfigKey.Domain("likemanga.io") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + val tag = tags.oneOrThrowIfMany() + val url = buildString { + append("https://") + append(domain) + append("/?act=search&f") + append("[sortby]".urlEncoded()) + append("=") + when (sortOrder) { + SortOrder.POPULARITY -> append("hot") + SortOrder.UPDATED -> append("lastest-chap") + SortOrder.NEWEST -> append("lastest-manga") + else -> append("lastest-chap") + } + if (page > 1) { + append("&pageNum=") + append(page) + } + if (!tags.isNullOrEmpty()) { + append("&f") + append("[genres]".urlEncoded()) + append("=") + append(tag?.key.orEmpty()) + } + if (!query.isNullOrEmpty()) { + append("&f") + append("[keyword]".urlEncoded()) + append("=") + append(query.urlEncoded()) + } + } + val doc = webClient.httpGet(url).parseHtml() + return doc.select("div.card-body div.video").map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.selectFirstOrThrow("p.title-manga").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 { + val doc = webClient.httpGet("https://$domain/genres/").parseHtml() + return doc.select("ul.nav-genres li:not(.text-center) a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast('/'), + title = a.text(), + source = source, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val mangaId = manga.url.toAbsoluteUrl(domain).removeSuffix("/").substringAfterLast("-").toInt() + val maxPageChapterSelect = doc.getElementById("nav_list_chapter_id_detail")?.select("a:not(.next)") + var maxPageChapter = 1 + if (!maxPageChapterSelect.isNullOrEmpty()) { + maxPageChapterSelect.map { + val i = it.text().toInt() + if (i > maxPageChapter) { + maxPageChapter = i + } + } + } + return manga.copy( + altTitle = doc.selectFirstOrThrow(".list-info li.othername h2").text(), + state = null, + tags = doc.select("li.kind a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast('/'), + title = a.text().toTitleCase(), + source = source, + ) + }, + author = doc.select("li.author p").last()?.text(), + description = doc.requireElementById("summary_shortened").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(mangaId, i) + } + }.awaitAll() + .flattenTo(result) + result + } + } + }.reversed(), + ) + } + + private suspend fun loadChapters(mangaId: Int, page: Int): List { + val json = + webClient.httpGet("https://$domain/?act=ajax&code=load_list_chapter&manga_id=$mangaId&page_num=$page&chap_id=0&keyword=") + .parseJson().getString("list_chap") + val chapters = json.split("wp-manga-chapter").drop(1) + return chapters.map { chapter -> + val url = chapter.substringAfter("href=\"").substringBefore("\">") + val name = chapter.substringAfter("/\">").substringBefore("") + val chapNum = url.substringAfter("chapter-").substringBefore("-") + val d = chapter.substringAfter("").substringBefore("") + val dateText = if (d.contains("New")) { + "today" + } else { + d + } + MangaChapter( + id = generateUid(url), + name = name, + number = chapNum.toInt(), + url = url, + scanlator = null, + uploadDate = parseChapterDate( + dateFormat, + dateText, + ), + branch = null, + source = source, + ) + } + } + + private val dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH) + private fun parseChapters(root: Element): List { + return root.select("li.wp-manga-chapter") + .map { li -> + val url = li.selectFirstOrThrow("a").attrAsRelativeUrl("href") + val dateText = if (li.selectFirstOrThrow(".chapter-release-date").text() == "New") { + "today" + } else { + li.selectFirstOrThrow(".chapter-release-date").text() + } + val chapNum = url.substringAfter("chapter-").substringBefore("-") + + MangaChapter( + id = generateUid(url), + name = li.selectFirstOrThrow("a").text(), + number = chapNum.toInt(), + url = url, + 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(".reading-detail img").map { img -> + val url = img.src() ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + val d = date?.lowercase() ?: return 0 + return when { + 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 + + else -> dateFormat.tryParse(date) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Stkissmanga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Stkissmanga.kt deleted file mode 100644 index 7653334f..00000000 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Stkissmanga.kt +++ /dev/null @@ -1,13 +0,0 @@ -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("STKISSMANGA", "Stkissmanga", "en") -internal class Stkissmanga(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.STKISSMANGA, "1stkissmanga.me") { - - override val datePattern = "MMMM dd, yyyy" -} From 519bb3053b010047a32d59d1600b046cf1a2266e Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 16:57:50 +0200 Subject: [PATCH 07/13] add ksk --- .../kotatsu/parsers/site/en/KskMoe.kt | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt new file mode 100644 index 00000000..1c64066d --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt @@ -0,0 +1,164 @@ +package org.koitharu.kotatsu.parsers.site.en + +import androidx.collection.ArraySet +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("KSKMOE", "Ksk Moe", "en") +internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.KSKMOE, 35) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("ksk.moe") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + val tag = tags.oneOrThrowIfMany() + + val url = buildString { + append("https://") + append(domain) + + if (!tags.isNullOrEmpty()) { + append("/tags/") + append(tag?.key.orEmpty()) + }else + { + append("/browse") + } + + if (page > 1) { + append("/page/") + append(page) + } + + when (sortOrder) { + SortOrder.POPULARITY -> append("?sort=32") + SortOrder.UPDATED -> append("") + SortOrder.NEWEST -> append("?sort=16") + SortOrder.ALPHABETICAL -> append("?sort=1") + else -> append("") + } + + if (!query.isNullOrEmpty()) { + append("?s=") + append(query.urlEncoded()) + } + } + val doc = webClient.httpGet(url).parseHtml() + + if(!doc.html().contains("pagination") && page > 1) + { + return emptyList() + } + return doc.requireElementById("galleries").select("article").map { div -> + val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + Manga( + id = generateUid(href), + title = div.selectLastOrThrow("h3 span").text(), + altTitle = null, + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = div.selectFirstOrThrow("img").src()?.toAbsoluteUrl(domain).orEmpty(), + tags = div.select("footer span").mapNotNullToSet { span -> + MangaTag( + key = span.text().urlEncoded(), + title = span.text(), + source = source, + ) + }, + state = null, + author = null, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + return coroutineScope { + (1..2).map { page -> + async { getTags(page) } + } + }.awaitAll().flattenTo(ArraySet(360)) + } + + private suspend fun getTags(page: Int): Set { + val root = if(page == 1) + { + webClient.httpGet("https://${domain}/tags").parseHtml().body() + .getElementById("tags") + }else + { + webClient.httpGet("https://${domain}/tags/page/$page").parseHtml().body() + .getElementById("tags") + } + return root?.parseTags().orEmpty() + } + + private fun Element.parseTags() = select("section.tags div a").mapToSet { a -> + MangaTag( + key = a.attr("href").substringAfter("/tags/"), + title = a.selectFirstOrThrow("span").text(), + source = source, + ) + } + + + private val date = SimpleDateFormat("dd.MM.yyyy hh:mm 'UTC'", Locale.US) + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + return manga.copy( + tags = doc.requireElementById("metadata").select("main div:contains(Tag) a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").substringAfter("/tags/"), + title = a.selectFirstOrThrow("span").text(), + source = source, + ) + }, + author = doc.requireElementById("metadata").selectFirstOrThrow("main div:contains(Artist) a span").text(), + chapters = listOf( + MangaChapter( + id = generateUid(manga.id), + name = manga.title, + number = 1, + url = manga.url, + scanlator = null, + uploadDate = date.tryParse(doc.selectFirstOrThrow("time.updated").text()), + branch = null, + source = source, + ), + ), + ) + } + + + //For the moment the pages are in poor quality. + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.requireElementById("previews").select("main div img").map { img -> + val url = img.src() ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} From 45113e419e2aee63545cb5b6d3bc819135fd56df Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 17:18:09 +0200 Subject: [PATCH 08/13] small fix --- .../kotatsu/parsers/site/en/KskMoe.kt | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt index 1c64066d..3914d694 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt @@ -17,7 +17,8 @@ import java.util.* @MangaSourceParser("KSKMOE", "Ksk Moe", "en") internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.KSKMOE, 35) { - override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL) + override val sortOrders: Set = + EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL) override val configKeyDomain = ConfigKey.Domain("ksk.moe") override suspend fun getListPage( @@ -35,8 +36,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M if (!tags.isNullOrEmpty()) { append("/tags/") append(tag?.key.orEmpty()) - }else - { + } else { append("/browse") } @@ -60,8 +60,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M } val doc = webClient.httpGet(url).parseHtml() - if(!doc.html().contains("pagination") && page > 1) - { + if (!doc.html().contains("pagination") && page > 1) { return emptyList() } return doc.requireElementById("galleries").select("article").map { div -> @@ -98,12 +97,10 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M } private suspend fun getTags(page: Int): Set { - val root = if(page == 1) - { + val root = if (page == 1) { webClient.httpGet("https://${domain}/tags").parseHtml().body() .getElementById("tags") - }else - { + } else { webClient.httpGet("https://${domain}/tags/page/$page").parseHtml().body() .getElementById("tags") } @@ -122,6 +119,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M private val date = SimpleDateFormat("dd.MM.yyyy hh:mm 'UTC'", Locale.US) override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + return manga.copy( tags = doc.requireElementById("metadata").select("main div:contains(Tag) a").mapNotNullToSet { a -> MangaTag( @@ -131,19 +129,25 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M ) }, author = doc.requireElementById("metadata").selectFirstOrThrow("main div:contains(Artist) a span").text(), - chapters = listOf( - MangaChapter( - id = generateUid(manga.id), - name = manga.title, - number = 1, - url = manga.url, - scanlator = null, - uploadDate = date.tryParse(doc.selectFirstOrThrow("time.updated").text()), - branch = null, - source = source, - ), - ), - ) + chapters = + if ((doc.html().contains("previews"))) { + listOf( + MangaChapter( + id = generateUid(manga.id), + name = manga.title, + number = 1, + url = manga.url, + scanlator = null, + uploadDate = date.tryParse(doc.selectFirstOrThrow("time.updated").text()), + branch = null, + source = source, + ), + ) + } else { + emptyList() + }, + + ) } From 853b95363e695ecf9a98ee237e5059fa5a02416d Mon Sep 17 00:00:00 2001 From: devi Date: Sun, 17 Sep 2023 19:49:21 +0200 Subject: [PATCH 09/13] add missing content type --- src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt index 3914d694..3d4dab91 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt @@ -14,7 +14,7 @@ import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.* -@MangaSourceParser("KSKMOE", "Ksk Moe", "en") +@MangaSourceParser("KSKMOE", "Ksk Moe", "en", ContentType.HENTAI) internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.KSKMOE, 35) { override val sortOrders: Set = From a106b343794884b89d7a0f08c38ad776ad4839b2 Mon Sep 17 00:00:00 2001 From: devi Date: Mon, 18 Sep 2023 18:39:05 +0200 Subject: [PATCH 10/13] add sources and fix --- .../kotatsu/parsers/site/madara/ar/ShadowxManga.kt | 13 +++++++++++++ .../kotatsu/parsers/site/madara/en/Manga1k.kt | 13 +++++++++++++ .../parsers/site/mangareader/en/AnigliScans.kt | 4 +--- .../parsers/site/mangareader/en/Nightscans.kt | 5 ++--- .../parsers/site/mangareader/tr/AsuraTRParser.kt | 7 ++----- .../kotatsu/parsers/site/mmrcms/MmrcmsParser.kt | 2 +- .../kotatsu/parsers/site/mmrcms/fr/FrScansCom.kt | 3 +-- .../koitharu/kotatsu/parsers/site/pt/GoldenManga.kt | 9 ++------- 8 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ShadowxManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ShadowxManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ShadowxManga.kt new file mode 100644 index 00000000..65fce534 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/ShadowxManga.kt @@ -0,0 +1,13 @@ +package org.koitharu.kotatsu.parsers.site.madara.ar + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("SHADOWXMANGA", "Shadow X Manga", "ar") +internal class ShadowxManga(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.SHADOWXMANGA, "shadowxmanga.com") { + + override val datePattern = "yyyy/MM/dd" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt new file mode 100644 index 00000000..bf1edc59 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.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("MANGA1K", "Manga1k", "en", ContentType.HENTAI) +internal class Manga1k(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MANGA1K, "manga1k.com", 20) { + override val withoutAjax = true +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt index 78bc9aa7..2d82e0bb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt @@ -7,8 +7,6 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ANIGLISCANS", "Anigli Scans", "en") internal class AnigliScans(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.ANIGLISCANS, "anigliscans.com", pageSize = 47, searchPageSize = 47) { - + MangaReaderParser(context, MangaSource.ANIGLISCANS, "anigliscans.xyz", pageSize = 47, searchPageSize = 47) { override val listUrl = "/series" - override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt index 2d52b26e..a296dd42 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt @@ -5,9 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("NIGHTSCANS", "Nightscans", "en") +@MangaSourceParser("NIGHTSCANS", "Night scans", "en") internal class Nightscans(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.NIGHTSCANS, "nightscans.org", pageSize = 20, searchPageSize = 20) { - - override val datePattern = "MMM d, yyyy" + override val selectMangaListImg = "img.ts-post-image, picture img" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AsuraTRParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AsuraTRParser.kt index f2e6c93d..f9cb2ae7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AsuraTRParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AsuraTRParser.kt @@ -5,9 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("ASURATR", "Asura Scans (tr)", "tr") +@MangaSourceParser("ASURATR", "Armoni Scans", "tr") internal class AsuraTRParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.ASURATR, "asurascanstr.com", pageSize = 30, searchPageSize = 10) { - - override val datePattern = "MMM d, yyyy" -} + MangaReaderParser(context, MangaSource.ASURATR, "armoniscans.com", pageSize = 30, 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 0d87807b..193a04e0 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 @@ -114,7 +114,7 @@ internal abstract class MmrcmsParser( url = href, publicUrl = href.toAbsoluteUrl(div.host ?: domain), coverUrl = "https://$domain/uploads/manga/$deeplink$imgUpdated", - title = div.selectFirstOrThrow("a").text().orEmpty(), + title = div.selectFirstOrThrow("h3 a").text().orEmpty(), altTitle = null, rating = RATING_UNKNOWN, tags = emptySet(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/FrScansCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/FrScansCom.kt index 6538c913..a9d60be4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/FrScansCom.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/fr/FrScansCom.kt @@ -6,9 +6,8 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mmrcms.MmrcmsParser import java.util.Locale -@MangaSourceParser("FRSCANSCOM", "FrScansCom", "fr") +@MangaSourceParser("FRSCANSCOM", "Fr Scans Com", "fr") internal class FrScansCom(context: MangaLoaderContext) : MmrcmsParser(context, MangaSource.FRSCANSCOM, "frscans.com") { - override val sourceLocale: Locale = Locale.ENGLISH } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt index 56bcf6f9..69f64541 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt @@ -15,14 +15,11 @@ import java.util.* internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.GOLDENMANGA, 36) { override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) - - override val configKeyDomain = ConfigKey.Domain("goldenmanga.top") - + override val configKeyDomain = ConfigKey.Domain("www.goldenmangas.top") override val headers: Headers = Headers.Builder() - .add("User-Agent", UserAgents.CHROME_DESKTOP) + .add("User-Agent", UserAgents.CHROME_MOBILE) .build() - override suspend fun getListPage( page: Int, query: String?, @@ -36,12 +33,10 @@ internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(conte append("/mangas") append("?pagina=") append(page.toString()) - if (!query.isNullOrEmpty()) { append("&search=") append(query.urlEncoded()) } - if (!tags.isNullOrEmpty()) { append("&genero=") for (tag in tags) { From 609c7188c0296badf06ee89c0a506c314e50e95f Mon Sep 17 00:00:00 2001 From: devi Date: Mon, 18 Sep 2023 19:48:24 +0200 Subject: [PATCH 11/13] add sources tr --- .../parsers/site/mangareader/tr/MangaGezgini.kt | 10 ++++++++++ .../parsers/site/mangareader/tr/Mangaefendisi.kt | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt new file mode 100644 index 00000000..a96dd7f8 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.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("MANGAGEZGINI", "Manga Gezgini", "tr") +internal class MangaGezgini(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGAGEZGINI, "mangagezgini.com", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt new file mode 100644 index 00000000..fedb4452 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.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("MANGAEFENDISI", "Mangaefendisi", "tr") +internal class Mangaefendisi(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGAEFENDISI, "mangaefendisi.net", pageSize = 30, searchPageSize = 20) From f471a3513e4fba13195e6005f6f887fdfa001b34 Mon Sep 17 00:00:00 2001 From: devi Date: Tue, 19 Sep 2023 18:48:26 +0200 Subject: [PATCH 12/13] add mangashiro.me and fix --- .../kotatsu/parsers/site/en/Manhwa18Parser.kt | 11 +++++------ .../kotatsu/parsers/site/mangareader/ar/SwaTeam.kt | 6 +++--- .../parsers/site/mangareader/en/Nightscans.kt | 3 ++- .../parsers/site/mangareader/id/MangaShiro.kt | 13 +++++++++++++ .../koitharu/kotatsu/parsers/site/pt/GoldenManga.kt | 2 +- .../parsers/site/ru/grouple/MintMangaParser.kt | 1 + 6 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt index 6d9aff90..1e8bbbd3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt @@ -32,19 +32,19 @@ class Manhwa18Parser(context: MangaLoaderContext) : override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val cardInfoElement = docs.selectFirst("div.series-information") - val author = cardInfoElement?.selectFirst(".info-name:contains(Author(s))")?.parent() + val author = cardInfoElement?.selectFirst(".info-name:contains(Author)")?.parent() ?.select("a") ?.joinToString(", ") { it.text() } val availableTags = tagsMap.get() - val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre(s))")?.parent() + val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre)")?.parent() ?.select("a") ?.mapNotNullToSet { availableTags[it.text().lowercase(Locale.ENGLISH)] } val state = cardInfoElement?.selectFirst(".info-name:contains(Status)")?.parent() ?.selectFirst("a") ?.let { - when (it.text()) { - "On going" -> MangaState.ONGOING - "Completed" -> MangaState.FINISHED + when (it.text().lowercase()) { + "on going" -> MangaState.ONGOING + "completed" -> MangaState.FINISHED else -> null } } @@ -56,7 +56,6 @@ class Manhwa18Parser(context: MangaLoaderContext) : tags = tags.orEmpty(), state = state, chapters = docs.select(".card-body > .list-chapters > a").mapChapters(reversed = true) { index, element -> - // attrAsRelativeUrl only return page url without the '/' val chapterUrl = element.attrAsAbsoluteUrlOrNull("href")?.toRelativeUrl(domain) ?: return@mapChapters null val uploadDate = parseUploadDate(element.selectFirst(".chapter-time")?.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 cf1dd2fe..71cfb309 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, "swatmanga.co", pageSize = 42, searchPageSize = 39) { + MangaReaderParser(context, MangaSource.SWATEAM, "stmanga.me", pageSize = 42, searchPageSize = 39) { override val datePattern = "MMMM dd, yyyy" override val selectMangalist = ".listupd .bs .bsx" @@ -44,8 +44,8 @@ internal class SwaTeam(context: MangaLoaderContext) : } val sortQuery = when (sortOrder) { - SortOrder.ALPHABETICAL -> "title" - SortOrder.NEWEST -> "latest" + SortOrder.ALPHABETICAL -> "a-z" + SortOrder.NEWEST -> "added" SortOrder.POPULARITY -> "popular" SortOrder.UPDATED -> "update" else -> "" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt index a296dd42..b7fc109d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NIGHTSCANS", "Night scans", "en") internal class Nightscans(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.NIGHTSCANS, "nightscans.org", pageSize = 20, searchPageSize = 20) { + MangaReaderParser(context, MangaSource.NIGHTSCANS, "nightscans.net", pageSize = 20, searchPageSize = 10) { + override val listUrl = "/series" override val selectMangaListImg = "img.ts-post-image, picture img" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt new file mode 100644 index 00000000..425ecc0b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt @@ -0,0 +1,13 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.id + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser +import java.util.Locale + +@MangaSourceParser("MANGASHIRO", "Manga Shiro", "id") +internal class MangaShiro(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGASHIRO, "mangashiro.me", pageSize = 20, searchPageSize = 10) { + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt index 69f64541..2b52dd49 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt @@ -15,7 +15,7 @@ import java.util.* internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.GOLDENMANGA, 36) { override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) - override val configKeyDomain = ConfigKey.Domain("www.goldenmangas.top") + override val configKeyDomain = ConfigKey.Domain("goldenmanga.top") override val headers: Headers = Headers.Builder() .add("User-Agent", UserAgents.CHROME_MOBILE) .build() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/MintMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/MintMangaParser.kt index d54cfc40..63c75a29 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/MintMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/MintMangaParser.kt @@ -13,6 +13,7 @@ internal class MintMangaParser( override val configKeyDomain = ConfigKey.Domain( "mintmanga.live", "mintmanga.com", + "m.mintmanga.live", ) } From 8fef459346057921165200c7d76d1895121dff14 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 20 Sep 2023 12:09:54 +0300 Subject: [PATCH 13/13] Misc fixes --- .../kotatsu/parsers/site/ar/TeamXNovel.kt | 2 +- .../parsers/site/en/CloneMangaParser.kt | 6 ++--- .../kotatsu/parsers/site/en/ComicExtra.kt | 4 +--- .../kotatsu/parsers/site/en/Comicastle.kt | 6 +---- .../kotatsu/parsers/site/en/DynastyScans.kt | 8 +++---- .../kotatsu/parsers/site/en/KskMoe.kt | 23 ++++++++----------- .../kotatsu/parsers/site/en/LikeManga.kt | 6 +++-- .../kotatsu/parsers/site/en/MangaGeko.kt | 4 +--- .../kotatsu/parsers/site/en/Manhwa18Parser.kt | 2 +- .../kotatsu/parsers/site/en/Po2Scans.kt | 2 +- .../kotatsu/parsers/site/fr/LireScan.kt | 2 +- .../kotatsu/parsers/site/fr/ScantradUnion.kt | 11 +++------ .../parsers/site/heancmsalt/HeanCmsAlt.kt | 9 ++++---- .../koitharu/kotatsu/parsers/site/pt/Bakai.kt | 5 +--- .../kotatsu/parsers/site/pt/BrMangas.kt | 3 +-- .../kotatsu/parsers/site/pt/GoldenManga.kt | 4 +--- .../kotatsu/parsers/site/tr/SadScans.kt | 2 +- 17 files changed, 38 insertions(+), 61 deletions(-) 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 index f67529c1..29362779 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt @@ -61,7 +61,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex return doc.select("div.listupd .bs .bsx").ifEmpty { doc.select("div.post-body .box") }.map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.select(".tt, h3").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt index 96e1d0ce..5e5327e2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt @@ -23,17 +23,17 @@ internal class CloneMangaParser(context: MangaLoaderContext) : MangaParser(conte if (query != null || offset > 0) { return emptyList() } - val link = "https://${domain}/viewer_landing.php" + val link = "https://$domain/viewer_landing.php" val doc = webClient.httpGet(link).parseHtml() val mangas = doc.getElementsByClass("comicPreviewContainer") return mangas.mapNotNull { item -> val background = item.selectFirstOrThrow(".comicPreview").styleValueOrNull("background") - val href = item.selectFirst("a")?.attrAsAbsoluteUrl("href") ?: return@mapNotNull null + val href = item.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapNotNull null val cover = background?.substring(background.indexOf("site/themes"), background.indexOf(")")) Manga( id = generateUid(href), title = item.selectFirst("h3")?.text() ?: return@mapNotNull null, - coverUrl = "https://${domain}/$cover", + coverUrl = "https://$domain/$cover", altTitle = null, author = "Dan Kim", rating = RATING_UNKNOWN, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt index a25e2b7d..82d0a4a5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt @@ -59,7 +59,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex val doc = webClient.httpGet(url).parseHtml() return doc.select("div.movie-list-index div.cartoon-box").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h3").text(), @@ -81,7 +81,6 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex } } - override suspend fun getTags(): Set { val doc = webClient.httpGet("https://$domain/popular-comic").parseHtml() return doc.select("li.tag-item a").mapNotNullToSet { a -> @@ -146,5 +145,4 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex ) } } - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Comicastle.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Comicastle.kt index 22213527..28311d8c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Comicastle.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Comicastle.kt @@ -49,7 +49,6 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex } val postdata = "submit=Submit&search=" + query.urlEncoded() webClient.httpPost(url, postdata).parseHtml() - } else if (!tags.isNullOrEmpty()) { val url = buildString { append("https://$domain/library/search/genre/") @@ -57,7 +56,6 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex } val postdata = "submit=Submit&search=" + tag?.key.orEmpty() webClient.httpPost(url, postdata).parseHtml() - } else { val url = buildString { append("https://$domain") @@ -75,7 +73,7 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex return doc.select("div.card-body div.match-height div.col-6") .map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("p").text(), @@ -93,7 +91,6 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex } } - override suspend fun getTags(): Set { val doc = webClient.httpGet("https://$domain/library/").parseHtml() return doc.requireElementById("sidebar").selectFirstOrThrow(".card-body").select("button") @@ -156,5 +153,4 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex ) } } - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt index 34286593..fb63265b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt @@ -52,11 +52,11 @@ internal class DynastyScans(context: MangaLoaderContext) : PagedMangaParser(cont } val doc = webClient.httpGet(url).parseHtml() - //There are no images on the search page + // There are no images on the search page if (!query.isNullOrEmpty()) { return doc.select("dl.chapter-list dd") .map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("a").text(), @@ -81,7 +81,7 @@ internal class DynastyScans(context: MangaLoaderContext) : PagedMangaParser(cont } else { return doc.select("li.span2") .map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("div.caption").text(), @@ -98,7 +98,6 @@ internal class DynastyScans(context: MangaLoaderContext) : PagedMangaParser(cont ) } } - } override suspend fun getTags(): Set = emptySet() @@ -169,4 +168,3 @@ internal class DynastyScans(context: MangaLoaderContext) : PagedMangaParser(cont return pages } } - diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt index 3d4dab91..3e290adb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/KskMoe.kt @@ -64,7 +64,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M return emptyList() } return doc.requireElementById("galleries").select("article").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectLastOrThrow("h3 span").text(), @@ -72,7 +72,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M url = href, publicUrl = href.toAbsoluteUrl(domain), rating = RATING_UNKNOWN, - isNsfw = false, + isNsfw = true, coverUrl = div.selectFirstOrThrow("img").src()?.toAbsoluteUrl(domain).orEmpty(), tags = div.select("footer span").mapNotNullToSet { span -> MangaTag( @@ -97,25 +97,23 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M } private suspend fun getTags(page: Int): Set { - val root = if (page == 1) { - webClient.httpGet("https://${domain}/tags").parseHtml().body() - .getElementById("tags") + val url = if (page == 1) { + "https://$domain/tags" } else { - webClient.httpGet("https://${domain}/tags/page/$page").parseHtml().body() - .getElementById("tags") + "https://$domain/tags/page/$page" } + val root = webClient.httpGet(url).parseHtml().body().getElementById("tags") return root?.parseTags().orEmpty() } private fun Element.parseTags() = select("section.tags div a").mapToSet { a -> MangaTag( - key = a.attr("href").substringAfter("/tags/"), + key = a.attr("href").substringAfterLast("/tags/"), title = a.selectFirstOrThrow("span").text(), source = source, ) } - private val date = SimpleDateFormat("dd.MM.yyyy hh:mm 'UTC'", Locale.US) override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() @@ -123,7 +121,7 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M return manga.copy( tags = doc.requireElementById("metadata").select("main div:contains(Tag) a").mapNotNullToSet { a -> MangaTag( - key = a.attr("href").substringAfter("/tags/"), + key = a.attr("href").substringAfterLast("/tags/"), title = a.selectFirstOrThrow("span").text(), source = source, ) @@ -147,11 +145,10 @@ internal class KskMoe(context: MangaLoaderContext) : PagedMangaParser(context, M emptyList() }, - ) + ) } - - //For the moment the pages are in poor quality. + // For the moment the pages are in poor quality. override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt index 264c1ab9..8c349aef 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/LikeManga.kt @@ -58,7 +58,7 @@ internal class LikeManga(context: MangaLoaderContext) : PagedMangaParser(context } val doc = webClient.httpGet(url).parseHtml() return doc.select("div.card-body div.video").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("p.title-manga").text(), @@ -134,7 +134,9 @@ internal class LikeManga(context: MangaLoaderContext) : PagedMangaParser(context private suspend fun loadChapters(mangaId: Int, page: Int): List { val json = - webClient.httpGet("https://$domain/?act=ajax&code=load_list_chapter&manga_id=$mangaId&page_num=$page&chap_id=0&keyword=") + webClient.httpGet( + "https://$domain/?act=ajax&code=load_list_chapter&manga_id=$mangaId&page_num=$page&chap_id=0&keyword=", + ) .parseJson().getString("list_chap") val chapters = json.split("wp-manga-chapter").drop(1) return chapters.map { chapter -> 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 9ef23aa7..01d70193 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 @@ -59,7 +59,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context val doc = webClient.httpGet(url).parseHtml() return doc.select("li.novel-item").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h4").text(), @@ -77,7 +77,6 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context } } - override suspend fun getTags(): Set { val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml() return doc.select("label.checkbox-inline").mapNotNullToSet { label -> @@ -143,5 +142,4 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context ) } } - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt index 1e8bbbd3..35547add 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt @@ -23,7 +23,7 @@ class Manhwa18Parser(context: MangaLoaderContext) : override suspend fun getFavicons(): Favicons { return Favicons( listOf( - Favicon("https://${domain}/uploads/logos/logo-mini.png", 92, null), + Favicon("https://$domain/uploads/logos/logo-mini.png", 92, null), ), domain, ) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt index e8594e0f..bf5b7838 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt @@ -28,7 +28,7 @@ internal class Po2Scans(context: MangaLoaderContext) : MangaParser(context, Mang } val doc = webClient.httpGet(url).parseHtml() return doc.select(".series-list").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h2").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt index 91553f23..69913484 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt @@ -55,7 +55,7 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context, } return doc.select("div.sect__content.grid-items div.item-poster").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.select(".item-poster__title").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt index 211f18e7..630bf2af 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt @@ -41,7 +41,6 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con append(page.toString()) append("/?s=") append(query.urlEncoded()) - } !tags.isNullOrEmpty() -> { @@ -55,7 +54,6 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con } else -> { - if (sortOrder == SortOrder.ALPHABETICAL) { append("/manga/") append("/page/") @@ -65,7 +63,6 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con if (sortOrder == SortOrder.UPDATED) { append("") } - } } } @@ -74,7 +71,7 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con val root = doc.requireElementById("dernierschapitres") return root.select("div.colonne") .map { article -> - val href = article.selectFirstOrThrow("a.index-top4-a").attrAsAbsoluteUrl("href") + val href = article.selectFirstOrThrow("a.index-top4-a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = article.select(".carteinfos a").text(), @@ -94,7 +91,7 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con val root = doc.requireElementById("main") return root.select("article.post-outer") .map { article -> - val href = article.selectFirstOrThrow("a.thumb-link").attrAsAbsoluteUrl("href") + val href = article.selectFirstOrThrow("a.thumb-link").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = article.select(".index-post-header a").text(), @@ -111,8 +108,6 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con ) } } - - } override suspend fun getDetails(manga: Manga): Manga { @@ -169,7 +164,7 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con ?: throw ParseException("Root not found", fullUrl) return root.select("img").map { img -> val url = img.attrAsRelativeUrlOrNull("data-src") ?: img.attrAsRelativeUrlOrNull("src") - ?: img.parseFailed("Image src not found") + ?: img.parseFailed("Image src not found") MangaPage( id = generateUid(url), url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt index ba3250b9..1c0b310d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.parsers.site.heancmsalt -import kotlinx.coroutines.coroutineScope import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.PagedMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey @@ -26,7 +25,6 @@ internal abstract class HeanCmsAlt( protected open val listUrl = "/comics" protected open val datePattern = "MMMM d, yyyy" - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 @@ -41,7 +39,7 @@ internal abstract class HeanCmsAlt( tags: Set?, sortOrder: SortOrder, ): List { - //No search or tag + // No search or tag if (!query.isNullOrEmpty()) { return emptyList() } @@ -82,11 +80,12 @@ internal abstract class HeanCmsAlt( protected open val selectChapter = "ul.MuiList-root a" protected open val selectChapterTitle = "div.MuiListItemText-multiline span" protected open val selectChapterDate = "div.MuiListItemText-multiline p" - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + + override suspend fun getDetails(manga: Manga): Manga { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() val dateFormat = SimpleDateFormat(datePattern, sourceLocale) - manga.copy( + return manga.copy( altTitle = doc.selectFirst(selectAlt)?.text().orEmpty(), description = doc.selectFirstOrThrow(selectDesc).html(), chapters = doc.select(selectChapter) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/Bakai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/Bakai.kt index 4eea28c4..8971e26e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/Bakai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/Bakai.kt @@ -27,7 +27,6 @@ internal class Bakai(context: MangaLoaderContext) : PagedMangaParser(context, Ma tags: Set?, sortOrder: SortOrder, ): List { - val url = buildString { append("https://") append(domain) @@ -45,7 +44,6 @@ internal class Bakai(context: MangaLoaderContext) : PagedMangaParser(context, Ma } append("&quick=1&type=cms_records1&page=") append(page.toString()) - } else { append("/hentai/") append("page/") @@ -54,10 +52,9 @@ internal class Bakai(context: MangaLoaderContext) : PagedMangaParser(context, Ma } val doc = webClient.httpGet(url).parseHtml() if (!tags.isNullOrEmpty() or !query.isNullOrEmpty()) { - return doc.select("ol.ipsStream li.ipsStreamItem") .map { div -> - val href = div.selectFirstOrThrow("div.ipsStreamItem_snippet a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("div.ipsStreamItem_snippet a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h2.ipsStreamItem_title").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt index 6f5c5aae..51fdcce3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt @@ -42,7 +42,6 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, } append("/?s=") append(query.urlEncoded()) - } else { when (sortOrder) { SortOrder.POPULARITY -> append("/") @@ -64,7 +63,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, } return item.map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h2").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt index 2b52dd49..1d7fd482 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/GoldenManga.kt @@ -26,7 +26,6 @@ internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(conte tags: Set?, sortOrder: SortOrder, ): List { - val url = buildString { append("https://") append(domain) @@ -48,7 +47,7 @@ internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(conte val doc = webClient.httpGet(url).parseHtml() return doc.select("section.row div.mangas") .map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("a h3").text(), @@ -102,7 +101,6 @@ internal class GoldenManga(context: MangaLoaderContext) : PagedMangaParser(conte author = root.select("h5.cg_color a")[1].text(), description = root.getElementById("manga_capitulo_descricao")?.html(), chapters = root.requireElementById("capitulos").select("li") - .mapChapters(reversed = true) { i, div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val dateText = div.selectFirstOrThrow("div.col-sm-5 span").text() 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 index c1bc6fb1..3e0594a3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt @@ -28,7 +28,7 @@ internal class SadScans(context: MangaLoaderContext) : MangaParser(context, Mang } val doc = webClient.httpGet(url).parseHtml() return doc.select(".series-list").map { div -> - val href = div.selectFirstOrThrow("a").attrAsAbsoluteUrl("href") + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( id = generateUid(href), title = div.selectFirstOrThrow("h2").text(),