From c602355e656e67ce3b0d30d3414b354ae7ca6212 Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 29 Dec 2023 13:57:01 +0100 Subject: [PATCH 1/4] Feature Add Exclude Tags --- .../org/koitharu/kotatsu/parsers/model/MangaListFilter.kt | 3 ++- .../org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt index b861068d..eaabf34e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt @@ -20,10 +20,11 @@ sealed interface MangaListFilter { data class Advanced( override val sortOrder: SortOrder, @JvmField val tags: Set, + @JvmField val tagsExclude: Set, @JvmField val locale: Locale?, @JvmField val states: Set, ) : MangaListFilter { - override fun isEmpty(): Boolean = tags.isEmpty() && locale == null && states.isEmpty() + override fun isEmpty(): Boolean = tags.isEmpty() && tagsExclude.isEmpty() && locale == null && states.isEmpty() } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt index f5c69966..e396aa87 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt @@ -63,6 +63,10 @@ internal class BentomangaParser(context: MangaLoaderContext) : PagedMangaParser( url.addQueryParameter("withCategories", filter.tags.joinToString(",") { it.key }) } + if (filter.tagsExclude.isNotEmpty()) { + url.addQueryParameter("withoutCategories", filter.tagsExclude.joinToString(",") { it.key }) + } + filter.states.oneOrThrowIfMany()?.let { url.addQueryParameter( "state", From a2e9f198147025a9d4d3eaf8bc01df180965b540 Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 29 Dec 2023 14:16:56 +0100 Subject: [PATCH 2/4] Fix Build --- .../koitharu/kotatsu/parsers/MangaParser.kt | 22 ++++++++++++++----- .../kotatsu/parsers/PagedMangaParser.kt | 8 ++++--- .../kotatsu/parsers/site/be/AnibelParser.kt | 1 + .../kotatsu/parsers/site/ru/DesuMeParser.kt | 1 + .../kotatsu/parsers/site/ru/NudeMoonParser.kt | 1 + .../kotatsu/parsers/site/ru/RemangaParser.kt | 1 + .../parsers/site/ru/multichan/ChanParser.kt | 1 + .../parsers/site/ru/rulib/MangaLibParser.kt | 1 + .../parsers/site/uk/HentaiUkrParser.kt | 8 ++++++- .../parsers/site/uk/HoneyMangaParser.kt | 1 + .../parsers/site/uk/MangaInUaParser.kt | 1 + .../kotatsu/parsers/MangaParserTest.kt | 13 ++++++----- 12 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index 5ee0df95..d3351235 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -95,6 +95,7 @@ abstract class MangaParser @InternalParsersApi constructor( offset: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List = throw NotImplementedError("Please implement getList(offset, filter) instead") @@ -130,18 +131,29 @@ abstract class MangaParser @InternalParsersApi constructor( "org.koitharu.kotatsu.parsers.model.MangaListFilter", ), ) - open suspend fun getList(offset: Int, tags: Set?, sortOrder: SortOrder?): List { + open suspend fun getList( + offset: Int, + tags: Set?, + tagsExclude: Set?, + sortOrder: SortOrder?, + ): List { return getList( offset, - MangaListFilter.Advanced(sortOrder ?: defaultSortOrder, tags.orEmpty(), null, emptySet()), + MangaListFilter.Advanced( + sortOrder ?: defaultSortOrder, + tags.orEmpty(), + tagsExclude.orEmpty(), + null, + emptySet(), + ), ) } open suspend fun getList(offset: Int, filter: MangaListFilter?): List { return when (filter) { - is MangaListFilter.Advanced -> getList(offset, null, filter.tags, filter.sortOrder) - is MangaListFilter.Search -> getList(offset, filter.query, null, defaultSortOrder) - null -> getList(offset, null, null, defaultSortOrder) + is MangaListFilter.Advanced -> getList(offset, null, filter.tags, filter.tagsExclude, filter.sortOrder) + is MangaListFilter.Search -> getList(offset, filter.query, null, null, defaultSortOrder) + null -> getList(offset, null, null, null, defaultSortOrder) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt index fffb1d01..1017b88e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt @@ -36,6 +36,7 @@ abstract class PagedMangaParser( offset: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List = throw UnsupportedOperationException("You should use getListPage for PagedMangaParser") @@ -43,14 +44,15 @@ abstract class PagedMangaParser( page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List = throw NotImplementedError("Please implement getListPage(page, filter) instead") open suspend fun getListPage(page: Int, filter: MangaListFilter?): List { return when (filter) { - is MangaListFilter.Advanced -> getListPage(page, null, filter.tags, filter.sortOrder) - is MangaListFilter.Search -> getListPage(page, filter.query, null, defaultSortOrder) - null -> getListPage(page, null, null, defaultSortOrder) + is MangaListFilter.Advanced -> getListPage(page, null, filter.tags, filter.tagsExclude, filter.sortOrder) + is MangaListFilter.Search -> getListPage(page, filter.query, null, null, defaultSortOrder) + null -> getListPage(page, null, null, null, defaultSortOrder) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt index f7e07905..bf5b76f7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt @@ -31,6 +31,7 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context, offset: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { if (!query.isNullOrEmpty()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt index 15db547e..49660567 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt @@ -37,6 +37,7 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { if (query != null && page != searchPaginator.firstPage) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt index abdd1c87..f1f19a68 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt @@ -48,6 +48,7 @@ internal class NudeMoonParser( offset: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { val domain = domain diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt index 95ac1174..b4b54239 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt @@ -77,6 +77,7 @@ internal class RemangaParser( page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { copyCookies() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt index ab88a623..a30a7ddb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt @@ -32,6 +32,7 @@ internal abstract class ChanParser( offset: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { val domain = domain diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/MangaLibParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/MangaLibParser.kt index adcad3b9..20b3e65b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/MangaLibParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/MangaLibParser.kt @@ -44,6 +44,7 @@ internal open class MangaLibParser( page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { if (!query.isNullOrEmpty()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt index a5cd7080..d681cc0b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt @@ -67,7 +67,13 @@ class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaS ) } - override suspend fun getList(offset: Int, query: String?, tags: Set?, sortOrder: SortOrder): List { + override suspend fun getList( + offset: Int, + query: String?, + tags: Set?, + tagsExclude: Set?, + sortOrder: SortOrder, + ): List { // Get all manga val json = allManga.get().toMutableList() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt index 0184d186..c8d37e42 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt @@ -80,6 +80,7 @@ class HoneyMangaParser(context: MangaLoaderContext) : PagedMangaParser(context, page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { val body = JSONObject() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt index 3b1fc2c6..7d96ff4b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt @@ -34,6 +34,7 @@ class MangaInUaParser(context: MangaLoaderContext) : PagedMangaParser( page: Int, query: String?, tags: Set?, + tagsExclude: Set?, sortOrder: SortOrder, ): List { val url = when { diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt index b086e735..4379d41e 100644 --- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt +++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt @@ -28,7 +28,7 @@ internal class MangaParserTest { @MangaSources fun list(source: MangaSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, sortOrder = SortOrder.POPULARITY, tags = null) + val list = parser.getList(0, sortOrder = SortOrder.POPULARITY, tags = null, tagsExclude = null) checkMangaList(list, "list") assert(list.all { it.source == source }) } @@ -61,7 +61,7 @@ internal class MangaParserTest { offset = 0, filter = MangaListFilter.Advanced( sortOrder = SortOrder.POPULARITY, - tags = emptySet(), locale = null, states = emptySet(), + tags = emptySet(), locale = null, states = emptySet(), tagsExclude = emptySet(), ), ).minByOrNull { it.title.length @@ -92,7 +92,7 @@ internal class MangaParserTest { assert(tags.all { it.source == source }) val tag = tags.last() - val list = parser.getList(offset = 0, tags = setOf(tag), sortOrder = null) + val list = parser.getList(offset = 0, tags = setOf(tag), tagsExclude = setOf(tag), sortOrder = null) checkMangaList(list, "${tag.title} (${tag.key})") assert(list.all { it.source == source }) } @@ -104,7 +104,7 @@ internal class MangaParserTest { val tags = parser.getAvailableTags().shuffled().take(2).toSet() val list = try { - parser.getList(offset = 0, tags = tags, sortOrder = null) + parser.getList(offset = 0, tags = tags, tagsExclude = tags, sortOrder = null) } catch (e: IllegalArgumentException) { if (e.message == "Multiple genres are not supported by this source") { return@runTest @@ -127,6 +127,7 @@ internal class MangaParserTest { val filter = MangaListFilter.Advanced( sortOrder = parser.availableSortOrders.first(), tags = setOf(), + tagsExclude = setOf(), locale = locales.random(), states = setOf(), ) @@ -140,7 +141,7 @@ internal class MangaParserTest { @MangaSources fun details(source: MangaSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, sortOrder = SortOrder.POPULARITY, tags = null) + val list = parser.getList(0, sortOrder = SortOrder.POPULARITY, tags = null, tagsExclude = null) val manga = list[3] parser.getDetails(manga).apply { assert(!chapters.isNullOrEmpty()) { "Chapters are null or empty" } @@ -169,7 +170,7 @@ internal class MangaParserTest { @MangaSources fun pages(source: MangaSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, sortOrder = SortOrder.UPDATED, tags = null) + val list = parser.getList(0, sortOrder = SortOrder.UPDATED, tags = null, tagsExclude = null) val manga = list.first() val chapter = parser.getDetails(manga).chapters?.firstOrNull() ?: error("Chapter is null at ${manga.publicUrl}") val pages = parser.getPages(chapter) From 2d7c120d199c55ced1901b31a142402ff4f7a77d Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 29 Dec 2023 14:59:50 +0100 Subject: [PATCH 3/4] Add Feature Content Rating Filter --- .../koitharu/kotatsu/parsers/ErrorMessages.kt | 2 ++ .../koitharu/kotatsu/parsers/MangaParser.kt | 5 ++++ .../kotatsu/parsers/model/ContentRating.kt | 7 +++++ .../kotatsu/parsers/model/MangaListFilter.kt | 3 ++- .../parsers/site/all/MangaDexParser.kt | 27 ++++++++++++------- .../kotatsu/parsers/util/MangaParserEnv.kt | 9 +++++++ .../kotatsu/parsers/MangaParserTest.kt | 3 ++- 7 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt index ade47d3b..57ae550d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt @@ -4,6 +4,8 @@ object ErrorMessages { const val FILTER_MULTIPLE_STATES_NOT_SUPPORTED = "Multiple states are not supported by this source" const val FILTER_MULTIPLE_GENRES_NOT_SUPPORTED = "Multiple genres are not supported by this source" + const val FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED = + "Multiple Content Rating are not supported by this source" const val FILTER_BOTH_LOCALE_GENRES_NOT_SUPPORTED = "Filtering by both genres and locale is not supported by this source" const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED = diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index d3351235..9af38c61 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -33,6 +33,10 @@ abstract class MangaParser @InternalParsersApi constructor( open val availableStates: Set get() = emptySet() + + open val availableContentRating: Set + get() = emptySet() + /** * Whether parser supports filtering by more than one tag */ @@ -145,6 +149,7 @@ abstract class MangaParser @InternalParsersApi constructor( tagsExclude.orEmpty(), null, emptySet(), + emptySet(), ), ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt new file mode 100644 index 00000000..eb5c8614 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt @@ -0,0 +1,7 @@ +package org.koitharu.kotatsu.parsers.model + +enum class ContentRating { + SAFE, + SUGGESTIVE, + ADULT +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt index eaabf34e..9688801b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt @@ -23,8 +23,9 @@ sealed interface MangaListFilter { @JvmField val tagsExclude: Set, @JvmField val locale: Locale?, @JvmField val states: Set, + @JvmField val contentRating: Set, ) : MangaListFilter { - override fun isEmpty(): Boolean = tags.isEmpty() && tagsExclude.isEmpty() && locale == null && states.isEmpty() + override fun isEmpty(): Boolean = tags.isEmpty() && tagsExclude.isEmpty() && locale == null && states.isEmpty() && contentRating.isEmpty() } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt index 1862bd30..cd4bcdfe 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt @@ -21,8 +21,6 @@ private const val CHAPTERS_FIRST_PAGE_SIZE = 120 private const val CHAPTERS_MAX_PAGE_SIZE = 500 private const val CHAPTERS_PARALLELISM = 3 private const val CHAPTERS_MAX_COUNT = 10_000 // strange api behavior, looks like a bug -private const val CONTENT_RATING = - "contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic" private const val LOCALE_FALLBACK = "en" @MangaSourceParser("MANGADEX", "MangaDex") @@ -32,6 +30,8 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context override val availableSortOrders: EnumSet = EnumSet.allOf(SortOrder::class.java) + override val availableContentRating: Set = EnumSet.allOf(ContentRating::class.java) + override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) @@ -45,22 +45,30 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context append(PAGE_SIZE) append("&offset=") append(offset) - append("&includes[]=cover_art&includes[]=author&includes[]=artist&") + append("&includes[]=cover_art&includes[]=author&includes[]=artist") when (filter) { is MangaListFilter.Search -> { - append("title=") + append("&title=") append(filter.query) - append('&') } is MangaListFilter.Advanced -> { filter.tags.forEach { tag -> - append("includedTags[]=") + append("&includedTags[]=") append(tag.key) - append('&') } - append(CONTENT_RATING) + + if (filter.contentRating.isNotEmpty()) { + filter.contentRating.forEach { + when (it) { + ContentRating.SAFE -> append("&contentRating[]=safe") + ContentRating.SUGGESTIVE -> append("&contentRating[]=suggestive&contentRating[]=erotica") + ContentRating.ADULT -> append("&contentRating[]=pornographic") + } + } + } + append("&order") append( when (filter.sortOrder) { @@ -251,8 +259,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context append(limitedLimit) append("&includes[]=scanlation_group&order[volume]=asc&order[chapter]=asc&offset=") append(offset) - append('&') - append(CONTENT_RATING) + append("&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic") } val json = webClient.httpGet(url).parseJson() if (json.getString("result") == "ok") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt index 8d2045b6..d3e5ee7f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt @@ -68,6 +68,15 @@ fun Set?.oneOrThrowIfMany(): MangaState? { } } +@InternalParsersApi +fun Set?.oneOrThrowIfMany(): ContentRating? { + return when { + isNullOrEmpty() -> null + size == 1 -> first() + else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED) + } +} + val MangaParser.domain: String get() { return config[configKeyDomain] diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt index 4379d41e..5f76cdc3 100644 --- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt +++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt @@ -61,7 +61,7 @@ internal class MangaParserTest { offset = 0, filter = MangaListFilter.Advanced( sortOrder = SortOrder.POPULARITY, - tags = emptySet(), locale = null, states = emptySet(), tagsExclude = emptySet(), + tags = emptySet(), locale = null, states = emptySet(), tagsExclude = emptySet(), contentRating = emptySet() ), ).minByOrNull { it.title.length @@ -130,6 +130,7 @@ internal class MangaParserTest { tagsExclude = setOf(), locale = locales.random(), states = setOf(), + contentRating = setOf(), ) val list = parser.getList(offset = 0, filter) checkMangaList(list, filter.locale.toString()) From 69332a85dacd260f106a77567456efd6aee2583c Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 31 Dec 2023 09:51:09 +0200 Subject: [PATCH 4/4] Add isTagsExclusionSupported flag --- src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt | 5 +++++ .../org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index 9af38c61..b608fe75 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -42,6 +42,11 @@ abstract class MangaParser @InternalParsersApi constructor( */ open val isMultipleTagsSupported: Boolean = true + /** + * Whether parser supports tagsExclude field in filter + */ + open val isTagsExclusionSupported: Boolean = false + @Deprecated( message = "Use availableSortOrders instead", replaceWith = ReplaceWith("availableSortOrders"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt index e396aa87..80e9b600 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt @@ -32,6 +32,8 @@ internal class BentomangaParser(context: MangaLoaderContext) : PagedMangaParser( override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) + override val isTagsExclusionSupported = true + init { paginator.firstPage = 0 searchPaginator.firstPage = 0