From db770351f36a465b7fc71af0a349297a5853861f Mon Sep 17 00:00:00 2001 From: devi Date: Tue, 28 Nov 2023 21:07:54 +0100 Subject: [PATCH] add filter on FmreaderParser, LugnicaScans Fix GetList on FoolSlideParser , FuryoSociety , LegacyScans, LireScan, ScansMangas.me, ScantradUnion --- .../parsers/site/fmreader/FmreaderParser.kt | 85 ++++++---- .../parsers/site/fmreader/en/Manhwa18Com.kt | 106 ++++++------ .../parsers/site/fmreader/es/OlimpoScans.kt | 69 +++++--- .../kotatsu/parsers/site/fmreader/ja/Klz9.kt | 44 +---- .../parsers/site/foolslide/FoolSlideParser.kt | 76 ++++++--- .../site/foolslide/en/AssortedScans.kt | 50 +++--- .../parsers/site/foolslide/fr/HniScantrad.kt | 2 +- .../parsers/site/foolslide/it/PowerManga.kt | 2 - .../parsers/site/foolslide/it/Ramareader.kt | 2 - .../parsers/site/foolslide/it/ReadNifteam.kt | 2 - .../kotatsu/parsers/site/fr/FuryoSociety.kt | 29 ++-- .../parsers/site/fr/LegacyScansParser.kt | 148 ++++++++-------- .../kotatsu/parsers/site/fr/LireScan.kt | 64 ++++--- .../kotatsu/parsers/site/fr/LugnicaScans.kt | 158 +++++++++++------- .../kotatsu/parsers/site/fr/ScansMangasMe.kt | 65 +++---- .../kotatsu/parsers/site/fr/ScantradUnion.kt | 60 +++---- 16 files changed, 506 insertions(+), 456 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt index 3dbceae2..7fa7a03d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt @@ -27,7 +27,11 @@ internal abstract class FmreaderParser( SortOrder.ALPHABETICAL, ) - override val isMultipleTagsSupported = false + override val availableStates: Set = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.ABANDONED, + ) protected open val listUrl = "/manga-list.html" protected open val datePattern = "MMMM d, yyyy" @@ -40,58 +44,73 @@ internal abstract class FmreaderParser( @JvmField protected val ongoing: Set = setOf( - "On going", - "Incomplete", - "En curso", + "on going", + "incomplete", + "en curso", ) @JvmField protected val finished: Set = setOf( - "Completed", - "Completado", + "completed", + "completado", ) @JvmField protected val abandoned: Set = hashSetOf( - "Canceled", - "Cancelled", - "Drop", + "canceled", + "cancelled", + "drop", ) - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val tag = tags.oneOrThrowIfMany() + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val url = buildString { append("https://") append(domain) append(listUrl) append("?page=") append(page.toString()) - when { - !query.isNullOrEmpty() -> { - + when (filter) { + is MangaListFilter.Search -> { append("&name=") - append(query.urlEncoded()) + append(filter.query.urlEncoded()) } - !tags.isNullOrEmpty() -> { + is MangaListFilter.Advanced -> { + append("&genre=") - append(tag?.key.orEmpty()) + append(filter.tags.joinToString(",") { it.key }) + + + append("&sort=") + when (filter.sortOrder) { + SortOrder.POPULARITY -> append("views") + SortOrder.UPDATED -> append("last_update") + SortOrder.ALPHABETICAL -> append("name") + else -> append("last_update") + } + + append("&m_status=") + filter.states.oneOrThrowIfMany()?.let { + append( + when (it) { + MangaState.ONGOING -> "2" + MangaState.FINISHED -> "1" + MangaState.ABANDONED -> "3" + else -> "" + }, + ) + } + } - } - append("&sort=") - when (sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("last_update") - SortOrder.ALPHABETICAL -> append("name") - else -> append("last_update") + + null -> append("&sort=last_update") } } - val doc = webClient.httpGet(url).parseHtml() + return parseMangaList(webClient.httpGet(url).parseHtml()) + + } + + protected open fun parseMangaList(doc: Document): List { return doc.select("div.thumb-item-flow").map { div -> val href = div.selectFirstOrThrow("div.series-title a").attrAsRelativeUrl("href") Manga( @@ -99,8 +118,8 @@ internal abstract class FmreaderParser( url = href, publicUrl = href.toAbsoluteUrl(div.host ?: domain), coverUrl = div.selectFirstOrThrow("div.img-in-ratio").attr("data-bg") - ?: div.selectFirstOrThrow("div.img-in-ratio").attr("style").substringAfter("('") - .substringBeforeLast("')"), + ?: div.selectFirstOrThrow("div.img-in-ratio").attr("style").substringAfter("(") + .substringBefore(")"), title = div.selectFirstOrThrow("div.series-title").text().orEmpty(), altTitle = null, rating = RATING_UNKNOWN, @@ -140,7 +159,7 @@ internal abstract class FmreaderParser( val desc = doc.selectFirst(selectDesc)?.html() val stateDiv = doc.selectFirst(selectState) val state = stateDiv?.let { - when (it.text()) { + when (it.text().lowercase()) { in ongoing -> MangaState.ONGOING in finished -> MangaState.FINISHED in abandoned -> MangaState.ABANDONED diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt index e3a86ef5..676c5564 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt @@ -20,79 +20,67 @@ internal class Manhwa18Com(context: MangaLoaderContext) : override val selectTag = "div.info-item:contains(Genre) span.info-value a" override val datePattern = "dd/MM/yyyy" override val selectPage = "div#chapter-content img" - override val selectBodyTag = "div.genres-menu a" + override val selectBodyTag = "div.advanced-wrapper .genre_label" - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val tag = tags.oneOrThrowIfMany() + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val url = buildString { append("https://") append(domain) - if (!tags.isNullOrEmpty()) { - append("/genre/") - append(tag?.key.orEmpty()) - append("?page=") - append(page.toString()) - append("&sort=") - when (sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("last_update") - SortOrder.ALPHABETICAL -> append("name") - else -> append("last_update") + append("/tim-kiem?page=") + append(page.toString()) + + when (filter) { + is MangaListFilter.Search -> { + append("&q=") + append(filter.query.urlEncoded()) } - } else { - append(listUrl) - append("?page=") - append(page.toString()) - when { - !query.isNullOrEmpty() -> { - append("&q=") - append(query.urlEncoded()) + + is MangaListFilter.Advanced -> { + + append("&accept_genres=") + if (filter.tags.isNotEmpty()) { + append( + filter.tags.joinToString(",") { it.key }, + ) + } + + append("&sort=") + append( + when (filter.sortOrder) { + SortOrder.ALPHABETICAL -> "az" + SortOrder.POPULARITY -> "top" + SortOrder.UPDATED -> "update" + SortOrder.NEWEST -> "new" + SortOrder.RATING -> "like" + }, + ) + + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "3" + MangaState.PAUSED -> "2" + else -> "" + }, + ) } - } - append("&sort=") - when (sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("last_update") - SortOrder.ALPHABETICAL -> append("name") - else -> append("last_update") } + null -> append("&sort=update") } } - val doc = webClient.httpGet(url).parseHtml() - return doc.select("div.thumb-item-flow").map { div -> - val href = div.selectFirstOrThrow("div.series-title a").attrAsRelativeUrl("href") - Manga( - id = generateUid(href), - url = href, - publicUrl = href.toAbsoluteUrl(div.host ?: domain), - coverUrl = div.selectFirstOrThrow("div.img-in-ratio").attr("data-bg") - ?: div.selectFirstOrThrow("div.img-in-ratio").attr("style").substringAfter("('") - .substringBeforeLast("')"), - title = div.selectFirstOrThrow("div.series-title").text().orEmpty(), - altTitle = null, - rating = RATING_UNKNOWN, - tags = emptySet(), - author = null, - state = null, - source = source, - isNsfw = isNsfwSource, - ) - } + return parseMangaList(webClient.httpGet(url).parseHtml()) } override suspend fun getAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() - return doc.select(selectBodyTag).mapNotNullToSet { a -> - val href = a.attr("href").substringAfterLast("/") + return doc.select(selectBodyTag).mapNotNullToSet { label -> + val key = label.attr("data-genre-id") MangaTag( - key = href, - title = a.text(), + key = key, + title = label.selectFirstOrThrow(".gerne-name").text(), source = source, ) } @@ -105,7 +93,7 @@ internal class Manhwa18Com(context: MangaLoaderContext) : val desc = doc.selectFirstOrThrow(selectDesc).html() val stateDiv = doc.selectFirst(selectState) val state = stateDiv?.let { - when (it.text()) { + when (it.text().lowercase()) { in ongoing -> MangaState.ONGOING in finished -> MangaState.FINISHED else -> null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt index 0b603f13..2ed70663 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt @@ -14,37 +14,60 @@ internal class OlimpoScans(context: MangaLoaderContext) : override val selectAlt = "ul.manga-info li:contains(Otros nombres)" override val selectTag = "ul.manga-info li:contains(Género) a" override val tagPrefix = "lista-de-comics-genero-" + override val isMultipleTagsSupported = false - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val tag = tags.oneOrThrowIfMany() + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val url = buildString { append("https://") append(domain) - append(listUrl) - append("?page=") - append(page.toString()) - when { - !query.isNullOrEmpty() -> { + when (filter) { + is MangaListFilter.Search -> { + append(listUrl) + append("?page=") + append(page.toString()) append("&name=") - append(query.urlEncoded()) + append(filter.query.urlEncoded()) } - !tags.isNullOrEmpty() -> { - append("&genre=") - append(tag?.key.orEmpty()) + is MangaListFilter.Advanced -> { + if (filter.tags.isNotEmpty()) { + filter.tags.oneOrThrowIfMany()?.let { + append("/lista-de-comics-genero-") + append(it.key) + append(".html") + } + } else { + append(listUrl) + append("?page=") + append(page.toString()) + append("&sort=") + when (filter.sortOrder) { + SortOrder.POPULARITY -> append("views") + SortOrder.UPDATED -> append("last_update") + SortOrder.ALPHABETICAL -> append("name") + else -> append("last_update") + } + } + + append("&m_status=") + filter.states.oneOrThrowIfMany()?.let { + append( + when (it) { + MangaState.ONGOING -> "2" + MangaState.FINISHED -> "1" + MangaState.ABANDONED -> "3" + else -> "" + }, + ) + } + } + + null -> { + append(listUrl) + append("?page=") + append(page.toString()) + append("&sort=last_update") } - } - append("&sort=") - when (sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("last_update") - SortOrder.ALPHABETICAL -> append("name") - else -> append("last_update") } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt index 9d363040..4ab0ee63 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt @@ -3,13 +3,7 @@ package org.koitharu.kotatsu.parsers.site.fmreader.ja import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.fmreader.FmreaderParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @@ -27,40 +21,7 @@ internal class Klz9(context: MangaLoaderContext) : override val selectPage = "img" override val selectBodyTag = "div.panel-body a" - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val tag = tags.oneOrThrowIfMany() - val url = buildString { - append("https://") - append(domain) - append("/$listUrl") - append("?page=") - append(page.toString()) - when { - !query.isNullOrEmpty() -> { - - append("&name=") - append(query.urlEncoded()) - } - - !tags.isNullOrEmpty() -> { - append("&genre=") - append(tag?.key.orEmpty()) - } - } - append("&sort=") - when (sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("last_update") - SortOrder.ALPHABETICAL -> append("name") - else -> append("last_update") - } - } - val doc = webClient.httpGet(url).parseHtml() + override fun parseMangaList(doc: Document): List { return doc.select("div.thumb-item-flow").map { div -> val href = "/" + div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( @@ -112,7 +73,6 @@ internal class Klz9(context: MangaLoaderContext) : val docLoad = webClient.httpGet("https://$domain/app/manga/controllers/cont.listImg.php?cid=$cid").parseHtml() return docLoad.select(selectPage).map { img -> val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") - MangaPage( id = generateUid(url), url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt index f0b5211c..70bf7e34 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt @@ -32,35 +32,63 @@ internal abstract class FoolSlideParser( searchPaginator.firstPage = 1 } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val doc = if (!query.isNullOrEmpty()) { - val url = buildString { - append("https://$domain/$searchUrl") - if (page > 1) { - return emptyList() - } - } - val q = query.urlEncoded() - webClient.httpPost(url, "search=$q").parseHtml() - } else { - val url = buildString { - append("https://$domain/$listUrl") - // For some sites that don't have enough manga and page 2 links to page 1 - if (!pagination) { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + val doc = + when (filter) { + is MangaListFilter.Search -> { if (page > 1) { return emptyList() } - } else { - append(page.toString()) + + val url = buildString { + append("https://") + append(domain) + append("/") + append(searchUrl) + } + + webClient.httpPost(url, "search=${filter.query.urlEncoded()}").parseHtml() + } + + is MangaListFilter.Advanced -> { + + val url = buildString { + append("https://") + append(domain) + append("/") + append(listUrl) + // For some sites that don't have enough manga and page 2 links to page 1 + if (!pagination) { + if (page > 1) { + return emptyList() + } + } else { + append(page.toString()) + } + } + webClient.httpGet(url).parseHtml() + + } + + null -> { + val url = buildString { + append("https://") + append(domain) + append("/") + append(listUrl) + if (!pagination) { + if (page > 1) { + return emptyList() + } + } else { + append(page.toString()) + } + } + webClient.httpGet(url).parseHtml() + } } - webClient.httpGet(url).parseHtml() - } + return doc.select("div.list div.group").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt index 957cab69..4dbf8916 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/en/AssortedScans.kt @@ -16,40 +16,30 @@ internal class AssortedScans(context: MangaLoaderContext) : override val pagination = false override val selectInfo = "div.#series-info" - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + if (page > 1) { + return emptyList() + } - val doc = if (!query.isNullOrEmpty()) { - if (page > 1) { - return emptyList() - } - val url = buildString { - append("https://") - append(domain) - append('/') - append(searchUrl) - append("?q=") - append(query.urlEncoded()) - } - webClient.httpGet(url).parseHtml() - } else { - val url = buildString { - append("https://$domain/$listUrl") - // For some sites that don't have enough manga and page 2 links to page 1 - if (!pagination) { - if (page > 1) { - return emptyList() - } - } else { - append(page.toString()) + val url = buildString { + append("https://") + append(domain) + append('/') + when (filter) { + is MangaListFilter.Search -> { + append(searchUrl) + append("?q=") + append(filter.query.urlEncoded()) } + + is MangaListFilter.Advanced -> { + append(listUrl) + } + + null -> append(listUrl) } - webClient.httpGet(url).parseHtml() } + val doc = webClient.httpGet(url).parseHtml() return doc.select("section.series, tr.result").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/fr/HniScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/fr/HniScantrad.kt index 0debd791..bd069d28 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/fr/HniScantrad.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/fr/HniScantrad.kt @@ -5,7 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser -@MangaSourceParser("HNISCANTRAD", "Hni Scantrad", "fr") +@MangaSourceParser("HNISCANTRAD", "HniScantrad", "fr") internal class HniScantrad(context: MangaLoaderContext) : FoolSlideParser(context, MangaSource.HNISCANTRAD, "hni-scantrad.com") { override val pagination = false diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/PowerManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/PowerManga.kt index e400f8a8..39f6844d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/PowerManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/PowerManga.kt @@ -1,12 +1,10 @@ package org.koitharu.kotatsu.parsers.site.foolslide.it - import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser - @MangaSourceParser("POWERMANGA", "PowerManga", "it") internal class PowerManga(context: MangaLoaderContext) : FoolSlideParser(context, MangaSource.POWERMANGA, "reader.powermanga.org") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/Ramareader.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/Ramareader.kt index 8a704477..cc0a6906 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/Ramareader.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/Ramareader.kt @@ -1,12 +1,10 @@ package org.koitharu.kotatsu.parsers.site.foolslide.it - import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser - @MangaSourceParser("RAMAREADER", "RamaReader", "it") internal class Ramareader(context: MangaLoaderContext) : FoolSlideParser(context, MangaSource.RAMAREADER, "www.ramareader.it") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/ReadNifteam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/ReadNifteam.kt index 1f567e69..5aee24f1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/ReadNifteam.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/it/ReadNifteam.kt @@ -1,12 +1,10 @@ package org.koitharu.kotatsu.parsers.site.foolslide.it - import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.foolslide.FoolSlideParser - @MangaSourceParser("READNIFTEAM", "ReadNifTeam", "it") internal class ReadNifteam(context: MangaLoaderContext) : FoolSlideParser(context, MangaSource.READNIFTEAM, "read-nifteam.info") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt index 2363c994..047e2eaa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt @@ -10,6 +10,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.lang.IllegalArgumentException import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* @@ -35,21 +36,27 @@ internal class FuryoSociety(context: MangaLoaderContext) : ) } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + if (page > 1) { + return emptyList() + } + val url = buildString { append("https://") append(domain) - if (page == 1) { - if (sortOrder == SortOrder.ALPHABETICAL) { - append("/mangas") + when (filter) { + is MangaListFilter.Search -> { + throw IllegalArgumentException("Search is not supported by this source") + } + + is MangaListFilter.Advanced -> { + + if (filter.sortOrder == SortOrder.ALPHABETICAL) { + append("/mangas") + } } - } else { - return emptyList() + + null -> {} } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt index cdb87743..3817021d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.parsers.site.fr import okhttp3.Headers +import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.PagedMangaParser @@ -16,9 +17,7 @@ import java.util.* internal class LegacyScansParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.LEGACY_SCANS, 18) { - override val availableSortOrders: Set = EnumSet.of( - SortOrder.POPULARITY, - ) + override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) override val configKeyDomain = ConfigKey.Domain("legacy-scans.com") @@ -26,86 +25,96 @@ internal class LegacyScansParser(context: MangaLoaderContext) : .add("User-Agent", UserAgents.CHROME_MOBILE) .build() - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val end = page * pageSize val start = end - (pageSize - 1) - val url = if (!query.isNullOrEmpty()) { - if (page > 1) { - return emptyList() - } - buildString { - append("https://api.$domain/misc/home/search?title=") - append(query.urlEncoded()) - } - } else { - buildString { - append("https://api.$domain/misc/comic/search/query?status=&order=&genreNames=") - if (!tags.isNullOrEmpty()) { - for (tag in tags) { - append(tag.key) - append(',') - } + + when (filter) { + is MangaListFilter.Search -> { + if (page > 1) { + return emptyList() } - append("&type=&start=") - append(start) - append("&end=") - append(end) + val url = buildString { + append("https://api.$domain/misc/home/search?title=") + append(filter.query.urlEncoded()) + } + return parseMangaListQuery(webClient.httpGet(url).parseJson()) } - } - val json = webClient.httpGet(url).parseJson() - return if (!query.isNullOrEmpty()) { - json.getJSONArray("results").mapJSON { j -> - val slug = j.getString("slug") - val urlManga = "https://$domain/comics/$slug" - Manga( - id = generateUid(urlManga), - title = j.getString("title"), - altTitle = null, - url = urlManga, - publicUrl = urlManga, - rating = RATING_UNKNOWN, - isNsfw = false, - coverUrl = "", - tags = setOf(), - state = null, - author = null, - source = source, - ) + is MangaListFilter.Advanced -> { + val url = buildString { + append("https://api.") + append(domain) + append("/misc/comic/search/query?status=&order=&genreNames=") + append(filter.tags.joinToString(",") { it.key }) + append("&type=&start=") + append(start) + append("&end=") + append(end) + } + return parseMangaList(webClient.httpGet(url).parseJson()) } - } else { - json.getJSONArray("comics").mapJSON { j -> - val slug = j.getString("slug") - val urlManga = "https://$domain/comics/$slug" - Manga( - id = generateUid(urlManga), - title = j.getString("title"), - altTitle = null, - url = urlManga, - publicUrl = urlManga, - rating = RATING_UNKNOWN, - isNsfw = false, - coverUrl = "https://api.$domain/" + j.getString("cover"), - tags = setOf(), - state = null, - author = null, - source = source, - ) + + null -> { + val url = buildString { + append("https://api.") + append(domain) + append("/misc/comic/search/query?status=&order=&genreNames=&type=&start=") + append(start) + append("&end=") + append(end) + } + return parseMangaList(webClient.httpGet(url).parseJson()) } } + } + + + private fun parseMangaList(json: JSONObject): List { + return json.getJSONArray("comics").mapJSON { j -> + val slug = j.getString("slug") + val urlManga = "https://$domain/comics/$slug" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga, + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = "https://api.$domain/" + j.getString("cover"), + tags = setOf(), + state = null, + author = null, + source = source, + ) + } + } + private fun parseMangaListQuery(json: JSONObject): List { + return json.getJSONArray("results").mapJSON { j -> + val slug = j.getString("slug") + val urlManga = "https://$domain/comics/$slug" + Manga( + id = generateUid(urlManga), + title = j.getString("title"), + altTitle = null, + url = urlManga, + publicUrl = urlManga, + rating = RATING_UNKNOWN, + isNsfw = false, + coverUrl = "", + tags = setOf(), + state = null, + author = null, + source = source, + ) + } } override suspend fun getDetails(manga: Manga): Manga { val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH) - return manga.copy( altTitle = null, tags = root.select("div.serieGenre span").mapNotNullToSet { span -> @@ -156,7 +165,6 @@ internal class LegacyScansParser(context: MangaLoaderContext) : val script = doc.requireElementById("__NUXT_DATA__").data() .substringAfterLast("\"genres\"").substringBeforeLast("\"comics\"") .split("\",\"").drop(1) - return script.mapNotNullToSet { tag -> MangaTag( key = tag.substringBeforeLast("\",{"), 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 6b3ca531..593d4470 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 @@ -24,36 +24,50 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context, .add("User-Agent", UserAgents.CHROME_MOBILE) .build() - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - val tag = tags.oneOrThrowIfMany() + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + val doc = - if (!query.isNullOrEmpty()) { // search only works with 4 or more letters - if (page > 1) { - return emptyList() + when (filter) { + is MangaListFilter.Search -> { + if (page > 1) { + return emptyList() + } + val q = filter.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() } - 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()) + + is MangaListFilter.Advanced -> { + val url = buildString { + append("https://") + append(domain) + + filter.tags.oneOrThrowIfMany()?.let { + append("/manga/") + append(it.key) + } + + if (page > 1) { + append("/page/") + append(page) + append('/') + } } - if (page > 1) { - append("/page/") - append(page) - append('/') + webClient.httpGet(url).parseHtml() + } + + null -> { + val url = buildString { + append("https://") + append(domain) + if (page > 1) { + append("/page/") + append(page) + append('/') + } } + webClient.httpGet(url).parseHtml() } - webClient.httpGet(url).parseHtml() } return doc.select("div.sect__content.grid-items div.item-poster").map { div -> 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 7ad9c279..3545a377 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 @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.parsers.site.fr import okhttp3.Headers +import org.json.JSONArray import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.PagedMangaParser @@ -21,6 +22,8 @@ internal class LugnicaScans(context: MangaLoaderContext) : PagedMangaParser(cont SortOrder.UPDATED, ) + override val availableStates: Set = EnumSet.allOf(MangaState::class.java) + override val configKeyDomain = ConfigKey.Domain("lugnica-scans.com") override val headers: Headers = Headers.Builder() @@ -43,77 +46,102 @@ internal class LugnicaScans(context: MangaLoaderContext) : PagedMangaParser(cont ) } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { - if (!query.isNullOrEmpty()) { - throw IllegalArgumentException("Search is not supported by this source") - } - if (sortOrder == SortOrder.ALPHABETICAL) { - if (page > 1) { - return emptyList() - } - 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, - ) + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + when (filter) { + is MangaListFilter.Search -> { + throw IllegalArgumentException("Search is not supported by this source") } - } else { - val url = buildString { - append("https://") - append(domain) - append("/api/get/homegrid/") - append(page) + + is MangaListFilter.Advanced -> { + + if (filter.sortOrder == SortOrder.ALPHABETICAL) { + if (page > 1) { + return emptyList() + } + val url = buildString { + append("https://") + append(domain) + append("/api/get/catalog?page=0&filter=") + filter.states.oneOrThrowIfMany()?.let { + when (it) { + MangaState.ONGOING -> append("0") + MangaState.FINISHED -> append("1") + MangaState.PAUSED -> append("4") + MangaState.ABANDONED -> append("3") + } + } + + + } + return parseMangaListAlpha(webClient.httpGet(url).parseJsonArray()) + } else { + val url = buildString { + append("https://") + append(domain) + append("/api/get/homegrid/") + append(page) + } + return parseMangaList(webClient.httpGet(url).parseJsonArray()) + } } - 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, - ) + + null -> { + val url = buildString { + append("https://") + append(domain) + append("/api/get/homegrid/") + append(page) + } + return parseMangaList(webClient.httpGet(url).parseJsonArray()) } + } + } + private fun parseMangaList(json: JSONArray): List { + 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, + ) } + } + private fun parseMangaListAlpha(json: JSONArray): List { + 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, + ) + } } override suspend fun getDetails(manga: Manga): Manga { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt index 15c285c2..1e9e892e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt @@ -30,46 +30,45 @@ internal class ScansMangasMe(context: MangaLoaderContext) : .add("User-Agent", UserAgents.CHROME_DESKTOP) .build() + override val isMultipleTagsSupported = false - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + if (page > 1) { + return emptyList() + } val url = buildString { append("https://") append(domain) - if (page == 1) { - if (!query.isNullOrEmpty()) { + when (filter) { + is MangaListFilter.Search -> { append("/?s=") - append(query.urlEncoded()) + append(filter.query.urlEncoded()) append("&post_type=manga") + } - } else if (!tags.isNullOrEmpty()) { - append("/genres/") - for (tag in tags) { - append(tag.key) - } - } else { - append("/tous-nos-mangas/") - append("?order=") - when (sortOrder) { - SortOrder.POPULARITY -> append("popular") - SortOrder.UPDATED -> append("update") - SortOrder.ALPHABETICAL -> append("title") - SortOrder.NEWEST -> append("create") - else -> append("update") + is MangaListFilter.Advanced -> { + if (filter.tags.isNotEmpty()) { + append("/genres/") + filter.tags.oneOrThrowIfMany()?.let { + append(it.key) + } + } else { + append("/tous-nos-mangas/?order=") + when (filter.sortOrder) { + SortOrder.POPULARITY -> append("popular") + SortOrder.UPDATED -> append("update") + SortOrder.ALPHABETICAL -> append("title") + SortOrder.NEWEST -> append("create") + else -> append("update") + } } } - } else { - return emptyList() - } + null -> append("/tous-nos-mangas/?order=update") + } } val doc = webClient.httpGet(url).parseHtml() - return doc.select("div.postbody .bs .bsx").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( @@ -90,7 +89,6 @@ internal class ScansMangasMe(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/tous-nos-mangas/").parseHtml() return doc.select("ul.genre li").mapNotNullToSet { li -> @@ -104,25 +102,18 @@ internal class ScansMangasMe(context: MangaLoaderContext) : } } - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() - val chaptersDeferred = getChapters(doc) - val desc = doc.selectFirstOrThrow("div.desc").html() - val state = if (doc.select("div.spe span:contains(En cours)").isNullOrEmpty()) { MangaState.FINISHED } else { MangaState.ONGOING } - val alt = doc.body().select("div.infox span.alter").text() - val aut = doc.select("div.spe span")[2].text().replace("Auteur:", "") - manga.copy( tags = doc.select("div.spe span:contains(Genres) a").mapNotNullToSet { a -> MangaTag( @@ -140,7 +131,6 @@ internal class ScansMangasMe(context: MangaLoaderContext) : ) } - private fun getChapters(doc: Document): List { return doc.body().requireElementById("chapter_list").select("li").mapChapters(reversed = true) { i, li -> val a = li.selectFirstOrThrow("a") @@ -161,10 +151,8 @@ internal class ScansMangasMe(context: MangaLoaderContext) : override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() - val script = doc.selectFirstOrThrow("script:containsData(page_image)") val images = JSONArray(script.data().substringAfterLast("var pages = ").substringBefore(';')) - val pages = ArrayList(images.length()) for (i in 0 until images.length()) { val pageTake = images.getJSONObject(i) @@ -177,7 +165,6 @@ internal class ScansMangasMe(context: MangaLoaderContext) : ), ) } - return pages } } 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 07f8dcc2..b5c13aff 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 @@ -20,49 +20,54 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con SortOrder.UPDATED, ) + override val isMultipleTagsSupported = false + override val configKeyDomain = ConfigKey.Domain("scantrad-union.com") override val headers: Headers = Headers.Builder() .add("User-Agent", UserAgents.CHROME_DESKTOP) .build() - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val url = buildString { append("https://") append(domain) - when { - !query.isNullOrEmpty() -> { + when (filter) { + is MangaListFilter.Search -> { append("/page/") append(page.toString()) append("/?s=") - append(query.urlEncoded()) + append(filter.query.urlEncoded()) } - !tags.isNullOrEmpty() -> { - append("/tag/") - for (tag in tags) { - append(tag.key) - append(',') - } - append("/page/") - append(page.toString()) - } + is MangaListFilter.Advanced -> { + if (filter.tags.isNotEmpty()) { + filter.tags.oneOrThrowIfMany()?.let { + append("/tag/") + append(it.key) + append("/page/") + append(page.toString()) + append("/") + } + } else { + if (filter.sortOrder == SortOrder.ALPHABETICAL) { + append("/manga/page/") + append(page.toString()) + append("/") + } - else -> { - if (sortOrder == SortOrder.ALPHABETICAL) { - append("/manga/") - append("/page/") - append(page.toString()) - } + if (filter.sortOrder == SortOrder.UPDATED && page > 1) { + return emptyList() + } - if (sortOrder == SortOrder.UPDATED) { - append("") } + + } + + null -> { + append("/manga/page/") + append(page.toString()) + append("/") } } } @@ -180,9 +185,8 @@ internal class ScantradUnion(context: MangaLoaderContext) : PagedMangaParser(con val root = body.select(".asp_gochosen")[1] val list = root?.select("option").orEmpty() return list.mapToSet { li -> - MangaTag( - key = li.text(), + key = li.text().lowercase().replace(" ", "-"), title = li.text(), source = source, )