From 2d7c120d199c55ced1901b31a142402ff4f7a77d Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 29 Dec 2023 14:59:50 +0100 Subject: [PATCH] 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())