Add Feature Content Rating Filter

pull/431/head
devi 2 years ago
parent a2e9f19814
commit 2d7c120d19

@ -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_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_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 = const val FILTER_BOTH_LOCALE_GENRES_NOT_SUPPORTED =
"Filtering by both genres and locale is not supported by this source" "Filtering by both genres and locale is not supported by this source"
const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED = const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED =

@ -33,6 +33,10 @@ abstract class MangaParser @InternalParsersApi constructor(
open val availableStates: Set<MangaState> open val availableStates: Set<MangaState>
get() = emptySet() get() = emptySet()
open val availableContentRating: Set<ContentRating>
get() = emptySet()
/** /**
* Whether parser supports filtering by more than one tag * Whether parser supports filtering by more than one tag
*/ */
@ -145,6 +149,7 @@ abstract class MangaParser @InternalParsersApi constructor(
tagsExclude.orEmpty(), tagsExclude.orEmpty(),
null, null,
emptySet(), emptySet(),
emptySet(),
), ),
) )
} }

@ -0,0 +1,7 @@
package org.koitharu.kotatsu.parsers.model
enum class ContentRating {
SAFE,
SUGGESTIVE,
ADULT
}

@ -23,8 +23,9 @@ sealed interface MangaListFilter {
@JvmField val tagsExclude: Set<MangaTag>, @JvmField val tagsExclude: Set<MangaTag>,
@JvmField val locale: Locale?, @JvmField val locale: Locale?,
@JvmField val states: Set<MangaState>, @JvmField val states: Set<MangaState>,
@JvmField val contentRating: Set<ContentRating>,
) : MangaListFilter { ) : 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()
} }
} }

@ -21,8 +21,6 @@ private const val CHAPTERS_FIRST_PAGE_SIZE = 120
private const val CHAPTERS_MAX_PAGE_SIZE = 500 private const val CHAPTERS_MAX_PAGE_SIZE = 500
private const val CHAPTERS_PARALLELISM = 3 private const val CHAPTERS_PARALLELISM = 3
private const val CHAPTERS_MAX_COUNT = 10_000 // strange api behavior, looks like a bug 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" private const val LOCALE_FALLBACK = "en"
@MangaSourceParser("MANGADEX", "MangaDex") @MangaSourceParser("MANGADEX", "MangaDex")
@ -32,6 +30,8 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context
override val availableSortOrders: EnumSet<SortOrder> = EnumSet.allOf(SortOrder::class.java) override val availableSortOrders: EnumSet<SortOrder> = EnumSet.allOf(SortOrder::class.java)
override val availableContentRating: Set<ContentRating> = EnumSet.allOf(ContentRating::class.java)
override val availableStates: Set<MangaState> = override val availableStates: Set<MangaState> =
EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) 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(PAGE_SIZE)
append("&offset=") append("&offset=")
append(offset) append(offset)
append("&includes[]=cover_art&includes[]=author&includes[]=artist&") append("&includes[]=cover_art&includes[]=author&includes[]=artist")
when (filter) { when (filter) {
is MangaListFilter.Search -> { is MangaListFilter.Search -> {
append("title=") append("&title=")
append(filter.query) append(filter.query)
append('&')
} }
is MangaListFilter.Advanced -> { is MangaListFilter.Advanced -> {
filter.tags.forEach { tag -> filter.tags.forEach { tag ->
append("includedTags[]=") append("&includedTags[]=")
append(tag.key) 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("&order")
append( append(
when (filter.sortOrder) { when (filter.sortOrder) {
@ -251,8 +259,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context
append(limitedLimit) append(limitedLimit)
append("&includes[]=scanlation_group&order[volume]=asc&order[chapter]=asc&offset=") append("&includes[]=scanlation_group&order[volume]=asc&order[chapter]=asc&offset=")
append(offset) append(offset)
append('&') append("&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic")
append(CONTENT_RATING)
} }
val json = webClient.httpGet(url).parseJson() val json = webClient.httpGet(url).parseJson()
if (json.getString("result") == "ok") { if (json.getString("result") == "ok") {

@ -68,6 +68,15 @@ fun Set<MangaState>?.oneOrThrowIfMany(): MangaState? {
} }
} }
@InternalParsersApi
fun Set<ContentRating>?.oneOrThrowIfMany(): ContentRating? {
return when {
isNullOrEmpty() -> null
size == 1 -> first()
else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED)
}
}
val MangaParser.domain: String val MangaParser.domain: String
get() { get() {
return config[configKeyDomain] return config[configKeyDomain]

@ -61,7 +61,7 @@ internal class MangaParserTest {
offset = 0, offset = 0,
filter = MangaListFilter.Advanced( filter = MangaListFilter.Advanced(
sortOrder = SortOrder.POPULARITY, sortOrder = SortOrder.POPULARITY,
tags = emptySet(), locale = null, states = emptySet(), tagsExclude = emptySet(), tags = emptySet(), locale = null, states = emptySet(), tagsExclude = emptySet(), contentRating = emptySet()
), ),
).minByOrNull { ).minByOrNull {
it.title.length it.title.length
@ -130,6 +130,7 @@ internal class MangaParserTest {
tagsExclude = setOf(), tagsExclude = setOf(),
locale = locales.random(), locale = locales.random(),
states = setOf(), states = setOf(),
contentRating = setOf(),
) )
val list = parser.getList(offset = 0, filter) val list = parser.getList(offset = 0, filter)
checkMangaList(list, filter.locale.toString()) checkMangaList(list, filter.locale.toString())

Loading…
Cancel
Save