From f1939391e852155a8211f41ad4ed0fa8d568f39b Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 21 Sep 2024 18:33:45 +0200 Subject: [PATCH] [AsuraComic] Fix close #1077 add ContentTypes , SearchWithFilters [CloneManga] isSearchSupported false [MangaGeko] fix tags, add MultipleTags, TagsExclusion [Manhwa18.net] add isSearchSupported [ManhwasMen] add Exception [Pururin] add SearchWithFilters, MultipleTags , TagsExclusion [VyManga] add SearchWithFilters, MultipleTags, TagsExclusion --- .../parsers/site/en/AsuraScansParser.kt | 103 +++++++++------- .../parsers/site/en/CloneMangaParser.kt | 8 +- .../kotatsu/parsers/site/en/ComicExtra.kt | 11 +- .../kotatsu/parsers/site/en/DynastyScans.kt | 16 +-- .../kotatsu/parsers/site/en/FlixScansOrg.kt | 2 + .../kotatsu/parsers/site/en/MangaGeko.kt | 22 ++-- .../kotatsu/parsers/site/en/MangaKawaiiEn.kt | 14 +-- .../kotatsu/parsers/site/en/Manhwa18Parser.kt | 106 ++++++++-------- .../kotatsu/parsers/site/en/ManhwasMen.kt | 5 + .../kotatsu/parsers/site/en/Po2Scans.kt | 2 + .../kotatsu/parsers/site/en/Pururin.kt | 82 ++++++++----- .../kotatsu/parsers/site/en/VyManga.kt | 116 ++++++++---------- 12 files changed, 258 insertions(+), 229 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt index 5d76952c..fbf1b7b8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt @@ -17,32 +17,38 @@ import java.util.* internal class AsuraScansParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.ASURASCANS, pageSize = 30) { + override val configKeyDomain = ConfigKey.Domain("asuracomic.net") + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + override val availableSortOrders: Set = EnumSet.of( SortOrder.RATING, SortOrder.UPDATED, - SortOrder.NEWEST, + SortOrder.POPULARITY, SortOrder.ALPHABETICAL_DESC, SortOrder.ALPHABETICAL, ) - override val configKeyDomain = ConfigKey.Domain("asuracomic.net") - override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isMultipleTagsSupported = true, isSearchSupported = true, + isSearchWithFiltersSupported = true, ) override suspend fun getFilterOptions() = MangaListFilterOptions( availableTags = getOrCreateTagMap().values.toSet(), availableStates = EnumSet.allOf(MangaState::class.java), + availableContentTypes = EnumSet.of( + ContentType.MANGA, + ContentType.MANHWA, + ContentType.MANHUA, + ), ) - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") @@ -50,42 +56,49 @@ internal class AsuraScansParser(context: MangaLoaderContext) : append("/series?page=") append(page) - when { - !filter.query.isNullOrEmpty() -> { - append("&name=") - append(filter.query.urlEncoded()) - } - - else -> { - - if (filter.tags.isNotEmpty()) { - append("&genres=") - append(filter.tags.joinToString(separator = ",") { it.key }) - } - - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - append( - when (it) { - MangaState.ONGOING -> "1" - MangaState.FINISHED -> "3" - MangaState.ABANDONED -> "4" - MangaState.PAUSED -> "2" - MangaState.UPCOMING -> "6" - }, - ) - } - - append("&types=-1&order=") - when (order) { - SortOrder.RATING -> append("rating") - SortOrder.UPDATED -> append("update") - SortOrder.NEWEST -> append("latest") - SortOrder.ALPHABETICAL_DESC -> append("desc") - SortOrder.ALPHABETICAL -> append("asc") - else -> append("update") - } - } + filter.query?.let { + append("&name=") + append(filter.query.urlEncoded()) + } + + if (filter.tags.isNotEmpty()) { + append("&genres=") + append(filter.tags.joinToString(separator = ",") { it.key }) + } + + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "3" + MangaState.ABANDONED -> "4" + MangaState.PAUSED -> "2" + MangaState.UPCOMING -> "6" + }, + ) + } + + filter.types.oneOrThrowIfMany()?.let { + append("&types=") + append( + when (it) { + ContentType.MANGA -> "3" + ContentType.MANHWA -> "1" + ContentType.MANHUA -> "2" + else -> "" + }, + ) + } + + append("&order=") + when (order) { + SortOrder.RATING -> append("rating") + SortOrder.UPDATED -> append("update") + SortOrder.POPULARITY -> append("bookmarks") + SortOrder.ALPHABETICAL_DESC -> append("desc") + SortOrder.ALPHABETICAL -> append("asc") + else -> append("update") } } val doc = webClient.httpGet(url).parseHtml() @@ -101,7 +114,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) : rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN, tags = emptySet(), author = null, - state = when (a.selectLastOrThrow("span.status").text()) { + state = when (a.selectLast("span.status")?.text().orEmpty()) { "Ongoing" -> MangaState.ONGOING "Completed" -> MangaState.FINISHED "Hiatus" -> MangaState.PAUSED diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt index f0b7f1a2..1505eb1a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt @@ -18,10 +18,7 @@ internal class CloneMangaParser(context: MangaLoaderContext) : override val configKeyDomain = ConfigKey.Domain("manga.clone-army.org") - override val filterCapabilities: MangaListFilterCapabilities - get() = MangaListFilterCapabilities( - isSearchSupported = true, - ) + override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities() override suspend fun getFilterOptions() = MangaListFilterOptions() @@ -31,9 +28,6 @@ internal class CloneMangaParser(context: MangaLoaderContext) : } override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { - if (!filter.query.isNullOrEmpty()) { - return emptyList() - } val doc = webClient.httpGet("https://$domain/viewer_landing.php").parseHtml() val mangas = doc.getElementsByClass("comicPreviewContainer") return mangas.mapNotNull { item -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt index b23abf80..1afd62f4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt @@ -14,9 +14,6 @@ import java.util.* @MangaSourceParser("COMICEXTRA", "ComicExtra", "en") internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.COMICEXTRA, 25) { - override val availableSortOrders: Set = - EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST) - override val configKeyDomain = ConfigKey.Domain("comixextra.com") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) @@ -26,6 +23,9 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex isSearchSupported = true, ) + override val availableSortOrders: Set = + EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST) + override suspend fun getFilterOptions() = MangaListFilterOptions( availableTags = fetchAvailableTags(), availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), @@ -52,6 +52,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex } else -> { + if (filter.tags.isNotEmpty() && filter.states.isEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append(it.key) @@ -67,7 +68,9 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex ) } - } else if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { + } + + if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED) } else { when (order) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt index 3bd3708e..0ab727f2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt @@ -20,11 +20,18 @@ import java.util.* @MangaSourceParser("DYNASTYSCANS", "DynastyScans", "en") internal class DynastyScans(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.DYNASTYSCANS, 117) { - override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("dynasty-scans.com") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + + override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, @@ -34,11 +41,6 @@ internal class DynastyScans(context: MangaLoaderContext) : availableTags = fetchAvailableTags(), ) - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { when { !filter.query.isNullOrEmpty() -> { @@ -67,7 +69,6 @@ internal class DynastyScans(context: MangaLoaderContext) : append("?view=groupings") } else { append("/series?view=cover") - } append("&page=") @@ -78,7 +79,6 @@ internal class DynastyScans(context: MangaLoaderContext) : } } - private fun parseMangaList(doc: Document): List { return doc.select("li.span2") .map { div -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt index dd07d84f..58f6311d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.en import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import org.json.JSONArray +import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -15,6 +16,7 @@ import org.koitharu.kotatsu.parsers.util.json.toJSONList import java.text.SimpleDateFormat import java.util.* +@Broken @MangaSourceParser("FLIXSCANSORG", "FlixScans.org", "en") internal class FlixScansOrg(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.FLIXSCANSORG, 18) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt index f6f253ce..06beb4f4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt @@ -22,6 +22,8 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, ) override suspend fun getFilterOptions() = MangaListFilterOptions( @@ -51,20 +53,24 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context append("/browse-comics/?results=") append(page) + if (filter.tags.isNotEmpty()) { + append("&tags_include=") + append(filter.tags.joinToString(separator = ",") { it.key }) + } + + if (filter.tagsExclude.isNotEmpty()) { + append("&tags_exclude=") + append(filter.tagsExclude.joinToString(separator = ",") { it.key }) + } + append("&filter=") when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("Updated") SortOrder.NEWEST -> append("New") + // SortOrder.RANDOM -> append("Random") else -> append("Updated") } - - if (filter.tags.isNotEmpty()) { - filter.tags.oneOrThrowIfMany()?.let { - append("&genre=") - append(it.key) - } - } } } } @@ -90,7 +96,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml() - return doc.select("label.checkbox-inline").mapNotNullToSet { label -> + return doc.selectFirstOrThrow("div.genre-select-i").select("label").mapNotNullToSet { label -> MangaTag( key = label.selectFirstOrThrow("input").attr("value"), title = label.text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt index 676e0955..03405b47 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt @@ -15,11 +15,16 @@ import java.util.* internal class MangaKawaiiEn(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAKAWAII_EN, 50) { + override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io") + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL) - override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io") - override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, @@ -29,11 +34,6 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : availableTags = fetchAvailableTags(), ) - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - override fun getRequestHeaders(): Headers = Headers.Builder() .add("Accept-Language", "en") .build() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt index b13d77a6..638ed5b8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt @@ -29,13 +29,12 @@ internal class Manhwa18Parser(context: MangaLoaderContext) : SortOrder.RATING, ) - private val tagsMap = SuspendLazy(::parseTags) - override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isMultipleTagsSupported = true, isTagsExclusionSupported = true, isSearchSupported = true, + isSearchWithFiltersSupported = true, ) override suspend fun getFilterOptions() = MangaListFilterOptions( @@ -63,54 +62,57 @@ internal class Manhwa18Parser(context: MangaLoaderContext) : append("/tim-kiem?page=") append(page.toString()) - when { - !filter.query.isNullOrEmpty() -> { - append("&q=") - append(filter.query.urlEncoded()) - } + filter.query?.let { + append("&q=") + append(filter.query.urlEncoded()) + } - else -> { - - append("&accept_genres=") - if (filter.tags.isNotEmpty()) { - append( - filter.tags.joinToString(",") { it.key }, - ) - } - - append("&reject_genres=") - if (filter.tagsExclude.isNotEmpty()) { - append( - filter.tagsExclude.joinToString(",") { it.key }, - ) - } - - append("&sort=") - append( - when (order) { - SortOrder.ALPHABETICAL -> "az" - SortOrder.ALPHABETICAL_DESC -> "za" - SortOrder.POPULARITY -> "top" - SortOrder.UPDATED -> "update" - SortOrder.NEWEST -> "new" - SortOrder.RATING -> "like" - else -> null - }, - ) - - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - append( - when (it) { - MangaState.ONGOING -> "1" - MangaState.FINISHED -> "3" - MangaState.PAUSED -> "2" - else -> "" - }, - ) - } - } + append("&accept_genres=") + if (filter.tags.isNotEmpty()) { + append( + filter.tags.joinToString(",") { it.key }, + ) } + + append("&reject_genres=") + if (filter.tagsExclude.isNotEmpty()) { + append( + filter.tagsExclude.joinToString(",") { it.key }, + ) + } + + append("&sort=") + append( + when (order) { + SortOrder.ALPHABETICAL -> "az" + SortOrder.ALPHABETICAL_DESC -> "za" + SortOrder.POPULARITY -> "top" + SortOrder.UPDATED -> "update" + SortOrder.NEWEST -> "new" + SortOrder.RATING -> "like" + else -> "update" + }, + ) + + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "3" + MangaState.PAUSED -> "2" + else -> "" + }, + ) + } + + // Support author + // filter.author.let{ + // the + // append("&artist=") + // append(filter.author) + // } + } val docs = webClient.httpGet(url).parseHtml() @@ -184,14 +186,8 @@ internal class Manhwa18Parser(context: MangaLoaderContext) : ) } - // 7 minutes ago - // 5 hours ago - // 2 days ago - // 2 weeks ago - // 4 years ago private fun parseUploadDate(timeStr: String?): Long { timeStr ?: return 0 - val timeWords = timeStr.split(' ') if (timeWords.size != 3) return 0 val timeWord = timeWords[1] @@ -226,6 +222,8 @@ internal class Manhwa18Parser(context: MangaLoaderContext) : } } + private val tagsMap = SuspendLazy(::parseTags) + private suspend fun parseTags(): Map { val doc = webClient.httpGet("https://$domain/tim-kiem?q=").parseHtml() val list = doc.getElementsByAttribute("data-genre-id") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt index 4983afe0..b5031864 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt @@ -53,6 +53,11 @@ internal class ManhwasMen(context: MangaLoaderContext) : else -> { + + if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { + throw IllegalArgumentException("The source supports one filter at a time") + } + filter.tags.oneOrThrowIfMany()?.let { append("&genero=") append(it.key) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt index a13cae0b..c9f450fe 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.parsers.site.en +import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.SinglePageMangaParser @@ -9,6 +10,7 @@ import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.* +@Broken @MangaSourceParser("PO2SCANS", "Po2Scans", "en") internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.PO2SCANS) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt index c91c0367..657c19a3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt @@ -17,58 +17,72 @@ import java.util.* internal class Pururin(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.PURURIN, pageSize = 20) { + override val configKeyDomain = ConfigKey.Domain("pururin.to") + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.RATING, SortOrder.ALPHABETICAL) - override val configKeyDomain = ConfigKey.Domain("pururin.to") - override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, + isSearchWithFiltersSupported = true, + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, ) override suspend fun getFilterOptions() = MangaListFilterOptions( availableTags = fetchAvailableTags(), ) - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when { - !filter.query.isNullOrEmpty() -> { - append("/search?q=") - append(filter.query.urlEncoded()) - append("&page=") - append(page.toString()) + append("/search?tag_condition=contains&page=") + append(page.toString()) + + filter.query?.let { + append("&q=") + append(filter.query.urlEncoded()) + } + + if (filter.tags.isNotEmpty()) { + append("&included_tags=[") + filter.tags.joinToString(separator = ",") { + append("{\"id\":") + append(it.key) + append(",\"name\":\"") + append(it.title.replace(" ", "+")) + append("[Content]\"}") } + append("]") + } - else -> { - append("/browse") - - filter.tags.oneOrThrowIfMany()?.let { - append("/tags/content/") - append(it.key) - append("/") - } - - append("?page=") - append(page) - - append("&sort=") - when (order) { - SortOrder.UPDATED -> append("") - SortOrder.POPULARITY -> append("most-viewed") - SortOrder.RATING -> append("highest-rated") - SortOrder.ALPHABETICAL -> append("title") - else -> append("") - } + if (filter.tagsExclude.isNotEmpty()) { + append("&excluded_tags=[") + filter.tagsExclude.joinToString(separator = ",") { + append("{\"id\":") + append(it.key) + append(",\"name\":\"") + append(it.title.replace(" ", "+")) + append("[Content]\"}") } + append("]") + } + + + append("&sort=") + when (order) { + SortOrder.UPDATED -> append("") + SortOrder.POPULARITY -> append("most-viewed") + SortOrder.RATING -> append("highest-rated") + SortOrder.ALPHABETICAL -> append("title") + else -> append("") } } val doc = webClient.httpGet(url).parseHtml() @@ -109,7 +123,7 @@ internal class Pururin(context: MangaLoaderContext) : val href = it.attr("href").substringAfterLast("content/").substringBeforeLast('/') MangaTag( key = href, - title = it.text(), + title = it.text().substringBefore(" /"), source = source, ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt index ef1e3f83..32cc85ce 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt @@ -35,6 +35,9 @@ internal class VyManga(context: MangaLoaderContext) : override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, + isSearchWithFiltersSupported = true, + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, ) override suspend fun getFilterOptions() = MangaListFilterOptions( @@ -46,65 +49,54 @@ internal class VyManga(context: MangaLoaderContext) : val url = buildString { append("https://") append(domain) - when { - !filter.query.isNullOrEmpty() -> { - append("/search?search_po=0&q=") - append(filter.query.urlEncoded()) - append("&author_po=0&author=&completed=2&sort=updated_at&sort_type=desc&page=") - append(page) - } - - else -> { - - if (filter.tags.isEmpty()) { - - append("/search?search_po=0&q=&author_po=0&author=&completed=") - filter.states.oneOrThrowIfMany()?.let { - append( - when (it) { - MangaState.ONGOING -> "0" - MangaState.FINISHED -> "1" - else -> "2" - }, - ) - } - - } else { - - append("/genre/") - filter.tags.oneOrThrowIfMany()?.let { - append(it.key) - } - - append("?status=") - filter.states.oneOrThrowIfMany()?.let { - append( - when (it) { - MangaState.ONGOING -> "0" - MangaState.FINISHED -> "1" - else -> "" - }, - ) - } - } - - append("&sort=") - when (order) { - SortOrder.POPULARITY -> append("viewed&sort_type=desc") - SortOrder.POPULARITY_ASC -> append("viewed&sort_type=asc") - SortOrder.RATING -> append("scored&sort_type=desc") - SortOrder.RATING_ASC -> append("scored&sort_type=asc") - SortOrder.NEWEST -> append("created_at&sort_type=desc") - SortOrder.NEWEST_ASC -> append("created_at&sort_type=asc") - SortOrder.UPDATED -> append("updated_at&sort_type=desc") - SortOrder.UPDATED_ASC -> append("updated_at&sort_type=asc") - else -> append("Updated") - } - - append("&page=") - append(page) - } + append("/search?search_po=0&q=") + + filter.query?.let { + append(filter.query.urlEncoded()) } + + append("&author_po=0&author=") + + // filter.author?.let { + // append(filter.author.urlEncoded()) + // } + + append("&completed=") + filter.states.oneOrThrowIfMany()?.let { + append( + when (it) { + MangaState.ONGOING -> "0" + MangaState.FINISHED -> "1" + else -> "" + }, + ) + } + + append("&sort=") + when (order) { + SortOrder.POPULARITY -> append("viewed&sort_type=desc") + SortOrder.POPULARITY_ASC -> append("viewed&sort_type=asc") + SortOrder.RATING -> append("scored&sort_type=desc") + SortOrder.RATING_ASC -> append("scored&sort_type=asc") + SortOrder.NEWEST -> append("created_at&sort_type=desc") + SortOrder.NEWEST_ASC -> append("created_at&sort_type=asc") + SortOrder.UPDATED -> append("updated_at&sort_type=desc") + SortOrder.UPDATED_ASC -> append("updated_at&sort_type=asc") + else -> append("updated_at&sort_type=desc") + } + + filter.tags.forEach { tag -> + append("&genre[]=") + append(tag.key) + } + + filter.tagsExclude.forEach { tagsExclude -> + append("&exclude_genre[]=") + append(tagsExclude.key) + } + + append("&page=") + append(page.toString()) } val doc = webClient.httpGet(url).parseHtml() return doc.select(".comic-item").map { div -> @@ -127,11 +119,11 @@ internal class VyManga(context: MangaLoaderContext) : } private suspend fun fetchAvailableTags(): Set { - val doc = webClient.httpGet("https://$domain/").parseHtml() - return doc.select("div.dropdown-menu.custom-menu ul li a[href*=genre]").mapNotNullToSet { + val doc = webClient.httpGet("https://$domain/search").parseHtml() + return doc.select("#advance-search .check-genre .d-flex").mapNotNullToSet { MangaTag( - key = it.attr("href").substringAfterLast('/'), - title = it.text(), + key = it.selectFirstOrThrow(".checkbox-genre").attr("data-value"), + title = it.selectFirstOrThrow("label").text(), source = source, ) }