From 03d7e138e9561c57af5dae957af85fb303e7d24e Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 14 Sep 2024 10:55:56 +0200 Subject: [PATCH] Add support type and Demographic on MangaListFilter --- .../koitharu/kotatsu/parsers/MangaParser.kt | 23 ++++++++++ .../kotatsu/parsers/model/Demographic.kt | 9 ++++ .../kotatsu/parsers/model/MangaListFilter.kt | 22 +++++++++- .../koitharu/kotatsu/parsers/model/Type.kt | 10 +++++ .../parsers/site/all/ComickFunParser.kt | 43 +++++++++++++++++-- .../parsers/site/all/MangaDexParser.kt | 17 ++++++++ .../site/mangareader/MangaReaderParser.kt | 18 ++++++++ .../kotatsu/parsers/util/MangaParserEnv.kt | 18 ++++++++ .../kotatsu/parsers/MangaParserTest.kt | 4 ++ 9 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/model/Type.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index 7de7f70ec..8e8be672c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -34,9 +34,30 @@ abstract class MangaParser @InternalParsersApi constructor( get() = emptySet() + /** + * Supported [ContentRating] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ open val availableContentRating: Set get() = emptySet() + /** + * Supported [Type] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + open val availableType: Set + get() = emptySet() + + /** + * Supported [Demographic] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + open val availableDemographic: Set + get() = emptySet() + /** * Whether parser supports filtering by more than one tag */ @@ -163,6 +184,8 @@ abstract class MangaParser @InternalParsersApi constructor( locale = null, states = emptySet(), contentRating = emptySet(), + type = emptySet(), + demographic = emptySet(), ), ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt new file mode 100644 index 000000000..d94bda672 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt @@ -0,0 +1,9 @@ +package org.koitharu.kotatsu.parsers.model + +enum class Demographic { + SHOUNEN, + SHOUJO, + SEINEN, + JOSEI, + NONE, +} 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 2abe61f67..58af8e3e7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt @@ -14,7 +14,9 @@ sealed interface MangaListFilter { (tags.size <= 1 || parser.isMultipleTagsSupported) && (tagsExclude.isEmpty() || parser.isTagsExclusionSupported) && (contentRating.isEmpty() || parser.availableContentRating.containsAll(contentRating)) && - (states.isEmpty() || parser.availableStates.containsAll(states)) + (states.isEmpty() || parser.availableStates.containsAll(states) && + (type.isEmpty() || parser.availableType.containsAll(type)) && + (demographic.isEmpty() || parser.availableDemographic.containsAll(demographic))) is Search -> parser.isSearchSupported } @@ -35,10 +37,12 @@ sealed interface MangaListFilter { @JvmField val locale: Locale?, @JvmField val states: Set, @JvmField val contentRating: Set, + @JvmField val type: Set, + @JvmField val demographic: Set, ) : MangaListFilter { override fun isEmpty(): Boolean = - tags.isEmpty() && tagsExclude.isEmpty() && locale == null && states.isEmpty() && contentRating.isEmpty() + tags.isEmpty() && tagsExclude.isEmpty() && locale == null && states.isEmpty() && contentRating.isEmpty() && type.isEmpty() && demographic.isEmpty() fun newBuilder() = Builder(sortOrder) .tags(tags) @@ -46,6 +50,8 @@ sealed interface MangaListFilter { .locale(locale) .states(states) .contentRatings(contentRating) + .type(type) + .demographic(demographic) class Builder(sortOrder: SortOrder) { @@ -55,6 +61,8 @@ sealed interface MangaListFilter { private var _locale: Locale? = null private var _states: Set? = null private var _contentRating: Set? = null + private var _type: Set? = null + private var _demographic: Set? = null fun sortOrder(order: SortOrder) = apply { _sortOrder = order @@ -80,6 +88,14 @@ sealed interface MangaListFilter { _contentRating = rating } + fun type(type: Set?) = apply { + _type = type + } + + fun demographic(demographic: Set?) = apply { + _demographic = demographic + } + fun build() = Advanced( sortOrder = _sortOrder, tags = _tags.orEmpty(), @@ -87,6 +103,8 @@ sealed interface MangaListFilter { locale = _locale, states = _states.orEmpty(), contentRating = _contentRating.orEmpty(), + type = _type.orEmpty(), + demographic = _demographic.orEmpty() ) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Type.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Type.kt new file mode 100644 index 000000000..3140fa7f1 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Type.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.model + +enum class Type { + MANGA, + MANHWA, + MANHUA, + COMIC, + NOVEL, + OTHERS, +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt index f7bbcbfe4..85bb1c5a0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt @@ -36,6 +36,15 @@ internal class ComickFunParser(context: MangaLoaderContext) : SortOrder.NEWEST, ) + override val availableType: Set = EnumSet.of( + Type.MANGA, + Type.MANHWA, + Type.MANHUA, + Type.OTHERS, + ) + + override val availableDemographic: Set = EnumSet.allOf(Demographic::class.java) + override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) @@ -56,10 +65,6 @@ internal class ComickFunParser(context: MangaLoaderContext) : url.addQueryParameter("q", filter.query) } - null -> { - url.addQueryParameter("sort", "view") - } - is MangaListFilter.Advanced -> { filter.tags.forEach { @@ -93,6 +98,36 @@ internal class ComickFunParser(context: MangaLoaderContext) : }, ) } + + filter.type.forEach { + url.addQueryParameter( + "country", + when (it) { + Type.MANGA -> "jp" + Type.MANHWA -> "kr" + Type.MANHUA -> "cn" + Type.OTHERS -> "others" + else -> "" + }, + ) + } + + filter.demographic.forEach { + url.addQueryParameter( + "demographic", + when (it) { + Demographic.SHOUNEN -> "1" + Demographic.SHOUJO -> "2" + Demographic.SEINEN -> "3" + Demographic.JOSEI -> "4" + Demographic.NONE -> "5" + }, + ) + } + } + + null -> { + url.addQueryParameter("sort", "uploaded") } } val ja = webClient.httpGet(url.build()).parseJsonArray() 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 3f7980f24..50b72d0e6 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 @@ -37,6 +37,8 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context override val availableContentRating: Set = EnumSet.allOf(ContentRating::class.java) + override val availableDemographic: Set = EnumSet.allOf(Demographic::class.java) + override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) @@ -96,6 +98,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context SortOrder.POPULARITY_ASC -> "[followedCount]=asc" }, ) + filter.states.forEach { append("&status[]=") when (it) { @@ -106,6 +109,20 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context else -> append("") } } + + filter.demographic.forEach { + append("&publicationDemographic[]=") + append( + when (it) { + Demographic.SHOUNEN -> "shounen" + Demographic.SHOUJO -> "shoujo" + Demographic.SEINEN -> "seinen" + Demographic.JOSEI -> "josei" + Demographic.NONE -> "none" + }, + ) + } + filter.locale?.let { append("&availableTranslatedLanguage[]=") append(it.language) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt index 99188a2c0..0a3f0c904 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt @@ -46,6 +46,10 @@ internal abstract class MangaReaderParser( override val availableStates: Set get() = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED) + override val availableType: Set + get() = EnumSet.of(Type.MANGA, Type.MANHWA, Type.MANHUA, Type.COMIC, Type.NOVEL) + + override val isTagsExclusionSupported = true protected open val listUrl = "/manga" @@ -110,6 +114,20 @@ internal abstract class MangaReaderParser( } } + filter.type.oneOrThrowIfMany()?.let { + append("&type=") + append( + when (it) { + Type.MANGA -> "manga" + Type.MANHWA -> "manhwa" + Type.MANHUA -> "manhua" + Type.COMIC -> "comic" + Type.NOVEL -> "novel" + else -> "" + }, + ) + } + append("&page=") append(page.toString()) } 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 79fb7bc07..c804e1050 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,24 @@ fun Set?.oneOrThrowIfMany(): MangaState? { } } +@InternalParsersApi +fun Set?.oneOrThrowIfMany(): Type? { + return when { + isNullOrEmpty() -> null + size == 1 -> first() + else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_STATES_NOT_SUPPORTED) + } +} + +@InternalParsersApi +fun Set?.oneOrThrowIfMany(): Demographic? { + return when { + isNullOrEmpty() -> null + size == 1 -> first() + else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_STATES_NOT_SUPPORTED) + } +} + @InternalParsersApi fun Set?.oneOrThrowIfMany(): ContentRating? { return when { diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt index bdb66622a..45e5eaa82 100644 --- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt +++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt @@ -59,6 +59,8 @@ internal class MangaParserTest { states = emptySet(), tagsExclude = emptySet(), contentRating = emptySet(), + type = emptySet(), + demographic = emptySet(), ), ).minByOrNull { it.title.length @@ -133,6 +135,8 @@ internal class MangaParserTest { locale = locales.random(), states = setOf(), contentRating = setOf(), + type = emptySet(), + demographic = emptySet(), ) val list = parser.getList(offset = 0, filter) checkMangaList(list, filter.locale.toString())