From 55e14e4cb35c83c7dcdae7c039b8448c3d1b0aea Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 16 Sep 2023 17:07:22 +0200 Subject: [PATCH] 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()