diff --git a/.gitignore b/.gitignore index e1ffa440..56a91252 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ bin/ src/test/resources/cookies.txt local.properties +.kotlin/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0458156f..9c481e8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,6 +64,8 @@ All functions in `MangaParser` class are documented. Pay attention to some pecul find issues during unit testing. - If your source website (or it's api) uses pages for pagination instead of offset you should extend `PagedMangaParser` instead of `MangaParser`. +- If your source website (or it's api) do not provide pagination (has only one page of content) you should extend + `SinglePageMangaParser` instead of `MangaParser` nor `PagedMangaParser. - Your parser may also implement the `Interceptor` interface for additional manipulation of all network requests and/or responses, including image loading. diff --git a/build.gradle b/build.gradle index 02592d60..08269bc5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,8 @@ import tasks.ReportGenerateTask plugins { id 'java-library' - id 'org.jetbrains.kotlin.jvm' version '2.0.10-RC' - id 'com.google.devtools.ksp' version '2.0.10-RC-1.0.23' + id 'org.jetbrains.kotlin.jvm' version '2.0.20' + id 'com.google.devtools.ksp' version '2.0.20-1.0.25' id 'maven-publish' } @@ -37,6 +37,7 @@ compileTestKotlin { kotlin { jvmToolchain(8) + explicitApi = 'warning' sourceSets { main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin' } @@ -53,7 +54,7 @@ afterEvaluate { } dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0' implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'com.squareup.okio:okio:3.9.0' api 'org.jsoup:jsoup:1.18.1' diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index e83d9827..422038ba 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.0.10-RC' + id 'org.jetbrains.kotlin.jvm' version '2.0.20' } repositories { @@ -14,5 +14,5 @@ dependencies { implementation gradleApi() implementation 'org.simpleframework:simple-xml:2.7.1' implementation 'com.soywiz.korlibs.korte:korte-jvm:4.0.10' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0' } diff --git a/kotatsu-parsers-ksp/build.gradle b/kotatsu-parsers-ksp/build.gradle index 4ec426dc..ce8c8886 100644 --- a/kotatsu-parsers-ksp/build.gradle +++ b/kotatsu-parsers-ksp/build.gradle @@ -7,5 +7,5 @@ kotlin { } dependencies { - implementation 'com.google.devtools.ksp:symbol-processing-api:2.0.10-RC-1.0.23' + implementation 'com.google.devtools.ksp:symbol-processing-api:2.0.20-1.0.25' } diff --git a/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt b/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt index b7a2144e..f64f9c5d 100644 --- a/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt +++ b/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt @@ -80,11 +80,11 @@ class ParserProcessor( """ package org.koitharu.kotatsu.parsers.model - enum class MangaParserSource( - val title: String, - val locale: String, - val contentType: ContentType, - val isBroken: Boolean, + public enum class MangaParserSource( + public val title: String, + public val locale: String, + public val contentType: ContentType, + public val isBroken: Boolean, ): MangaSource { """.trimIndent(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/Broken.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/Broken.kt index 8e4bea1a..24e68479 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/Broken.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/Broken.kt @@ -5,4 +5,4 @@ package org.koitharu.kotatsu.parsers */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.SOURCE) -annotation class Broken +internal annotation class Broken diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt index 57ae550d..d20d20ff 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/ErrorMessages.kt @@ -1,14 +1,18 @@ package org.koitharu.kotatsu.parsers -object ErrorMessages { +public 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 = + public const val FILTER_MULTIPLE_STATES_NOT_SUPPORTED: String = "Multiple states are not supported by this source" + public const val FILTER_MULTIPLE_GENRES_NOT_SUPPORTED: String = "Multiple genres are not supported by this source" + public const val FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED: String = + "Multiple Content ratings are not supported by this source" + public const val FILTER_MULTIPLE_CONTENT_TYPES_NOT_SUPPORTED: String = + "Multiple Content types are not supported by this source" + public const val FILTER_MULTIPLE_DEMOGRAPHICS_NOT_SUPPORTED: String = + "Multiple Demographics are not supported by this source" + public const val FILTER_BOTH_LOCALE_GENRES_NOT_SUPPORTED: String = "Filtering by both genres and locale is not supported by this source" - const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED = + public const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED: String = "Filtering by both genres and states is not supported by this source" - const val SEARCH_NOT_SUPPORTED = "Search is not supported by this source" + public const val SEARCH_NOT_SUPPORTED: String = "Search is not supported by this source" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt index 0cb69136..b3efbd67 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt @@ -11,4 +11,4 @@ package org.koitharu.kotatsu.parsers @SinceKotlin("1.3") @RequiresOptIn @MustBeDocumented -annotation class InternalParsersApi +public annotation class InternalParsersApi diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt index d7cde2cd..15510f29 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt @@ -9,30 +9,30 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import java.util.* -abstract class MangaLoaderContext { +public abstract class MangaLoaderContext { - abstract val httpClient: OkHttpClient + public abstract val httpClient: OkHttpClient - abstract val cookieJar: CookieJar + public abstract val cookieJar: CookieJar - fun newParserInstance(source: MangaParserSource): MangaParser = source.newParser(this) + public fun newParserInstance(source: MangaParserSource): MangaParser = source.newParser(this) - open fun encodeBase64(data: ByteArray): String = Base64.getEncoder().encodeToString(data) + public open fun encodeBase64(data: ByteArray): String = Base64.getEncoder().encodeToString(data) - open fun decodeBase64(data: String): ByteArray = Base64.getDecoder().decode(data) + public open fun decodeBase64(data: String): ByteArray = Base64.getDecoder().decode(data) - open fun getPreferredLocales(): List = listOf(Locale.getDefault()) + public open fun getPreferredLocales(): List = listOf(Locale.getDefault()) /** * Execute JavaScript code and return result * @param script JavaScript source code * @return execution result as string, may be null */ - abstract suspend fun evaluateJs(script: String): String? + public abstract suspend fun evaluateJs(script: String): String? - abstract fun getConfig(source: MangaSource): MangaSourceConfig + public abstract fun getConfig(source: MangaSource): MangaSourceConfig - abstract fun getDefaultUserAgent(): String + public abstract fun getDefaultUserAgent(): String /** * Helper function to be used in an interceptor @@ -40,7 +40,7 @@ abstract class MangaLoaderContext { * @param response Image response * @param redraw lambda function to implement descrambling logic */ - abstract fun redrawImageResponse( + public abstract fun redrawImageResponse( response: Response, redraw: (image: Bitmap) -> Bitmap, ): Response @@ -48,7 +48,7 @@ abstract class MangaLoaderContext { /** * create a new empty Bitmap with given dimensions */ - abstract fun createBitmap( + public abstract fun createBitmap( width: Int, height: Int, ): Bitmap diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index 7de7f70e..e6894333 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -1,9 +1,9 @@ package org.koitharu.kotatsu.parsers import androidx.annotation.CallSuper -import androidx.annotation.VisibleForTesting import okhttp3.Headers import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.config.MangaSourceConfig import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.network.OkHttpWebClient import org.koitharu.kotatsu.parsers.network.WebClient @@ -13,9 +13,9 @@ import org.koitharu.kotatsu.parsers.util.domain import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl import java.util.* -abstract class MangaParser @InternalParsersApi constructor( - @property:InternalParsersApi val context: MangaLoaderContext, - val source: MangaParserSource, +public abstract class MangaParser @InternalParsersApi constructor( + @property:InternalParsersApi public val context: MangaLoaderContext, + public val source: MangaParserSource, ) { /** @@ -23,48 +23,16 @@ abstract class MangaParser @InternalParsersApi constructor( * * For better performance use [EnumSet] for more than one item. */ - abstract val availableSortOrders: Set + public abstract val availableSortOrders: Set - /** - * Supported [MangaState] variants for filtering. May be empty. - * - * For better performance use [EnumSet] for more than one item. - */ - open val availableStates: Set - get() = emptySet() - - - open val availableContentRating: Set - get() = emptySet() + public abstract val filterCapabilities: MangaListFilterCapabilities - /** - * Whether parser supports filtering by more than one tag - */ - open val isMultipleTagsSupported: Boolean = true - - /** - * Whether parser supports tagsExclude field in filter - */ - open val isTagsExclusionSupported: Boolean = false + public val config: MangaSourceConfig by lazy { context.getConfig(source) } - /** - * Whether parser supports searching by string query using [MangaListFilter.Search] - */ - open val isSearchSupported: Boolean = true - - @Deprecated( - message = "Use availableSortOrders instead", - replaceWith = ReplaceWith("availableSortOrders"), - ) - open val sortOrders: Set - get() = availableSortOrders - - val config by lazy { context.getConfig(source) } - - open val sourceLocale: Locale + public open val sourceLocale: Locale get() = if (source.locale.isEmpty()) Locale.ROOT else Locale(source.locale) - val isNsfwSource = source.contentType == ContentType.HENTAI + protected val isNsfwSource: Boolean = source.contentType == ContentType.HENTAI /** * Provide default domain and available alternatives, if any. @@ -72,19 +40,18 @@ abstract class MangaParser @InternalParsersApi constructor( * Never hardcode domain in requests, use [domain] instead. */ @InternalParsersApi - abstract val configKeyDomain: ConfigKey.Domain + public abstract val configKeyDomain: ConfigKey.Domain - protected open val userAgentKey = ConfigKey.UserAgent(context.getDefaultUserAgent()) + protected open val userAgentKey: ConfigKey.UserAgent = ConfigKey.UserAgent(context.getDefaultUserAgent()) - open fun getRequestHeaders(): Headers = Headers.Builder() + public open fun getRequestHeaders(): Headers = Headers.Builder() .add("User-Agent", config[userAgentKey]) .build() /** - * Used as fallback if value of `sortOrder` passed to [getList] is null + * Used as fallback if value of `order` passed to [getList] is null */ - @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) - open val defaultSortOrder: SortOrder + public open val defaultSortOrder: SortOrder get() { val supported = availableSortOrders return SortOrder.entries.first { it in supported } @@ -98,157 +65,44 @@ abstract class MangaParser @InternalParsersApi constructor( * * @param offset starting from 0 and used for pagination. * Note than passed value may not be divisible by internal page size, so you should adjust it manually. - * @param query search query, may be null or empty if no search needed - * @param tags genres for filtering, values from [getAvailableTags] and [Manga.tags]. May be null or empty - * @param sortOrder one of [availableSortOrders] or null for default value - */ - @JvmSynthetic - @InternalParsersApi - @Deprecated( - "Use getList with filter instead", - replaceWith = ReplaceWith("getList(offset, filter)"), - ) - open suspend fun getList( - offset: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List = throw NotImplementedError("Please implement getList(offset, filter) instead") - - /** - * Parse list of manga with search by text query - * - * @param offset starting from 0 and used for pagination. - * @param query search query - */ - @Deprecated( - "Use getList with filter instead", - ReplaceWith( - "getList(offset, MangaListFilter.Search(query))", - "org.koitharu.kotatsu.parsers.model.MangaListFilter", - ), - ) - open suspend fun getList(offset: Int, query: String): List { - return getList(offset, MangaListFilter.Search(query)) - } - - /** - * Parse list of manga by specified criteria - * - * @param offset starting from 0 and used for pagination. - * Note than passed value may not be divisible by internal page size, so you should adjust it manually. - * @param tags genres for filtering, values from [getAvailableTags] and [Manga.tags]. May be null or empty - * @param sortOrder one of [availableSortOrders] or null for default value + * @param order one of [availableSortOrders] or [defaultSortOrder] for default value + * @param filter is a set of filter rules */ - @Deprecated( - "Use getList with filter instead", - ReplaceWith( - "getList(offset, MangaListFilter.Advanced(sortOrder, tags, null, emptySet()))", - "org.koitharu.kotatsu.parsers.model.MangaListFilter", - ), - ) - open suspend fun getList( - offset: Int, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder?, - ): List { - return getList( - offset, - MangaListFilter.Advanced( - sortOrder = sortOrder ?: defaultSortOrder, - tags = tags.orEmpty(), - tagsExclude = tagsExclude.orEmpty(), - locale = null, - states = emptySet(), - contentRating = emptySet(), - ), - ) - } - - @Suppress("DEPRECATION") - open suspend fun getList(offset: Int, filter: MangaListFilter?): List { - return when (filter) { - is MangaListFilter.Advanced -> getList( - offset = offset, - query = null, - tags = filter.tags, - tagsExclude = filter.tagsExclude, - sortOrder = filter.sortOrder, - ) - - is MangaListFilter.Search -> getList( - offset = offset, - query = filter.query, - tags = null, - tagsExclude = null, - sortOrder = defaultSortOrder, - ) - - null -> getList( - offset = offset, - query = null, - tags = null, - tagsExclude = null, - sortOrder = defaultSortOrder, - ) - } - } + public abstract suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List /** * Parse details for [Manga]: chapters list, description, large cover, etc. * Must return the same manga, may change any fields excepts id, url and source * @see Manga.copy */ - abstract suspend fun getDetails(manga: Manga): Manga + public abstract suspend fun getDetails(manga: Manga): Manga /** * Parse pages list for specified chapter. * @see MangaPage for details */ - abstract suspend fun getPages(chapter: MangaChapter): List + public abstract suspend fun getPages(chapter: MangaChapter): List /** * Fetch direct link to the page image. */ - open suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain) - - /** - * Fetch available tags (genres) for source - */ - abstract suspend fun getAvailableTags(): Set + internal open suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain) - /** - * Fetch available locales for multilingual sources - */ - open suspend fun getAvailableLocales(): Set = emptySet() - - @Deprecated( - message = "Use getAvailableTags instead", - replaceWith = ReplaceWith("getAvailableTags()"), - ) - suspend fun getTags(): Set = getAvailableTags() + public abstract suspend fun getFilterOptions(): MangaListFilterOptions /** * Parse favicons from the main page of the source`s website */ - open suspend fun getFavicons(): Favicons { + public open suspend fun getFavicons(): Favicons { return FaviconParser(webClient, domain).parseFavicons() } @CallSuper - open fun onCreateConfig(keys: MutableCollection>) { + public open fun onCreateConfig(keys: MutableCollection>) { keys.add(configKeyDomain) } - open suspend fun getRelatedManga(seed: Manga): List { + public open suspend fun getRelatedManga(seed: Manga): List { return RelatedMangaFinder(listOf(this)).invoke(seed) } - - protected fun getParser(source: MangaParserSource) = if (this.source == source) { - this - } else { - context.newParserInstance(source) - } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParserAuthProvider.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParserAuthProvider.kt index a2541072..a9c21787 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParserAuthProvider.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParserAuthProvider.kt @@ -6,19 +6,19 @@ import org.koitharu.kotatsu.parsers.exception.ParseException /** * Implement this in your parser for authorization support */ -interface MangaParserAuthProvider { +public interface MangaParserAuthProvider { /** * Return link to the login page, which will be opened in browser. * Must be an absolute url */ - val authUrl: String + public val authUrl: String /** * Quick check if user is logged in. * In most case you should check for cookies in [MangaLoaderContext.cookieJar]. */ - val isAuthorized: Boolean + public val isAuthorized: Boolean /** * Fetch and return current user`s name or login. @@ -26,5 +26,5 @@ interface MangaParserAuthProvider { * @throws [AuthRequiredException] if user is not logged in or authorization is expired * @throws [ParseException] on parsing error */ - suspend fun getUsername(): String -} \ No newline at end of file + public suspend fun getUsername(): String +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaSourceParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaSourceParser.kt index ebb45e67..b708d547 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaSourceParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaSourceParser.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.ContentType */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.SOURCE) -annotation class MangaSourceParser( +internal annotation class MangaSourceParser( /** * Name of manga source. Used as an Enum value, must be UPPER_CASE and unique. */ diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt index 3d38152a..20dff242 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/PagedMangaParser.kt @@ -1,11 +1,14 @@ package org.koitharu.kotatsu.parsers import androidx.annotation.VisibleForTesting -import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.util.Paginator @InternalParsersApi -abstract class PagedMangaParser( +public abstract class PagedMangaParser( context: MangaLoaderContext, source: MangaParserSource, @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @JvmField internal val pageSize: Int, @@ -13,77 +16,34 @@ abstract class PagedMangaParser( ) : MangaParser(context, source) { @JvmField - protected val paginator = Paginator(pageSize) + protected val paginator: Paginator = Paginator(pageSize) @JvmField - protected val searchPaginator = Paginator(searchPageSize) + protected val searchPaginator: Paginator = Paginator(searchPageSize) - final override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + final override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { return getList( - paginator = if (filter is MangaListFilter.Search) { - searchPaginator - } else { + paginator = if (filter.query.isNullOrEmpty()) { paginator + } else { + searchPaginator }, offset = offset, + order = order, filter = filter, ) } - @InternalParsersApi - @Deprecated("You should use getListPage for PagedMangaParser", level = DeprecationLevel.HIDDEN) - final override suspend fun getList( - offset: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List = throw UnsupportedOperationException("You should use getListPage for PagedMangaParser") - - @Deprecated("") - open suspend fun getListPage( - 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 = page, - query = null, - tags = filter.tags, - tagsExclude = filter.tagsExclude, - sortOrder = filter.sortOrder, - ) - - is MangaListFilter.Search -> getListPage( - page = page, - query = filter.query, - tags = null, - tagsExclude = null, - sortOrder = defaultSortOrder, - ) - - null -> getListPage( - page = page, - query = null, - tags = null, - tagsExclude = null, - sortOrder = defaultSortOrder, - ) - } - } + public abstract suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List private suspend fun getList( paginator: Paginator, offset: Int, - filter: MangaListFilter?, + order: SortOrder, + filter: MangaListFilter, ): List { val page = paginator.getPage(offset) - val list = getListPage(page, filter) + val list = getListPage(page, order, filter) paginator.onListReceived(offset, page, list.size) return list } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/SinglePageMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/SinglePageMangaParser.kt new file mode 100644 index 00000000..6e2425af --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/SinglePageMangaParser.kt @@ -0,0 +1,22 @@ +package org.koitharu.kotatsu.parsers + +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.model.SortOrder + +@InternalParsersApi +public abstract class SinglePageMangaParser( + context: MangaLoaderContext, + source: MangaParserSource, +) : MangaParser(context, source) { + + final override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + if (offset > 0) { + return emptyList() + } + return getList(order, filter) + } + + public abstract suspend fun getList(order: SortOrder, filter: MangaListFilter): List +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Bitmap.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Bitmap.kt index e9be3b8d..f39d29de 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Bitmap.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Bitmap.kt @@ -1,9 +1,9 @@ package org.koitharu.kotatsu.parsers.bitmap -interface Bitmap { +public interface Bitmap { - val width: Int - val height: Int + public val width: Int + public val height: Int - fun drawBitmap(sourceBitmap: Bitmap, src: Rect, dst: Rect) + public fun drawBitmap(sourceBitmap: Bitmap, src: Rect, dst: Rect) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Rect.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Rect.kt index 95d972f1..60218296 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Rect.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/bitmap/Rect.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.bitmap -data class Rect( +public data class Rect( val left: Int = 0, val top: Int = 0, val right: Int = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/config/ConfigKey.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/config/ConfigKey.kt index 45c86648..cd233f1d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/config/ConfigKey.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/config/ConfigKey.kt @@ -1,13 +1,13 @@ package org.koitharu.kotatsu.parsers.config -sealed class ConfigKey( - @JvmField val key: String, +public sealed class ConfigKey( + @JvmField public val key: String, ) { - abstract val defaultValue: T + public abstract val defaultValue: T - class Domain( - @JvmField @JvmSuppressWildcards vararg val presetValues: String, + public class Domain( + @JvmField @JvmSuppressWildcards public vararg val presetValues: String, ) : ConfigKey("domain") { init { @@ -18,20 +18,20 @@ sealed class ConfigKey( get() = presetValues.first() } - class ShowSuspiciousContent( + public class ShowSuspiciousContent( override val defaultValue: Boolean, ) : ConfigKey("show_suspicious") - class UserAgent( + public class UserAgent( override val defaultValue: String, ) : ConfigKey("user_agent") - class SplitByTranslations( + public class SplitByTranslations( override val defaultValue: Boolean, ) : ConfigKey("split_translations") - class PreferredImageServer( - val presetValues: Map, + public class PreferredImageServer( + public val presetValues: Map, override val defaultValue: String?, ) : ConfigKey("img_server") } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/config/MangaSourceConfig.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/config/MangaSourceConfig.kt index fe8e4590..d4e11c49 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/config/MangaSourceConfig.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/config/MangaSourceConfig.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.config -interface MangaSourceConfig { +public interface MangaSourceConfig { - operator fun get(key: ConfigKey): T -} \ No newline at end of file + public operator fun get(key: ConfigKey): T +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/AuthRequiredException.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/AuthRequiredException.kt index 494c2583..4a77de5b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/AuthRequiredException.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/AuthRequiredException.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource /** * Authorization is required for access to the requested content */ -class AuthRequiredException @InternalParsersApi @JvmOverloads constructor( - val source: MangaSource, +public class AuthRequiredException @InternalParsersApi @JvmOverloads constructor( + public val source: MangaSource, cause: Throwable? = null, ) : RuntimeException("Authorization required", cause) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt index e3eb1ecf..695440fb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt @@ -7,6 +7,10 @@ enum class ContentType { */ MANGA, + MANHWA, + + MANHUA, + /** * Use this if the source provides mostly nsfw content. */ @@ -17,6 +21,8 @@ enum class ContentType { */ COMICS, + NOVEL, + /** * Use this type if no other suits your needs. For example, for an indie manga */ 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 00000000..d94bda67 --- /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/Manga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Manga.kt index 39c14b79..76919ca2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Manga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Manga.kt @@ -2,86 +2,86 @@ package org.koitharu.kotatsu.parsers.model import org.koitharu.kotatsu.parsers.InternalParsersApi -class Manga( +public class Manga( /** * Unique identifier for manga */ - @JvmField val id: Long, + @JvmField public val id: Long, /** * Manga title, human-readable */ - @JvmField val title: String, + @JvmField public val title: String, /** * Alternative title (for example on other language), may be null */ - @JvmField val altTitle: String?, + @JvmField public val altTitle: String?, /** * Relative url to manga (**without** a domain) or any other uri. * Used principally in parsers */ - @JvmField val url: String, + @JvmField public val url: String, /** * Absolute url to manga, must be ready to open in browser */ - @JvmField val publicUrl: String, + @JvmField public val publicUrl: String, /** * Normalized manga rating, must be in range of 0..1 or [RATING_UNKNOWN] if rating s unknown * @see hasRating */ - @JvmField val rating: Float, + @JvmField public val rating: Float, /** * Indicates that manga may contain sensitive information (18+, NSFW) */ - @JvmField val isNsfw: Boolean, + @JvmField public val isNsfw: Boolean, /** * Absolute link to the cover * @see largeCoverUrl */ - @JvmField val coverUrl: String, + @JvmField public val coverUrl: String, /** * Tags (genres) of the manga */ - @JvmField val tags: Set, + @JvmField public val tags: Set, /** * Manga status (ongoing, finished) or null if unknown */ - @JvmField val state: MangaState?, + @JvmField public val state: MangaState?, /** * Author of the manga, may be null */ - @JvmField val author: String?, + @JvmField public val author: String?, /** * Large cover url (absolute), null if is no large cover * @see coverUrl */ - @JvmField val largeCoverUrl: String? = null, + @JvmField public val largeCoverUrl: String? = null, /** * Manga description, may be html or null */ - @JvmField val description: String? = null, + @JvmField public val description: String? = null, /** * List of chapters */ - @JvmField val chapters: List? = null, + @JvmField public val chapters: List? = null, /** * Manga source */ - @JvmField val source: MangaSource, + @JvmField public val source: MangaSource, ) { /** * Return if manga has a specified rating * @see rating */ - val hasRating: Boolean + public val hasRating: Boolean get() = rating > 0f && rating <= 1f - fun getChapters(branch: String?): List? { + public fun getChapters(branch: String?): List? { return chapters?.filter { x -> x.branch == branch } } @InternalParsersApi - fun copy( + public fun copy( title: String = this.title, altTitle: String? = this.altTitle, publicUrl: String = this.publicUrl, @@ -95,7 +95,7 @@ class Manga( description: String? = this.description, chapters: List? = this.chapters, source: MangaSource = this.source, - ) = Manga( + ): Manga = Manga( id = id, title = title, altTitle = altTitle, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt index 2493a25d..7efb8a79 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt @@ -1,45 +1,45 @@ package org.koitharu.kotatsu.parsers.model -class MangaChapter( +public class MangaChapter( /** * An unique id of chapter */ - @JvmField val id: Long, + @JvmField public val id: Long, /** * User-readable name of chapter */ - @JvmField val name: String, + @JvmField public val name: String, /** * Chapter number starting from 1, 0 if unknown */ - @JvmField val number: Float, + @JvmField public val number: Float, /** * Volume number starting from 1, 0 if unknown */ - @JvmField val volume: Int, + @JvmField public val volume: Int, /** * Relative url to chapter (**without** a domain) or any other uri. * Used principally in parsers */ - @JvmField val url: String, + @JvmField public val url: String, /** * User-readable name of scanlator (releaser) or null if unknown */ - @JvmField val scanlator: String?, + @JvmField public val scanlator: String?, /** * Chapter upload date in milliseconds */ - @JvmField val uploadDate: Long, + @JvmField public val uploadDate: Long, /** * User-readable name of branch. * A branch is a group of chapters that overlap (e.g. different languages) */ - @JvmField val branch: String?, - @JvmField val source: MangaSource, + @JvmField public val branch: String?, + @JvmField public val source: MangaSource, ) { @Deprecated(message = "Consider using constructor with volume value") - constructor( + internal constructor( id: Long, name: String, number: Int, 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 2abe61f6..a90a9169 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt @@ -1,93 +1,38 @@ package org.koitharu.kotatsu.parsers.model -import org.koitharu.kotatsu.parsers.MangaParser import java.util.* -sealed interface MangaListFilter { - - fun isEmpty(): Boolean - - val sortOrder: SortOrder? - - fun isValid(parser: MangaParser): Boolean = when (this) { - is Advanced -> (sortOrder in parser.availableSortOrders) && - (tags.size <= 1 || parser.isMultipleTagsSupported) && - (tagsExclude.isEmpty() || parser.isTagsExclusionSupported) && - (contentRating.isEmpty() || parser.availableContentRating.containsAll(contentRating)) && - (states.isEmpty() || parser.availableStates.containsAll(states)) - - is Search -> parser.isSearchSupported - } - - data class Search( - @JvmField val query: String, - ) : MangaListFilter { - - override val sortOrder: SortOrder? = null - - override fun isEmpty() = query.isBlank() - } - - data class Advanced( - override val sortOrder: SortOrder, - @JvmField val tags: Set, - @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() && contentRating.isEmpty() - - fun newBuilder() = Builder(sortOrder) - .tags(tags) - .tagsExclude(tagsExclude) - .locale(locale) - .states(states) - .contentRatings(contentRating) - - class Builder(sortOrder: SortOrder) { - - private var _sortOrder: SortOrder = sortOrder - private var _tags: Set? = null - private var _tagsExclude: Set? = null - private var _locale: Locale? = null - private var _states: Set? = null - private var _contentRating: Set? = null - - fun sortOrder(order: SortOrder) = apply { - _sortOrder = order - } - - fun tags(tags: Set?) = apply { - _tags = tags - } - - fun tagsExclude(tags: Set?) = apply { - _tagsExclude = tags - } - - fun locale(locale: Locale?) = apply { - _locale = locale - } - - fun states(states: Set?) = apply { - _states = states - } - - fun contentRatings(rating: Set?) = apply { - _contentRating = rating - } - - fun build() = Advanced( - sortOrder = _sortOrder, - tags = _tags.orEmpty(), - tagsExclude = _tagsExclude.orEmpty(), - locale = _locale, - states = _states.orEmpty(), - contentRating = _contentRating.orEmpty(), - ) - } +public data class MangaListFilter( + @JvmField val query: String? = null, + @JvmField val tags: Set = emptySet(), + @JvmField val tagsExclude: Set = emptySet(), + @JvmField val locale: Locale? = null, + @JvmField val originalLocale: Locale? = null, + @JvmField val states: Set = emptySet(), + @JvmField val contentRating: Set = emptySet(), + @JvmField val types: Set = emptySet(), + @JvmField val demographics: Set = emptySet(), + @JvmField val year: Int = 0, + @JvmField val yearFrom: Int = 0, + @JvmField val yearTo: Int = 0, +) { + + public fun isEmpty(): Boolean = tags.isEmpty() && + tagsExclude.isEmpty() && + locale == null && + originalLocale == null && + states.isEmpty() && + contentRating.isEmpty() && + query == null && + year == 0 && + yearFrom == 0 && + yearTo == 0 && + types.isEmpty() && + demographics.isEmpty() + + public companion object { + + @JvmStatic + public val EMPTY: MangaListFilter = MangaListFilter() } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterCapabilities.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterCapabilities.kt new file mode 100644 index 00000000..33427f2a --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterCapabilities.kt @@ -0,0 +1,50 @@ +package org.koitharu.kotatsu.parsers.model + +import org.koitharu.kotatsu.parsers.InternalParsersApi + +public data class MangaListFilterCapabilities @InternalParsersApi constructor( + + /** + * Whether parser supports filtering by more than one tag + * @see [MangaListFilter.tags] + * @see [MangaListFilterOptions.availableTags] + */ + val isMultipleTagsSupported: Boolean = false, + + /** + * Whether parser supports tagsExclude field in filter + * @see [MangaListFilter.tagsExclude] + * @see [MangaListFilterOptions.availableTags] + */ + val isTagsExclusionSupported: Boolean = false, + + /** + * Whether parser supports searching by string query + * @see [MangaListFilter.query] + */ + val isSearchSupported: Boolean = false, + + /** + * Whether parser supports searching by string query combined within other filters + */ + val isSearchWithFiltersSupported: Boolean = false, + + /** + * Whether parser supports searching/filtering by year + * @see [MangaListFilter.year] + */ + val isYearSupported: Boolean = false, + + /** + * Whether parser supports searching by year range + * @see [MangaListFilter.yearFrom] and [MangaListFilter.yearTo] + */ + val isYearRangeSupported: Boolean = false, + + /** + * Whether parser supports searching Original Languages + * @see [MangaListFilter.originalLocale] + * @see [MangaListFilterOptions.availableLocales] + */ + val isOriginalLocaleSupported: Boolean = false, +) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterOptions.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterOptions.kt new file mode 100644 index 00000000..6dbc69ee --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilterOptions.kt @@ -0,0 +1,45 @@ +package org.koitharu.kotatsu.parsers.model + +import org.koitharu.kotatsu.parsers.InternalParsersApi +import java.util.* + +public data class MangaListFilterOptions @InternalParsersApi constructor( + + /** + * Available tags (genres) + */ + public val availableTags: Set = emptySet(), + + /** + * Supported [MangaState] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + public val availableStates: Set = emptySet(), + + /** + * Supported [ContentRating] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + public val availableContentRating: Set = emptySet(), + + /** + * Supported [ContentType] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + public val availableContentTypes: Set = emptySet(), + + /** + * Supported [Demographic] variants for filtering. May be empty. + * + * For better performance use [EnumSet] for more than one item. + */ + public val availableDemographics: Set = emptySet(), + + /** + * Supported content locales for multilingual sources + */ + public val availableLocales: Set = emptySet(), +) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaSource.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaSource.kt index 20a481f4..cbed3dc1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaSource.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaSource.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.model -interface MangaSource { +public interface MangaSource { - val name: String + public val name: String } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaState.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaState.kt index 3801ff04..1355c1d9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaState.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaState.kt @@ -1,5 +1,5 @@ package org.koitharu.kotatsu.parsers.model -enum class MangaState { +public enum class MangaState { ONGOING, FINISHED, ABANDONED, PAUSED, UPCOMING } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaTag.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaTag.kt index 184a850e..a694b4c8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaTag.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaTag.kt @@ -2,17 +2,17 @@ package org.koitharu.kotatsu.parsers.model import org.koitharu.kotatsu.parsers.MangaParser -class MangaTag( +public class MangaTag( /** * User-readable tag title, should be in Title case */ - @JvmField val title: String, + @JvmField public val title: String, /** * Identifier of a tag, must be unique among the source. * @see MangaParser.getList */ - @JvmField val key: String, - @JvmField val source: MangaSource, + @JvmField public val key: String, + @JvmField public val source: MangaSource, ) { override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt index 9d743ae2..0b60e306 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt @@ -11,4 +11,12 @@ enum class SortOrder { NEWEST_ASC, ALPHABETICAL, ALPHABETICAL_DESC, + ADDED, + ADDED_ASC, + RELEVANCE, + POPULARITY_HOUR, + POPULARITY_TODAY, + POPULARITY_WEEK, + POPULARITY_MONTH, + POPULARITY_YEAR, } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/WordSet.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/WordSet.kt index c2e91358..9ca977d8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/WordSet.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/WordSet.kt @@ -3,9 +3,9 @@ package org.koitharu.kotatsu.parsers.model import org.koitharu.kotatsu.parsers.InternalParsersApi @InternalParsersApi -class WordSet(private vararg val words: String) { +public class WordSet(private vararg val words: String) { - fun anyWordIn(dateString: String): Boolean = words.any { + public fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt index 1d4e5520..1d4a1ed5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt @@ -53,13 +53,42 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.ALPHABETICAL, + SortOrder.POPULARITY_YEAR, + SortOrder.POPULARITY_MONTH, + SortOrder.POPULARITY_WEEK, + SortOrder.POPULARITY_TODAY, + SortOrder.POPULARITY_HOUR, ) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - - override val isTagsExclusionSupported: Boolean = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val availableContentRating: Set = EnumSet.of(ContentRating.SAFE) + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = EnumSet.of(ContentRating.SAFE), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = setOf( + Locale.CHINESE, Locale.ENGLISH, Locale.US, Locale.FRENCH, Locale.GERMAN, Locale.ITALIAN, Locale.JAPANESE, + Locale("af"), Locale("ar"), Locale("az"), Locale("eu"), Locale("be"), + Locale("bn"), Locale("bs"), Locale("bg"), Locale("my"), Locale("km"), + Locale("ceb"), Locale("zh_hk"), Locale("zh_tw"), Locale("hr"), Locale("cs"), + Locale("da"), Locale("nl"), Locale("eo"), Locale("et"), Locale("fil"), + Locale("fi"), Locale("ka"), Locale("el"), Locale("ht"), Locale("he"), + Locale("hi"), Locale("hu"), Locale("id"), Locale("kk"), Locale("ko"), + Locale("lv"), Locale("ms"), Locale("ml"), Locale("mo"), Locale("mn"), + Locale("ne"), Locale("no"), Locale("fa"), Locale("pl"), Locale("pt"), + Locale("pt_br"), Locale("pt_pt"), Locale("ro"), Locale("ru"), Locale("sr"), + Locale("si"), Locale("sk"), Locale("es"), Locale("es_419"), Locale("ta"), + Locale("te"), Locale("th"), Locale("ti"), Locale("tr"), Locale("uk"), + Locale("vi"), Locale("zu"), + ), + ) override val configKeyDomain = ConfigKey.Domain( "bato.to", @@ -89,25 +118,29 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( "zbato.org", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { return search(page, filter.query) } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) append("/browse?sort=") - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> append("update.za") SortOrder.POPULARITY -> append("views_a.za") SortOrder.NEWEST -> append("create.za") SortOrder.ALPHABETICAL -> append("title.az") + SortOrder.POPULARITY_YEAR -> append("views_y.za") + SortOrder.POPULARITY_MONTH -> append("views_m.za") + SortOrder.POPULARITY_WEEK -> append("views_w.za") + SortOrder.POPULARITY_TODAY -> append("views_d.za") + SortOrder.POPULARITY_HOUR -> append("views_h.za") else -> append("update.za") } @@ -161,17 +194,6 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( return parseList(url, page) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/browse?sort=update.za") - append("&page=") - append(page.toString()) - } - return parseList(url, page) - } } } @@ -242,7 +264,7 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( throw ParseException("Cannot find images list", fullUrl) } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val scripts = webClient.httpGet( "https://${domain}/browse", ).parseHtml().selectOrThrow("script") @@ -263,22 +285,6 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( throw ParseException("Cannot find gernes list", scripts[0].baseUri()) } - override suspend fun getAvailableLocales(): Set = setOf( - Locale.CHINESE, Locale.ENGLISH, Locale.US, Locale.FRENCH, Locale.GERMAN, Locale.ITALIAN, Locale.JAPANESE, - Locale("af"), Locale("ar"), Locale("az"), Locale("eu"), Locale("be"), - Locale("bn"), Locale("bs"), Locale("bg"), Locale("my"), Locale("km"), - Locale("ceb"), Locale("zh_hk"), Locale("zh_tw"), Locale("hr"), Locale("cs"), - Locale("da"), Locale("nl"), Locale("eo"), Locale("et"), Locale("fil"), - Locale("fi"), Locale("ka"), Locale("el"), Locale("ht"), Locale("he"), - Locale("hi"), Locale("hu"), Locale("id"), Locale("kk"), Locale("ko"), - Locale("lv"), Locale("ms"), Locale("ml"), Locale("mo"), Locale("mn"), - Locale("ne"), Locale("no"), Locale("fa"), Locale("pl"), Locale("pt"), - Locale("pt_br"), Locale("pt_pt"), Locale("ro"), Locale("ru"), Locale("sr"), - Locale("si"), Locale("sk"), Locale("es"), Locale("es_419"), Locale("ta"), - Locale("te"), Locale("th"), Locale("ti"), Locale("tr"), Locale("uk"), - Locale("vi"), Locale("zu"), - ) - private suspend fun search(page: Int, query: String): List { val url = buildString { append("https://") 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 f7bbcbfe..c514ea49 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 @@ -27,8 +27,6 @@ internal class ComickFunParser(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isTagsExclusionSupported = true - override val availableSortOrders: Set = EnumSet.of( SortOrder.POPULARITY, SortOrder.UPDATED, @@ -36,12 +34,31 @@ internal class ComickFunParser(context: MangaLoaderContext) : SortOrder.NEWEST, ) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - private val tagsArray = SuspendLazy(::loadTags) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + isYearRangeSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = emptySet(), + availableContentTypes = EnumSet.of( + ContentType.MANGA, + ContentType.MANHWA, + ContentType.MANHUA, + ContentType.OTHER, + ), + availableDemographics = EnumSet.allOf(Demographic::class.java), + availableLocales = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val domain = domain val url = urlBuilder() .host("api.$domain") @@ -51,16 +68,12 @@ internal class ComickFunParser(context: MangaLoaderContext) : .addQueryParameter("tachiyomi", "true") .addQueryParameter("limit", pageSize.toString()) .addQueryParameter("page", page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { url.addQueryParameter("q", filter.query) } - null -> { - url.addQueryParameter("sort", "view") - } - - is MangaListFilter.Advanced -> { + else -> { filter.tags.forEach { url.addQueryParameter("genres", it.key) @@ -72,7 +85,7 @@ internal class ComickFunParser(context: MangaLoaderContext) : url.addQueryParameter( "sort", - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> "view" SortOrder.UPDATED -> "uploaded" SortOrder.NEWEST -> "created_at" @@ -93,6 +106,40 @@ internal class ComickFunParser(context: MangaLoaderContext) : }, ) } + + if (filter.yearFrom != 0) { + url.addQueryParameter("from", filter.yearFrom.toString()) + } + + if (filter.yearTo != 0) { + url.addQueryParameter("to", filter.yearTo.toString()) + } + + filter.types.forEach { + url.addQueryParameter( + "country", + when (it) { + ContentType.MANGA -> "jp" + ContentType.MANHWA -> "kr" + ContentType.MANHUA -> "cn" + ContentType.OTHER -> "others" + else -> "" + }, + ) + } + + filter.demographics.forEach { + url.addQueryParameter( + "demographic", + when (it) { + Demographic.SHOUNEN -> "1" + Demographic.SHOUJO -> "2" + Demographic.SEINEN -> "3" + Demographic.JOSEI -> "4" + Demographic.NONE -> "5" + }, + ) + } } } val ja = webClient.httpGet(url.build()).parseJsonArray() @@ -163,7 +210,7 @@ internal class ComickFunParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val sparseArray = tagsArray.get() val set = ArraySet(sparseArray.size()) for (i in 0 until sparseArray.size()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt index 305cb98e..07583572 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt @@ -33,7 +33,6 @@ internal class ExHentaiParser( ) : PagedMangaParser(context, MangaParserSource.EXHENTAI, pageSize = 25), MangaParserAuthProvider, Interceptor { override val availableSortOrders: Set = setOf(SortOrder.NEWEST) - override val isTagsExclusionSupported: Boolean = true override val configKeyDomain: ConfigKey.Domain get() = ConfigKey.Domain( @@ -51,6 +50,13 @@ internal class ExHentaiParser( private val suspiciousContentKey = ConfigKey.ShowSuspiciousContent(false) private val tagsMap = SuspendLazy(::fetchTags) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + override val isAuthorized: Boolean get() { val authorized = isAuthorized(DOMAIN_UNAUTHORIZED) @@ -75,7 +81,28 @@ internal class ExHentaiParser( searchPaginator.firstPage = 0 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsMap.get().values.toSet(), + availableLocales = setOf( + Locale.JAPANESE, + Locale.ENGLISH, + Locale.CHINESE, + Locale("nl"), + Locale.FRENCH, + Locale.GERMAN, + Locale("hu"), + Locale.ITALIAN, + Locale("kr"), + Locale("pl"), + Locale("pt"), + Locale("ru"), + Locale("es"), + Locale("th"), + Locale("vi"), + ), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val next = nextPages.get(page, 0L) if (page > 0 && next == 0L) { @@ -90,15 +117,15 @@ internal class ExHentaiParser( append(domain) append("/?next=") append(next) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { search += filter.query.urlEncoded() append("&f_search=") append(search.trim().replace(' ', '+')) } - is MangaListFilter.Advanced -> { + else -> { filter.toSearchQuery()?.let { sq -> append("&f_search=") @@ -121,8 +148,6 @@ internal class ExHentaiParser( append(fCats) } } - - null -> {} } // by unknown reason cookie "sl=dm_2" is ignored, so, we should request it again if (updateDm) { @@ -141,7 +166,7 @@ internal class ExHentaiParser( body.parseFailed("Cannot find root") } else { updateDm = true - return getListPage(page, filter) + return getListPage(page, order, filter) } updateDm = false nextPages[page + 1] = getNextTimestamp(body) @@ -267,10 +292,6 @@ internal class ExHentaiParser( "unusual pupils,urination,vore,vtuber,widow,wings,witch,wolf girl,x-ray,yuri,zombie,sole male,males only,yaoi," + "tomgirl,tall man,oni,shotacon,prostate massage,policeman,males only,huge penis,fox boy,feminization,dog boy,dickgirl on male,big penis" - override suspend fun getAvailableTags(): Set { - return tagsMap.get().values.toSet() - } - private suspend fun fetchTags(): Map { val tagMap = ArrayMap() val tagElements = tags.split(",") @@ -297,24 +318,6 @@ internal class ExHentaiParser( return tagMap } - override suspend fun getAvailableLocales(): Set = setOf( - Locale.JAPANESE, - Locale.ENGLISH, - Locale.CHINESE, - Locale("nl"), - Locale.FRENCH, - Locale.GERMAN, - Locale("hu"), - Locale.ITALIAN, - Locale("kr"), - Locale("pl"), - Locale("pt"), - Locale("ru"), - Locale("es"), - Locale("th"), - Locale("vi"), - ) - override fun intercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) if (response.headersContentLength() <= 256) { @@ -420,7 +423,7 @@ internal class ExHentaiParser( ?.toLongOrNull() ?: 1 } - private fun MangaListFilter.Advanced.toSearchQuery(): String? { + private fun MangaListFilter.toSearchQuery(): String? { val joiner = StringUtil.StringJoiner(" ") for (tag in tags) { if (tag.key.isNumeric()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt index 3f8863fb..7c7f32b5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt @@ -27,7 +27,7 @@ import kotlin.math.min @OptIn(ExperimentalUnsignedTypes::class) @MangaSourceParser("HITOMILA", "Hitomi.La", type = ContentType.HENTAI) -class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HITOMILA) { +internal class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HITOMILA) { override val configKeyDomain = ConfigKey.Domain("hitomi.la") override fun onCreateConfig(keys: MutableCollection>) { @@ -70,14 +70,23 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa Locale.JAPANESE to "japanese", ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableLocales = localeMap.keys, + ) + private fun Locale?.getSiteLang(): String = when (this) { null -> "all" else -> localeMap[this] ?: "all" } - override suspend fun getAvailableLocales(): Set = localeMap.keys - - override suspend fun getAvailableTags(): Set = coroutineScope { + private suspend fun fetchAvailableTags(): Set = coroutineScope { ('a'..'z').map { alphabet -> async { val doc = webClient.httpGet("https://$domain/alltags-$alphabet.html").parseHtml() @@ -109,13 +118,10 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa private var cachedSearchIds: List = emptyList() - override suspend fun getList( - offset: Int, - filter: MangaListFilter?, - ): List = when (filter) { - is MangaListFilter.Advanced -> { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List = when { + filter.query.isNullOrEmpty() -> { if (filter.tags.isEmpty()) { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> { getGalleryIDsFromNozomi( "popular", @@ -134,7 +140,7 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa cachedSearchIds = hitomiSearch( filter.tags.joinToString(" ") { it.key }, - filter.sortOrder == SortOrder.POPULARITY, + order == SortOrder.POPULARITY, filter.locale.getSiteLang(), ).toList() } @@ -142,14 +148,12 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa } } - is MangaListFilter.Search -> { + else -> { if (offset == 0) { - cachedSearchIds = hitomiSearch(filter.query, filter.sortOrder == SortOrder.POPULARITY).toList() + cachedSearchIds = hitomiSearch(filter.query, order == SortOrder.POPULARITY).toList() } cachedSearchIds.subList(offset, min(offset + 25, cachedSearchIds.size)) } - - else -> getGalleryIDsFromNozomi(null, "popular", "all", offset.nextOffsetRange()) }.toMangaList() private fun Int.nextOffsetRange(): LongRange { @@ -478,14 +482,14 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa title = doc.selectFirstOrThrow("h1").text(), url = id.toString(), coverUrl = - "https:" + - doc.selectFirstOrThrow("picture > source") - .attr("data-srcset") - .substringBefore(" "), + "https:" + + doc.selectFirstOrThrow("picture > source") + .attr("data-srcset") + .substringBefore(" "), publicUrl = - doc.selectFirstOrThrow("h1 > a") - .attrAsRelativeUrl("href") - .toAbsoluteUrl(domain), + doc.selectFirstOrThrow("h1 > a") + .attrAsRelativeUrl("href") + .toAbsoluteUrl(domain), author = null, tags = emptySet(), isNsfw = true, @@ -508,37 +512,37 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa return manga.copy( title = json.getString("title"), largeCoverUrl = - json.getJSONArray("files").getJSONObject(0).let { - val hash = it.getString("hash") - val commonId = commonImageId() - val imageId = imageIdFromHash(hash) - val subDomain = 'a' + subdomainOffset(imageId) - - "https://${getDomain("${subDomain}a")}/webp/$commonId$imageId/$hash.webp" - }, + json.getJSONArray("files").getJSONObject(0).let { + val hash = it.getString("hash") + val commonId = commonImageId() + val imageId = imageIdFromHash(hash) + val subDomain = 'a' + subdomainOffset(imageId) + + "https://${getDomain("${subDomain}a")}/webp/$commonId$imageId/$hash.webp" + }, author = - json.optJSONArray("artists") - ?.mapJSON { it.getString("artist").toCamelCase() } - ?.joinToString(), + json.optJSONArray("artists") + ?.mapJSON { it.getString("artist").toCamelCase() } + ?.joinToString(), publicUrl = json.getString("galleryurl").toAbsoluteUrl(domain), tags = - buildSet { - json.optJSONArray("characters") - ?.mapToTags("character") - ?.let(::addAll) - json.optJSONArray("tags") - ?.mapToTags("tag") - ?.let(::addAll) - json.optJSONArray("artists") - ?.mapToTags("artist") - ?.let(::addAll) - json.optJSONArray("parodys") - ?.mapToTags("parody") - ?.let(::addAll) - json.optJSONArray("groups") - ?.mapToTags("group") - ?.let(::addAll) - }, + buildSet { + json.optJSONArray("characters") + ?.mapToTags("character") + ?.let(::addAll) + json.optJSONArray("tags") + ?.mapToTags("tag") + ?.let(::addAll) + json.optJSONArray("artists") + ?.mapToTags("artist") + ?.let(::addAll) + json.optJSONArray("parodys") + ?.mapToTags("parody") + ?.let(::addAll) + json.optJSONArray("groups") + ?.mapToTags("group") + ?.let(::addAll) + }, chapters = listOf( MangaChapter( id = generateUid(manga.url), @@ -562,15 +566,15 @@ class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context, MangaPa mapJSON { MangaTag( title = - it.getString(key).toCamelCase().let { title -> - if (it.getStringOrNull("female")?.toIntOrNull() == 1) { - "$title ♀" - } else if (it.getStringOrNull("male")?.toIntOrNull() == 1) { - "$title ♂" - } else { - title - } - }, + it.getString(key).toCamelCase().let { title -> + if (it.getStringOrNull("female")?.toIntOrNull() == 1) { + "$title ♀" + } else if (it.getStringOrNull("male")?.toIntOrNull() == 1) { + "$title ♂" + } else { + title + } + }, key = it.getString("url").tagUrlToTag(), source = source, ).let(tags::add) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt index 27f2ee3e..fb5262b7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt @@ -22,24 +22,41 @@ internal class ImHentai(context: MangaLoaderContext) : override val configKeyDomain = ConfigKey.Domain("imhentai.xxx") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableLocales = setOf( + Locale.ENGLISH, Locale.JAPANESE, Locale("es"), Locale.FRENCH, Locale("kr"), Locale.GERMAN, Locale("ru"), + ), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = buildString { append("https://") append(domain) append("/search/?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&key=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { append("&key=") @@ -53,17 +70,13 @@ internal class ImHentai(context: MangaLoaderContext) : } append(lang) - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> append("<=1&pp=0") SortOrder.POPULARITY -> append("<=0&pp=1") SortOrder.RATING -> append("<=0&pp=0") else -> append("<=1&pp=0") } } - - null -> { - append("<=1&pp=0") - } } } @@ -90,7 +103,7 @@ internal class ImHentai(context: MangaLoaderContext) : //Tags are deliberately reduced because there are too many and this slows down the application. //only the most popular ones are taken. - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return coroutineScope { (1..3).map { page -> async { getTags(page) } @@ -113,10 +126,6 @@ internal class ImHentai(context: MangaLoaderContext) : ) } - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, Locale.JAPANESE, Locale("es"), Locale.FRENCH, Locale("kr"), Locale.GERMAN, Locale("ru"), - ) - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt index d0260a42..98ab058a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt @@ -23,7 +23,14 @@ internal abstract class LineWebtoonsParser( source: MangaParserSource, ) : MangaParser(context, source) { - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) private val signer by lazy { WebtoonsUrlSigner("gUtPzJFZch4ZyAGviiyH94P99lQ3pFdRTwpJWDlSGFfwgpr6ses5ALOxWHOIT7R1") @@ -135,116 +142,79 @@ internal abstract class LineWebtoonsParser( } } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - val manga = - when (filter) { - is MangaListFilter.Search -> { - makeRequest("/lineWebtoon/webtoon/searchChallenge?query=${filter.query.urlEncoded()}&startIndex=${offset + 1}&pageSize=20") - .getJSONObject("challengeSearch") - .getJSONArray("titleList") - .mapJSON { jo -> - val titleNo = jo.getLong("titleNo") - - Manga( - id = generateUid(titleNo), - title = jo.getString("title"), - altTitle = null, - url = titleNo.toString(), - publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo", - rating = RATING_UNKNOWN, - isNsfw = isNsfwSource, - coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain), - largeCoverUrl = null, - tags = emptySet(), - author = jo.getStringOrNull("writingAuthorName"), - description = null, - state = null, - source = source, - ) - } - } - - is MangaListFilter.Advanced -> { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + val manga = when { + !filter.query.isNullOrEmpty() -> { + makeRequest("/lineWebtoon/webtoon/searchChallenge?query=${filter.query.urlEncoded()}&startIndex=${offset + 1}&pageSize=20") + .getJSONObject("challengeSearch") + .getJSONArray("titleList") + .mapJSON { jo -> + val titleNo = jo.getLong("titleNo") + + Manga( + id = generateUid(titleNo), + title = jo.getString("title"), + altTitle = null, + url = titleNo.toString(), + publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo", + rating = RATING_UNKNOWN, + isNsfw = isNsfwSource, + coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain), + largeCoverUrl = null, + tags = emptySet(), + author = jo.getStringOrNull("writingAuthorName"), + description = null, + state = null, + source = source, + ) + } + } - val genre = filter.tags.oneOrThrowIfMany()?.key ?: "ALL" + else -> { - val sortOrderStr = when (filter.sortOrder) { - SortOrder.UPDATED -> "UPDATE" - SortOrder.POPULARITY -> "READ_COUNT" - SortOrder.RATING -> "LIKEIT" - else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}") - } + val genre = filter.tags.oneOrThrowIfMany()?.key ?: "ALL" - val result = - makeRequest("/lineWebtoon/webtoon/challengeGenreTitleList.json?genre=$genre&sortOrder=$sortOrderStr&startIndex=${offset + 1}&pageSize=20") - - val genres = result.getJSONObject("genreList") - .getJSONArray("challengeGenres") - .mapJSON { jo -> parseTag(jo) } - .associateBy { tag -> tag.key } - - result - .getJSONObject("titleList") - .getJSONArray("titles") - .mapJSON { jo -> - val titleNo = jo.getLong("titleNo") - - Manga( - id = generateUid(titleNo), - title = jo.getString("title"), - altTitle = null, - url = titleNo.toString(), - publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo", - rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f, - isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource), - coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain), - largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain), - tags = setOfNotNull(genres[jo.getString("representGenre")]), - author = jo.getStringOrNull("writingAuthorName"), - description = jo.getString("synopsis"), - // I don't think the API provides this info - state = null, - source = source, - ) - } + val sortOrderStr = when (order) { + SortOrder.UPDATED -> "UPDATE" + SortOrder.POPULARITY -> "READ_COUNT" + SortOrder.RATING -> "LIKEIT" + else -> throw IllegalArgumentException("Unsupported sort order: $order") } - null -> { - - val result = - makeRequest("/lineWebtoon/webtoon/challengeGenreTitleList.json?genre=ALL&sortOrder=UPDATE&startIndex=${offset + 1}&pageSize=20") - - val genres = result.getJSONObject("genreList") - .getJSONArray("challengeGenres") - .mapJSON { jo -> parseTag(jo) } - .associateBy { tag -> tag.key } - - result - .getJSONObject("titleList") - .getJSONArray("titles") - .mapJSON { jo -> - val titleNo = jo.getLong("titleNo") - - Manga( - id = generateUid(titleNo), - title = jo.getString("title"), - altTitle = null, - url = titleNo.toString(), - publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo", - rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f, - isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource), - coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain), - largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain), - tags = setOfNotNull(genres[jo.getString("representGenre")]), - author = jo.getStringOrNull("writingAuthorName"), - description = jo.getString("synopsis"), - // I don't think the API provides this info - state = null, - source = source, - ) - } - } + val result = + makeRequest("/lineWebtoon/webtoon/challengeGenreTitleList.json?genre=$genre&sortOrder=$sortOrderStr&startIndex=${offset + 1}&pageSize=20") + + val genres = result.getJSONObject("genreList") + .getJSONArray("challengeGenres") + .mapJSON { jo -> parseTag(jo) } + .associateBy { tag -> tag.key } + + result + .getJSONObject("titleList") + .getJSONArray("titles") + .mapJSON { jo -> + val titleNo = jo.getLong("titleNo") + + Manga( + id = generateUid(titleNo), + title = jo.getString("title"), + altTitle = null, + url = titleNo.toString(), + publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo", + rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f, + isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource), + coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain), + largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain), + tags = setOfNotNull(genres[jo.getString("representGenre")]), + author = jo.getStringOrNull("writingAuthorName"), + description = jo.getString("synopsis"), + // I don't think the API provides this info + state = null, + source = source, + ) + } } + } return manga @@ -274,7 +244,7 @@ internal abstract class LineWebtoonsParser( ) } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return makeRequest("/lineWebtoon/webtoon/challengeGenreList.json") .getJSONObject("genreList") .getJSONArray("challengeGenres") 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 c0b01040..e623f677 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 @@ -44,17 +44,38 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context keys.add(preferredServerKey) } - 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) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + isYearSupported = true, + isYearRangeSupported = true, + isOriginalLocaleSupported = true, + ) - override val isTagsExclusionSupported: Boolean = true + override val availableSortOrders: EnumSet = EnumSet.allOf(SortOrder::class.java) + override suspend fun getFilterOptions(): MangaListFilterOptions = coroutineScope { + val localesDeferred = async { fetchAvailableLocales() } + val tagsDeferred = async { fetchAvailableTags() } + MangaListFilterOptions( + availableTags = tagsDeferred.await(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.PAUSED, + MangaState.ABANDONED, + ), + availableContentRating = EnumSet.allOf(ContentRating::class.java), + availableContentTypes = emptySet(), + availableDemographics = EnumSet.allOf(Demographic::class.java), + availableLocales = localesDeferred.await(), + ) + } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val domain = domain val url = buildString { append("https://api.") @@ -64,69 +85,91 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context append("&offset=") append(offset) append("&includes[]=cover_art&includes[]=author&includes[]=artist") - when (filter) { - is MangaListFilter.Search -> { - append("&title=") - append(filter.query) - append("&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic") - } - is MangaListFilter.Advanced -> { - filter.tags.forEach { - append("&includedTags[]=") - append(it.key) - } + filter.query?.let { + append("&title=") + append(filter.query.urlEncoded()) + } - filter.tagsExclude.forEach { - append("&excludedTags[]=") - append(it.key) - } + filter.tags.forEach { + append("&includedTags[]=") + append(it.key) + } - 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") - } - } - } + filter.tagsExclude.forEach { + append("&excludedTags[]=") + append(it.key) + } + + 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) { - SortOrder.UPDATED -> "[latestUploadedChapter]=desc" - SortOrder.UPDATED_ASC -> "[latestUploadedChapter]=asc" - SortOrder.RATING -> "[rating]=desc" - SortOrder.RATING_ASC -> "[rating]=asc" - SortOrder.ALPHABETICAL -> "[title]=asc" - SortOrder.ALPHABETICAL_DESC -> "[title]=desc" - SortOrder.NEWEST -> "[createdAt]=desc" - SortOrder.NEWEST_ASC -> "[createdAt]=asc" - SortOrder.POPULARITY -> "[followedCount]=desc" - SortOrder.POPULARITY_ASC -> "[followedCount]=asc" - }, - ) - filter.states.forEach { - append("&status[]=") - when (it) { - MangaState.ONGOING -> append("ongoing") - MangaState.FINISHED -> append("completed") - MangaState.ABANDONED -> append("cancelled") - MangaState.PAUSED -> append("hiatus") - else -> append("") - } - } - filter.locale?.let { - append("&availableTranslatedLanguage[]=") - append(it.language) } } + } else append("&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic") - null -> { - append("&order[latestUploadedChapter]=desc") + append("&order") + append( + when (order) { + SortOrder.UPDATED -> "[latestUploadedChapter]=desc" + SortOrder.UPDATED_ASC -> "[latestUploadedChapter]=asc" + SortOrder.RATING -> "[rating]=desc" + SortOrder.RATING_ASC -> "[rating]=asc" + SortOrder.ALPHABETICAL -> "[title]=asc" + SortOrder.ALPHABETICAL_DESC -> "[title]=desc" + SortOrder.NEWEST -> "[year]=desc" + SortOrder.NEWEST_ASC -> "[year]=asc" + SortOrder.POPULARITY -> "[followedCount]=desc" + SortOrder.POPULARITY_ASC -> "[followedCount]=asc" + SortOrder.ADDED -> "[createdAt]=desc" + SortOrder.ADDED_ASC -> "[createdAt]=asc" + SortOrder.RELEVANCE -> "&order[relevance]=desc" + else -> "[latestUploadedChapter]=desc" + }, + ) + + filter.states.forEach { + append("&status[]=") + when (it) { + MangaState.ONGOING -> append("ongoing") + MangaState.FINISHED -> append("completed") + MangaState.ABANDONED -> append("cancelled") + MangaState.PAUSED -> append("hiatus") + else -> append("") } } + + filter.demographics.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) + } + + filter.originalLocale?.let { + append("&originalLanguage[]=") + append(it.language) + } + + if (filter.year != 0) { + append("&year=") + append(filter.year) + } } val json = webClient.httpGet(url).parseJson().getJSONArray("data") return json.mapJSON { jo -> @@ -219,7 +262,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val tags = webClient.httpGet("https://api.${domain}/manga/tag").parseJson() .getJSONArray("data") return tags.mapJSONToSet { jo -> @@ -233,7 +276,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context } } - override suspend fun getAvailableLocales(): Set { + private suspend fun fetchAvailableLocales(): Set { val head = webClient.httpGet("https://$domain/").parseHtml().head() return head.getElementsByAttributeValue("property", "og:locale:alternate") .mapNotNullToSet { meta -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt index 0d3b1665..bf9f148b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt @@ -60,8 +60,6 @@ internal abstract class MangaFireParser( ?: body.parseFailed("Cannot find username") } - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - private val tags = SoftSuspendLazy { webClient.httpGet("https://$domain/filter").parseHtml() .select(".genres > li").map { @@ -73,36 +71,44 @@ internal abstract class MangaFireParser( }.associateBy { it.title } } - override suspend fun getAvailableTags(): Set { - return tags.get().values.toSet() - } + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val isTagsExclusionSupported = true + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tags.get().values.toSet(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = "https://$domain/filter".toHttpUrl().newBuilder().apply { addQueryParameter("page", page.toString()) addQueryParameter("language[]", siteLang) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { addQueryParameter("keyword", filter.query) - filter.sortOrder?.let { - addQueryParameter( - name = "sort", - value = when (it) { - SortOrder.UPDATED -> "recently_updated" - SortOrder.POPULARITY -> "most_viewed" - SortOrder.RATING -> "scores" - SortOrder.NEWEST -> "release_date" - SortOrder.ALPHABETICAL -> "title_az" - else -> "" - }, - ) - } + addQueryParameter( + name = "sort", + value = when (order) { + SortOrder.UPDATED -> "recently_updated" + SortOrder.POPULARITY -> "most_viewed" + SortOrder.RATING -> "scores" + SortOrder.NEWEST -> "release_date" + SortOrder.ALPHABETICAL -> "title_az" + else -> "" + }, + ) } - is MangaListFilter.Advanced -> { + else -> { filter.tagsExclude.forEach { tag -> addQueryParameter("genre[]", "-${tag.key}") } @@ -126,7 +132,7 @@ internal abstract class MangaFireParser( } addQueryParameter( name = "sort", - value = when (filter.sortOrder) { + value = when (order) { SortOrder.UPDATED -> "recently_updated" SortOrder.POPULARITY -> "most_viewed" SortOrder.RATING -> "scores" @@ -136,8 +142,6 @@ internal abstract class MangaFireParser( }, ) } - - null -> {} } }.build() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt index eeb9480f..8783e9f3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt @@ -19,13 +19,44 @@ internal class MangaPark(context: MangaLoaderContext) : override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - - override val availableContentRating: Set = EnumSet.of(ContentRating.SAFE) + override val configKeyDomain = ConfigKey.Domain("mangapark.net") - override val isTagsExclusionSupported: Boolean = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val configKeyDomain = ConfigKey.Domain("mangapark.net") + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsMap.get().values.toSet(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = EnumSet.of(ContentRating.SAFE), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = setOf( + Locale("af"), Locale("sq"), Locale("am"), Locale("ar"), Locale("hy"), + Locale("az"), Locale("be"), Locale("bn"), Locale("zh_hk"), Locale("zh_tw"), + Locale.CHINESE, Locale("ceb"), Locale("ca"), Locale("km"), Locale("my"), + Locale("bg"), Locale("bs"), Locale("hr"), Locale("cs"), Locale("da"), + Locale("nl"), Locale.ENGLISH, Locale("et"), Locale("fo"), Locale("fil"), + Locale("fi"), Locale("he"), Locale("ha"), Locale("jv"), Locale("lb"), + Locale("mn"), Locale("ro"), Locale("si"), Locale("ta"), Locale("uz"), + Locale("ur"), Locale("tg"), Locale("sd"), Locale("pt_br"), Locale("mo"), + Locale("lt"), Locale.JAPANESE, Locale.ITALIAN, Locale("ht"), Locale("lv"), + Locale("mr"), Locale("pt"), Locale("sn"), Locale("sv"), Locale("uk"), + Locale("tk"), Locale("sw"), Locale("st"), Locale("pl"), Locale("mi"), + Locale("lo"), Locale("ga"), Locale("gu"), Locale("gn"), Locale("id"), + Locale("ky"), Locale("mt"), Locale("fa"), Locale("sh"), Locale("es_419"), + Locale("tr"), Locale("to"), Locale("vi"), Locale("es"), Locale("sr"), + Locale("ps"), Locale("ml"), Locale("ku"), Locale("ig"), Locale("el"), + Locale.GERMAN, Locale("is"), Locale.KOREAN, Locale("ms"), Locale("ny"), Locale("sm"), + Locale("so"), Locale("ti"), Locale("zu"), Locale("yo"), Locale("th"), + Locale("sl"), Locale("ru"), Locale("no"), Locale("mg"), Locale("kk"), + Locale("hu"), Locale("ka"), Locale.FRENCH, Locale("hi"), Locale("kn"), + Locale("mk"), Locale("ne"), Locale("rm"), Locale("sk"), Locale("te"), + ), + ) override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) @@ -38,19 +69,19 @@ internal class MangaPark(context: MangaLoaderContext) : context.cookieJar.insertCookies(domain, "nsfw", "2") } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/search?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&word=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&genres=") if (filter.tags.isNotEmpty()) { @@ -88,7 +119,7 @@ internal class MangaPark(context: MangaLoaderContext) : append("&sortby=") append( - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> "views_d000" SortOrder.UPDATED -> "field_update" SortOrder.NEWEST -> "field_create" @@ -104,8 +135,6 @@ internal class MangaPark(context: MangaLoaderContext) : append(it.language) } } - - null -> append("&sortby=field_update") } } @@ -129,10 +158,6 @@ internal class MangaPark(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { - return tagsMap.get().values.toSet() - } - private suspend fun parseTags(): Map { val tagElements = webClient.httpGet("https://$domain/search").parseHtml() .select("div.flex-col:contains(Genres) div.whitespace-nowrap") @@ -149,29 +174,6 @@ internal class MangaPark(context: MangaLoaderContext) : return tagMap } - override suspend fun getAvailableLocales(): Set = setOf( - Locale("af"), Locale("sq"), Locale("am"), Locale("ar"), Locale("hy"), - Locale("az"), Locale("be"), Locale("bn"), Locale("zh_hk"), Locale("zh_tw"), - Locale.CHINESE, Locale("ceb"), Locale("ca"), Locale("km"), Locale("my"), - Locale("bg"), Locale("bs"), Locale("hr"), Locale("cs"), Locale("da"), - Locale("nl"), Locale.ENGLISH, Locale("et"), Locale("fo"), Locale("fil"), - Locale("fi"), Locale("he"), Locale("ha"), Locale("jv"), Locale("lb"), - Locale("mn"), Locale("ro"), Locale("si"), Locale("ta"), Locale("uz"), - Locale("ur"), Locale("tg"), Locale("sd"), Locale("pt_br"), Locale("mo"), - Locale("lt"), Locale.JAPANESE, Locale.ITALIAN, Locale("ht"), Locale("lv"), - Locale("mr"), Locale("pt"), Locale("sn"), Locale("sv"), Locale("uk"), - Locale("tk"), Locale("sw"), Locale("st"), Locale("pl"), Locale("mi"), - Locale("lo"), Locale("ga"), Locale("gu"), Locale("gn"), Locale("id"), - Locale("ky"), Locale("mt"), Locale("fa"), Locale("sh"), Locale("es_419"), - Locale("tr"), Locale("to"), Locale("vi"), Locale("es"), Locale("sr"), - Locale("ps"), Locale("ml"), Locale("ku"), Locale("ig"), Locale("el"), - Locale.GERMAN, Locale("is"), Locale.KOREAN, Locale("ms"), Locale("ny"), Locale("sm"), - Locale("so"), Locale("ti"), Locale("zu"), Locale("yo"), Locale("th"), - Locale("sl"), Locale("ru"), Locale("no"), Locale("mg"), Locale("kk"), - Locale("hu"), Locale("ka"), Locale.FRENCH, Locale("hi"), Locale("kn"), - Locale("mk"), Locale("ne"), Locale("rm"), Locale("sk"), Locale("te"), - ) - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val tagMap = tagsMap.get() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt index 69a15811..601e5092 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt @@ -9,8 +9,8 @@ import okhttp3.ResponseBody.Companion.toResponseBody import org.json.JSONArray import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -24,7 +24,7 @@ internal abstract class MangaPlusParser( context: MangaLoaderContext, source: MangaParserSource, private val sourceLang: String, -) : MangaParser(context, source), Interceptor { +) : SinglePageMangaParser(context, source), Interceptor { private val apiUrl = "https://jumpg-webapi.tokyo-cdn.com/api" override val configKeyDomain = ConfigKey.Domain("mangaplus.shueisha.co.jp") @@ -40,27 +40,26 @@ internal abstract class MangaPlusParser( SortOrder.ALPHABETICAL, ) - private val extraHeaders = Headers.headersOf("Session-Token", UUID.randomUUID().toString()) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - // no tags or tag search available - override suspend fun getAvailableTags(): Set = emptySet() + override suspend fun getFilterOptions() = MangaListFilterOptions() - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - if (offset > 0) { - return emptyList() - } + private val extraHeaders = Headers.headersOf("Session-Token", UUID.randomUUID().toString()) - return when (filter) { - is MangaListFilter.Advanced -> { - when (filter.sortOrder) { + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { + return when { + filter.query.isNullOrEmpty() -> { + when (order) { SortOrder.POPULARITY -> getPopularList() SortOrder.UPDATED -> getLatestList() else -> getAllTitleList() } } - is MangaListFilter.Search -> getAllTitleList(filter.query) - else -> getAllTitleList() + else -> getAllTitleList(filter.query) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt index 2d7c85c6..034e740d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt @@ -21,7 +21,8 @@ import javax.crypto.spec.SecretKeySpec import kotlin.math.min @MangaSourceParser("MANGAREADERTO", "MangaReader.To") -class MangaReaderToParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAREADERTO, 16), +internal class MangaReaderToParser(context: MangaLoaderContext) : + PagedMangaParser(context, MangaParserSource.MANGAREADERTO, 16), Interceptor, MangaParserAuthProvider { override val configKeyDomain = ConfigKey.Domain("mangareader.to") @@ -55,8 +56,6 @@ class MangaReaderToParser(context: MangaLoaderContext) : PagedMangaParser(contex SortOrder.ALPHABETICAL, ) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - val tags = SoftSuspendLazy { val document = webClient.httpGet("https://$domain/filter").parseHtml() @@ -69,27 +68,36 @@ class MangaReaderToParser(context: MangaLoaderContext) : PagedMangaParser(contex }.associateBy { it.title } } - override suspend fun getAvailableTags(): Set { - return tags.get().values.toSet() - } + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) - override val isTagsExclusionSupported = false + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tags.get().values.toSet(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = "https://$domain".toHttpUrl().newBuilder().apply { - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { addPathSegment("search") addQueryParameter("keyword", filter.query) addQueryParameter("page", page.toString()) } - is MangaListFilter.Advanced -> { + else -> { addPathSegment("filter") addQueryParameter("page", page.toString()) addQueryParameter( name = "sort", - value = when (filter.sortOrder) { + value = when (order) { SortOrder.POPULARITY -> "most-viewed" SortOrder.RATING -> "score" SortOrder.UPDATED -> "latest-updated" @@ -111,11 +119,6 @@ class MangaReaderToParser(context: MangaLoaderContext) : PagedMangaParser(contex }, ) } - - null -> { - addPathSegment("filter") - addQueryParameter("page", page.toString()) - } } }.build() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt index 7de52d1c..d1f14a25 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt @@ -40,12 +40,24 @@ internal abstract class NineMangaParser( SortOrder.POPULARITY, ) - override val availableStates: Set = EnumSet.of( - MangaState.ONGOING, - MangaState.FINISHED, - ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val isTagsExclusionSupported: Boolean = true + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + ), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() @@ -57,12 +69,12 @@ internal abstract class NineMangaParser( return chain.proceed(newRequest) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search/?name_sel=&wd=") append(filter.query.urlEncoded()) append("&page=") @@ -70,7 +82,7 @@ internal abstract class NineMangaParser( append(".html") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty() || filter.tagsExclude.isNotEmpty() || filter.states.isNotEmpty()) { append("/search/?category_id=") @@ -94,12 +106,6 @@ internal abstract class NineMangaParser( append(page.toString()) append(".html") } - - null -> { - append("/category/index_") - append(page) - append(".html") - } } } val doc = webClient.httpGet(url).parseHtml() @@ -184,10 +190,6 @@ internal abstract class NineMangaParser( private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - private suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt index c3f155e3..c0f90105 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt @@ -34,13 +34,19 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : SortOrder.NEWEST, ) - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getAvailableLocales() = setOf( - Locale.ENGLISH, - Locale.CHINESE, - Locale.JAPANESE, - Locale("es"), + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableLocales = setOf( + Locale.ENGLISH, + Locale.CHINESE, + Locale.JAPANESE, + Locale("es"), + ), ) private fun Locale?.getSiteLang(): String { @@ -75,7 +81,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : return cdn?.toHttpUrlOrNull()?.host ?: "edge.fast4speed.rsvp" } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val query = """ queryTags( search: {format:"tagchapter",sortBy:Popular} @@ -102,23 +108,15 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : } } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - return when (filter) { - is MangaListFilter.Advanced -> { - if (filter.tags.isEmpty() && filter.sortOrder == SortOrder.POPULARITY) { - getPopularList(page, filter.locale) - } else { - getSearchList(page, null, filter.locale, filter.tags, filter.sortOrder) - } - } - - is MangaListFilter.Search -> { - getSearchList(page, filter.query, null, null, filter.sortOrder) - } - - else -> { - getPopularList(page, null) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + return if (filter.query.isNullOrEmpty()) { + if (filter.tags.isEmpty() && order == SortOrder.POPULARITY) { + getPopularList(page, filter.locale) + } else { + getSearchList(page, null, filter.locale, filter.tags, order) } + } else { + getSearchList(page, filter.query, null, null, order) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt index 93ec230f..f86b1090 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt @@ -25,8 +25,6 @@ internal abstract class WebtoonsParser( source: MangaParserSource, ) : MangaParser(context, source) { - override val isMultipleTagsSupported = false - private val signer by lazy { WebtoonsUrlSigner("gUtPzJFZch4ZyAGviiyH94P99lQ3pFdRTwpJWDlSGFfwgpr6ses5ALOxWHOIT7R1") } @@ -49,8 +47,17 @@ internal abstract class WebtoonsParser( SortOrder.UPDATED, ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + override val userAgentKey = ConfigKey.UserAgent("nApps (Android 12;; linewebtoon; 3.1.0)") + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getAllGenreList().values.toSet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -182,10 +189,9 @@ internal abstract class WebtoonsParser( } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - - val webtoons = when (filter) { - is MangaListFilter.Search -> { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + val webtoons = when { + !filter.query.isNullOrEmpty() -> { makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch") .getJSONArray("titleList").mapJSON { jo -> val titleNo = jo.getLong("titleNo") @@ -210,7 +216,7 @@ internal abstract class WebtoonsParser( } } - is MangaListFilter.Advanced -> { + else -> { val genre = filter.tags.oneOrThrowIfMany()?.key ?: "ALL" val genres = getAllGenreList() @@ -220,17 +226,14 @@ internal abstract class WebtoonsParser( result = result.filter { it.manga.tags.contains(genres[genre]) } } - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> result.sortedByDescending { it.date } SortOrder.POPULARITY -> result.sortedByDescending { it.readCount } SortOrder.RATING -> result.sortedByDescending { it.manga.rating } //SortOrder.LIKE -> result.sortedBy { it.likeitCount } - else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}") + else -> throw IllegalArgumentException("Unsupported sort order: $order") } } - - else -> getAllTitleList() - } return webtoons.map { it.manga }.subList(offset, (offset + 20).coerceAtMost(webtoons.size)) } @@ -257,10 +260,6 @@ internal abstract class WebtoonsParser( ) } - override suspend fun getAvailableTags(): Set { - return getAllGenreList().values.toSet() - } - private suspend fun makeRequest(url: String): JSONObject { val resp = webClient.httpGet(finalizeUrl(url)) val message: JSONObject? = resp.parseJson().optJSONObject("message") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt index 67a28410..f0c440e1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt @@ -25,8 +25,6 @@ internal abstract class AnimeBootstrapParser( keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val availableSortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, @@ -37,13 +35,21 @@ internal abstract class AnimeBootstrapParser( protected open val listUrl = "/manga" protected open val datePattern = "dd MMM. yyyy" - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) @@ -52,13 +58,13 @@ internal abstract class AnimeBootstrapParser( append(page.toString()) append("&type=all") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&search=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("&categorie=") @@ -66,7 +72,7 @@ internal abstract class AnimeBootstrapParser( } append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("view") SortOrder.UPDATED -> append("updated") SortOrder.ALPHABETICAL -> append("default") @@ -75,8 +81,6 @@ internal abstract class AnimeBootstrapParser( } } - - null -> append("&sort=updated") } } val doc = webClient.httpGet(url).parseHtml() @@ -100,7 +104,7 @@ internal abstract class AnimeBootstrapParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain$listUrl").parseHtml() return doc.select("div.product__page__filter div:contains(Genre:) option ").mapNotNullToSet { option -> val key = option.attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt index edb06554..9beffb8d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt @@ -17,7 +17,6 @@ import java.util.* internal class PapScan(context: MangaLoaderContext) : AnimeBootstrapParser(context, MangaParserSource.PAPSCAN, "papscan.com") { override val sourceLocale: Locale = Locale.ENGLISH - override val isMultipleTagsSupported = false override val listUrl = "/liste-manga" override val selectState = "div.anime__details__widget li:contains(En cours)" override val selectTag = "div.anime__details__widget li:contains(Genre) a" @@ -29,20 +28,20 @@ internal class PapScan(context: MangaLoaderContext) : SortOrder.ALPHABETICAL_DESC, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/filterList") append("?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&alpha=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("&cat=") @@ -50,7 +49,7 @@ internal class PapScan(context: MangaLoaderContext) : } append("&sortBy=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.ALPHABETICAL_DESC -> append("name&asc=false") SortOrder.ALPHABETICAL -> append("name&asc=true") @@ -58,8 +57,6 @@ internal class PapScan(context: MangaLoaderContext) : } } - - null -> append("&sortBy=updated") } } val doc = webClient.httpGet(url).parseHtml() @@ -82,7 +79,7 @@ internal class PapScan(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain$listUrl").parseHtml() return doc.select("a.category ").mapNotNullToSet { a -> val key = a.attr("href").substringAfterLast('=') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt index 1f84bddc..0a80545f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt @@ -19,19 +19,31 @@ import java.util.* internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.FLIXSCANS, 18) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - override val availableContentRating: Set = EnumSet.of(ContentRating.ADULT) override val configKeyDomain = ConfigKey.Domain("flixscans.net") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = EnumSet.of(ContentRating.ADULT), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - val json = when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val json = when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -41,7 +53,7 @@ internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context webClient.httpPost(url, body).parseJson().getJSONArray("data") } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://api.") append(domain) @@ -90,11 +102,6 @@ internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context webClient.httpGet(url).parseJson().getJSONArray("data") } - - null -> { - val url = "https://api.$domain/api/v1/webtoon/pages/latest/romance?page=$page" - webClient.httpGet(url).parseJson().getJSONArray("data") - } } return json.mapJSON { j -> val href = "https://$domain/series/${j.getString("prefix")}-${j.getString("id")}-${j.getString("slug")}" @@ -122,7 +129,7 @@ internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/search/advance").parseHtml() val json = JSONArray(doc.requireElementById("__NUXT_DATA__").data()) val tagsList = json.getJSONArray(3).toString().replace("[", "").replace("]", "").split(",") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt index 9eea168f..23328529 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt @@ -16,7 +16,13 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED) override val configKeyDomain = ConfigKey.Domain("mangastorm.org") - override val isMultipleTagsSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) @@ -25,19 +31,19 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/mangas?page=") append(page) append("&query=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { val tag = filter.tags.oneOrThrowIfMany() @@ -46,7 +52,7 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex append("?page=") append(page) } else { - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("/mangas?page=") append(page) } else { @@ -56,11 +62,6 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex } } } - - null -> { - append("/mangas?page=") - append(page) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -83,8 +84,6 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex } } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val root = doc.selectFirstOrThrow(".card-body .col-lg-9") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt index 9ef6ab20..23461a6b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt @@ -17,8 +17,6 @@ import java.util.* internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.TEAMXNOVEL, 10) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED) override val configKeyDomain = ConfigKey.Domain("teamoney.site") @@ -27,16 +25,27 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/?search=") append(filter.query.urlEncoded()) if (page > 1) { @@ -45,7 +54,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex } } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { val tag = filter.tags.oneOrThrowIfMany() append("/series?genre=") @@ -56,7 +65,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex } append("&") } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("/series") SortOrder.UPDATED -> append("/") else -> append("/") @@ -70,7 +79,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex } } - if (filter.sortOrder == SortOrder.POPULARITY || filter.tags.isNotEmpty()) { + if (order == SortOrder.POPULARITY || filter.tags.isNotEmpty()) { filter.states.oneOrThrowIfMany()?.let { append("status=") append( @@ -84,8 +93,6 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex } } } - - null -> append("/?page=$page") } } val doc = webClient.httpGet(url).parseHtml() @@ -115,7 +122,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/series").parseHtml() return doc.requireElementById("select_genre").select("option").mapNotNullToSet { MangaTag( 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 89c24638..357cce50 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 @@ -25,6 +25,16 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context, override val configKeyDomain = ConfigKey.Domain("anibel.net") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -34,31 +44,26 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context, SortOrder.NEWEST, ) - override suspend fun getList( - offset: Int, - filter: MangaListFilter?, - ): List { - val filters = - when (filter) { - is MangaListFilter.Search -> { - return if (offset == 0) { - search(filter.query) - } else { - emptyList() - } - } - - is MangaListFilter.Advanced -> { - filter.tags.takeUnless { it.isEmpty() }?.joinToString( - separator = ",", - prefix = "genres: [", - postfix = "]", - ) { "\"${it.key}\"" }.orEmpty() + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + val filters = when { + !filter.query.isNullOrEmpty() -> { + return if (offset == 0) { + search(filter.query) + } else { + emptyList() } + } - null -> "" + else -> { + filter.tags.takeUnless { it.isEmpty() }?.joinToString( + separator = ",", + prefix = "genres: [", + postfix = "]", + ) { "\"${it.key}\"" }.orEmpty() } + } + val array = apiCall( """ getMediaList(offset: $offset, limit: 20, mediaType: manga, filters: {$filters}) { @@ -191,7 +196,7 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context, } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val json = apiCall( """ getFilters(mediaType: manga) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt index 05734d3a..6dac79be 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt @@ -27,26 +27,36 @@ internal abstract class CupFoxParser( SortOrder.UPDATED, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override val isMultipleTagsSupported = false + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search/") append(filter.query.urlEncoded()) append('/') append(page) } - is MangaListFilter.Advanced -> { + else -> { append("/category/") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("order/hits/") SortOrder.UPDATED -> append("order/addtime/") else -> append("order/addtime/") @@ -73,11 +83,6 @@ internal abstract class CupFoxParser( append("page/") append(page) } - - null -> { - append("/category/order/addtime/page/") - append(page) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -193,7 +198,8 @@ internal abstract class CupFoxParser( } protected open val selectAvailableTags = "div.swiper-wrapper a[href*=tags], ul.stui-screen__list li a[href*=tags]" - override suspend fun getAvailableTags(): Set { + + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/category/").parseHtml() return doc.select(selectAvailableTags) .mapNotNullToSet { a -> 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 3c364c07..7f27b748 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 @@ -25,31 +25,42 @@ internal class AsuraScansParser(context: MangaLoaderContext) : SortOrder.ALPHABETICAL, ) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - override val configKeyDomain = ConfigKey.Domain("asuracomic.net") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = true - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/series?page=") append(page) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&name=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { append("&genres=") @@ -70,7 +81,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) : } append("&types=-1&order=") - when (filter.sortOrder) { + when (order) { SortOrder.RATING -> append("rating") SortOrder.UPDATED -> append("update") SortOrder.NEWEST -> append("latest") @@ -79,8 +90,6 @@ internal class AsuraScansParser(context: MangaLoaderContext) : else -> append("update") } } - - null -> append("&genres=&status=-1&order=update&types=-1") } } val doc = webClient.httpGet(url).parseHtml() @@ -113,10 +122,6 @@ internal class AsuraScansParser(context: MangaLoaderContext) : private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - private suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt index d33473ef..9d118d96 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt @@ -22,14 +22,21 @@ internal class BeeToon(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -37,7 +44,7 @@ internal class BeeToon(context: MangaLoaderContext) : append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { val tag = filter.tags.oneOrThrowIfMany() @@ -47,7 +54,7 @@ internal class BeeToon(context: MangaLoaderContext) : append(page) append("/") } else { - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> append("/latest-update/") SortOrder.POPULARITY -> append("/popular-manga/") else -> append("/latest-update/") @@ -57,8 +64,6 @@ internal class BeeToon(context: MangaLoaderContext) : append("/") } } - - null -> append("/latest-update/page-$page/") } } val doc = webClient.httpGet(url).parseHtml() @@ -85,7 +90,7 @@ internal class BeeToon(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml() return doc.requireElementById("menu-item-3").select("ul.sub-menu li a").mapNotNullToSet { MangaTag( 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 fc373e93..f0b7f1a2 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 @@ -1,16 +1,16 @@ package org.koitharu.kotatsu.parsers.site.en -import org.koitharu.kotatsu.parsers.InternalParsersApi import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* import java.util.* @MangaSourceParser("CLONEMANGA", "CloneManga", "en") -internal class CloneMangaParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.CLONEMANGA) { +internal class CloneMangaParser(context: MangaLoaderContext) : + SinglePageMangaParser(context, MangaParserSource.CLONEMANGA) { override val availableSortOrders: Set = Collections.singleton( SortOrder.POPULARITY, @@ -18,32 +18,23 @@ internal class CloneMangaParser(context: MangaLoaderContext) : MangaParser(conte override val configKeyDomain = ConfigKey.Domain("manga.clone-army.org") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - @InternalParsersApi - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - - val link = when (filter) { - is MangaListFilter.Search -> { - return emptyList() - } - - is MangaListFilter.Advanced -> { - if (offset > 0) { - return emptyList() - } - - "https://$domain/viewer_landing.php" - } - - null -> "https://$domain/viewer_landing.php" - + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { + if (!filter.query.isNullOrEmpty()) { + return emptyList() } - - val doc = webClient.httpGet(link).parseHtml() + val doc = webClient.httpGet("https://$domain/viewer_landing.php").parseHtml() val mangas = doc.getElementsByClass("comicPreviewContainer") return mangas.mapNotNull { item -> val background = item.selectFirstOrThrow(".comicPreview").styleValueOrNull("background") @@ -108,6 +99,4 @@ internal class CloneMangaParser(context: MangaLoaderContext) : MangaParser(conte ), ) } - - override suspend fun getAvailableTags(): Set = emptySet() } 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 af06d6be..c9824aae 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 @@ -17,27 +17,33 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - override val configKeyDomain = ConfigKey.Domain("comixextra.com") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentRating = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("search?keyword=") append(filter.query.urlEncoded()) if (page > 1) { @@ -46,7 +52,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex } } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty() && filter.states.isEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append(it.key) @@ -65,7 +71,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex } else if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED) } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("popular-comic") SortOrder.UPDATED -> append("new-comic") SortOrder.NEWEST -> append("recent-comic") @@ -78,15 +84,6 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex append(page.toString()) } } - - null -> { - append("popular-comic") - if (page > 1) { - append("/") - append(page.toString()) - } - } - } } val doc = webClient.httpGet(url).parseHtml() @@ -113,7 +110,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/popular-comic").parseHtml() return doc.select("li.tag-item a").mapNotNullToSet { a -> MangaTag( 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 d7a42055..3bd3708e 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 @@ -25,16 +25,23 @@ internal class DynastyScans(context: MangaLoaderContext) : override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -47,7 +54,7 @@ internal class DynastyScans(context: MangaLoaderContext) : return parseMangaListQuery(webClient.httpGet(url).parseHtml()) } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") @@ -68,16 +75,6 @@ internal class DynastyScans(context: MangaLoaderContext) : } return parseMangaList(webClient.httpGet(url).parseHtml()) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/series?view=cover&page=") - append(page.toString()) - } - return parseMangaList(webClient.httpGet(url).parseHtml()) - } } } @@ -130,7 +127,7 @@ internal class DynastyScans(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return coroutineScope { (1..3).map { page -> async { getTags(page) } 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 94b988ff..dd07d84f 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 @@ -20,25 +20,31 @@ internal class FlixScansOrg(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.FLIXSCANSORG, 18) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - override val availableContentRating: Set = EnumSet.of(ContentRating.ADULT) override val configKeyDomain = ConfigKey.Domain("flixscans.org") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = EnumSet.of(ContentRating.ADULT), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isSearchSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - val json = when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val json = when { + !filter.query.isNullOrEmpty() -> { throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://api.") @@ -75,11 +81,6 @@ internal class FlixScansOrg(context: MangaLoaderContext) : } webClient.httpGet(url).parseJson().getJSONArray("data") } - - null -> { - val url = "https://api.$domain/api/v1/search/advance?=&serie_type=webtoon&page=$page" - webClient.httpGet(url).parseJson().getJSONArray("data") - } } return json.mapJSON { j -> val href = "https://$domain/series/${j.getString("prefix")}-${j.getString("id")}-${j.getString("slug")}" @@ -107,7 +108,7 @@ internal class FlixScansOrg(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/search/advance").parseHtml() val json = JSONArray(doc.requireElementById("__NUXT_DATA__").data()) val tagsList = json.getJSONArray(3).toString().replace("[", "").replace("]", "").split(",") 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 2a7aa0cb..f6f253ce 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 @@ -19,20 +19,26 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context override val configKeyDomain = ConfigKey.Domain("www.mgeko.cc", "www.mgeko.com", "www.mangageko.com") - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = 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, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -40,13 +46,13 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("/browse-comics/?results=") append(page) append("&filter=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("Updated") SortOrder.NEWEST -> append("New") @@ -60,12 +66,6 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context } } } - - null -> { - append("/browse-comics/?results=") - append(page) - append("&filter=Updated") - } } } val doc = webClient.httpGet(url).parseHtml() @@ -88,7 +88,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml() return doc.select("label.checkbox-inline").mapNotNullToSet { label -> MangaTag( 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 9c849dc9..676e0955 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 @@ -20,6 +20,15 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -29,28 +38,25 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : .add("Accept-Language", "en") .build() - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?query=") append(filter.query.urlEncoded()) append("&search_type=manga&page=") append(page) } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.UPDATED && filter.tags.isNotEmpty()) { + if (order == SortOrder.UPDATED && filter.tags.isNotEmpty()) { throw IllegalArgumentException("Filter part tag is not available with sort not updated") } - if (filter.sortOrder == SortOrder.ALPHABETICAL) { + if (order == SortOrder.ALPHABETICAL) { append("/manga-list") filter.tags.oneOrThrowIfMany()?.let { append("/category/") @@ -62,12 +68,6 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : return emptyList() } } - - null -> { - if (page > 1) { - return emptyList() - } - } } } @@ -162,7 +162,7 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/manga-list/").parseHtml() return doc.select("ul li a.category").mapNotNullToSet { a -> val name = a.text() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt index 612114b5..4539cfd5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt @@ -28,26 +28,33 @@ internal class MangaTownParser(context: MangaLoaderContext) : SortOrder.UPDATED, ) - override val availableStates: Set = EnumSet.of( - MangaState.ONGOING, - MangaState.FINISHED, - ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override val isMultipleTagsSupported = false + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + ), + availableContentRating = emptySet(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?name=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append("/directory/") append("0-") @@ -79,7 +86,7 @@ internal class MangaTownParser(context: MangaLoaderContext) : append(".htm") append( - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> "" SortOrder.UPDATED -> "?last_chapter_time.za" SortOrder.ALPHABETICAL -> "?name.az" @@ -88,8 +95,6 @@ internal class MangaTownParser(context: MangaLoaderContext) : }, ) } - - null -> append("/directory/$page.htm?last_chapter_time.za") } } val doc = webClient.httpGet(url).parseHtml() @@ -222,7 +227,7 @@ internal class MangaTownParser(context: MangaLoaderContext) : return doc.requireElementById("image").attrAsAbsoluteUrl("src") } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("/directory/".toAbsoluteUrl(domain)).parseHtml() val root = doc.body().selectFirst("aside.right") ?.getElementsContainingOwnText("Genres") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt index 92b3ea3c..c11bf064 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt @@ -25,32 +25,48 @@ internal class Mangaowl(context: MangaLoaderContext) : SortOrder.UPDATED, SortOrder.RATING, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) override val configKeyDomain = ConfigKey.Domain("mangaowl.to") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/10-search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append("/10-comics") append("?page=") @@ -74,7 +90,7 @@ internal class Mangaowl(context: MangaLoaderContext) : append("&ordering=") append( - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> "view_count" SortOrder.UPDATED -> "-modified_at" SortOrder.NEWEST -> "created_at" @@ -83,11 +99,6 @@ internal class Mangaowl(context: MangaLoaderContext) : }, ) } - - null -> { - append("/10-comics?ordering=-modified_at&page=") - append(page.toString()) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -110,7 +121,7 @@ internal class Mangaowl(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/10-genres").parseHtml() return doc.select("div.genres-container span.genre-item a").mapNotNullToSet { a -> val key = a.attr("href").removeSuffix('/').substringAfterLast('/').substringBefore("-") 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 11ebaebe..fafd8113 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 @@ -10,7 +10,7 @@ import org.koitharu.kotatsu.parsers.util.* import java.util.* @MangaSourceParser("MANHWA18", "Manhwa18.net", "en", type = ContentType.HENTAI) -class Manhwa18Parser(context: MangaLoaderContext) : +internal class Manhwa18Parser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANHWA18, pageSize = 18, searchPageSize = 18) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("manhwa18.net") @@ -29,15 +29,24 @@ class Manhwa18Parser(context: MangaLoaderContext) : SortOrder.RATING, ) - override val availableStates: Set = EnumSet.of( - MangaState.ONGOING, - MangaState.FINISHED, - MangaState.PAUSED, - ) + private val tagsMap = SuspendLazy(::parseTags) - override val isTagsExclusionSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - private val tagsMap = SuspendLazy(::parseTags) + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsMap.get().values.toSet(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.PAUSED, + ), + availableContentRating = emptySet(), + ) override suspend fun getFavicons(): Favicons { return Favicons( @@ -48,21 +57,20 @@ class Manhwa18Parser(context: MangaLoaderContext) : ) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/tim-kiem?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&q=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&accept_genres=") if (filter.tags.isNotEmpty()) { @@ -80,7 +88,7 @@ class Manhwa18Parser(context: MangaLoaderContext) : append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "az" SortOrder.ALPHABETICAL_DESC -> "za" SortOrder.POPULARITY -> "top" @@ -103,8 +111,6 @@ class Manhwa18Parser(context: MangaLoaderContext) : ) } } - - null -> append("&sort=update") } } @@ -221,10 +227,6 @@ class Manhwa18Parser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { - return tagsMap.get().values.toSet() - } - 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 da308af1..64ed63f2 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 @@ -11,7 +11,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("MANHWASMEN", "ManhwasMen", "en", type = ContentType.HENTAI) -class ManhwasMen(context: MangaLoaderContext) : +internal class ManhwasMen(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANHWASMEN, pageSize = 30, searchPageSize = 30) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("manhwas.men") @@ -21,28 +21,38 @@ class ManhwasMen(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val availableSortOrders: Set get() = EnumSet.of(SortOrder.POPULARITY) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = buildString { append("https://") append(domain) append("/manga-list") append("?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&search=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("&genero=") @@ -60,8 +70,6 @@ class ManhwasMen(context: MangaLoaderContext) : ) } } - - null -> {} } } val doc = webClient.httpGet(url).parseHtml() @@ -84,7 +92,7 @@ class ManhwasMen(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val tags = webClient.httpGet("https://$domain/manga-list").parseHtml() .selectLastOrThrow(".filter-bx .form-group select.custom-select").select("option").drop(1) return tags.mapNotNullToSet { option -> 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 90e478fa..a13cae0b 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,8 +1,8 @@ package org.koitharu.kotatsu.parsers.site.en import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -10,33 +10,31 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("PO2SCANS", "Po2Scans", "en") -internal class Po2Scans(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.PO2SCANS) { +internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.PO2SCANS) { override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) override val configKeyDomain = ConfigKey.Domain("po2scans.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - if (offset > 0) { - return emptyList() - } + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/series") - when (filter) { - is MangaListFilter.Search -> { - append("?search=") - append(filter.query.urlEncoded()) - } - - is MangaListFilter.Advanced -> {} - - null -> {} + if (!filter.query.isNullOrEmpty()) { + append("?search=") + append(filter.query.urlEncoded()) } } val doc = webClient.httpGet(url).parseHtml() @@ -59,8 +57,6 @@ internal class Po2Scans(context: MangaLoaderContext) : MangaParser(context, Mang } } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH) 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 4d141636..c91c0367 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 @@ -22,26 +22,33 @@ internal class Pururin(context: MangaLoaderContext) : override val configKeyDomain = ConfigKey.Domain("pururin.to") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append("/browse") filter.tags.oneOrThrowIfMany()?.let { @@ -54,7 +61,7 @@ internal class Pururin(context: MangaLoaderContext) : append(page) append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> append("") SortOrder.POPULARITY -> append("most-viewed") SortOrder.RATING -> append("highest-rated") @@ -62,11 +69,6 @@ internal class Pururin(context: MangaLoaderContext) : else -> append("") } } - - null -> { - append("/browse?page=") - append(page) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -89,7 +91,7 @@ internal class Pururin(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return coroutineScope { (1..4).map { page -> async { getTags(page) } 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 82e3d797..aab04fbd 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 @@ -11,7 +11,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("VYMANGA", "VyManga", "en") -class VyManga(context: MangaLoaderContext) : +internal class VyManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.VYMANGA, pageSize = 36) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("vymanga.net") @@ -21,8 +21,6 @@ class VyManga(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val availableSortOrders: Set = EnumSet.of( SortOrder.POPULARITY, SortOrder.POPULARITY_ASC, @@ -34,22 +32,30 @@ class VyManga(context: MangaLoaderContext) : SortOrder.UPDATED_ASC, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentRating = emptySet(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + 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) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isEmpty()) { @@ -84,7 +90,7 @@ class VyManga(context: MangaLoaderContext) : } append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("viewed&sort_type=desc") SortOrder.POPULARITY_ASC -> append("viewed&sort_type=asc") SortOrder.RATING -> append("scored&sort_type=desc") @@ -99,11 +105,6 @@ class VyManga(context: MangaLoaderContext) : append("&page=") append(page) } - - null -> { - append("/search?search_po=0&q=&author_po=0&author=&completed=2&sort=updated_at&sort_type=desc&page=") - append(page) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -126,7 +127,7 @@ class VyManga(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + 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 { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TempleScanEsp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TempleScanEsp.kt index 2312ff8f..ae3b7de2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TempleScanEsp.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TempleScanEsp.kt @@ -2,7 +2,6 @@ package org.koitharu.kotatsu.parsers.site.es import kotlinx.coroutines.coroutineScope import org.jsoup.nodes.Document -import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.PagedMangaParser @@ -27,32 +26,21 @@ internal class TempleScanEsp(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isSearchSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() + override suspend fun getFilterOptions() = MangaListFilterOptions() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { - throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) - } - - is MangaListFilter.Advanced -> { - if (filter.sortOrder == SortOrder.NEWEST) { - append("/comics?page=") - append(page.toString()) - } else { - if (page > 1) { - return emptyList() - } - } - } - - null -> { - append("/comics?page=") - append(page.toString()) + if (order == SortOrder.NEWEST) { + append("/comics?page=") + append(page) + } else { + if (page > 1) { + return emptyList() } } } @@ -79,8 +67,6 @@ internal class TempleScanEsp(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt index 7b3220fc..30ff75ce 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/es/TuMangaOnlineParser.kt @@ -14,7 +14,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("TUMANGAONLINE", "TuMangaOnline", "es") -class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( +internal class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( context, source = MangaParserSource.TUMANGAONLINE, pageSize = 24, @@ -29,8 +29,6 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( private val chapterDateFormat = SimpleDateFormat("yyyy-MM-dd", sourceLocale) - override val availableContentRating: Set = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT) - override val availableSortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL_DESC, @@ -40,22 +38,33 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( SortOrder.RATING, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableContentRating = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/library") - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("?title=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("?order_item=") append( - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> "likes_count&order_dir=desc" SortOrder.POPULARITY_ASC -> "likes_count&order_dir=asc" SortOrder.UPDATED -> "release_date&order_dir=desc" @@ -66,6 +75,7 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( SortOrder.ALPHABETICAL_DESC -> "alphabetically&order_dir=desc" SortOrder.RATING -> "score&order_dir=desc" SortOrder.RATING_ASC -> "score&order_dir=asc" + else -> "release_date&order_dir=desc" }, ) append("&filter_by=title") @@ -87,10 +97,6 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( ) } } - - null -> { - append("?order_item=release_date&order_dir=desc&filter_by=title") - } } append("&_pg=1&page=") append(page.toString()) @@ -286,7 +292,7 @@ class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaParser( } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/library", getRequestHeaders()).parseHtml() val elements = doc.body().select("div#books-genders > div > div") return elements.mapNotNullToSet { element -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt index 1bbdd9e4..a99b10c9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt @@ -35,18 +35,17 @@ internal abstract class FmreaderParser( SortOrder.ALPHABETICAL_DESC, ) - override val availableStates: Set = EnumSet.of( - MangaState.ONGOING, - MangaState.FINISHED, - MangaState.ABANDONED, - ) - - override val isTagsExclusionSupported = true - protected open val listUrl = "/manga-list.html" protected open val datePattern = "MMMM d, yyyy" protected open val tagPrefix = "manga-list-genre-" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + init { paginator.firstPage = 1 searchPaginator.firstPage = 1 @@ -72,20 +71,33 @@ internal abstract class FmreaderParser( "drop", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.ABANDONED, + ), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append(listUrl) append("?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&name=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&genre=") append(filter.tags.joinToString(",") { it.key }) @@ -95,7 +107,7 @@ internal abstract class FmreaderParser( append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views&sort_type=DESC") SortOrder.POPULARITY_ASC -> append("views&sort_type=ASC") SortOrder.UPDATED -> append("last_update&sort_type=DESC") @@ -118,8 +130,6 @@ internal abstract class FmreaderParser( } } - - null -> append("&sort=last_update") } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -150,7 +160,7 @@ internal abstract class FmreaderParser( protected open val selectBodyTag = "ul.filter-type li a" - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select(selectBodyTag).mapNotNullToSet { a -> val href = a.attr("href").substringAfter(tagPrefix).substringBeforeLast(".html") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt index f8afd14f..c3134873 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/en/Manhwa18Com.kt @@ -22,20 +22,20 @@ internal class Manhwa18Com(context: MangaLoaderContext) : override val selectPage = "div#chapter-content img" override val selectBodyTag = "div.advanced-wrapper .genre_label" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/tim-kiem?page=") append(page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&q=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&accept_genres=") append(filter.tags.joinToString(",") { it.key }) @@ -45,7 +45,7 @@ internal class Manhwa18Com(context: MangaLoaderContext) : append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "az" SortOrder.ALPHABETICAL_DESC -> "za" SortOrder.POPULARITY -> "top" @@ -68,14 +68,12 @@ internal class Manhwa18Com(context: MangaLoaderContext) : ) } } - - null -> append("&sort=update") } } return parseMangaList(webClient.httpGet(url).parseHtml()) } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select(selectBodyTag).mapNotNullToSet { label -> val key = label.attr("data-genre-id") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt index 7fa39583..547aed23 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/es/OlimpoScans.kt @@ -14,15 +14,19 @@ internal class OlimpoScans(context: MangaLoaderContext) : override val selectAlt = "ul.manga-info li:contains(Otros nombres)" override val selectTag = "ul.manga-info li:contains(Género) a" override val tagPrefix = "lista-de-comics-genero-" - override val isMultipleTagsSupported = false - override val isTagsExclusionSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + isTagsExclusionSupported = false, + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append(listUrl) append("?page=") append(page.toString()) @@ -30,7 +34,7 @@ internal class OlimpoScans(context: MangaLoaderContext) : append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/lista-de-comics-genero-") @@ -42,7 +46,7 @@ internal class OlimpoScans(context: MangaLoaderContext) : append("?page=") append(page.toString()) append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views&sort_type=DESC") SortOrder.POPULARITY_ASC -> append("views&sort_type=ASC") SortOrder.UPDATED -> append("last_update&sort_type=DESC") @@ -65,13 +69,6 @@ internal class OlimpoScans(context: MangaLoaderContext) : ) } } - - null -> { - append(listUrl) - append("?page=") - append(page.toString()) - append("&sort=last_update") - } } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt index f9b3ee0c..131401be 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt @@ -32,67 +32,55 @@ internal abstract class FoolSlideParser( protected open val pagination = true // false if the manga list has no pages protected open val datePattern = "yyyy.MM.dd" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val doc = - when (filter) { - is MangaListFilter.Search -> { - if (page > 1) { - return emptyList() - } + override suspend fun getFilterOptions() = MangaListFilterOptions() - val url = buildString { - append("https://") - append(domain) - append("/") - append(searchUrl) - } - - webClient.httpPost(url, "search=${filter.query.urlEncoded()}").parseHtml() + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val doc = when { + !filter.query.isNullOrEmpty() -> { + if (page > 1) { + return emptyList() } - is MangaListFilter.Advanced -> { - - val url = buildString { - append("https://") - append(domain) - append("/") - append(listUrl) - // For some sites that don't have enough manga and page 2 links to page 1 - if (!pagination) { - if (page > 1) { - return emptyList() - } - } else { - append(page.toString()) - } - } - webClient.httpGet(url).parseHtml() - + val url = buildString { + append("https://") + append(domain) + append("/") + append(searchUrl) } - null -> { - val url = buildString { - append("https://") - append(domain) - append("/") - append(listUrl) - if (!pagination) { - if (page > 1) { - return emptyList() - } - } else { - append(page.toString()) + webClient.httpPost(url, "search=${filter.query.urlEncoded()}").parseHtml() + } + + else -> { + + val url = buildString { + append("https://") + append(domain) + append("/") + append(listUrl) + // For some sites that don't have enough manga and page 2 links to page 1 + if (!pagination) { + if (page > 1) { + return emptyList() } + } else { + append(page.toString()) } - webClient.httpGet(url).parseHtml() - } + webClient.httpGet(url).parseHtml() + } + } return doc.select("div.list div.group").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") @@ -114,8 +102,6 @@ internal abstract class FoolSlideParser( } - override suspend fun getAvailableTags(): Set = emptySet() - protected open val selectInfo = "div.info" override suspend fun getDetails(manga: Manga): Manga = coroutineScope { 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 eb14491d..108e5b27 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 @@ -37,29 +37,37 @@ internal class BentomangaParser(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - - override val isTagsExclusionSupported: Boolean = true - init { paginator.firstPage = 0 searchPaginator.firstPage = 0 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = urlBuilder() .host(domain) .addPathSegment("manga_list") .addQueryParameter("limit", page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { url.addQueryParameter("search", filter.query) } - is MangaListFilter.Advanced -> { + else -> { - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> url.addQueryParameter("order_by", "update") .addQueryParameter("order", "desc") @@ -104,8 +112,6 @@ internal class BentomangaParser(context: MangaLoaderContext) : } } - - null -> url.addQueryParameter("order_by", "update") } val root = webClient.httpGet(url.build()).parseHtml().requireElementById("mangas_content") return root.select(".manga[data-manga]").map { div -> @@ -208,7 +214,7 @@ internal class BentomangaParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val root = webClient.httpGet(urlBuilder().addPathSegment("manga_list").build()) .parseHtml() .requireElementById("search_options-form") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt index c7d25ff8..dc6170a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt @@ -5,7 +5,7 @@ import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.network.UserAgents @@ -16,7 +16,7 @@ import java.util.* @MangaSourceParser("FURYOSOCIETY", "FuryoSociety", "fr") internal class FuryoSociety(context: MangaLoaderContext) : - PagedMangaParser(context, MangaParserSource.FURYOSOCIETY, 0) { + SinglePageMangaParser(context, MangaParserSource.FURYOSOCIETY) { override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL, SortOrder.UPDATED) @@ -24,13 +24,16 @@ internal class FuryoSociety(context: MangaLoaderContext) : override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isSearchSupported = false - override suspend fun getFavicons(): Favicons { return Favicons( listOf( @@ -40,27 +43,20 @@ internal class FuryoSociety(context: MangaLoaderContext) : ) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) { - return emptyList() - } - + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) } - is MangaListFilter.Advanced -> { - - if (filter.sortOrder == SortOrder.ALPHABETICAL) { + else -> { + if (order == SortOrder.ALPHABETICAL) { append("/mangas") } } - - null -> {} } } @@ -86,10 +82,6 @@ internal class FuryoSociety(context: MangaLoaderContext) : } } - - override suspend fun getAvailableTags(): Set = emptySet() - - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() @@ -101,7 +93,6 @@ internal class FuryoSociety(context: MangaLoaderContext) : ) } - private fun getChapters(doc: Document): List { return doc.body().select("div.list.fs-chapter-list div.element").mapChapters(reversed = true) { i, div -> val a = div.selectFirstOrThrow("div.title a") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt index 6d50790f..37eecc83 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -17,22 +17,31 @@ internal class LegacyScansParser(context: MangaLoaderContext) : override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED, MangaState.PAUSED) - override val configKeyDomain = ConfigKey.Domain("legacy-scans.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED, MangaState.PAUSED), + availableContentRating = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val end = page * pageSize val start = end - (pageSize - 1) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -43,7 +52,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) : return parseMangaListQuery(webClient.httpGet(url).parseJson()) } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://api.") append(domain) @@ -68,18 +77,6 @@ internal class LegacyScansParser(context: MangaLoaderContext) : } return parseMangaList(webClient.httpGet(url).parseJson()) } - - null -> { - val url = buildString { - append("https://api.") - append(domain) - append("/misc/comic/search/query?status=&order=&genreNames=&type=&start=") - append(start) - append("&end=") - append(end) - } - return parseMangaList(webClient.httpGet(url).parseJson()) - } } } @@ -175,7 +172,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/comics").parseHtml() val script = doc.requireElementById("__NUXT_DATA__").data() .substringAfterLast("\"genres\"").substringBeforeLast("\"comics\"") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt index 57ac1c51..50dbeb34 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt @@ -18,58 +18,50 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context, override val configKeyDomain = ConfigKey.Domain("lire-scan.me") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - val doc = - when (filter) { - is MangaListFilter.Search -> { - if (page > 1) { - return emptyList() - } - val q = filter.query.urlEncoded().replace("%20", "+") - val post = "do=search&subaction=search&search_start=0&full_search=0&result_from=1&story=$q" - webClient.httpPost("https://$domain/index.php?do=search", post).parseHtml() + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val doc = when { + !filter.query.isNullOrEmpty() -> { + if (page > 1) { + return emptyList() } + val q = filter.query.urlEncoded().replace("%20", "+") + val post = "do=search&subaction=search&search_start=0&full_search=0&result_from=1&story=$q" + webClient.httpPost("https://$domain/index.php?do=search", post).parseHtml() + } - is MangaListFilter.Advanced -> { - val url = buildString { - append("https://") - append(domain) - - filter.tags.oneOrThrowIfMany()?.let { - append("/manga/") - append(it.key) - } + else -> { + val url = buildString { + append("https://") + append(domain) - if (page > 1) { - append("/page/") - append(page) - append('/') - } + filter.tags.oneOrThrowIfMany()?.let { + append("/manga/") + append(it.key) } - webClient.httpGet(url).parseHtml() - } - null -> { - val url = buildString { - append("https://") - append(domain) - if (page > 1) { - append("/page/") - append(page) - append('/') - } + if (page > 1) { + append("/page/") + append(page) + append('/') } - webClient.httpGet(url).parseHtml() } + webClient.httpGet(url).parseHtml() } + } return doc.select("div.sect__content.grid-items div.item-poster").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") @@ -148,7 +140,7 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml() return doc.select(".nav-menu li a").mapNotNullToSet { a -> val key = a.attr("href").removeSuffix('/').substringAfterLast("manga/", "") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt index 4f4a4e12..69bf0150 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt @@ -23,15 +23,24 @@ internal class LugnicaScans(context: MangaLoaderContext) : SortOrder.UPDATED, ) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - - override val isSearchSupported = false - override val configKeyDomain = ConfigKey.Domain("lugnica-scans.com") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = emptySet(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -53,15 +62,15 @@ internal class LugnicaScans(context: MangaLoaderContext) : ) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.ALPHABETICAL) { + if (order == SortOrder.ALPHABETICAL) { if (page > 1) { return emptyList() } @@ -92,16 +101,6 @@ internal class LugnicaScans(context: MangaLoaderContext) : return parseMangaList(webClient.httpGet(url).parseJsonArray()) } } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/api/get/homegrid/") - append(page) - } - return parseMangaList(webClient.httpGet(url).parseJsonArray()) - } } } @@ -220,7 +219,4 @@ internal class LugnicaScans(context: MangaLoaderContext) : } return pages } - - override suspend fun getAvailableTags(): Set = emptySet() - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt index a773597e..8dda82ed 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt @@ -19,6 +19,15 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -28,28 +37,25 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte .add("Accept-Language", "fr") .build() - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?query=") append(filter.query.urlEncoded()) append("&search_type=manga&page=") append(page) } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.UPDATED && filter.tags.isNotEmpty()) { + if (order == SortOrder.UPDATED && filter.tags.isNotEmpty()) { throw IllegalArgumentException("Filtrer part tag n'est pas disponible avec le tri pas mis à jour") } - if (filter.sortOrder == SortOrder.ALPHABETICAL) { + if (order == SortOrder.ALPHABETICAL) { append("/manga-list") filter.tags.oneOrThrowIfMany()?.let { append("/category/") @@ -61,12 +67,6 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte return emptyList() } } - - null -> { - if (page > 1) { - return emptyList() - } - } } } @@ -161,7 +161,7 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/manga-list/").parseHtml() return doc.select("ul li a.category").mapNotNullToSet { a -> val name = a.text() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt index faba6a93..5f005b9c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt @@ -32,25 +32,30 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context SortOrder.NEWEST, ) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED) - override val configKeyDomain = ConfigKey.Domain("www.manga-mana.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED), + availableContentRating = emptySet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val postData = buildString { append("page=") append(page) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -88,9 +93,9 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.UPDATED) { + if (order == SortOrder.UPDATED) { if (filter.tags.isNotEmpty() or filter.states.isNotEmpty()) { throw IllegalArgumentException("Le filtrage par « tri par : mis à jour » avec les genres ou les statuts n'est pas pris en charge par cette source.") @@ -99,7 +104,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context val doc = webClient.httpGet("https://$domain/?page=$page").parseHtml() return doc.select("div.row div.col_home").map { div -> val href = div.selectFirstOrThrow("h4 a").attrAsRelativeUrl("href") - val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() ?: false + val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true val img = if (isNsfw) { div.selectFirst("img")?.attr("data-adult") } else { @@ -140,7 +145,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context } append("&sort_by=") - when (filter.sortOrder) { + when (order) { SortOrder.RATING -> append("score&sort_dir=desc") SortOrder.NEWEST -> append("updated_at&sort_dir=desc") SortOrder.ALPHABETICAL -> append("name&sort_dir=asc") @@ -149,8 +154,6 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context } } } - - null -> append("&sort_by=updated_at&sort_dir=desc") } } @@ -162,7 +165,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context return doc.select("div.p-2 div.col").map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") - val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() ?: false + val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true val img = if (isNsfw) { div.selectFirst("img")?.attr("data-adult") } else { @@ -323,7 +326,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context return pages } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/liste-mangas").parseHtml() return doc.select("select.selectpicker option").drop(1).mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt index 87e15c21..2014ba87 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt @@ -5,7 +5,7 @@ import org.json.JSONArray import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.network.UserAgents @@ -14,7 +14,7 @@ import java.util.* @MangaSourceParser("SCANS_MANGAS_ME", "ScansMangas.me", "fr") internal class ScansMangasMe(context: MangaLoaderContext) : - PagedMangaParser(context, MangaParserSource.SCANS_MANGAS_ME, 0) { + SinglePageMangaParser(context, MangaParserSource.SCANS_MANGAS_ME) { override val availableSortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, @@ -27,28 +27,32 @@ internal class ScansMangasMe(context: MangaLoaderContext) : override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) { - return emptyList() - } + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/?s=") append(filter.query.urlEncoded()) append("&post_type=manga") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { append("/genres/") filter.tags.oneOrThrowIfMany()?.let { @@ -56,7 +60,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) : } } else { append("/tous-nos-mangas/?order=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("popular") SortOrder.UPDATED -> append("update") SortOrder.ALPHABETICAL -> append("title") @@ -65,8 +69,6 @@ internal class ScansMangasMe(context: MangaLoaderContext) : } } } - - null -> append("/tous-nos-mangas/?order=update") } } @@ -91,7 +93,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/tous-nos-mangas/").parseHtml() return doc.select("ul.genre li").mapNotNullToSet { li -> val key = li.selectFirstOrThrow("a").attr("href").removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt index 482bed09..f9740c21 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt @@ -20,30 +20,37 @@ internal class ScantradUnion(context: MangaLoaderContext) : SortOrder.UPDATED, ) - override val isMultipleTagsSupported = false - override val configKeyDomain = ConfigKey.Domain("scantrad-union.com") override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = 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, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page.toString()) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/tag/") @@ -53,25 +60,19 @@ internal class ScantradUnion(context: MangaLoaderContext) : append("/") } } else { - if (filter.sortOrder == SortOrder.ALPHABETICAL) { + if (order == SortOrder.ALPHABETICAL) { append("/manga/page/") append(page.toString()) append("/") } - if (filter.sortOrder == SortOrder.UPDATED && page > 1) { + if (order == SortOrder.UPDATED && page > 1) { return emptyList() } } } - - null -> { - append("/manga/page/") - append(page.toString()) - append("/") - } } } val doc = webClient.httpGet(url).parseHtml() @@ -183,7 +184,7 @@ internal class ScantradUnion(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml() val body = doc.body() val list = body.select(".asp_gochosen")[1].select("option").orEmpty() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt index 4149a8c4..5a6a479c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt @@ -31,12 +31,6 @@ internal abstract class FuzzyDoodleParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.NEWEST) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - - override val isMultipleTagsSupported = true - - @JvmField protected val ongoing = scatterSetOf( "en cours", @@ -73,21 +67,33 @@ internal abstract class FuzzyDoodleParser( protected open val pausedValue = "haitus" protected open val abandonedValue = "dropped" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/manga?page=") append(page) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("&title=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&type=") append("&status=") @@ -110,8 +116,6 @@ internal abstract class FuzzyDoodleParser( append(it.key) } } - - null -> {} } } @@ -252,7 +256,7 @@ internal abstract class FuzzyDoodleParser( protected open val selectTagsList = "div.mt-1 div.items-center:has(label)" - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/manga").parseHtml() return doc.select(selectTagsList).mapNotNullToSet { val key = it.selectFirst("input")?.attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/ar/HentaiSlayer.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/ar/HentaiSlayer.kt index 91fb00e0..d3a3b3ed 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/ar/HentaiSlayer.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/ar/HentaiSlayer.kt @@ -6,14 +6,17 @@ import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.site.fuzzydoodle.FuzzyDoodleParser -import java.util.EnumSet +import java.util.* @MangaSourceParser("HENTAISLAYER", "HentaiSlayer", "ar", ContentType.HENTAI) internal class HentaiSlayer(context: MangaLoaderContext) : FuzzyDoodleParser(context, MangaParserSource.HENTAISLAYER, "hentaislayer.net") { - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED) + override val ongoingValue = "مستمر" override val finishedValue = "مكتمل" override val abandonedValue = "متوقف" + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED), + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/fr/LelScanVf.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/fr/LelScanVf.kt index 55b906f2..59664f46 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/fr/LelScanVf.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/fr/LelScanVf.kt @@ -5,12 +5,16 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.site.fuzzydoodle.FuzzyDoodleParser -import java.util.EnumSet +import java.util.* @MangaSourceParser("LELSCANVF", "LelScanFr", "fr") internal class LelScanVf(context: MangaLoaderContext) : FuzzyDoodleParser(context, MangaParserSource.LELSCANVF, "lelscanfr.com") { - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val ongoingValue = "en-cours" override val finishedValue = "termin" + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt index faf1bd26..24dfa5e2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt @@ -30,20 +30,47 @@ internal abstract class GalleryAdultsParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale.CHINESE, + Locale("es"), + Locale("ru"), + Locale("ko"), + Locale.GERMAN, + Locale("id"), + Locale.ITALIAN, + Locale("pt"), + Locale("tr"), + Locale("th"), + Locale("vi"), + ), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search/?q=") append(filter.query.urlEncoded()) append("&") } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() val lang = filter.locale if (tag != null && lang != null) { @@ -61,8 +88,6 @@ internal abstract class GalleryAdultsParser( append("/?") } } - - null -> append("/?") } append("page=") append(page) @@ -102,7 +127,7 @@ internal abstract class GalleryAdultsParser( //Tags are deliberately reduced because there are too many and this slows down the application. //only the most popular ones are taken. - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return coroutineScope { (1..3).map { page -> async { getTags(page) } @@ -110,23 +135,6 @@ internal abstract class GalleryAdultsParser( }.awaitAll().flattenTo(ArraySet(360)) } - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale.CHINESE, - Locale("es"), - Locale("ru"), - Locale("ko"), - Locale.GERMAN, - Locale("id"), - Locale.ITALIAN, - Locale("pt"), - Locale("tr"), - Locale("th"), - Locale("vi"), - ) - protected open val pathTagUrl = "/tags/popular/?page=" protected open val selectTags = ".tags_page ul.tags li" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/AsmHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/AsmHentai.kt index 4219efb2..84983689 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/AsmHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/AsmHentai.kt @@ -14,6 +14,7 @@ import java.util.* @MangaSourceParser("ASMHENTAI", "AsmHentai", type = ContentType.HENTAI) internal class AsmHentai(context: MangaLoaderContext) : GalleryAdultsParser(context, MangaParserSource.ASMHENTAI, "asmhentai.com") { + override val selectGallery = ".preview_item" override val selectGalleryLink = ".image a" override val selectGalleryImg = ".image img" @@ -21,11 +22,13 @@ internal class AsmHentai(context: MangaLoaderContext) : override val selectAuthor = "div.tags:contains(Artists:) .tag_list a span.tag" override val idImg = "fimg" - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.JAPANESE, - Locale.CHINESE, - Locale("tr"), + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.JAPANESE, + Locale.CHINESE, + Locale("tr"), + ), ) override fun Element.parseTags() = select("a").mapToSet { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/DoujinDesuUk.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/DoujinDesuUk.kt index 03c8ce2a..778184c5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/DoujinDesuUk.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/DoujinDesuUk.kt @@ -21,10 +21,12 @@ internal class DoujinDesuUk(context: MangaLoaderContext) : override val selectLanguageChapter = "div.tag-container:contains(Languages) a" override val idImg = "image-container" - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.JAPANESE, - Locale.CHINESE, + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.JAPANESE, + Locale.CHINESE, + ), ) override fun parseMangaList(doc: Document): List { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/Hentai3.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/Hentai3.kt index aed6ed58..69a57475 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/Hentai3.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/Hentai3.kt @@ -23,38 +23,43 @@ internal class Hentai3(context: MangaLoaderContext) : override val selectUrlChapter = "#main-cover a" override val idImg = ".js-main-img" - override val isMultipleTagsSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + ) override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale("es"), - Locale("ru"), - Locale.ITALIAN, - Locale("pt"), + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale("es"), + Locale("ru"), + Locale.ITALIAN, + Locale("pt"), + ), ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.size > 1 || (filter.tags.isNotEmpty() && filter.locale != null)) { append("/search?q=") append(buildQuery(filter.tags, filter.locale)) - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("&sort=popular") } append("&page=") @@ -64,7 +69,7 @@ internal class Hentai3(context: MangaLoaderContext) : append(filter.locale.toLanguagePath()) append("/") append(page.toString()) - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("?sort=popular") } } else if (filter.tags.isNotEmpty()) { @@ -74,7 +79,7 @@ internal class Hentai3(context: MangaLoaderContext) : } append("/") append(page.toString()) - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("?sort=popular") } } else { @@ -82,11 +87,6 @@ internal class Hentai3(context: MangaLoaderContext) : append(page) } } - - null -> { - append("/") - append(page) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt index d71a21f0..c1b64e56 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt @@ -24,18 +24,32 @@ internal class HentaiEnvy(context: MangaLoaderContext) : override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale.CHINESE, + Locale("es"), + Locale("ru"), + Locale("ko"), + Locale.GERMAN, + Locale("pt"), + ), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search/?s_key=") append(filter.query.urlEncoded()) append("&") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { if (filter.locale != null) { throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_LOCALE_GENRES_NOT_SUPPORTED) @@ -43,7 +57,7 @@ internal class HentaiEnvy(context: MangaLoaderContext) : filter.tags.oneOrThrowIfMany()?.let { append("/tag/") append(it.key) - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("/popular") } append("/?") @@ -56,8 +70,6 @@ internal class HentaiEnvy(context: MangaLoaderContext) : append("/?") } } - - null -> append("/?") } append("page=") append(page) @@ -65,16 +77,4 @@ internal class HentaiEnvy(context: MangaLoaderContext) : } return parseMangaList(webClient.httpGet(url).parseHtml()) } - - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale.CHINESE, - Locale("es"), - Locale("ru"), - Locale("ko"), - Locale.GERMAN, - Locale("pt"), - ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt index af2565c5..0394583a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt @@ -18,17 +18,21 @@ internal class HentaiEra(context: MangaLoaderContext) : override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override val isMultipleTagsSupported = true - + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + ) - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale("es"), - Locale("ru"), - Locale("ko"), - Locale.GERMAN, + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale("es"), + Locale("ru"), + Locale("ko"), + Locale.GERMAN, + ), ) override fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet { @@ -41,22 +45,22 @@ internal class HentaiEra(context: MangaLoaderContext) : ) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search/?key=") append(filter.query.urlEncoded()) append("&") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.size > 1 || (filter.tags.isNotEmpty() && filter.locale != null)) { append("/search/?key=") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append( buildQuery(filter.tags, filter.locale) .replace("<=1&dl=0&pp=0&tr=0", "<=0&dl=0&pp=1&tr=0"), @@ -72,7 +76,7 @@ internal class HentaiEra(context: MangaLoaderContext) : } append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } append("?") @@ -81,7 +85,7 @@ internal class HentaiEra(context: MangaLoaderContext) : append(filter.locale.toLanguagePath()) append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } append("?") @@ -89,8 +93,6 @@ internal class HentaiEra(context: MangaLoaderContext) : append("/?") } } - - null -> append("/?") } append("page=") append(page.toString()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt index 5b22bdac..5a39fb09 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt @@ -21,47 +21,54 @@ internal class HentaiForce(context: MangaLoaderContext) : override val selectLanguageChapter = "div.tag-container:contains(Languages:) a" override val idImg = ".gallery-reader-img-wrapper img" - override val isMultipleTagsSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + ) override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale.CHINESE, - Locale("es"), - Locale("ru"), - Locale("ko"), - Locale.GERMAN, - Locale("id"), - Locale.ITALIAN, - Locale("pt"), - Locale("th"), - Locale("vi"), - ) + override suspend fun getFilterOptions(): MangaListFilterOptions { + return super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale.CHINESE, + Locale("es"), + Locale("ru"), + Locale("ko"), + Locale.GERMAN, + Locale("id"), + Locale.ITALIAN, + Locale("pt"), + Locale("th"), + Locale("vi"), + ), + ) + } override suspend fun getPageUrl(page: MangaPage): String { val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml() return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found") } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.size > 1 || (filter.tags.isNotEmpty() && filter.locale != null)) { append("/search?q=") append(buildQuery(filter.tags, filter.locale)) - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("&sort=popular") } append("&page=") @@ -72,7 +79,7 @@ internal class HentaiForce(context: MangaLoaderContext) : } append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } append("?") @@ -81,7 +88,7 @@ internal class HentaiForce(context: MangaLoaderContext) : append(filter.locale.toLanguagePath()) append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } append("?") @@ -89,8 +96,6 @@ internal class HentaiForce(context: MangaLoaderContext) : append("/page/") } } - - null -> append("/page/") } append(page.toString()) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt index e61d9b90..60d5a62a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt @@ -18,16 +18,19 @@ internal class HentaiFox(context: MangaLoaderContext) : override val selectTag = "ul.tags" override val selectLanguageChapter = "ul.languages a.tag_btn" - override val isMultipleTagsSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + ) override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search/?q=") append(filter.query.urlEncoded()) if (page > 1) { @@ -36,7 +39,7 @@ internal class HentaiFox(context: MangaLoaderContext) : } } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.size > 1 || (filter.tags.isNotEmpty() && filter.locale != null)) { append("/search/?q=") append(buildQuery(filter.tags, filter.locale)) @@ -45,7 +48,7 @@ internal class HentaiFox(context: MangaLoaderContext) : append(page.toString()) } - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("&sort=popular") } } else if (filter.tags.isNotEmpty()) { @@ -54,7 +57,7 @@ internal class HentaiFox(context: MangaLoaderContext) : append(it.key) } append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } @@ -67,7 +70,7 @@ internal class HentaiFox(context: MangaLoaderContext) : append("/language/") append(filter.locale.toLanguagePath()) append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { + if (order == SortOrder.POPULARITY) { append("popular/") } @@ -88,18 +91,6 @@ internal class HentaiFox(context: MangaLoaderContext) : } } } - - null -> { - if (page > 2) { - append("/pag/") - append(page.toString()) - append("/") - } else if (page > 1) { - append("/page/") - append(page.toString()) - append("/") - } - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiRox.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiRox.kt index cbe1f5be..5cdd5c4e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiRox.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiRox.kt @@ -4,6 +4,7 @@ import org.jsoup.nodes.Element import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser @@ -20,15 +21,19 @@ internal class HentaiRox(context: MangaLoaderContext) : override val selectAuthor = "li:contains(Artists:) span.item_name" override val selectLanguageChapter = "li:contains(Languages:) .item_name" - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.FRENCH, - Locale.JAPANESE, - Locale("es"), - Locale("ru"), - Locale("ko"), - Locale.GERMAN, - ) + override suspend fun getFilterOptions(): MangaListFilterOptions { + return super.getFilterOptions().copy( + availableLocales = setOf( + Locale.ENGLISH, + Locale.FRENCH, + Locale.JAPANESE, + Locale("es"), + Locale("ru"), + Locale("ko"), + Locale.GERMAN, + ), + ) + } override fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet { val key = it.attr("href").removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt index 8ea0f31d..c06fa41f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt @@ -26,22 +26,28 @@ internal class NHentaiParser(context: MangaLoaderContext) : ".tag-container:contains(Languages:) span.tags a:not(.tag-17249) span.name" // tag-17249 = translated override val idImg = "image-container" - override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override val availableSortOrders: Set = + EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.POPULARITY_TODAY, SortOrder.POPULARITY_WEEK) - override val isMultipleTagsSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableLocales = setOf(Locale.ENGLISH, Locale.JAPANESE, Locale.CHINESE), + ) override fun getRequestHeaders(): Headers = super.getRequestHeaders().newBuilder() .set("User-Agent", config[userAgentKey]) .build() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { // Check if the query is all numbers val numericQuery = filter.query.trim() if (numericQuery.matches("\\d+".toRegex())) { @@ -56,49 +62,23 @@ internal class NHentaiParser(context: MangaLoaderContext) : } } - is MangaListFilter.Advanced -> { - if (filter.tags.size > 1 || (filter.tags.isNotEmpty() && filter.locale != null)) { - append("/search/?q=") - append(buildQuery(filter.tags, filter.locale).urlEncoded()) - if (filter.sortOrder == SortOrder.POPULARITY) { - append("&sort=popular") - } - append("&") - } else if (filter.tags.isNotEmpty()) { - filter.tags.oneOrThrowIfMany()?.let { - append("/tag/") - append(it.key) - } - append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { - append("popular") - } - if (page > 1) { - append("?") - } - } else if (filter.locale != null) { - append("/language/") - append(filter.locale.toLanguagePath()) - append("/") - if (filter.sortOrder == SortOrder.POPULARITY) { - append("popular") - } - if (page > 1) { - append("?") - } - } else { - if (filter.sortOrder == SortOrder.POPULARITY) { - append("/?sort=popular&") - } else { - append("/?") - } + else -> { + append("/search/?q=pages:>0 ") + // for Search with query + // append(filter.query.urlEncoded()) + // append(' ') + append(buildQuery(filter.tags, filter.locale).urlEncoded()) + when (order) { + SortOrder.POPULARITY -> append("&sort=popular") + SortOrder.POPULARITY_TODAY -> append("&sort=popular-today") + SortOrder.POPULARITY_WEEK -> append("&sort=popular-week") + SortOrder.UPDATED -> {} + else -> {} } } - - null -> append("/?") } if (page > 1) { - append("page=") + append("&page=") append(page.toString()) } } @@ -146,12 +126,6 @@ internal class NHentaiParser(context: MangaLoaderContext) : ) } - override suspend fun getAvailableLocales(): Set = setOf( - Locale.ENGLISH, - Locale.JAPANESE, - Locale.CHINESE, - ) - override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt index e3152db4..b0a08a80 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt @@ -25,25 +25,31 @@ internal abstract class GattsuParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - protected open val tagPrefix = "tag" + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + protected open val tagPrefix = "tag" + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page.toString()) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("/$tagPrefix/") @@ -54,11 +60,6 @@ internal abstract class GattsuParser( append(page.toString()) } - - null -> { - append("/page/") - append(page.toString()) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -92,7 +93,7 @@ internal abstract class GattsuParser( protected open val tagUrl = "generos" - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$tagUrl/").parseHtml() return doc.selectLastOrThrow(".meio-conteudo p, div.lista-tags ul").parseTags() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/pt/UniversoHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/pt/UniversoHentai.kt index a7a52ee8..950f19d4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/pt/UniversoHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/pt/UniversoHentai.kt @@ -13,7 +13,17 @@ internal class UniversoHentai(context: MangaLoaderContext) : override val tagPrefix = "category" - override suspend fun getAvailableTags(): Set { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/tags/").parseHtml() return doc.requireElementById("menu-topo").parseTags() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt index a507a961..6a90e50b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.parsers.site.guya import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -12,11 +12,17 @@ internal abstract class GuyaParser( context: MangaLoaderContext, source: MangaParserSource, domain: String, - pageSize: Int = 0, -) : PagedMangaParser(context, source, pageSize) { +) : SinglePageMangaParser(context, source) { override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -24,25 +30,17 @@ internal abstract class GuyaParser( override val configKeyDomain = ConfigKey.Domain(domain) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) return emptyList() + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/api/get_all_series/") } - when (filter) { - - is MangaListFilter.Search -> { - return parseMangaList(webClient.httpGet(url).parseJson(), filter.query) - } - - is MangaListFilter.Advanced -> {} - - null -> {} + return if (!filter.query.isNullOrEmpty()) { + parseMangaList(webClient.httpGet(url).parseJson(), filter.query) + } else { + parseMangaList(webClient.httpGet(url).parseJson(), "") } - - return parseMangaList(webClient.httpGet(url).parseJson(), "") } protected open fun parseMangaList(json: JSONObject, query: String): List { @@ -79,8 +77,6 @@ internal abstract class GuyaParser( ) } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { val json = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseJson().getJSONObject("chapters") val slug = manga.url.removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt index 7dec6b31..78b283f3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt @@ -39,27 +39,38 @@ internal abstract class HeanCms( SortOrder.POPULARITY_ASC, ) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - - protected open val pathManga = "series" protected open val apiPath get() = getDomain("api") protected open val paramsUpdated = "latest" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = emptySet(), + availableContentTypes = emptySet(), + availableDemographics = emptySet(), + availableLocales = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(apiPath) append("/query?query_string=&series_type=Comic&perPage=$pageSize") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.states.oneOrThrowIfMany()?.let { append("&status=") @@ -75,7 +86,7 @@ internal abstract class HeanCms( } append("&orderBy=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("total_views&order=desc") SortOrder.POPULARITY_ASC -> append("total_views&order=asc") SortOrder.UPDATED -> append("$paramsUpdated&order=desc") @@ -91,7 +102,6 @@ internal abstract class HeanCms( append(filter.tags.joinToString(",") { it.key }) append("]".urlEncoded()) } - null -> append("&status=All&orderBy=$paramsUpdated&order=desc") } append("&page=") append(page.toString()) @@ -180,7 +190,7 @@ internal abstract class HeanCms( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/comics").parseHtml() val regex = Regex("\"tags\\\\?\":\\s*\\[(.+?)]\\s*[},]") val tags = doc.select("script").joinToString("") { it.html() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt index 3ec1f299..c0dffcfb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt @@ -28,11 +28,12 @@ internal abstract class HeanCmsAlt( override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val isSearchSupported = false - protected open val listUrl = "/comics" protected open val datePattern = "MMMM d, yyyy" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() + init { paginator.firstPage = 1 searchPaginator.firstPage = 1 @@ -41,20 +42,15 @@ internal abstract class HeanCmsAlt( protected open val selectManga = "div.grid.grid-cols-2 div:not([class]):contains(M)" protected open val selectMangaTitle = "h5" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions() + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append(listUrl) - when (filter) { - is MangaListFilter.Search -> { - throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) - } - - is MangaListFilter.Advanced -> { - } - - null -> {} + if (!filter.query.isNullOrEmpty()) { + throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) } if (page > 1) { append("?page=") @@ -82,8 +78,6 @@ internal abstract class HeanCmsAlt( } } - override suspend fun getAvailableTags(): Set = emptySet() - protected open val selectDesc = "div.description-container" protected open val selectAlt = "div.series-alternative-names" protected open val selectChapter = "ul.MuiList-root a" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt index 460c52fd..35b29f9a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt @@ -31,17 +31,26 @@ internal abstract class HotComicsParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.NEWEST) - override val isMultipleTagsSupported = false - protected open val mangasUrl = "/genres" protected open val onePage = false + protected open val isSearchSupported: Boolean = true + + final override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = isSearchSupported, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun getRequestHeaders(): Headers = Headers.Builder() .add("User-Agent", UserAgents.CHROME_DESKTOP) .build() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { if (onePage && page > 1) { return emptyList() } @@ -49,16 +58,16 @@ internal abstract class HotComicsParser( val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search?keyword=") append(filter.query.urlEncoded()) append("&page=") append(page) } - is MangaListFilter.Advanced -> { + else -> { append(mangasUrl) filter.tags.oneOrThrowIfMany()?.let { append('/') @@ -70,11 +79,6 @@ internal abstract class HotComicsParser( append(page) } } - - null -> { - append("/genres?page=") - append(page) - } } } val tagMap = getOrCreateTagMap() @@ -174,7 +178,7 @@ internal abstract class HotComicsParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val map = getOrCreateTagMap() val tagSet = ArraySet(map.size) for (entry in map) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt index 1dc40e26..d711d39e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt @@ -11,7 +11,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("DOUJINDESU", "DoujinDesu.tv", "id") -class DoujinDesuParser(context: MangaLoaderContext) : +internal class DoujinDesuParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.DOUJINDESU, pageSize = 18) { override val configKeyDomain: ConfigKey.Domain @@ -25,30 +25,38 @@ class DoujinDesuParser(context: MangaLoaderContext) : override val availableSortOrders: Set get() = EnumSet.of(SortOrder.UPDATED, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.POPULARITY) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) override fun getRequestHeaders(): Headers = Headers.Builder() .add("X-Requested-With", "XMLHttpRequest") .add("Referer", "https://$domain/") .build() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = urlBuilder().apply { addPathSegment("manga") addPathSegment("page") addPathSegment("$page/") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { addQueryParameter("title", filter.query) } - is MangaListFilter.Advanced -> { + else -> { addQueryParameter("title", "") addQueryParameter( "order", - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "update" SortOrder.POPULARITY -> "popular" SortOrder.ALPHABETICAL -> "title" @@ -72,8 +80,6 @@ class DoujinDesuParser(context: MangaLoaderContext) : ) } } - - null -> addQueryParameter("order", "update") } }.build() @@ -160,7 +166,7 @@ class DoujinDesuParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return webClient.httpGet("/genre/".toAbsoluteUrl(domain)).parseHtml() .requireElementById("taxonomy") .selectFirstOrThrow(".entries") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt index 22f5ded2..2eb3fcd9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt @@ -17,27 +17,33 @@ internal class HentaiCrot(context: MangaLoaderContext) : ) override val configKeyDomain = ConfigKey.Domain("hentaicrot.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("/category/") @@ -49,12 +55,6 @@ internal class HentaiCrot(context: MangaLoaderContext) : append(page) append('/') } - - null -> { - append("/page/") - append(page) - append('/') - } } } @@ -78,7 +78,7 @@ internal class HentaiCrot(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.select("ul.megamenu li").mapNotNullToSet { li -> val key = li.selectFirstOrThrow("a").attr("href").removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt index 4980ad64..d968d04b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt @@ -17,27 +17,33 @@ internal class PixHentai(context: MangaLoaderContext) : ) override val configKeyDomain = ConfigKey.Domain("pixhentai.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("/genre/") @@ -49,12 +55,6 @@ internal class PixHentai(context: MangaLoaderContext) : append(page) append('/') } - - null -> { - append("/page/") - append(page) - append('/') - } } } @@ -78,7 +78,7 @@ internal class PixHentai(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.select("ul.megamenu li").mapNotNullToSet { li -> val key = li.selectFirstOrThrow("a").attr("href").removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt index bf4fc712..0e6c19fc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt @@ -28,25 +28,36 @@ internal abstract class IkenParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED, MangaState.UPCOMING) - - override val isMultipleTagsSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.ABANDONED, + MangaState.UPCOMING, + ), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/api/query?page=") append(page) append("&perPage=18&searchTerm=") - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { append("&genreIds=") @@ -66,8 +77,6 @@ internal abstract class IkenParser( ) } } - - null -> {} } } return parseMangaList(webClient.httpGet(url).parseJson()) @@ -145,7 +154,7 @@ internal abstract class IkenParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/series").parseHtml() return doc.selectLastOrThrow("select").select("option[value]").mapNotNullToSet { val key = it.attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt index 0d5e422c..53255806 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt @@ -1,6 +1,9 @@ package org.koitharu.kotatsu.parsers.site.ja -import org.koitharu.kotatsu.parsers.* +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaParser +import org.koitharu.kotatsu.parsers.MangaParserAuthProvider +import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.model.* @@ -12,12 +15,21 @@ private const val STATUS_ONGOING = "連載" private const val STATUS_FINISHED = "完結" @MangaSourceParser("NICOVIDEO_SEIGA", "NicoVideo Seiga", "ja") -class NicovideoSeigaParser(context: MangaLoaderContext) : +internal class NicovideoSeigaParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.NICOVIDEO_SEIGA), MangaParserAuthProvider { override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -41,36 +53,30 @@ class NicovideoSeigaParser(context: MangaLoaderContext) : SortOrder.POPULARITY, ) - override val isMultipleTagsSupported = false - override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("nicovideo.jp") - @InternalParsersApi - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val page = (offset / 20f).toIntUp().inc() val domain = getDomain("seiga") - val url = - when (filter) { - is MangaListFilter.Search -> { - return if (offset == 0) getSearchList(filter.query, page) else emptyList() - } - - is MangaListFilter.Advanced -> { + val url = when { + !filter.query.isNullOrEmpty() -> { + return if (offset == 0) getSearchList(filter.query, page) else emptyList() + } + else -> { - if (filter.tags.isNotEmpty()) { - filter.tags.oneOrThrowIfMany().let { - "https://$domain/manga/list?category=${it?.key}&page=$page&sort=${getSortKey(filter.sortOrder)}" - } - } else { - "https://$domain/manga/list?page=$page&sort=${getSortKey(filter.sortOrder)}" + if (filter.tags.isNotEmpty()) { + filter.tags.oneOrThrowIfMany().let { + "https://$domain/manga/list?category=${it?.key}&page=$page&sort=${getSortKey(order)}" } + } else { + "https://$domain/manga/list?page=$page&sort=${getSortKey(order)}" } - null -> "https://$domain/manga/list?page=$page" } + } val doc = webClient.httpGet(url).parseHtml() val comicList = doc.body().select("#comic_list > ul > li") ?: doc.parseFailed("Container not found") @@ -143,7 +149,7 @@ class NicovideoSeigaParser(context: MangaLoaderContext) : override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(getDomain("seiga")) val doc = webClient.httpGet(fullUrl).parseHtml() - if (!doc.select("#login_manga").isEmpty()) + if (!doc.select("#login_manga").isEmpty) throw AuthRequiredException(source) val root = doc.body().select("#page_contents > li") return root.map { li -> @@ -157,7 +163,7 @@ class NicovideoSeigaParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://${getDomain("seiga")}/manga/list").parseHtml() val root = doc.body().selectOrThrow("#mg_category_list > ul > li").drop(1) return root.mapToSet { li -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt index 95bbece8..ab7ffc3e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.coroutineScope import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -17,8 +17,7 @@ internal abstract class KeyoappParser( context: MangaLoaderContext, source: MangaParserSource, domain: String, - pageSize: Int = 24, -) : PagedMangaParser(context, source, pageSize) { +) : SinglePageMangaParser(context, source) { override val configKeyDomain = ConfigKey.Domain(domain) @@ -27,8 +26,6 @@ internal abstract class KeyoappParser( keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val availableSortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.NEWEST, @@ -58,29 +55,28 @@ internal abstract class KeyoappParser( "dropped", ) - init { - paginator.firstPage = 1 - searchPaginator.firstPage = 1 - } + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { var query = "" var tag = "" - if (page > 1) { - return emptyList() - } - val url = urlBuilder().apply { - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { addPathSegment("series") query = filter.query } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -88,15 +84,13 @@ internal abstract class KeyoappParser( } } - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> addPathSegment("latest") SortOrder.NEWEST -> addPathSegment("series") else -> addPathSegment("latest") } } - - null -> addPathSegment("latest") } }.build() @@ -172,7 +166,7 @@ internal abstract class KeyoappParser( } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.requireElementById("series_tags_page").select("button").mapNotNullToSet { button -> val key = button.attr("tag") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt index 8ea7aae5..4b9d4e5d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt @@ -32,30 +32,35 @@ internal abstract class LikeMangaParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override val isMultipleTagsSupported = false + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/?act=search") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&f") append("[keyword]".urlEncoded()) append("=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("&f") append("[sortby]".urlEncoded()) append("=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("hot") SortOrder.UPDATED -> append("lastest-chap") SortOrder.NEWEST -> append("lastest-manga") @@ -85,12 +90,6 @@ internal abstract class LikeMangaParser( ) } } - - null -> { - append("&f") - append("[sortby]".urlEncoded()) - append("=lastest-chap") - } } if (page > 1) { @@ -119,7 +118,7 @@ internal abstract class LikeMangaParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/genres/").parseHtml() return doc.select("ul.nav-genres li:not(.text-center) a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt index 9c4bda49..a2fac726 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt @@ -32,6 +32,45 @@ internal abstract class MadaraParser( keys.add(userAgentKey) } + // Change these values only if the site does not support manga listings via ajax + protected open val withoutAjax = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = !withoutAjax, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + availableContentRating = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT), + ) + + override val availableSortOrders: Set = setupAvailableSortOrders() + + private fun setupAvailableSortOrders(): Set { + return if(!withoutAjax) + { + EnumSet.of( + SortOrder.UPDATED, + SortOrder.UPDATED_ASC, + SortOrder.POPULARITY, + SortOrder.POPULARITY_ASC, + SortOrder.NEWEST, + SortOrder.NEWEST_ASC, + SortOrder.ALPHABETICAL, + SortOrder.ALPHABETICAL_DESC, + SortOrder.RATING, + SortOrder.RATING_ASC, + ) + }else + { + EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) + } + } + override val authUrl: String get() = "https://${domain}" @@ -54,27 +93,6 @@ internal abstract class MadaraParser( } } - override val isMultipleTagsSupported = true - - override val availableSortOrders: Set = EnumSet.of( - SortOrder.UPDATED, - SortOrder.UPDATED_ASC, - SortOrder.POPULARITY, - SortOrder.POPULARITY_ASC, - SortOrder.NEWEST, - SortOrder.NEWEST_ASC, - SortOrder.ALPHABETICAL, - SortOrder.ALPHABETICAL_DESC, - SortOrder.RATING, - SortOrder.RATING_ASC, - ) - - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - - override val availableContentRating: Set = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT) - - override val isTagsExclusionSupported = true - protected open val tagPrefix = "manga-genre/" protected open val datePattern = "MMMM d, yyyy" protected open val stylePage = "?style=list" @@ -190,13 +208,10 @@ internal abstract class MadaraParser( "à venir", ) - // Change these values only if the site does not support manga listings via ajax - protected open val withoutAjax = false - // can be changed to retrieve tags see getTags protected open val listUrl = "manga/" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { if (withoutAjax) { val pages = page + 1 @@ -204,9 +219,9 @@ internal abstract class MadaraParser( append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { if (pages > 1) { append("/page/") append(pages.toString()) @@ -216,7 +231,7 @@ internal abstract class MadaraParser( append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { if (pages > 1) { append("/page/") append(pages.toString()) @@ -278,7 +293,7 @@ internal abstract class MadaraParser( append("&m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -288,10 +303,6 @@ internal abstract class MadaraParser( else -> append("latest") } } - - null -> { - append("/?s=&post_type=wp-manga&m_orderby=latest") - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -301,13 +312,13 @@ internal abstract class MadaraParser( payload["page"] = page.toString() - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { payload["vars[s]"] = filter.query.urlEncoded() } - is MangaListFilter.Advanced -> { + else -> { // Support query @@ -362,7 +373,7 @@ internal abstract class MadaraParser( payload["vars[tax_query][relation]"] = "AND" } - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> { payload["vars[meta_key]"] = "_wp_manga_views" payload["vars[orderby]"] = "meta_value_num" @@ -453,10 +464,6 @@ internal abstract class MadaraParser( } } } - - null -> { - payload["vars[meta_key]"] = "_latest_update" - } } return parseMangaList( @@ -510,7 +517,7 @@ internal abstract class MadaraParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val body = doc.body() val root1 = body.selectFirst("header")?.selectFirst("ul.second-menu") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt index 757a6590..1050bd89 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manga18Fx.kt @@ -25,24 +25,24 @@ internal class Manga18Fx(context: MangaLoaderContext) : searchPaginator.firstPage = 1 } - override val availableContentRating: Set = emptySet() - - override val availableStates: Set get() = emptySet() - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -59,13 +59,6 @@ internal class Manga18Fx(context: MangaLoaderContext) : } } } - - null -> { - if (page > 1) { - append("/page/") - append(page) - } - } } } val doc = webClient.httpGet(url).parseHtml() @@ -89,7 +82,7 @@ internal class Manga18Fx(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val list = doc.body().selectFirstOrThrow("div.genre-menu").select("ul li").orEmpty() val keySet = HashSet(list.size) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt index 1090b618..f31b9dc6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Manhwa18Cc.kt @@ -15,9 +15,6 @@ internal class Manhwa18Cc(context: MangaLoaderContext) : override val listUrl = "webtoons/" override val tagPrefix = "webtoon-genre/" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override val selectTestAsync = "ul.row-content-chapter" override val selectDate = "span.chapter-time" override val selectChapter = "li.a-h" @@ -28,24 +25,25 @@ internal class Manhwa18Cc(context: MangaLoaderContext) : searchPaginator.firstPage = 1 } - override val availableContentRating: Set = emptySet() + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) - override val availableStates: Set get() = emptySet() - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -60,7 +58,7 @@ internal class Manhwa18Cc(context: MangaLoaderContext) : } append("?orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("trending") SortOrder.UPDATED -> append("latest") SortOrder.ALPHABETICAL -> append("alphabet") @@ -68,10 +66,6 @@ internal class Manhwa18Cc(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("?s&post_type=wp-manga&m_orderby=latest") - } } } val doc = webClient.httpGet(url).parseHtml() @@ -94,7 +88,7 @@ internal class Manhwa18Cc(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val list = doc.body().selectFirstOrThrow("div.sub-menu").select("ul li").orEmpty() val keySet = HashSet(list.size) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/GateManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/GateManga.kt index acdec391..7dc2cd10 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/GateManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/GateManga.kt @@ -3,9 +3,7 @@ package org.koitharu.kotatsu.parsers.site.madara.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("GATEMANGA", "GateManga", "ar") internal class GateManga(context: MangaLoaderContext) : @@ -14,8 +12,4 @@ internal class GateManga(context: MangaLoaderContext) : override val datePattern = "d MMMM، yyyy" override val listUrl = "ar/" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt index e2cc5c71..ece785f8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt @@ -1,10 +1,6 @@ package org.koitharu.kotatsu.parsers.site.madara.en -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import okhttp3.Headers import okhttp3.Request import okhttp3.RequestBody @@ -16,175 +12,162 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("ADULT_WEBTOON", "AdultWebtoon", "en", ContentType.HENTAI) internal class AdultWebtoon(context: MangaLoaderContext) : - MadaraParser(context, MangaParserSource.ADULT_WEBTOON, "adultwebtoon.com") { - override val tagPrefix = "adult-webtoon-genre/" - override val listUrl = "adult-webtoon/" - override val postReq = true - override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - - override val availableStates: Set = emptySet() - - override val availableContentRating: Set = emptySet() - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val pages = page + 1 - - val url = buildString { - append("https://") - append(domain) - when (filter) { - is MangaListFilter.Search -> { - if (pages > 1) { - append("/page/") - append(pages.toString()) - } - append("/?s=") - append(filter.query.urlEncoded()) - append("&post_type=wp-manga") - } - - is MangaListFilter.Advanced -> { - - if (filter.tags.isNotEmpty()) { - filter.tags.oneOrThrowIfMany()?.let { - append('/') - append(tagPrefix) - append(it.key) - append('/') - } - } else { - append('/') - append(listUrl) - } - - if (pages > 1) { - append("page/") - append(pages) - append('/') - } - - append("?m_orderby=") - when (filter.sortOrder) { - SortOrder.POPULARITY -> append("views") - SortOrder.UPDATED -> append("latest") - SortOrder.NEWEST -> append("new-manga") - SortOrder.ALPHABETICAL -> append("alphabet") - SortOrder.RATING -> append("rating") - else -> append("latest") - } - } - - null -> { - append('/') - append(listUrl) - if (pages > 1) { - append("page/") - append(pages) - append("/?m_orderby=latest") - } - } - } - } - return parseMangaList(webClient.httpGet(url).parseHtml()) - } - - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { - val fullUrl = manga.url.toAbsoluteUrl(domain) - val doc = webClient.httpGet(fullUrl).parseHtml() - val body = doc.body() - val chaptersDeferred = async { loadChapters(manga.url, doc) } - val desc = body.select(selectDesc).html() - val stateDiv = if (selectState.isEmpty()) { - body.selectFirst("div.post-content_item:contains(Status)")?.selectLast("div.summary-content") - } else { - body.selectFirst(selectState) - } - - - val state = stateDiv?.let { - when (it.text()) { - in ongoing -> MangaState.ONGOING - in finished -> MangaState.FINISHED - in abandoned -> MangaState.ABANDONED - in paused -> MangaState.PAUSED - else -> null - } - } - - val alt = - doc.body().select(".post-content_item:contains(Alt) .summary-content").firstOrNull()?.tableValue()?.text() - ?.trim() - - manga.copy( - tags = doc.body().select(selectGenre).mapNotNullToSet { a -> - MangaTag( - key = a.attr("href").removeSuffix("/").substringAfterLast('/'), - title = a.text().toTitleCase(), - source = source, - ) - }, - description = desc, - altTitle = alt, - state = state, - chapters = chaptersDeferred.await(), - ) - } - - override suspend fun loadChapters(mangaUrl: String, document: Document): List { - val mangaId = document.select("div#manga-chapters-holder").attr("data-id") - val url = "https://$domain/wp-admin/admin-ajax.php" - val postData = "post_id=$mangaId&action=ajax_chap" - val headers = Headers.Builder().add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").build() - val doc = makeRequest(url, postData.toRequestBody(), headers) - val dateFormat = SimpleDateFormat(datePattern, sourceLocale) - return doc.select(selectChapter).mapChapters(reversed = true) { i, li -> - val a = li.selectFirst("a") - val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing") - val link = href + stylePage - val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text() - val name = a.selectFirst("p")?.text() ?: a.ownText() - MangaChapter( - id = generateUid(href), - url = link, - name = name, - number = i + 1f, - volume = 0, - branch = null, - uploadDate = parseChapterDate( - dateFormat, - dateText, - ), - scanlator = null, - source = source, - ) - } - } - - private suspend fun makeRequest(url: String, payload: RequestBody, headers: Headers): Document { - var retryCount = 0 - val backoffDelay = 2000L // Initial delay (milliseconds) - val request = Request.Builder().url(url).post(payload).headers(headers).build() - while (true) { - try { - return context.httpClient.newCall(request).execute().parseHtml() - - } catch (e: Exception) { - // Log or handle the exception as needed - if (++retryCount <= 5) { - withContext(Dispatchers.Default) { - delay(backoffDelay) - } - } else { - throw e - } - } - } - } + MadaraParser(context, MangaParserSource.ADULT_WEBTOON, "adultwebtoon.com") { + override val tagPrefix = "adult-webtoon-genre/" + override val listUrl = "adult-webtoon/" + override val postReq = true + override val withoutAjax = true + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val pages = page + 1 + + val url = buildString { + append("https://") + append(domain) + when { + !filter.query.isNullOrEmpty() -> { + if (pages > 1) { + append("/page/") + append(pages.toString()) + } + append("/?s=") + append(filter.query.urlEncoded()) + append("&post_type=wp-manga") + } + + else -> { + + if (filter.tags.isNotEmpty()) { + filter.tags.oneOrThrowIfMany()?.let { + append('/') + append(tagPrefix) + append(it.key) + append('/') + } + } else { + append('/') + append(listUrl) + } + + if (pages > 1) { + append("page/") + append(pages) + append('/') + } + + append("?m_orderby=") + when (order) { + SortOrder.POPULARITY -> append("views") + SortOrder.UPDATED -> append("latest") + SortOrder.NEWEST -> append("new-manga") + SortOrder.ALPHABETICAL -> append("alphabet") + SortOrder.RATING -> append("rating") + else -> append("latest") + } + } + } + } + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + val fullUrl = manga.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + val body = doc.body() + val chaptersDeferred = async { loadChapters(manga.url, doc) } + val desc = body.select(selectDesc).html() + val stateDiv = if (selectState.isEmpty()) { + body.selectFirst("div.post-content_item:contains(Status)")?.selectLast("div.summary-content") + } else { + body.selectFirst(selectState) + } + + + val state = stateDiv?.let { + when (it.text()) { + in ongoing -> MangaState.ONGOING + in finished -> MangaState.FINISHED + in abandoned -> MangaState.ABANDONED + in paused -> MangaState.PAUSED + else -> null + } + } + + val alt = + doc.body().select(".post-content_item:contains(Alt) .summary-content").firstOrNull()?.tableValue()?.text() + ?.trim() + + manga.copy( + tags = doc.body().select(selectGenre).mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast('/'), + title = a.text().toTitleCase(), + source = source, + ) + }, + description = desc, + altTitle = alt, + state = state, + chapters = chaptersDeferred.await(), + ) + } + + override suspend fun loadChapters(mangaUrl: String, document: Document): List { + val mangaId = document.select("div#manga-chapters-holder").attr("data-id") + val url = "https://$domain/wp-admin/admin-ajax.php" + val postData = "post_id=$mangaId&action=ajax_chap" + val headers = Headers.Builder().add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").build() + val doc = makeRequest(url, postData.toRequestBody(), headers) + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + return doc.select(selectChapter).mapChapters(reversed = true) { i, li -> + val a = li.selectFirst("a") + val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing") + val link = href + stylePage + val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text() + val name = a.selectFirst("p")?.text() ?: a.ownText() + MangaChapter( + id = generateUid(href), + url = link, + name = name, + number = i + 1f, + volume = 0, + branch = null, + uploadDate = parseChapterDate( + dateFormat, + dateText, + ), + scanlator = null, + source = source, + ) + } + } + + private suspend fun makeRequest(url: String, payload: RequestBody, headers: Headers): Document { + var retryCount = 0 + val backoffDelay = 2000L // Initial delay (milliseconds) + val request = Request.Builder().url(url).post(payload).headers(headers).build() + while (true) { + try { + return context.httpClient.newCall(request).execute().parseHtml() + + } catch (e: Exception) { + // Log or handle the exception as needed + if (++retryCount <= 5) { + withContext(Dispatchers.Default) { + delay(backoffDelay) + } + } else { + throw e + } + } + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/BestManhuaCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/BestManhuaCom.kt index d6d2096c..e07045bc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/BestManhuaCom.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/BestManhuaCom.kt @@ -3,16 +3,10 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("BESTMANHUACOM", "BestManhua.com", "en") internal class BestManhuaCom(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.BESTMANHUACOM, "bestmanhua.com", 10) { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FireScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FireScans.kt index 25ba1598..2b8ccbd0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FireScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/FireScans.kt @@ -3,7 +3,10 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.model.MangaState +import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* @@ -52,7 +55,7 @@ internal class FireScans(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/?s=&post_type=wp-manga").parseHtml() return doc.select("form.search-advanced-form div.form-group div.checkbox ").mapNotNullToSet { div -> val key = div.selectFirst("input")?.attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai3z.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai3z.kt index 9f32f48e..d295454c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai3z.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai3z.kt @@ -5,17 +5,11 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken // Redirect to @hentai20 @MangaSourceParser("HENTAI3Z", "Hentai3z", "en", ContentType.HENTAI) internal class Hentai3z(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.HENTAI3Z, "manga18h.xyz", pageSize = 20) { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai4Free.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai4Free.kt index fc47770e..afe8d9b2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai4Free.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentai4Free.kt @@ -6,7 +6,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.EnumSet @MangaSourceParser("HENTAI_4FREE", "Hentai4Free", "en", ContentType.HENTAI) internal class Hentai4Free(context: MangaLoaderContext) : @@ -15,9 +14,6 @@ internal class Hentai4Free(context: MangaLoaderContext) : override val tagPrefix = "hentai-tag/" override val listUrl = "" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override val datePattern = "MMMM dd, yyyy" override val selectGenre = "div.tags-content a" @@ -26,12 +22,12 @@ internal class Hentai4Free(context: MangaLoaderContext) : searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page.toString()) append("/?s=") @@ -39,7 +35,7 @@ internal class Hentai4Free(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -85,7 +81,7 @@ internal class Hentai4Free(context: MangaLoaderContext) : } append("m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -96,14 +92,6 @@ internal class Hentai4Free(context: MangaLoaderContext) : } - - null -> { - if (page > 1) { - append("/page/") - append(page.toString()) - } - append("/?m_orderby=latest") - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt index 637b06e8..0273dc49 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt @@ -1,10 +1,6 @@ package org.koitharu.kotatsu.parsers.site.madara.en -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import okhttp3.Headers import okhttp3.Request import okhttp3.RequestBody @@ -16,27 +12,26 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("HENTAIMANGA", "HentaiManga", "en", ContentType.HENTAI) internal class HentaiManga(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.HENTAIMANGA, "hentaimanga.me", 36) { override val postReq = true override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val pages = page + 1 val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (pages > 1) { append("/page/") append(pages.toString()) @@ -46,7 +41,7 @@ internal class HentaiManga(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -67,7 +62,7 @@ internal class HentaiManga(context: MangaLoaderContext) : } append("?m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -76,16 +71,6 @@ internal class HentaiManga(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append('/') - append(listUrl) - if (pages > 1) { - append("page/") - append(pages) - append("/?m_orderby=latest") - } - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt index 4848a5d1..97592467 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt @@ -1,10 +1,6 @@ package org.koitharu.kotatsu.parsers.site.madara.en -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import okhttp3.Headers import okhttp3.Request import okhttp3.RequestBody @@ -16,27 +12,26 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("HENTAIWEBTOON", "HentaiWebtoon", "en", ContentType.HENTAI) internal class HentaiWebtoon(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.HENTAIWEBTOON, "hentaiwebtoon.com") { override val postReq = true override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableContentRating = emptySet(), + availableStates = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val pages = page + 1 val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (pages > 1) { append("/page/") append(pages.toString()) @@ -46,7 +41,7 @@ internal class HentaiWebtoon(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -67,7 +62,7 @@ internal class HentaiWebtoon(context: MangaLoaderContext) : } append("?m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -76,16 +71,6 @@ internal class HentaiWebtoon(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append('/') - append(listUrl) - if (pages > 1) { - append("page/") - append(pages) - append("/?m_orderby=latest") - } - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt index abd9b902..0d853c4b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt @@ -20,21 +20,23 @@ internal class IsekaiScan(context: MangaLoaderContext) : SortOrder.POPULARITY, SortOrder.UPDATED, ) - override val availableContentRating: Set = emptySet() - override val availableStates: Set = emptySet() init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/?search=") append(filter.query.urlEncoded()) append("&page=") @@ -42,14 +44,14 @@ internal class IsekaiScan(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { append("/$tagPrefix") append(tag?.key.orEmpty()) append("?orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("2") SortOrder.UPDATED -> append("3") else -> append("3") @@ -57,7 +59,7 @@ internal class IsekaiScan(context: MangaLoaderContext) : append("&page=") append(page.toString()) } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("/popular-manga") SortOrder.UPDATED -> append("/latest-manga") else -> append("/latest-manga") @@ -66,11 +68,6 @@ internal class IsekaiScan(context: MangaLoaderContext) : append(page.toString()) } } - - null -> { - append("/latest-manga?page=") - append(page.toString()) - } } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt index 157c3c9b..60672113 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt @@ -5,7 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.EnumSet @MangaSourceParser("ISEKAISCAN_EU", "ParagonScans", "en") internal class IsekaiScanEuParser(context: MangaLoaderContext) : @@ -13,9 +12,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) : override val datePattern = "MM/dd/yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) + override val listUrl = "mangax/" init { @@ -23,15 +20,14 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) : searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { if (page > 1) { append("/page/") append(page.toString()) @@ -41,7 +37,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -84,7 +80,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) : } append("m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -93,10 +89,6 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("/?s&post_type=wp-manga&m_orderby=latest") - } } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt index a271ffac..8f96c34d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga1k.kt @@ -5,16 +5,11 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("MANGA1K", "Manga1k", "en", ContentType.HENTAI) internal class Manga1k(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANGA1K, "manga1k.com", 20) { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga68.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga68.kt index 88c01292..d2314a72 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga68.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manga68.kt @@ -4,16 +4,11 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("MANGA68", "Manga68", "en") internal class Manga68(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANGA68, "manga68.com") { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt index c4653fa3..fad44766 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt @@ -9,7 +9,6 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("MANGADASS", "MangaDass", "en", ContentType.HENTAI) internal class MangaDass(context: MangaLoaderContext) : @@ -17,35 +16,32 @@ internal class MangaDass(context: MangaLoaderContext) : override val datePattern = "dd MMM yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override val selectChapter = "li.a-h" override val selectDesc = "div.ss-manga" - override val availableStates: Set get() = emptySet() - - override val availableContentRating: Set = emptySet() - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -62,7 +58,7 @@ internal class MangaDass(context: MangaLoaderContext) : } append("orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -71,13 +67,6 @@ internal class MangaDass(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("/$listUrl") - append("/") - append(page.toString()) - append("?orderby=latest") - } } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt index 502fe325..83254e9b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt @@ -9,7 +9,6 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("MANGADNA", "MangaDna", "en", ContentType.HENTAI) internal class MangaDna(context: MangaLoaderContext) : @@ -17,27 +16,27 @@ internal class MangaDna(context: MangaLoaderContext) : override val datePattern = "dd MMM yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override val selectDesc = "div.dsct" override val selectChapter = "li.a-h" - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -52,7 +51,7 @@ internal class MangaDna(context: MangaLoaderContext) : } append("?orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("trending") SortOrder.UPDATED -> append("latest") SortOrder.ALPHABETICAL -> append("alphabet") @@ -60,13 +59,6 @@ internal class MangaDna(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("/$listUrl") - append("/page/") - append(page.toString()) - append("?orderby=latest") - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEffect.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEffect.kt index db600803..e68f4f1e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEffect.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaEffect.kt @@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("MANGAEFFECT", "MangaEffect", "en") @@ -14,7 +12,4 @@ internal class MangaEffect(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANGAEFFECT, "mangaeffect.com") { override val datePattern = "dd.MM.yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaFastNet.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaFastNet.kt index 2091babf..6bb12037 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaFastNet.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaFastNet.kt @@ -3,15 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("MANGAFASTNET", "MangaFast.net", "en") internal class MangaFastNet(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANGAFASTNET, "manhuafast.net") { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) + } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt index f1e8a37c..f0207d7b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt @@ -23,21 +23,24 @@ internal class MangaPure(context: MangaLoaderContext) : SortOrder.UPDATED, ) - override val availableStates: Set = emptySet() - - override val availableContentRating: Set = emptySet() - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions(): MangaListFilterOptions { + return super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + } + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?s=") append(filter.query.urlEncoded()) append("&page=") @@ -45,14 +48,14 @@ internal class MangaPure(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { append("/$tagPrefix") append(tag?.key.orEmpty()) append("?orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("2") SortOrder.UPDATED -> append("3") else -> append("3") @@ -60,7 +63,7 @@ internal class MangaPure(context: MangaLoaderContext) : append("&page=") append(page.toString()) } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("/popular-manga") SortOrder.UPDATED -> append("/latest-manga") else -> append("/latest-manga") @@ -69,11 +72,6 @@ internal class MangaPure(context: MangaLoaderContext) : append(page.toString()) } } - - null -> { - append("/latest-manga?page=") - append(page.toString()) - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangaus.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangaus.kt index fa657873..f5debf78 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangaus.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Mangaus.kt @@ -4,16 +4,11 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("MANGAUS", "Mangaus", "en") internal class Mangaus(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANGAUS, "mangaus.xyz") { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuaplus.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuaplus.kt index cb7f476d..9d099e30 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuaplus.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuaplus.kt @@ -3,22 +3,15 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.exception.ParseException -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.EnumSet @MangaSourceParser("MANHUAPLUS", "ManhuaPlus", "en") internal class Manhuaplus(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANHUAPLUS, "manhuaplus.com") { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt index 538f3f98..aa180576 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhuauss.kt @@ -3,15 +3,10 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("MANHUAUSS", "Manhuauss", "en") internal class Manhuauss(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MANHUAUSS, "manhuauss.com") { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt index 444de2d3..987f358a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt @@ -5,17 +5,10 @@ import kotlinx.coroutines.coroutineScope import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("MANHWAHENTAI", "ManhwaHentai", "en", ContentType.HENTAI) internal class ManhwaHentai(context: MangaLoaderContext) : @@ -23,9 +16,7 @@ internal class ManhwaHentai(context: MangaLoaderContext) : override val tagPrefix = "webtoon-genre/" override val listUrl = "webtoon/" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) + override val postReq = true override suspend fun getDetails(manga: Manga): Manga = coroutineScope { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhwaz.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhwaz.kt index cfcbea71..87d57724 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhwaz.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Manhwaz.kt @@ -5,7 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.* @MangaSourceParser("MANHWAZ", "ManhwaZ", "en") internal class Manhwaz(context: MangaLoaderContext) : @@ -14,38 +13,31 @@ internal class Manhwaz(context: MangaLoaderContext) : override val listUrl = "genre/manhwa" override val tagPrefix = "genre/" override val withoutAjax = true - override val isTagsExclusionSupported = false override val selectTestAsync = "div.list-chapter" - override val availableStates: Set = emptySet() - - override val availableContentRating: Set = emptySet() - - override val availableSortOrders: Set = EnumSet.of( - SortOrder.UPDATED, - SortOrder.POPULARITY, - SortOrder.NEWEST, - SortOrder.RATING, - ) - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?s=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -62,7 +54,7 @@ internal class Manhwaz(context: MangaLoaderContext) : } append("m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new") @@ -70,13 +62,6 @@ internal class Manhwaz(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("/$listUrl") - append("?page=") - append(page.toString()) - append("&m_orderby=latest") - } } } val doc = webClient.httpGet(url).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt index 0ab378b0..d9e06c0a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt @@ -1,10 +1,6 @@ package org.koitharu.kotatsu.parsers.site.madara.en -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import okhttp3.Headers import okhttp3.Request import okhttp3.RequestBody @@ -16,7 +12,6 @@ import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.EnumSet @MangaSourceParser("MANYTOON", "ManyToon", "en", ContentType.HENTAI) internal class ManyToon(context: MangaLoaderContext) : @@ -24,22 +19,22 @@ internal class ManyToon(context: MangaLoaderContext) : override val listUrl = "comic/" override val postReq = true override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) - override val availableStates: Set = emptySet() - - override val availableContentRating: Set = emptySet() + override suspend fun getFilterOptions(): MangaListFilterOptions { + return super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val pages = page + 1 val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (pages > 1) { append("/page/") append(pages.toString()) @@ -49,7 +44,7 @@ internal class ManyToon(context: MangaLoaderContext) : append("&post_type=wp-manga") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -70,7 +65,7 @@ internal class ManyToon(context: MangaLoaderContext) : } append("?m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("new-manga") @@ -79,16 +74,6 @@ internal class ManyToon(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append('/') - append(listUrl) - if (pages > 1) { - append("page/") - append(pages) - append("/?m_orderby=latest") - } - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MmScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MmScans.kt index 91c98794..b9c1b46e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MmScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MmScans.kt @@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("MMSCANS", "MmScans", "en") @@ -15,7 +13,4 @@ internal class MmScans(context: MangaLoaderContext) : override val selectChapter = "li.chapter-li" override val selectDesc = "div.summary-text" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) -} + } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt index f448467a..fa8b31d3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ToonGod.kt @@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("TOONGOD", "ToonGod", "en", ContentType.HENTAI) internal class ToonGod(context: MangaLoaderContext) : @@ -15,7 +13,4 @@ internal class ToonGod(context: MangaLoaderContext) : override val tagPrefix = "webtoon-genre/" override val datePattern = "d MMM yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Zinmanga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Zinmanga.kt index 7e4c44c0..5dbc7747 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Zinmanga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Zinmanga.kt @@ -3,16 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("ZINMANGA", "ZinManga.net", "en") internal class Zinmanga(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.ZINMANGA, "zinmanga.net") { override val datePattern = "MM/dd/yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/DragonTranslationParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/DragonTranslationParser.kt index b50f133d..78116c8e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/DragonTranslationParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/DragonTranslationParser.kt @@ -2,74 +2,76 @@ package org.koitharu.kotatsu.parsers.site.madara.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* import java.util.* @MangaSourceParser("DRAGONTRANSLATION", "Dragon Translation", "es") internal class DragonTranslationParser(context: MangaLoaderContext) : - MadaraParser(context, MangaParserSource.DRAGONTRANSLATION, "dragontranslation.net", 30) { - override val selectPage = "div#chapter_imgs img" - override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() + MadaraParser(context, MangaParserSource.DRAGONTRANSLATION, "dragontranslation.net", 30) { - init { - paginator.firstPage = 1 - searchPaginator.firstPage = 1 - } + override val selectPage = "div#chapter_imgs img" + override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val url = buildString { - append("https://") - append(domain) - when (filter) { - is MangaListFilter.Search -> { - append("/mangas?buscar=") - append(filter.query.urlEncoded()) - append("&page=") - append(page.toString()) - } + init { + paginator.firstPage = 1 + searchPaginator.firstPage = 1 + } - is MangaListFilter.Advanced -> { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) - append("/mangas?page=") - append(page.toString()) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val url = buildString { + append("https://") + append(domain) + when { + !filter.query.isNullOrEmpty() -> { + append("/mangas?buscar=") + append(filter.query.urlEncoded()) + append("&page=") + append(page.toString()) + } - val tag = filter.tags.oneOrThrowIfMany() - if (filter.tags.isNotEmpty()) { - append("&tag=") - append(tag?.key.orEmpty()) - } - } + else -> { - null -> { - append("/mangas?page=") - append(page.toString()) - } - } - } + append("/mangas?page=") + append(page.toString()) - val doc = webClient.httpGet(url).parseHtml() + val tag = filter.tags.oneOrThrowIfMany() + if (filter.tags.isNotEmpty()) { + append("&tag=") + append(tag?.key.orEmpty()) + } + } + } + } - return doc.select("div.video-bg div.col-6 ").map { div -> - val href = - div.selectFirst("a.series-link")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found") - Manga( - id = generateUid(href), - url = href, - publicUrl = href.toAbsoluteUrl(div.host ?: domain), - coverUrl = div.selectFirst("img.thumb-img")?.src().orEmpty(), - title = div.selectFirst("div.series-box h5")?.text().orEmpty(), - altTitle = null, - rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f, - tags = emptySet(), - author = null, - state = null, - source = source, - isNsfw = isNsfwSource, - ) - } - } + val doc = webClient.httpGet(url).parseHtml() + + return doc.select("div.video-bg div.col-6 ").map { div -> + val href = + div.selectFirst("a.series-link")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found") + Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(div.host ?: domain), + coverUrl = div.selectFirst("img.thumb-img")?.src().orEmpty(), + title = div.selectFirst("div.series-box h5")?.text().orEmpty(), + altTitle = null, + rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f, + tags = emptySet(), + author = null, + state = null, + source = source, + isNsfw = isNsfwSource, + ) + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt index 283d790d..008e9af4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt @@ -6,7 +6,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.* @MangaSourceParser("TMOMANGA", "TmoManga", "es") internal class TmoManga(context: MangaLoaderContext) : @@ -16,22 +15,23 @@ internal class TmoManga(context: MangaLoaderContext) : override val listUrl = "biblioteca/" override val selectGenre = "div.summary-content a.tags_manga" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/$listUrl") append("?search=") append(filter.query.urlEncoded()) @@ -41,7 +41,7 @@ internal class TmoManga(context: MangaLoaderContext) : } } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -59,14 +59,6 @@ internal class TmoManga(context: MangaLoaderContext) : } } } - - null -> { - append("/$listUrl") - if (page > 1) { - append("?page=") - append(page) - } - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Indo18h.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Indo18h.kt index ce019030..bc2e33b7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Indo18h.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/Indo18h.kt @@ -5,16 +5,11 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("INDO18H", "Indo18h", "id", ContentType.HENTAI) internal class Indo18h(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.INDO18H, "indo18h.com", 24) { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt index 9d8e69e8..34b3b304 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ManhwaHub.kt @@ -15,11 +15,8 @@ internal class ManhwaHub(context: MangaLoaderContext) : override val datePattern = "MMMM d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH override val withoutAjax = true - override val isTagsExclusionSupported = false override val listUrl = "genre/manhwa" override val selectTestAsync = "ul.box-list-chapter" - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) init { @@ -27,19 +24,24 @@ internal class ManhwaHub(context: MangaLoaderContext) : searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?s=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -55,11 +57,6 @@ internal class ManhwaHub(context: MangaLoaderContext) : } - - null -> { - append("/?page=") - append(page.toString()) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -98,7 +95,7 @@ internal class ManhwaHub(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.select("div.genres li").mapNotNullToSet { li -> val a = li.selectFirst("a") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AncientComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AncientComics.kt index 823e6e08..ea515639 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AncientComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/AncientComics.kt @@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @Broken @MangaSourceParser("ANCIENTCOMICS", "AncientComics", "pt") @@ -14,7 +12,4 @@ internal class AncientComics(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.ANCIENTCOMICS, "ancientcomics.com.br") { override val datePattern: String = "dd/MM/yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Atemporal.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Atemporal.kt index b63fa424..b67ff8c1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Atemporal.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Atemporal.kt @@ -3,16 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("ATEMPORAL", "Atemporal", "pt") internal class Atemporal(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.ATEMPORAL, "atemporal.cloud") { override val datePattern: String = "d 'de' MMMM 'de' yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GalinhaSamurai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GalinhaSamurai.kt index 75c2c36f..33e8b7fe 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GalinhaSamurai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/GalinhaSamurai.kt @@ -3,16 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("GALINHASAMURAI", "GalinhaSamurai", "pt") internal class GalinhaSamurai(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.GALINHASAMURAI, "galinhasamurai.com") { override val datePattern = "dd/MM/yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ImperiodaBritannia.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ImperiodaBritannia.kt index 918774a0..34ef2cf1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ImperiodaBritannia.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ImperiodaBritannia.kt @@ -3,16 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("IMPERIODABRITANNIA", "ImperioDaBritannia", "pt") internal class ImperiodaBritannia(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.IMPERIODABRITANNIA, "imperiodabritannia.com", 10) { override val datePattern: String = "dd 'de' MMMMM 'de' yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/MrBenne.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/MrBenne.kt index 45ec457a..75a19e2d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/MrBenne.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/MrBenne.kt @@ -16,7 +16,7 @@ internal class MrBenne(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.MRBENNE, "mrbenne.com", 10) { override val datePattern: String = "dd/MM/yyyy" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/?s=&post_type=wp-manga").parseHtml() val body = doc.body() val root = body.selectFirst("div.form-group.checkbox-group") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Norterose.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Norterose.kt index 0fdb50d2..304a0c22 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Norterose.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Norterose.kt @@ -3,16 +3,11 @@ package org.koitharu.kotatsu.parsers.site.madara.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("NORTEROSE", "Norterose", "pt") internal class Norterose(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.NORTEROSE, "norterose.com.br", 10) { override val datePattern: String = "dd/MM/yyyy" override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt index 57a700e6..679b9466 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/th/Doujinza.kt @@ -4,17 +4,12 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.madara.MadaraParser -import java.util.EnumSet @MangaSourceParser("DOUJINZA", "Doujinza", "th", ContentType.HENTAI) internal class Doujinza(context: MangaLoaderContext) : MadaraParser(context, MangaParserSource.DOUJINZA, "doujinza.com", 24) { override val withoutAjax = true - override val isTagsExclusionSupported = false - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.ALPHABETICAL, SortOrder.RATING) override val datePattern = "MMMM dd, yyyy" override val listUrl = "doujin/" override val tagPrefix = "doujin-genre/" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt index 5099a107..7b6315e3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt @@ -8,7 +8,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.* -import java.util.* @Broken @MangaSourceParser("SAYTRUYENHAY", "PheTruyen", "vi") @@ -17,31 +16,31 @@ internal class Saytruyenhay(context: MangaLoaderContext) : override val tagPrefix = "genre/" override val withoutAjax = true - override val isTagsExclusionSupported = false override val listUrl = "public/genre/manga/" - override val availableStates: Set = emptySet() - override val availableContentRating: Set = emptySet() - override val availableSortOrders: Set = - EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.RATING, SortOrder.NEWEST) init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + availableContentRating = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?s=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { val tag = filter.tags.oneOrThrowIfMany() if (filter.tags.isNotEmpty()) { @@ -54,7 +53,7 @@ internal class Saytruyenhay(context: MangaLoaderContext) : append("?page=") append(page.toString()) append("&m_orderby=") - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> append("latest") SortOrder.RATING -> append("rating") SortOrder.POPULARITY -> append("views") @@ -62,12 +61,6 @@ internal class Saytruyenhay(context: MangaLoaderContext) : else -> append("latest") } } - - null -> { - append("/$listUrl") - append("?page=") - append(page.toString()) - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt index 81c4d9b8..2eb51d13 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt @@ -34,8 +34,6 @@ internal abstract class MadthemeParser( SortOrder.RATING, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - protected open val listUrl = "search/" protected open val datePattern = "MMM dd, yyyy" @@ -59,23 +57,34 @@ internal abstract class MadthemeParser( "COMPLETED", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') append(listUrl) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("?sort=updated_at&q=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("?sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("updated_at") SortOrder.ALPHABETICAL -> append("name") // On some sites without tags or searches, the alphabetical option is empty. @@ -104,8 +113,6 @@ internal abstract class MadthemeParser( } } - - null -> append("?sort=updated_at") } append("&page=") @@ -140,7 +147,7 @@ internal abstract class MadthemeParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select("div.genres .checkbox").mapNotNullToSet { checkbox -> val key = checkbox.selectFirstOrThrow("input").attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/ManhuaScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/ManhuaScan.kt index cd29c461..f9a5c911 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/ManhuaScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/ManhuaScan.kt @@ -17,23 +17,23 @@ internal class ManhuaScan(context: MangaLoaderContext) : override val sourceLocale: Locale = Locale.ENGLISH override val listUrl = "search" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') append(listUrl) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("?sort=updated_at&q=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("?sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("updated_at") SortOrder.ALPHABETICAL -> append("name") @@ -62,8 +62,6 @@ internal class ManhuaScan(context: MangaLoaderContext) : } } - - null -> append("?sort=updated_at") } append("&page=") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt index 9d7a98f2..49bad9bd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt @@ -31,8 +31,6 @@ internal abstract class Manga18Parser( SortOrder.ALPHABETICAL, ) - override val isMultipleTagsSupported = false - protected open val listUrl = "list-manga/" protected open val tagUrl = "manga-list/" protected open val datePattern = "dd-MM-yyyy" @@ -54,14 +52,23 @@ internal abstract class Manga18Parser( "Completed", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(listUrl) append(page.toString()) append("?search=") @@ -69,7 +76,7 @@ internal abstract class Manga18Parser( append("&order_by=latest") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append(tagUrl) @@ -82,19 +89,13 @@ internal abstract class Manga18Parser( append(page.toString()) append("?order_by=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views") SortOrder.UPDATED -> append("lastest") SortOrder.ALPHABETICAL -> append("name") else -> append("latest") } } - - null -> { - append(listUrl) - append(page.toString()) - append("?order_by=latest") - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -120,7 +121,7 @@ internal abstract class Manga18Parser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl/").parseHtml() return doc.select("div.grid_cate li").mapNotNullToSet { li -> val a = li.selectFirst("a") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt index 6181d3af..0b1ca59d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt @@ -35,5 +35,5 @@ internal class Hanman18(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set = emptySet() // search by tag does not work + private suspend fun fetchAvailableTags(): Set = emptySet() // search by tag does not work } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt index f3b96354..964b614c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt @@ -30,10 +30,6 @@ internal abstract class MangaboxParser( SortOrder.ALPHABETICAL, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - - override val isTagsExclusionSupported = true - protected open val listUrl = "/advanced_search" protected open val searchUrl = "/search/story/" protected open val datePattern = "MMM dd,yy" @@ -44,7 +40,6 @@ internal abstract class MangaboxParser( searchPaginator.firstPage = 1 } - @JvmField protected val ongoing: Set = setOf( "ongoing", @@ -55,20 +50,32 @@ internal abstract class MangaboxParser( "completed", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append(listUrl) append("/?s=all") - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("&keyw=") append(filter.query.replace(" ", "_").urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { append("&g_i=") @@ -100,7 +107,7 @@ internal abstract class MangaboxParser( } append("&orby=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("topview") SortOrder.UPDATED -> append("") SortOrder.NEWEST -> append("newest") @@ -108,8 +115,6 @@ internal abstract class MangaboxParser( else -> append("") } } - - null -> {} } append("&page=") @@ -141,7 +146,7 @@ internal abstract class MangaboxParser( protected open val selectTagMap = "div.panel-genres-list a:not(.genres-select)" - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val tags = doc.select(selectTagMap).drop(1) // remove all tags return tags.mapNotNullToSet { a -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangairo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangairo.kt index c6cac7e0..f1507d0d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangairo.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangairo.kt @@ -31,24 +31,28 @@ internal class Mangairo(context: MangaLoaderContext) : SortOrder.POPULARITY, SortOrder.NEWEST, ) - override val isTagsExclusionSupported = false - override val isMultipleTagsSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + isMultipleTagsSupported = false, + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(searchUrl) append(filter.query.urlEncoded()) append("?page=") } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/type-") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("topview") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("newest") @@ -81,11 +85,6 @@ internal class Mangairo(context: MangaLoaderContext) : append("/page-") } - - null -> { - append(listUrl) - append("/type-latest/ctg-all/state-all/page-") - } } append(page.toString()) } @@ -109,7 +108,7 @@ internal class Mangairo(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl/type-latest/ctg-all/state-all/page-1").parseHtml() return doc.select("div.panel_category a:not(.ctg_select)").mapNotNullToSet { a -> val key = a.attr("href").substringAfterLast("ctg-").substringBefore("/") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt index 9e95a365..f333a350 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt @@ -19,18 +19,21 @@ internal class Mangakakalot(context: MangaLoaderContext) : SortOrder.POPULARITY, SortOrder.NEWEST, ) - override val isTagsExclusionSupported = false - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + isMultipleTagsSupported = false, + ) override val otherDomain = "chapmanganato.com" override val listUrl = "/manga_list" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(searchUrl) val regex = Regex("[^A-Za-z0-9 ]") val q = regex.replace(filter.query, "") @@ -38,10 +41,10 @@ internal class Mangakakalot(context: MangaLoaderContext) : append("?page=") } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("?type=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("topview") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("newest") @@ -67,11 +70,6 @@ internal class Mangakakalot(context: MangaLoaderContext) : append("&page=") } - - null -> { - append(listUrl) - append("?type=latest&page=") - } } append(page.toString()) } @@ -99,7 +97,7 @@ internal class Mangakakalot(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val tags = doc.select("ul.tag li a").drop(1) return tags.mapNotNullToSet { a -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangakakalotTv.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangakakalotTv.kt index da550d59..6de10c3f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangakakalotTv.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/MangakakalotTv.kt @@ -22,25 +22,28 @@ internal class MangakakalotTv(context: MangaLoaderContext) : SortOrder.POPULARITY, SortOrder.NEWEST, ) - override val isMultipleTagsSupported = false - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + isMultipleTagsSupported = false, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(searchUrl) append(filter.query.urlEncoded()) append("?page=") } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("?type=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("topview") SortOrder.UPDATED -> append("latest") SortOrder.NEWEST -> append("newest") @@ -66,11 +69,6 @@ internal class MangakakalotTv(context: MangaLoaderContext) : append("&page=") } - - null -> { - append(listUrl) - append("?type=latest&page=") - } } append(page.toString()) } @@ -130,7 +128,7 @@ internal class MangakakalotTv(context: MangaLoaderContext) : override val selectTagMap = "ul.tag li a" - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select(selectTagMap).mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt index ce987662..d7ae5404 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt @@ -27,16 +27,6 @@ internal abstract class MangAdventureParser( keys.add(userAgentKey) } - override val availableStates: Set = EnumSet.of( - MangaState.ONGOING, - MangaState.FINISHED, - MangaState.ABANDONED, - MangaState.PAUSED, - ) - - override val availableContentRating: Set = - EnumSet.of(ContentRating.SAFE) - override val availableSortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL_DESC, @@ -46,18 +36,38 @@ internal abstract class MangAdventureParser( override val defaultSortOrder = SortOrder.ALPHABETICAL - override val isTagsExclusionSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of( + MangaState.ONGOING, + MangaState.FINISHED, + MangaState.ABANDONED, + MangaState.PAUSED, + ), + availableContentRating = EnumSet.of(ContentRating.SAFE), + ) + + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = apiUrl.addEncodedPathSegment("series") .addEncodedQueryParameter("limit", pageSize.toString()) .addEncodedQueryParameter("page", page.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { url.addQueryParameter("title", filter.query) } - is MangaListFilter.Advanced -> { + else -> { url.addQueryParameter( "categories", buildString { @@ -79,7 +89,7 @@ internal abstract class MangAdventureParser( MangaState.PAUSED -> url.addEncodedQueryParameter("status", "hiatus") else -> throw IllegalArgumentException(ERROR_UNSUPPORTED_STATE) } - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> url.addEncodedQueryParameter("sort", "title") SortOrder.ALPHABETICAL_DESC -> url.addEncodedQueryParameter("sort", "-title") SortOrder.UPDATED -> url.addEncodedQueryParameter("sort", "-latest_upload") @@ -87,8 +97,6 @@ internal abstract class MangAdventureParser( else -> throw IllegalArgumentException(ERROR_UNSUPPORTED_SORT_ORDER) } } - - else -> {} } return runCatchingCancellable { getManga(url.get()) }.getOrElse { if (it is NotFoundException) emptyList() else throw it @@ -159,7 +167,7 @@ internal abstract class MangAdventureParser( override suspend fun getPageUrl(page: MangaPage) = page.url - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val url = apiUrl.addEncodedPathSegment("categories") return url.get()?.optJSONArray("results")?.mapJSONToSet { val name = it.getString("name") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/en/AssortedScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/en/AssortedScans.kt index d983985d..dd5ef75e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/en/AssortedScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/en/AssortedScans.kt @@ -3,8 +3,8 @@ package org.koitharu.kotatsu.parsers.site.mangadventure.en import androidx.collection.ArraySet import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.mangadventure.MangAdventureParser @MangaSourceParser("ASSORTEDSCANS", "AssortedScans", "en") @@ -16,8 +16,10 @@ internal class AssortedScans(context: MangaLoaderContext) : "Shoujo Ai", "Shounen Ai", "Smut", "Yaoi", ) - override suspend fun getAvailableTags(): Set { - val tags = super.getAvailableTags() - return tags.filterNotTo(ArraySet(tags.size)) { it.key in emptyTags } + override suspend fun getFilterOptions(): MangaListFilterOptions { + val options = super.getFilterOptions() + return options.copy( + availableTags = options.availableTags.filterNotTo(ArraySet(options.availableTags.size)) { it.key in emptyTags }, + ) } } 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 99188a2c..8c957f0b 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 @@ -43,10 +43,24 @@ internal abstract class MangaReaderParser( SortOrder.NEWEST, ) - override val availableStates: Set - get() = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val isTagsExclusionSupported = true + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED), + availableContentTypes = EnumSet.of( + ContentType.MANGA, + ContentType.MANHWA, + ContentType.MANHUA, + ContentType.COMICS, + ContentType.NOVEL, + ), + ) protected open val listUrl = "/manga" protected open val datePattern = "MMMM d, yyyy" @@ -55,26 +69,26 @@ internal abstract class MangaReaderParser( protected var tagCache: ArrayMap? = null protected val mutex = Mutex() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page.toString()) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "title" SortOrder.ALPHABETICAL_DESC -> "titlereverse" SortOrder.NEWEST -> "latest" @@ -110,13 +124,21 @@ internal abstract class MangaReaderParser( } } - append("&page=") - append(page.toString()) - } + filter.types.oneOrThrowIfMany()?.let { + append("&type=") + append( + when (it) { + ContentType.MANGA -> "manga" + ContentType.MANHWA -> "manhwa" + ContentType.MANHUA -> "manhua" + ContentType.COMICS -> "comic" + ContentType.NOVEL -> "novel" + else -> "" + }, + ) + } - null -> { - append(listUrl) - append("/?order=update&page=") + append("&page=") append(page.toString()) } } @@ -223,18 +245,18 @@ internal abstract class MangaReaderParser( "En cours de publication", "Đang tiến hành", "Em lançamento", "em lançamento", "Em Lançamento", "Онгоінг", "Publishing", "Devam Ediyor", "Em Andamento", "In Corso", "Güncel", "Berjalan", "Продолжается", "Updating", "Lançando", "In Arrivo", "Emision", "En emision", "مستمر", "Curso", "En marcha", "Publicandose", "Publicando", "连载中", "Devam ediyor", "Devam Etmekte", - -> MangaState.ONGOING + -> MangaState.ONGOING "Completed", "Completo", "Complété", "Fini", "Achevé", "Terminé", "Terminé ⚫", "Tamamlandı", "Đã hoàn thành", "Hoàn Thành", "مكتملة", "Завершено", "Finished", "Finalizado", "Completata", "One-Shot", "Bitti", "Tamat", "Completado", "Concluído", "Concluido", "已完结", "Bitmiş", - -> MangaState.FINISHED + -> MangaState.FINISHED "Canceled", "Cancelled", "Cancelado", "cancellato", "Cancelados", "Dropped", "Discontinued", "abandonné", "Abandonné", - -> MangaState.ABANDONED + -> MangaState.ABANDONED "Hiatus", "On Hold", "Pausado", "En espera", "En pause", "En Pause", "En attente", - -> MangaState.PAUSED + -> MangaState.PAUSED else -> null } @@ -324,10 +346,6 @@ internal abstract class MangaReaderParser( } } - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - protected open suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ArAreaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ArAreaScans.kt index 41986b3c..77cd2e32 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ArAreaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ArAreaScans.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ARAREASCANS", "ArAreaScans", "ar") internal class ArAreaScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ARAREASCANS, "ar.areascans.org", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AreaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AreaScans.kt index 03e49c17..7d07f277 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AreaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/AreaScans.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("AREASCANS", "AreaScans", "ar") internal class AreaScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.AREASCANS, "www.areascans.net", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/FlAres.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/FlAres.kt index 3abdc190..8ef3116d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/FlAres.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/FlAres.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ internal class FlAres(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.FLARES, "fl-ares.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" override val encodedSrc = true - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaPro.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaPro.kt index 79a3ebde..6a61ad8b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaPro.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/MangaPro.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGAPRO", "MangaPro", "ar") internal class MangaPro(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAPRO, "promanga.pro", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt index 640b80a9..835b9bd2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt @@ -16,26 +16,28 @@ internal class Normoyun(context: MangaLoaderContext) : override val selectMangaList = ".listupd .bs .bsx" override val selectMangaListImg = "img" override val isNetShieldProtected = true - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "a-z" SortOrder.ALPHABETICAL_DESC -> "z-a" SortOrder.NEWEST -> "added" @@ -63,11 +65,6 @@ internal class Normoyun(context: MangaLoaderContext) : } } } - - null -> { - append(listUrl) - append("/?order=update") - } } append("&page=") append(page.toString()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/PotatoManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/PotatoManga.kt index 97944d11..d21058fb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/PotatoManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/PotatoManga.kt @@ -5,6 +5,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,7 +16,10 @@ import org.koitharu.kotatsu.parsers.util.* internal class PotatoManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.POTATOMANGA, "potatomanga.xyz", pageSize = 30, searchPageSize = 10) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override suspend fun getPages(chapter: MangaChapter): List { val chapterUrl = chapter.url.toAbsoluteUrl(domain) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ScarManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ScarManga.kt index c10eb11b..c7fa3a16 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ScarManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ScarManga.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class ScarManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SCARMANGA, "scarmanga.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ThunderScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ThunderScans.kt index 2d1857a5..7ebca1cc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ThunderScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/ThunderScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,6 +15,9 @@ internal class ThunderScans(context: MangaLoaderContext) : pageSize = 32, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val selectChapter = ".eplister > ul > li" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/UmiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/UmiManga.kt index e19efbc1..2873f0ed 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/UmiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/UmiManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("UMIMANGA", "UmiManga", "ar") internal class UmiManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.UMIMANGA, "www.umimanga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt index b6c3b57b..24d73e6b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt @@ -3,10 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ar import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.WordSet +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.* import java.text.DateFormat @@ -19,7 +16,10 @@ internal class VexManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.VEXMANGA, "vexmanga.com", pageSize = 10, searchPageSize = 10) { override val selectMangaList = ".listarchives .latest-recom" override val selectChapter = ".ulChapterList > a" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AgsComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AgsComics.kt index 57688f76..ba0d9be6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AgsComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AgsComics.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class AgsComics(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.AGSCOMICS, "agscomics.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AltayScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AltayScans.kt index 41b4643c..ea4d07c8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AltayScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AltayScans.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ALTAYSCANS", "AltayScans", "en") internal class AltayScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ALTAYSCANS, "altayscans.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt index 1840e369..52166447 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AnigliScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class AnigliScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ANIGLISCANS, "anigliscans.xyz", pageSize = 47, searchPageSize = 47) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AscalonScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AscalonScans.kt index 3a04d0f5..27fe4e22 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AscalonScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AscalonScans.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ASCALONSCANS", "AscalonScans", "en") internal class AscalonScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ASCALONSCANS, "ascalonscans.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AstraScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AstraScans.kt index 3b0214d2..af1d751a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AstraScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AstraScans.kt @@ -2,12 +2,18 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ASTRASCANS", "AstraScans", "en") internal class AstraScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ASTRASCANS, "astrascans.org", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val listUrl = "/series" + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BirdManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BirdManga.kt index 3f851c5d..27acdb55 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BirdManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BirdManga.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class BirdManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.BIRDMANGA, "birdmanga.com", pageSize = 20, searchPageSize = 10) { override val encodedSrc = true - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt index 118ddbee..a6f2cbc5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt @@ -2,10 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.* @@ -19,7 +16,11 @@ internal class Constellarcomic(context: MangaLoaderContext) : searchPageSize = 18, ) { override val selectTestScript = "script:containsData(ts_rea_der_._run)" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/CosmicScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/CosmicScansParser.kt index e5d664a2..9ee682cd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/CosmicScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/CosmicScansParser.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -11,5 +12,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class CosmicScansParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.COSMICSCANS, "cosmic-scans.com", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnThunderScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnThunderScans.kt index c8a20a21..bd07227e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnThunderScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnThunderScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,9 @@ internal class EnThunderScans(context: MangaLoaderContext) : searchPageSize = 10, ) { override val listUrl = "/comics" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt index c8fb10d8..2ea3f0f2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/EnryuManga.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ENRYUMANGA", "EnryuManga", "en") internal class EnryuManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ENRYUMANGA, "enryumanga.net", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt index 0d818b51..55bc1c19 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakComic.kt @@ -6,6 +6,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -18,7 +19,11 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase @MangaSourceParser("FREAKCOMIC", "FreakComic", "en") internal class FreakComic(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.FREAKCOMIC, "freakcomic.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val selectMangaList = ".listupd .lastest-serie" override val selectMangaListImg = "img" override val selectChapter = ".chapter-li a:not(:has(svg))" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakScans.kt index dfb0bdb8..b50ed361 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/FreakScans.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -11,5 +12,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class FreakScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.FREAKSCANS, "freakscans.com", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KaiScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KaiScans.kt index b06861af..a11ec390 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KaiScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KaiScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KAISCANS", "KaiScans", "en") internal class KaiScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KAISCANS, "ponvi.online", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt index b2411952..ff0ffd89 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/KomikLabParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class KomikLabParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKLAB, "komiklab.com", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaComicCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaComicCom.kt index c199deb6..29ffbdcf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaComicCom.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaComicCom.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("LUACOMIC_COM", "luaComic.com", "en") internal class LuaComicCom(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LUACOMIC_COM, "ponvi.online", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt index dde08da5..5aa9a634 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuminousScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -16,5 +17,8 @@ internal class LuminousScans(context: MangaLoaderContext) : ) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LunarScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LunarScan.kt index b4697867..58c2d56c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LunarScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LunarScan.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class LunarScan(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LUNAR_SCAN, "lunarscan.org", pageSize = 20, searchPageSize = 20) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaLover.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaLover.kt index f2d802dd..d9ccb75b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaLover.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ManhwaLover.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -16,5 +17,8 @@ internal class ManhwaLover(context: MangaLoaderContext) : searchPageSize = 20, ) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manhwax.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manhwax.kt index 58994fa0..7ceccbc7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manhwax.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manhwax.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Manhwax(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANHWAX, "manhwax.org", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manjanoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manjanoon.kt index bbb9e50e..c20cf5dc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manjanoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Manjanoon.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANJANOON_EN", "NoonScan.net", "en") internal class Manjanoon(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANJANOON_EN, "noonscan.net", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MyShojo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MyShojo.kt index 4041c38e..cc482f65 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MyShojo.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/MyShojo.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MYSHOJO", "MyShojo", "en") internal class MyShojo(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MYSHOJO, "myshojo.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt index c0cccdf7..fd99e278 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Nightscans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ internal class Nightscans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NIGHTSCANS, "nightsup.net", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" override val selectMangaListImg = "img.ts-post-image, picture img" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RavenScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RavenScans.kt index 336616e8..eb9e7cbf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RavenScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RavenScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class RavenScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.RAVENSCANS, "ravenscans.com", pageSize = 10, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReadersPoint.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReadersPoint.kt index 34e0617d..1dd67e72 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReadersPoint.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReadersPoint.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class ReadersPoint(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.READERSPOINT, "qscomics.org", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Readkomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Readkomik.kt index c7b124bf..9362a995 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Readkomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Readkomik.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,6 +10,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Readkomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.READKOMIK, "novelstreams.com", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RizzComic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RizzComic.kt index 37dbdc85..8a0a0fa4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RizzComic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/RizzComic.kt @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en +import androidx.collection.ArrayMap +import kotlinx.coroutines.sync.withLock import okhttp3.FormBody import okhttp3.Request import org.koitharu.kotatsu.parsers.MangaLoaderContext @@ -25,11 +27,13 @@ internal class RizzComic(context: MangaLoaderContext) : SortOrder.POPULARITY, SortOrder.ALPHABETICAL_DESC, ) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED) - override val isMultipleTagsSupported = true - override val isSearchSupported = true - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + isSearchSupported = true, + isTagsExclusionSupported = false, + ) private val filterUrl = "/Index/filter_series" private val searchUrl = "/Index/live_search" @@ -55,14 +59,18 @@ internal class RizzComic(context: MangaLoaderContext) : return randomPartRegex.find(slug)?.groupValues?.get(1) ?: "" } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { if (page > 1) { return emptyList() } var url = "https://$domain$filterUrl" - val payload = when (filter) { - is MangaListFilter.Search -> { + val payload = when { + !filter.query.isNullOrEmpty() -> { url = "https://$domain$searchUrl" if (filter.query != "") { FormBody.Builder() @@ -73,7 +81,7 @@ internal class RizzComic(context: MangaLoaderContext) : } } - is MangaListFilter.Advanced -> { + else -> { val state = filter.states.oneOrThrowIfMany()?.toPayloadValue() ?: "all" val genres = filter.tags.map { it.key } @@ -81,21 +89,13 @@ internal class RizzComic(context: MangaLoaderContext) : val formBuilder = FormBody.Builder() .add("StatusValue", state) .add("TypeValue", "all") - .add("OrderValue", filter.sortOrder.toPayloadValue()) + .add("OrderValue", order.toPayloadValue()) genres.forEach { genre -> formBuilder.add("genres_checked[]", genre) } formBuilder.build() } - - else -> { - FormBody.Builder() - .add("StatusValue", "all") - .add("TypeValue", "all") - .add("OrderValue", "all") - .build() - } } val request = Request.Builder() .url(url) @@ -157,13 +157,14 @@ internal class RizzComic(context: MangaLoaderContext) : else -> "all" } - override suspend fun getAvailableTags(): Set { + override suspend fun getOrCreateTagMap(): Map = mutex.withLock { + tagCache?.let { return@withLock it } val url = "https://$domain/series" val doc = webClient.httpGet(url).parseHtml() val genreElements = doc.select("input.genre-item") - return genreElements.mapNotNullToSet { element -> + val genres = genreElements.mapNotNull { element -> val id = element.attr("value") val name = element.nextElementSibling()?.text() @@ -177,5 +178,6 @@ internal class RizzComic(context: MangaLoaderContext) : null } } + genres.associateByTo(ArrayMap(genres.size)) { it.title }.also { tagCache = it } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/SuryaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/SuryaScans.kt index 06b97082..707b54c5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/SuryaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/SuryaScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("SURYASCANS", "SuryaScans", "en") internal class SuryaScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SURYASCANS, "suryacomics.com", pageSize = 5, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt index dc4588ba..d89583cf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("TECNOSCANS", "TecnoScans", "en") internal class TecnoScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TECNOSCANS, "olyscans.xyz", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/VoidScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/VoidScans.kt index e6c505d1..a0a16d6c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/VoidScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/VoidScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class VoidScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.VOIDSCANS, "hivetoon.com", pageSize = 150, searchPageSize = 150) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt index 94945bce..0ec5ad1b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/XCalibrScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.en import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("XCALIBRSCANS", "XCalibrScans", "en") internal class XCalibrScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.XCALIBRSCANS, "xcalibrscans.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt index 72325ae2..c595f418 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt @@ -18,32 +18,36 @@ internal class Zahard(context: MangaLoaderContext) : override val selectChapter = "#chapterlist > ul > a" override val selectPage = "div#chapter_imgs img" override val availableSortOrders: Set = EnumSet.of(SortOrder.NEWEST) - override val availableStates: Set = emptySet() - override val isMultipleTagsSupported = false - override val isTagsExclusionSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + isTagsExclusionSupported = false, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append(listUrl) append("?page=") append(page.toString()) - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&search=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("tag=") append(it.key) } } - - null -> {} } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Doujins.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Doujins.kt index 05e25419..dae2e143 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Doujins.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Doujins.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Doujins(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.DOUJINS, "doujins.lat", pageSize = 20, searchPageSize = 10) { override val listUrl = "/comic" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/GremoryMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/GremoryMangas.kt index 929d0a2d..434ba80b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/GremoryMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/GremoryMangas.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class GremoryMangas(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 20, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt index f3c54129..b6fcf510 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt @@ -3,33 +3,28 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat -import java.util.ArrayList @MangaSourceParser("HENTAIREADER", "HentaiReader", "es", ContentType.HENTAI) internal class HentaiReader(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.HENTAIREADER, "hentaireader.org", pageSize = 25, searchPageSize = 25) { override val listUrl = "/tipo/all" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(listUrl) append("?s=") append(filter.query.urlEncoded()) @@ -37,11 +32,11 @@ internal class HentaiReader(context: MangaLoaderContext) : append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "title" SortOrder.ALPHABETICAL_DESC -> "titlereverse" SortOrder.NEWEST -> "latest" @@ -73,12 +68,6 @@ internal class HentaiReader(context: MangaLoaderContext) : append("&page=") append(page.toString()) } - - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt index 2a7f3154..d1b622c9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("INARIMANGA", "InariManga", "es") internal class InariManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.INARIMANGA, "nakamatoon.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariPikav.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariPikav.kt index 056fbde3..def5b4ab 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariPikav.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariPikav.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("INARIPIKAV", "InariPikav", "es") internal class InariPikav(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.INARIPIKAV, "inaripikav.org", pageSize = 10, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt index 6dec4109..469ffe85 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt @@ -2,14 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @@ -18,16 +11,19 @@ import java.text.SimpleDateFormat internal class LectorHentai(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LECTORHENTAI, "lectorhentai.com", pageSize = 25, searchPageSize = 25) { override val listUrl = "/tipo/all" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(listUrl) append("?s=") append(filter.query.urlEncoded()) @@ -35,11 +31,11 @@ internal class LectorHentai(context: MangaLoaderContext) : append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "title" SortOrder.ALPHABETICAL_DESC -> "titlereverse" SortOrder.NEWEST -> "latest" @@ -71,12 +67,6 @@ internal class LectorHentai(context: MangaLoaderContext) : append("&page=") append(page.toString()) } - - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaTv.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaTv.kt index 2f679c7d..3077b566 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaTv.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/MangaTv.kt @@ -3,50 +3,40 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import org.koitharu.kotatsu.parsers.util.domain -import org.koitharu.kotatsu.parsers.util.generateUid -import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany -import org.koitharu.kotatsu.parsers.util.parseHtml -import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow -import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.urlEncoded -import java.util.ArrayList +import org.koitharu.kotatsu.parsers.util.* @MangaSourceParser("MANGATV", "MangaTv", "es") internal class MangaTv(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGATV, "www.mangatv.net", pageSize = 25, searchPageSize = 25) { override val listUrl = "/lista" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val datePattern = "yyyy-MM-dd" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/lista?s=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "title" SortOrder.ALPHABETICAL_DESC -> "titlereverse" SortOrder.NEWEST -> "latest" @@ -85,12 +75,6 @@ internal class MangaTv(context: MangaLoaderContext) : append("&page=") append(page.toString()) } - - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/RagnaScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/RagnaScan.kt index 36f24d81..754ae9d4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/RagnaScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/RagnaScan.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("RAGNASCAN", "RagnaScan", "es") internal class RagnaScan(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.RAGNASCAN, "ragnascan.com", pageSize = 5, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Shadowmangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Shadowmangas.kt index ec072992..2014e2b3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Shadowmangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Shadowmangas.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Shadowmangas(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SHADOWMANGAS, "shadowmangas.com", pageSize = 10, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/SkyMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/SkyMangas.kt index 345587fe..2bf075aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/SkyMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/SkyMangas.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class SkyMangas(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SKYMANGAS, "skymangas.com", pageSize = 20, searchPageSize = 10) { override val encodedSrc = true - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt index db1365fe..401ec122 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TecnoScann.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("TECNOSCANN", "TecnoScann", "es") internal class TecnoScann(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TECNOSCANN, "tecnoscann.com", 20, 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TenkaiScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TenkaiScan.kt index cd7fef00..c7eddd47 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TenkaiScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TenkaiScan.kt @@ -4,6 +4,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -13,5 +14,8 @@ import java.util.* internal class TenkaiScan(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TENKAISCAN, "tenkaiscan.net", 20, 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Tresdaos.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Tresdaos.kt index eb6cb7f9..984e14a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Tresdaos.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/Tresdaos.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("TRESDAOS", "Tresdaos", "es") internal class Tresdaos(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TRESDAOS, "tresdaos.com", 20, 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt index 1314b7cd..33056dd8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt @@ -13,37 +13,40 @@ import java.util.* @MangaSourceParser("TU_MANHWAS", "TuManhwas.com", "es") internal class TuManhwas(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TU_MANHWAS, "tumanhwas.com", 20, 20) { + override val listUrl = "/biblioteca" override val selectPage = "div#readerarea img" override val availableSortOrders: Set = EnumSet.of(SortOrder.NEWEST) - override val availableStates: Set = emptySet() - override val isMultipleTagsSupported = false - override val isTagsExclusionSupported = false - override suspend fun getAvailableTags(): Set = emptySet() + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + isTagsExclusionSupported = false, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append(listUrl) append("?page=") append(page.toString()) - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("&search=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append("&genero=") append(it.key) } } - - null -> {} } } return parseMangaList(webClient.httpGet(url).parseHtml()) @@ -123,6 +126,8 @@ internal class TuManhwas(context: MangaLoaderContext) : return pages } + override suspend fun getOrCreateTagMap(): Map = emptyMap() + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { // Clean date (e.g. 5th December 2019 to 5 December 2019) before parsing it val d = date?.lowercase() ?: return 0 diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/UkiyoToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/UkiyoToon.kt index db54e5cf..992dec97 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/UkiyoToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/UkiyoToon.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.es import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("UKIYOTOON", "UkiyoToon", "es") internal class UkiyoToon(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.UKIYOTOON, "ukiyotoon.com", 30, 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/EtheralRadiance.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/EtheralRadiance.kt index e7f37522..1d0a0cf1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/EtheralRadiance.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/EtheralRadiance.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.domain @@ -16,7 +17,11 @@ internal class EtheralRadiance(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) init { context.cookieJar.insertCookies( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarHentai.kt index bf0b71e7..51ff123b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarHentai.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("LUNARHENTAI", "GloryScans", "fr") internal class LunarHentai(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LUNARHENTAI, "gloryscans.fr", pageSize = 40, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarScans.kt index dc0ff249..e36907b3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/LunarScans.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("LUNARSCANS", "LunarScans", "fr") internal class LunarScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LUNARSCANS, "lunarscans.fr", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt index 0bd6e5e0..df670db2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class PhenixscansParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.PHENIXSCANS, "phenixscans.fr", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PornhwaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PornhwaScans.kt index 82cbf1a3..76e5ff5b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PornhwaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PornhwaScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("PORNHWASCANS", "PornhwaScans", "fr") internal class PornhwaScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.PORNHWASCANS, "pornhwascans.fr", pageSize = 24, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt index da94e929..e3522f91 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt @@ -4,25 +4,9 @@ import org.jsoup.nodes.Document import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl -import org.koitharu.kotatsu.parsers.util.domain -import org.koitharu.kotatsu.parsers.util.generateUid -import org.koitharu.kotatsu.parsers.util.mapChapters -import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany -import org.koitharu.kotatsu.parsers.util.parseHtml -import org.koitharu.kotatsu.parsers.util.src -import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.tryParse -import org.koitharu.kotatsu.parsers.util.urlEncoded +import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @MangaSourceParser("REVOLUTIONSCANTRAD", "RevolutionScantrad", "fr") @@ -36,74 +20,67 @@ internal class RevolutionScantrad(context: MangaLoaderContext) : ) { override val listUrl = "/series.html" override val datePattern = "yyyy" - override val isTagsExclusionSupported = false - override val isSearchSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + isSearchSupported = false, + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { if (page > 1) { return emptyList() } + if (!filter.query.isNullOrEmpty()) { + throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) + } val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { - throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) - } + append(listUrl) - is MangaListFilter.Advanced -> { - append(listUrl) - - append("?order=") - append( - when (filter.sortOrder) { - SortOrder.ALPHABETICAL -> "title" - SortOrder.ALPHABETICAL_DESC -> "titlereverse" - SortOrder.NEWEST -> "latest" - SortOrder.POPULARITY -> "popular" - SortOrder.UPDATED -> "update" - else -> "" - }, - ) - - filter.tags.forEach { - append("&") - append("genre[]".urlEncoded()) - append("=") - append(it.key) - } - - filter.tagsExclude.forEach { - append("&") - append("genre[]".urlEncoded()) - append("=-") - append(it.key) - } + append("?order=") + append( + when (order) { + SortOrder.ALPHABETICAL -> "title" + SortOrder.ALPHABETICAL_DESC -> "titlereverse" + SortOrder.NEWEST -> "latest" + SortOrder.POPULARITY -> "popular" + SortOrder.UPDATED -> "update" + else -> "" + }, + ) - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - when (it) { - MangaState.ONGOING -> append("ongoing") - MangaState.FINISHED -> append("completed") - MangaState.PAUSED -> append("hiatus") - else -> append("") - } - } - } + filter.tags.forEach { + append("&") + append("genre[]".urlEncoded()) + append("=") + append(it.key) + } - append("&page=") - append(page.toString()) - } + filter.tagsExclude.forEach { + append("&") + append("genre[]".urlEncoded()) + append("=-") + append(it.key) + } - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) + if (filter.states.isNotEmpty()) { + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + when (it) { + MangaState.ONGOING -> append("ongoing") + MangaState.FINISHED -> append("completed") + MangaState.PAUSED -> append("hiatus") + else -> append("") + } } } + + append("&page=") + append(page.toString()) } return parseMangaList(webClient.httpGet(url).parseHtml()) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/SushiScanFR.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/SushiScanFR.kt index c8829f7b..31d09ee3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/SushiScanFR.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/SushiScanFR.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class SushiScanFR(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SUSHISCANFR, "sushiscan.fr", pageSize = 36, searchPageSize = 10) { override val listUrl = "/catalogue" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt index 2ec39257..1e6390aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("VFSCAN", "VfScan", "fr") internal class VfScan(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.VFSCAN, "www.vfscan.net", pageSize = 18, searchPageSize = 18) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt index 3aeb4ff4..dd94661e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt @@ -1,29 +1,11 @@ package org.koitharu.kotatsu.parsers.site.mangareader.fr import org.jsoup.nodes.Document -import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl -import org.koitharu.kotatsu.parsers.util.domain -import org.koitharu.kotatsu.parsers.util.generateUid -import org.koitharu.kotatsu.parsers.util.mapChapters -import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany -import org.koitharu.kotatsu.parsers.util.parseHtml -import org.koitharu.kotatsu.parsers.util.src -import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.tryParse -import org.koitharu.kotatsu.parsers.util.urlEncoded +import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @MangaSourceParser("XXXREVOLUTIONSCANTRAD", "Xxx.RevolutionScantrad", "fr", ContentType.HENTAI) @@ -37,74 +19,62 @@ internal class XxxRevolutionScantrad(context: MangaLoaderContext) : ) { override val listUrl = "/series.html" override val datePattern = "yyyy" - override val isTagsExclusionSupported = false - override val isSearchSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isSearchSupported = false, + isTagsExclusionSupported = false, + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { if (page > 1) { return emptyList() } val url = buildString { append("https://") append(domain) + append(listUrl) + + append("?order=") + append( + when (order) { + SortOrder.ALPHABETICAL -> "title" + SortOrder.ALPHABETICAL_DESC -> "titlereverse" + SortOrder.NEWEST -> "latest" + SortOrder.POPULARITY -> "popular" + SortOrder.UPDATED -> "update" + else -> "" + }, + ) - when (filter) { - - is MangaListFilter.Search -> { - throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) - } - - is MangaListFilter.Advanced -> { - append(listUrl) - - append("?order=") - append( - when (filter.sortOrder) { - SortOrder.ALPHABETICAL -> "title" - SortOrder.ALPHABETICAL_DESC -> "titlereverse" - SortOrder.NEWEST -> "latest" - SortOrder.POPULARITY -> "popular" - SortOrder.UPDATED -> "update" - else -> "" - }, - ) - - filter.tags.forEach { - append("&") - append("genre[]".urlEncoded()) - append("=") - append(it.key) - } + filter.tags.forEach { + append("&") + append("genre[]".urlEncoded()) + append("=") + append(it.key) + } - filter.tagsExclude.forEach { - append("&") - append("genre[]".urlEncoded()) - append("=-") - append(it.key) - } + filter.tagsExclude.forEach { + append("&") + append("genre[]".urlEncoded()) + append("=-") + append(it.key) + } - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - when (it) { - MangaState.ONGOING -> append("ongoing") - MangaState.FINISHED -> append("completed") - MangaState.PAUSED -> append("hiatus") - else -> append("") - } - } + if (filter.states.isNotEmpty()) { + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + when (it) { + MangaState.ONGOING -> append("ongoing") + MangaState.FINISHED -> append("completed") + MangaState.PAUSED -> append("hiatus") + else -> append("") } - - append("&page=") - append(page.toString()) - } - - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) } } + + append("&page=") + append(page.toString()) } return parseMangaList(webClient.httpGet(url).parseHtml()) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/AinzScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/AinzScans.kt index e7147838..50cd8c2f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/AinzScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/AinzScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -12,5 +13,9 @@ internal class AinzScans(context: MangaLoaderContext) : override val listUrl = "/series" override val datePattern = "MMM d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comic21.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comic21.kt index 53271669..e0d212b7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comic21.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comic21.kt @@ -2,13 +2,18 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import java.util.Locale +import java.util.* @MangaSourceParser("COMIC21", "Comic21", "id") internal class Comic21(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.COMIC21, "comic21.me", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comicaso.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comicaso.kt index 3d628980..c4d6e581 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comicaso.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Comicaso.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -11,5 +12,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Comicaso(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.COMICASO, "comicaso.id", pageSize = 20, searchPageSize = 10) { override val encodedSrc = true - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt index 81b98120..a87f5bc4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/CosmicScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,9 @@ import java.util.* internal class CosmicScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.COSMIC_SCANS, "cosmicscans.id", pageSize = 30, searchPageSize = 30) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Dojing.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Dojing.kt index ae9a58dc..df07204d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Dojing.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Dojing.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Dojing(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.DOJING, "dojing.net", pageSize = 12, searchPageSize = 12) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt index 5a559d5c..0c4c0a02 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinDesuRip.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,9 @@ internal class DoujinDesuRip(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinKu.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinKu.kt index a2d9ebd1..a454df72 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinKu.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/DoujinKu.kt @@ -3,11 +3,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("DOUJINKU", "DoujinKu", "id", ContentType.HENTAI) internal class DoujinKu(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.DOUJINKU, "doujinku.xyz", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt index 2fe7e8a0..aafc352c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Futari.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("FUTARI", "Futari", "id") internal class Futari(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.FUTARI, "futari.info", pageSize = 25, searchPageSize = 10) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt index 2aa19173..af548020 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Kanzenin.kt @@ -3,11 +3,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KANZENIN", "Kanzenin", "id", ContentType.HENTAI) internal class Kanzenin(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KANZENIN, "kanzenin.info", pageSize = 27, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KataKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KataKomik.kt index 7e3b483c..b25394d2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KataKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KataKomik.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class KataKomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KATAKOMIK, "katakomik.my.id", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KiryuuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KiryuuParser.kt index 5586287d..bf1cf936 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KiryuuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KiryuuParser.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KIRYUU", "Kiryuu", "id") internal class KiryuuParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KIRYUU, "kiryuu.org", pageSize = 30, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomBatch.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomBatch.kt index 65727cc7..594c90c7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomBatch.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomBatch.kt @@ -3,11 +3,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KOMBATCH", "KomBatch", "id", ContentType.HENTAI) internal class KomBatch(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMBATCH, "kombatch.cc", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikAvParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikAvParser.kt index c3ee1621..72c17d56 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikAvParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikAvParser.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KOMIKAV", "KomikAv", "id") internal class KomikAvParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKAV, "komikav.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasa.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasa.kt index 7b4f2cb1..dfed949a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasa.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasa.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -17,5 +18,8 @@ internal class KomikDewasa(context: MangaLoaderContext) : searchPageSize = 10, ) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasaParser.kt index 49bfa587..0bad82d3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikDewasaParser.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -12,5 +13,8 @@ internal class KomikDewasaParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKDEWASA, "komikremaja.icu", pageSize = 20, searchPageSize = 10) { override val listUrl: String = "/komik" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikIndoParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikIndoParser.kt index 53b368ff..ecef96f4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikIndoParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikIndoParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class KomikIndoParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKINDO, "komikindo.co", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikPoi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikPoi.kt index 06c085b9..4da89b3b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikPoi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikPoi.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ import java.util.* internal class KomikPoi(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKPOI, "komikpoi.com", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSan.kt index 05a8c428..e4f667c6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSan.kt @@ -18,27 +18,30 @@ internal class KomikSan(context: MangaLoaderContext) : override val selectMangaListImg = "img.attachment-medium" override val listUrl = "/list" override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search?search=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/?order=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "title" SortOrder.ALPHABETICAL_DESC -> "titlereverse" SortOrder.NEWEST -> "latest" @@ -69,12 +72,6 @@ internal class KomikSan(context: MangaLoaderContext) : append("&page=") append(page.toString()) } - - null -> { - append(listUrl) - append("/?order=update&page=") - append(page.toString()) - } } } return parseMangaList(webClient.httpGet(url).parseHtml()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSay.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSay.kt index c7f614c7..46f9edd6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSay.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikSay.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class KomikSay(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKSAY, "komiksay.info", pageSize = 30, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt index ed02edcb..9abc9a0c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/KomikTapParser.kt @@ -3,11 +3,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("KOMIKTAP", "KomikTap", "id", ContentType.HENTAI) internal class KomikTapParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKTAP, "komiktap.info", pageSize = 25, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt index f0792ad9..5a662a75 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt @@ -20,30 +20,37 @@ internal class Komikcast(context: MangaLoaderContext) : override val sourceLocale: Locale = Locale.ENGLISH override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.ALPHABETICAL) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - override val isTagsExclusionSupported = false - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/page/") append(page.toString()) append("/?s=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("/page/") append(page.toString()) append("/?type=") append( - when (filter.sortOrder) { + when (order) { SortOrder.ALPHABETICAL -> "&orderby=titleasc" SortOrder.ALPHABETICAL_DESC -> "&orderby=titledesc" SortOrder.POPULARITY -> "&orderby=popular" @@ -68,13 +75,6 @@ internal class Komikcast(context: MangaLoaderContext) : } } } - - null -> { - append("/page/") - append(page.toString()) - append(listUrl) - append("/?order=update") - } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikstation.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikstation.kt index 51c63a5c..83f687d5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikstation.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikstation.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Komikstation(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKSTATION, "komikstation.co", pageSize = 30, searchPageSize = 30) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komiku.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komiku.kt index 94013dc3..4bb29973 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komiku.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komiku.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Komiku(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.KOMIKU, "komiku.com", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt index e69d264b..61c380f2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaShiro.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -12,5 +13,8 @@ import java.util.* internal class MangaShiro(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGASHIRO, "mangashiro.me", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaTaleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaTaleParser.kt index 482a7b4d..6397a2e1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaTaleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangaTaleParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class MangaTaleParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGATALE, "mangatale.id", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangakKita.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangakKita.kt index a357e077..82c4c4c4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangakKita.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MangakKita.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class MangakKita(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAKITA, "mangakita.id", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt index d23da43f..a6bdf0d8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ internal class Mangakyo(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAKYO, "mangakyo.vip", pageSize = 40, searchPageSize = 20) { override val listUrl = "/komik" override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt index 2f66c234..eb8cf8f9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLand.kt @@ -3,12 +3,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANHWALAND", "ManhwaLand.vip", "id", ContentType.HENTAI) internal class ManhwaLand(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANHWALAND, "manhwaland.vip", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLandInk.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLandInk.kt index 9185128c..87624ec8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLandInk.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaLandInk.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,6 +16,9 @@ internal class ManhwaLandInk(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val datePattern = "MMM d, yyyy" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaListOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaListOrg.kt index 127d575e..5082fd30 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaListOrg.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaListOrg.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class ManhwaListOrg(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANHWALIST_ORG, "manhwalist.org", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH override val listUrl = "/manhwa" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaPlus.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaPlus.kt index 7eb9d7f7..36c2a11d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaPlus.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaPlus.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -12,5 +13,8 @@ internal class ManhwaPlus(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANHWAPLUS, "manhwablue.com", 20, 10) { override val sourceLocale: Locale = Locale.ENGLISH override val listUrl = "/komik" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt index 0cd393c2..a95afc2a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwadesuParser.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class ManhwadesuParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANHWADESU, "manhwadesu.cc", pageSize = 20, searchPageSize = 10) { override val listUrl = "/komik" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiHentai.kt index 720c0ba3..10e17af4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MiHentai.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class MiHentai(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MIHENTAI, "mihentai.com", pageSize = 30, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MonzeeKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MonzeeKomik.kt index 8b9b9a90..a1ed0eee 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MonzeeKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/MonzeeKomik.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -16,5 +17,8 @@ internal class MonzeeKomik(context: MangaLoaderContext) : searchPageSize = 10, ) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Natsu.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Natsu.kt index 44ae2566..496e33c4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Natsu.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Natsu.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NATSU", "Natsu", "id") internal class Natsu(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NATSU, "natsu.id", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Noromax.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Noromax.kt index c82223bf..9e3929c4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Noromax.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Noromax.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class Noromax(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NOROMAX, "noromax.my.id", pageSize = 20, searchPageSize = 10) { override val listUrl = "/Komik" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SekaikomikParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SekaikomikParser.kt index 86f07d7c..5983faca 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SekaikomikParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SekaikomikParser.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("SEKAIKOMIK", "SekaiKomik", "id") internal class SekaikomikParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SEKAIKOMIK, "sekaikomik.guru", pageSize = 20, searchPageSize = 100) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sektedoujin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sektedoujin.kt index 9ff7640c..fa08ce3f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sektedoujin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sektedoujin.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Sektedoujin(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SEKTEDOUJIN, "sektedoujin.cc", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sheakomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sheakomik.kt index 50b05c2a..1bcac95f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sheakomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Sheakomik.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -11,5 +12,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Sheakomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SHEAKOMIK, "sheakomik.com", pageSize = 40, searchPageSize = 40) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt index 49e3fde5..32f70253 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Shirakami.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,6 +11,9 @@ import java.util.* internal class Shirakami(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SHIRAKAMI, "shirakami.xyz", pageSize = 10, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt index 93fc1f25..6cff3375 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SirenKomik.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("SIRENKOMIK", "SirenKomik", "id") internal class SirenKomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SIRENKOMIK, "sirenkomik.my.id", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SoulScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SoulScans.kt index 6f019f0b..6b34da06 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SoulScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/SoulScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class SoulScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SOULSCANS, "soulscans.my.id", pageSize = 30, searchPageSize = 30) { override val datePattern = "MMM d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Tukangkomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Tukangkomik.kt index c5ee615b..3117f39b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Tukangkomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Tukangkomik.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class Tukangkomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TUKANGKOMIK, "tukangkomik.id", pageSize = 20, searchPageSize = 20) { override val datePattern = "MMM d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WarungKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WarungKomik.kt index 3561099f..a05ef03e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WarungKomik.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WarungKomik.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("WARUNGKOMIK", "WarungKomik", "id") internal class WarungKomik(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.WARUNGKOMIK, "warungkomik.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WestmangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WestmangaParser.kt index 15e29255..5ecac8a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WestmangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/WestmangaParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class WestmangaParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.WESTMANGA, "westmanga.fun", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YuriLab.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YuriLab.kt index 812364f7..63091c6e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YuriLab.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/YuriLab.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ import java.util.* internal class YuriLab(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.YURILAB, "yurilab.my.id", pageSize = 20, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/Walpurgiscan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/Walpurgiscan.kt index 19099b78..4e955cc5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/Walpurgiscan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/Walpurgiscan.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.it import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,8 @@ internal class Walpurgiscan(context: MangaLoaderContext) : searchPageSize = 20, ) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/WitComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/WitComics.kt index 5e93db63..8f1180c3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/WitComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/it/WitComics.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.it import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("WITCOMICS", "WitComics", "it") internal class WitComics(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.WITCOMICS, "www.witcomics.net", pageSize = 5, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt index e4ab18c3..f48f1640 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaJp.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ja import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -10,5 +11,8 @@ import java.util.* internal class MangaJp(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAJP, "mangajp.top", pageSize = 54, searchPageSize = 10) { override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaMate.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaMate.kt index 4c739ad6..462edbc8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaMate.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/MangaMate.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ja import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class MangaMate(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAMATE, "manga-mate.org", pageSize = 10, searchPageSize = 10) { override val datePattern = "M月 d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/Rawkuma.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/Rawkuma.kt index 6430cccf..709aed56 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/Rawkuma.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ja/Rawkuma.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.ja import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import java.util.* @@ -11,5 +12,8 @@ internal class Rawkuma(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.RAWKUMA, "rawkuma.com", pageSize = 54, searchPageSize = 54) { override val datePattern = "MMM d, yyyy" override val sourceLocale: Locale = Locale.ENGLISH - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pl/SkanlacjeFeniksy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pl/SkanlacjeFeniksy.kt index 695e80ca..213bdb30 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pl/SkanlacjeFeniksy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pl/SkanlacjeFeniksy.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.pl import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,8 @@ internal class SkanlacjeFeniksy(context: MangaLoaderContext) : searchPageSize = 10, ) { override val datePattern = "d MMMM, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DemonSect.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DemonSect.kt index 3217da7a..bc7bf3f1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DemonSect.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/DemonSect.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class DemonSect(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.DEMONSECT, "seitacelestial.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/comics" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/MangasOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/MangasOnline.kt index 9255486d..0a87c1a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/MangasOnline.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/MangasOnline.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.pt import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGASONLINE", "MangasOnline", "pt") internal class MangasOnline(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGASONLINE, "mangasonline.cc", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt index 30657c90..3aa7c01a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class SssScanlator(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Tsundoku.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Tsundoku.kt index 59a765b8..84379552 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Tsundoku.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/Tsundoku.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Tsundoku(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TSUNDOKU, "tsundoku.com.br", pageSize = 50, searchPageSize = 50) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Doujin69.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Doujin69.kt index 3f46c8d1..14ebf94a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Doujin69.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Doujin69.kt @@ -4,6 +4,7 @@ import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -12,5 +13,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Doujin69(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.DOUJIN69, "doujin69.com", pageSize = 40, searchPageSize = 21) { override val listUrl = "/doujin" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/EcchiDoujin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/EcchiDoujin.kt index 7a84e7e9..b347e4fb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/EcchiDoujin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/EcchiDoujin.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class EcchiDoujin(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ECCHIDOUJIN, "ecchi-doujin.com", pageSize = 30, searchPageSize = 10) { override val listUrl = "/doujin" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/InuManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/InuManga.kt index 600cea5d..7c47454b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/InuManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/InuManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("INUMANGA", "InuManga", "th") internal class InuManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.INUMANGA, "www.inu-manga.com", pageSize = 40, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/LamiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/LamiManga.kt index 92223d6e..e35252fb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/LamiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/LamiManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("LAMIMANGA", "LamiManga", "th") internal class LamiManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.LAMIMANGA, "mangalami.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MafiaManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MafiaManga.kt index 5a576982..f0ceeb9d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MafiaManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MafiaManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MAFIAMANGA", "MafiaManga", "th") internal class MafiaManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MAFIAMANGA, "mafia-manga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Makimaaaaa.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Makimaaaaa.kt index ba12c809..fd91b6e1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Makimaaaaa.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Makimaaaaa.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MAKIMAAAAA", "Makimaaaaa", "th") internal class Makimaaaaa(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MAKIMAAAAA, "makimaaaaa.com", pageSize = 30, searchPageSize = 30) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt index 4875cd3b..299b59a9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga168.kt @@ -3,11 +3,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGA168", "Manga168", "th", ContentType.HENTAI) internal class Manga168(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGA168, "manga168.net", pageSize = 40, searchPageSize = 30) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga689.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga689.kt index 0baf8b1f..cbbd6f4c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga689.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Manga689.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class Manga689(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGA689, "manga689.com", pageSize = 45, searchPageSize = 21) { override val listUrl = "/read" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaKimi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaKimi.kt index d82e0a95..a5ce81bd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaKimi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaKimi.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGAKIMI", "MangaKimi", "th") internal class MangaKimi(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAKIMI, "www.mangakimi.com", pageSize = 40, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaMoons.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaMoons.kt index 71aef053..60c089e7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaMoons.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/MangaMoons.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGAMOONS", "MangaMoons", "th") internal class MangaMoons(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAMOONS, "manga-moons.net", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/NtrManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/NtrManga.kt index 6c43f394..c116b8bc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/NtrManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/NtrManga.kt @@ -3,11 +3,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NTRMANGA", "NtrManga", "th", ContentType.HENTAI) internal class NtrManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NTRMANGA, "www.ntr-manga.com", pageSize = 30, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/PopsManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/PopsManga.kt index 55159826..49983300 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/PopsManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/PopsManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("POPSMANGA", "PopsManga", "th") internal class PopsManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.POPSMANGA, "popsmanga.com", pageSize = 20, searchPageSize = 14) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ReaperTrans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ReaperTrans.kt index 6758eccb..e0ee0d94 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ReaperTrans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ReaperTrans.kt @@ -2,13 +2,17 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import java.util.Locale +import java.util.* @MangaSourceParser("REAPERTRANS", "ReaperTrans", "th") internal class ReaperTrans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.REAPERTRANS, "reapertrans.com", pageSize = 30, searchPageSize = 14) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) override val sourceLocale: Locale = Locale.ENGLISH } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Sodsaime.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Sodsaime.kt index d25cbe91..cb9b5c28 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Sodsaime.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/Sodsaime.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class Sodsaime(context: MangaLoaderContext) : pageSize = 40, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/TanukiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/TanukiManga.kt index 29401b44..41c05303 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/TanukiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/TanukiManga.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class TanukiManga(context: MangaLoaderContext) : pageSize = 40, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ThaiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ThaiManga.kt index e80e63ad..e2b16743 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ThaiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ThaiManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("THAIMANGA", "ThaiManga", "th") internal class ThaiManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.THAIMANGA, "www.thaimanga.net", pageSize = 40, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt index bd8f3016..5921d74b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToomtamManga.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,8 @@ internal class ToomtamManga(context: MangaLoaderContext) : pageSize = 30, searchPageSize = 28, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToonHunterParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToonHunterParser.kt index 4e8baf8f..875e347c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToonHunterParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/th/ToonHunterParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.th import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -9,5 +10,8 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class ToonHunterParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TOONHUNTER, "toonhunter.com", pageSize = 30, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt index 0acbf991..14d2a337 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AdonisFansub.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,9 @@ internal class AdonisFansub(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 20, ) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AduManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AduManga.kt index 7e22381b..50ef33ab 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AduManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AduManga.kt @@ -2,13 +2,19 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import java.util.Locale +import java.util.* @MangaSourceParser("ADUMANGA", "AduManga", "tr") internal class AduManga(context: MangaLoaderContext) : - MangaReaderParser(context, MangaParserSource.ADUMANGA, "adumanga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false - override val sourceLocale: Locale = Locale.ENGLISH + MangaReaderParser(context, MangaParserSource.ADUMANGA, "adumanga.com", pageSize = 20, searchPageSize = 10) { + + override val sourceLocale: Locale = Locale.ENGLISH + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AfroditScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AfroditScans.kt index cdbd1900..96078bdb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AfroditScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AfroditScans.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,9 @@ internal class AfroditScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.AFRODITSCANS, "afroditscans.com", pageSize = 20, searchPageSize = 10) { override val datePattern = "MMM d, yyyy" override val isNetShieldProtected = true - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt index 4e1c7990..50cd837e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ArcuraFansub.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,5 +11,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser internal class ArcuraFansub(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ARCURAFANSUB, "arcurafansub.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/seri" - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AthenaManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AthenaManga.kt index f5b12810..7facd7e6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AthenaManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/AthenaManga.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ATHENAMANGA", "AthenaManga", "tr") internal class AthenaManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ATHENAMANGA, "athenamanga.com", pageSize = 20, searchPageSize = 10) { - override val isMultipleTagsSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Ayatoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Ayatoon.kt index 328008ad..52adb6a2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Ayatoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Ayatoon.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("AYATOON", "AyaToon", "tr") internal class Ayatoon(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.AYATOON, "ayatoon.com", pageSize = 20, searchPageSize = 20) { - override val isTagsExclusionSupported = false + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/CultureSubs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/CultureSubs.kt index 7c3e6469..2101c9f3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/CultureSubs.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/CultureSubs.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("CULTURESUBS", "CultureSubs", "tr") internal class CultureSubs(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.CULTURESUBS, "culturesubs.com", pageSize = 20, searchPageSize = 10) { - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/GaiaToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/GaiaToon.kt index 1d46e727..9225acd3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/GaiaToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/GaiaToon.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("GAIATOON", "GaiaToon", "tr") internal class GaiaToon(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.GAIATOON, "gaiatoon.com", pageSize = 50, searchPageSize = 10) { - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Golgebahcesi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Golgebahcesi.kt index 1d0fd2bd..d797a458 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Golgebahcesi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Golgebahcesi.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("GOLGEBAHCESI", "GolgeBahcesi", "tr") internal class Golgebahcesi(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.GOLGEBAHCESI, "golgebahcesi.com", pageSize = 14, searchPageSize = 9) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt index aa8f360e..b7e26484 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaGezgini.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGAGEZGINI", "MangaGezgini", "tr") internal class MangaGezgini(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAGEZGINI, "mangagezgini.net", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt index 0dc672f4..0c3d471b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("MANGAKINGS", "MangaKings", "tr") internal class MangaKings(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.MANGAKINGS, "mangakings.com.tr", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt index 383bb62b..9430fd46 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Mangaefendisi.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class Mangaefendisi(context: MangaLoaderContext) : pageSize = 30, searchPageSize = 20, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt index a58a49b0..360a4ab8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MoonDaisyScans.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -15,5 +16,8 @@ internal class MoonDaisyScans(context: MangaLoaderContext) : pageSize = 20, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt index 47c0d79f..db92fbfc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NirvanaManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NIRVANAMANGA", "NirvanaManga", "tr") internal class NirvanaManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NIRVANAMANGA, "nirvanamanga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NoxScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NoxScans.kt index 85b34e62..08ddeff1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NoxScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NoxScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NOXSCANS", "NoxScans", "tr") internal class NoxScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NOXSCANS, "noxscans.com", pageSize = 30, searchPageSize = 20) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NyxManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NyxManga.kt index cd11712c..7cd7cc80 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NyxManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/NyxManga.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -10,6 +11,9 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("NYXMANGA", "NyxManga", "tr") internal class NyxManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.NYXMANGA, "nyxmanga.com", pageSize = 14, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/PatiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/PatiManga.kt index 9e4ffbc5..32f8ef4d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/PatiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/PatiManga.kt @@ -2,12 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("PATIMANGA", "PatiManga", "tr") internal class PatiManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.PATIMANGA, "www.patimanga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt index 3bce709c..84fbae72 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/Raindropteamfan.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,6 +15,9 @@ internal class Raindropteamfan(context: MangaLoaderContext) : pageSize = 25, searchPageSize = 10, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/RobinManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/RobinManga.kt index 8cae6550..77571b12 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/RobinManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/RobinManga.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ROBINMANGA", "RobinManga", "tr") internal class RobinManga(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ROBINMANGA, "robinmanga.com", pageSize = 20, searchPageSize = 25) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt index 2f9f9acd..652a743a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/SummerToon.kt @@ -2,12 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("SUMMERTOON", "SummerToon", "tr") internal class SummerToon(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.SUMMERTOON, "summertoon.biz", pageSize = 10, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt index 92de4866..00ad35cf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TarotScans.kt @@ -2,12 +2,16 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("TAROTSCANS", "TarotScans", "tr") internal class TarotScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TAROTSCANS, "www.tarotscans.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestScans.kt index d44b5621..4c24775c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("TEMPESTSCANS", "TempestScans", "tr") internal class TempestScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.TEMPESTSCANS, "adumanga.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestfansubParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestfansubParser.kt index fc24b6ee..27fb2258 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestfansubParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/TempestfansubParser.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @@ -14,5 +15,8 @@ internal class TempestfansubParser(context: MangaLoaderContext) : pageSize = 25, searchPageSize = 40, ) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ZenithScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ZenithScans.kt index 663a4289..574a8c84 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ZenithScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/ZenithScans.kt @@ -2,11 +2,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("ZENITHSCANS", "ZenithScans", "tr") internal class ZenithScans(context: MangaLoaderContext) : MangaReaderParser(context, MangaParserSource.ZENITHSCANS, "zenithscans.com", pageSize = 20, searchPageSize = 10) { - override val isTagsExclusionSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt index 1c137a78..b5263435 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt @@ -9,53 +9,56 @@ import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.* -abstract class MangaWorldParser( +internal abstract class MangaWorldParser( context: MangaLoaderContext, source: MangaParserSource, domain: String, pageSize: Int = 16, ) : PagedMangaParser(context, source, pageSize) { - override val availableSortOrders: Set = - EnumSet.of( - SortOrder.POPULARITY, - SortOrder.ALPHABETICAL, - SortOrder.NEWEST, - SortOrder.ALPHABETICAL_DESC, - SortOrder.UPDATED, - ) + + override val availableSortOrders: Set = EnumSet.of( + SortOrder.POPULARITY, + SortOrder.ALPHABETICAL, + SortOrder.NEWEST, + SortOrder.ALPHABETICAL_DESC, + SortOrder.UPDATED, + ) override val defaultSortOrder: SortOrder get() = SortOrder.ALPHABETICAL override val configKeyDomain = ConfigKey.Domain(domain) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED, MangaState.PAUSED), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED, MangaState.PAUSED) - - override val isMultipleTagsSupported = true - - override suspend fun getListPage( - page: Int, - filter: MangaListFilter?, - ): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/archive?") - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("keyword=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { - if (filter.tags.isEmpty() && filter.states.isEmpty() && filter.sortOrder == SortOrder.UPDATED) return parseMangaList( + else -> { + if (filter.tags.isEmpty() && filter.states.isEmpty() && order == SortOrder.UPDATED) return parseMangaList( webClient.httpGet("https://$domain/?page=$page").parseHtml(), ) @@ -63,7 +66,7 @@ abstract class MangaWorldParser( filter.tags.joinTo(this, "&") { it.key.substringAfter("archive?") } } - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("&sort=most_read") SortOrder.ALPHABETICAL -> append("&sort=a-z") SortOrder.NEWEST -> append("&sort=newest") @@ -78,8 +81,6 @@ abstract class MangaWorldParser( else -> Unit } } - - null -> Unit } append("&page=$page") } @@ -103,13 +104,13 @@ abstract class MangaWorldParser( tags = tags, author = div.selectFirst(".author a")?.text(), state = - when (div.selectFirst(".status a")?.text()) { - "In corso" -> MangaState.ONGOING - "Finito" -> MangaState.FINISHED - "Droppato" -> MangaState.ABANDONED - "In pausa" -> MangaState.PAUSED - else -> null - }, + when (div.selectFirst(".status a")?.text()) { + "In corso" -> MangaState.ONGOING + "Finito" -> MangaState.FINISHED + "Droppato" -> MangaState.ABANDONED + "In pausa" -> MangaState.PAUSED + else -> null + }, source = source, isNsfw = isNsfwSource, ) @@ -117,7 +118,7 @@ abstract class MangaWorldParser( } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml() val genres = doc.select("div[aria-labelledby=genresDropdown] a").mapNotNullToSet { MangaTag( @@ -142,30 +143,30 @@ abstract class MangaWorldParser( val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() return manga.copy( altTitle = - doc.selectFirst(".meta-data .font-weight-bold:contains(Titoli alternativi:)") - ?.parent() - ?.ownText() - ?.substringAfter(": ") - ?.trim(), + doc.selectFirst(".meta-data .font-weight-bold:contains(Titoli alternativi:)") + ?.parent() + ?.ownText() + ?.substringAfter(": ") + ?.trim(), description = doc.getElementById("noidungm")?.text().orEmpty(), chapters = - doc.select(".chapters-wrapper .chapter a").mapChapters(reversed = true) { i, a -> - val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) - MangaChapter( - id = generateUid(url), - name = a.selectFirstOrThrow("span.d-inline-block").text(), - number = i + 1f, - volume = 0, - url = "$url?style=list", - scanlator = null, - uploadDate = - SimpleDateFormat("dd MMMM yyyy", Locale.ITALIAN).tryParse( - a.selectFirst(".chap-date")?.text(), - ), - branch = null, - source = source, - ) - }, + doc.select(".chapters-wrapper .chapter a").mapChapters(reversed = true) { i, a -> + val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) + MangaChapter( + id = generateUid(url), + name = a.selectFirstOrThrow("span.d-inline-block").text(), + number = i + 1f, + volume = 0, + url = "$url?style=list", + scanlator = null, + uploadDate = + SimpleDateFormat("dd MMMM yyyy", Locale.ITALIAN).tryParse( + a.selectFirst(".chap-date")?.text(), + ), + branch = null, + source = source, + ) + }, ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt index 3e6b7949..8662f877 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt @@ -62,15 +62,21 @@ internal abstract class MmrcmsParser( "مكتملة", ) - override val isMultipleTagsSupported = false - protected open val imgUpdated = "/cover/cover_250x350.jpg" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = false, + isSearchSupported = true, + ) - when (filter) { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -85,9 +91,9 @@ internal abstract class MmrcmsParser( return parseMangaList(webClient.httpGet(url).parseHtml()) } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.UPDATED) { + if (order == SortOrder.UPDATED) { val url = buildString { append("https://") append(domain) @@ -109,7 +115,7 @@ internal abstract class MmrcmsParser( append(it.key) } append("&sortBy=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views&asc=false") SortOrder.POPULARITY_ASC -> append("views&asc=true") SortOrder.ALPHABETICAL -> append("name&asc=true") @@ -120,16 +126,6 @@ internal abstract class MmrcmsParser( return parseMangaList(webClient.httpGet(url).parseHtml()) } } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/latest-release?page=") - append(page.toString()) - } - return parseMangaList(webClient.httpGet(url).parseHtml()) - } } } @@ -174,7 +170,7 @@ internal abstract class MmrcmsParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$tagUrl/").parseHtml() return doc.select("ul.list-category li").mapNotNullToSet { li -> val a = li.selectFirst("a") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt index 8e5951f1..90d99dea 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt @@ -32,20 +32,30 @@ internal abstract class NepnepParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL, SortOrder.POPULARITY, SortOrder.UPDATED) - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - override val isTagsExclusionSupported = true private val searchDoc = SoftSuspendLazy { webClient.httpGet("https://$domain/search/").parseHtml() } + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + ) + data class MangaWithLastUpdate( val manga: Manga, val lastUpdate: Long, val views: String, ) - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val doc = searchDoc.get() val json = JSONArray( doc.selectFirstOrThrow("script:containsData(MainFunction)").data() @@ -65,9 +75,8 @@ internal abstract class NepnepParser( val views = m.getString("v") //val viewMonth = m.getString("vm") - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (m.getString("s").contains(filter.query, ignoreCase = true) || (m.getJSONArray("al") .length() > 0 && m.getJSONArray("al").getString(0) .contains(filter.query, ignoreCase = true)) @@ -78,7 +87,7 @@ internal abstract class NepnepParser( } } - is MangaListFilter.Advanced -> { + else -> { val tags = filter.tags val tagsExcluded = filter.tagsExclude val tagsJson = m.getJSONArray("g").toString() @@ -111,22 +120,14 @@ internal abstract class NepnepParser( } sort = true } - - null -> { - mangaWithLastUpdateList.add( - MangaWithLastUpdate(addManga(href, imgUrl, m), lastUpdate, views), - ) - } } } if (sort) { - when (filter?.sortOrder) { + when (order) { SortOrder.POPULARITY -> mangaWithLastUpdateList.sortByDescending { it.views } SortOrder.UPDATED -> mangaWithLastUpdateList.sortByDescending { it.lastUpdate } SortOrder.ALPHABETICAL -> {} - else -> if (filter != null) { - throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}") - } + else -> throw IllegalArgumentException("Unsupported sort order: $order") } } return mangaWithLastUpdateList.map { it.manga } @@ -150,8 +151,7 @@ internal abstract class NepnepParser( ) } - - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = searchDoc.get() val tags = doc.selectFirstOrThrow("script:containsData(vm.AvailableFilters)").data() .substringAfter("\"Genre\"") @@ -186,17 +186,17 @@ internal abstract class NepnepParser( altTitle = null, state = when (doc.selectFirstOrThrow(".list-group-item:contains(Status:) a").text()) { "Ongoing (Scan)", "Ongoing (Publish)", - -> MangaState.ONGOING + -> MangaState.ONGOING "Complete (Scan)", "Complete (Publish)", - -> MangaState.FINISHED + -> MangaState.FINISHED "Cancelled (Scan)", "Cancelled (Publish)", "Discontinued (Scan)", "Discontinued (Publish)", - -> MangaState.ABANDONED + -> MangaState.ABANDONED "Hiatus (Scan)", "Hiatus (Publish)", - -> MangaState.PAUSED + -> MangaState.PAUSED else -> null }, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt index 883dfe25..4f645007 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.parsers.site.onemanga import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -11,8 +11,7 @@ internal abstract class OneMangaParser( context: MangaLoaderContext, source: MangaParserSource, domain: String, - pageSize: Int = 1, -) : PagedMangaParser(context, source, pageSize) { +) : SinglePageMangaParser(context, source) { override val configKeyDomain = ConfigKey.Domain(domain) @@ -21,16 +20,14 @@ internal abstract class OneMangaParser( keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val isSearchSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() - override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) + override suspend fun getFilterOptions() = MangaListFilterOptions() - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) { - return emptyList() - } + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = "https://$domain" val doc = webClient.httpGet(url).parseHtml() val manga = ArrayList() @@ -56,8 +53,6 @@ internal abstract class OneMangaParser( return manga } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { val fullUrl = manga.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt index c2b71bbd..cebff999 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt @@ -20,6 +20,15 @@ internal abstract class OtakuSanctuaryParser( override val configKeyDomain = ConfigKey.Domain(domain) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -45,68 +54,54 @@ internal abstract class OtakuSanctuaryParser( "Done", ) - override val isMultipleTagsSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val doc = - when (filter) { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val doc = when { + !filter.query.isNullOrEmpty() -> { + if (page > 1) { + return emptyList() + } + val url = buildString { + append("https://") + append(domain) + append("/Home/Search?search=") + append(filter.query.urlEncoded()) + } + webClient.httpGet(url).parseHtml().requireElementById("collection-manga") + } - is MangaListFilter.Search -> { - if (page > 1) { - return emptyList() - } + else -> { + if (filter.tags.isNotEmpty()) { val url = buildString { append("https://") append(domain) - append("/Home/Search?search=") - append(filter.query.urlEncoded()) - } - webClient.httpGet(url).parseHtml().requireElementById("collection-manga") - } - - is MangaListFilter.Advanced -> { - - if (filter.tags.isNotEmpty()) { - val url = buildString { - append("https://") - append(domain) - append("/Genre/MangaGenrePartial?id=") - filter.tags.oneOrThrowIfMany()?.let { - append(it.key) - } - append("&lang=") - append(lang) - append("&offset=") - append(page * pageSize) - append("&pagesize=") - append(pageSize) - } - webClient.httpGet(url).parseHtml() - - } else { - val payload = HashMap() - payload["Lang"] = lang - payload["Page"] = page.toString() - payload["Type"] = "Include" - when (filter.sortOrder) { - SortOrder.NEWEST -> payload["Dir"] = "CreatedDate" - SortOrder.UPDATED -> payload["Dir"] = "NewPostedDate" - else -> payload["Dir"] = "NewPostedDate" + append("/Genre/MangaGenrePartial?id=") + filter.tags.oneOrThrowIfMany()?.let { + append(it.key) } - webClient.httpPost("https://$domain/$listUrl", payload).parseHtml() + append("&lang=") + append(lang) + append("&offset=") + append(page * pageSize) + append("&pagesize=") + append(pageSize) } + webClient.httpGet(url).parseHtml() - } - - null -> { + } else { val payload = HashMap() payload["Lang"] = lang payload["Page"] = page.toString() payload["Type"] = "Include" - payload["Dir"] = "NewPostedDate" + when (order) { + SortOrder.NEWEST -> payload["Dir"] = "CreatedDate" + SortOrder.UPDATED -> payload["Dir"] = "NewPostedDate" + else -> payload["Dir"] = "NewPostedDate" + } webClient.httpPost("https://$domain/$listUrl", payload).parseHtml() } + } + } return doc.select("div.picture-card").map { div -> @@ -130,7 +125,7 @@ internal abstract class OtakuSanctuaryParser( protected open val selectBodyTag = "div#genre-table a" - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/Home/LoadingGenresMenu").parseHtml() return doc.select(selectBodyTag).mapNotNullToSet { a -> val href = a.attr("href").substringAfterLast("/").substringBefore("?") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt index 241a5262..b3462790 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.coroutineScope import org.json.JSONArray import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -18,8 +18,7 @@ internal abstract class PizzaReaderParser( context: MangaLoaderContext, source: MangaParserSource, domain: String, - pageSize: Int = 20, -) : PagedMangaParser(context, source, pageSize) { +) : SinglePageMangaParser(context, source) { override val configKeyDomain = ConfigKey.Domain(domain) @@ -29,10 +28,18 @@ internal abstract class PizzaReaderParser( } override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) - override val availableContentRating: Set = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT) - override val isTagsExclusionSupported = true + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + availableContentRating = EnumSet.of(ContentRating.SAFE, ContentRating.ADULT), + ) @JvmField protected val ongoing: Set = hashSetOf( @@ -71,10 +78,7 @@ internal abstract class PizzaReaderParser( protected open val hiatusFilter = "in pausa" protected open val abandonedFilter = "droppato" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) { - return emptyList() - } + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { var foundTag = true var foundTagExclude = true var foundState = true @@ -82,8 +86,8 @@ internal abstract class PizzaReaderParser( val manga = ArrayList() - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { val jsonManga = webClient.httpGet("https://$domain/api/search/${filter.query.urlEncoded()}").parseJson() .getJSONArray("comics") for (i in 0 until jsonManga.length()) { @@ -93,7 +97,7 @@ internal abstract class PizzaReaderParser( } } - is MangaListFilter.Advanced -> { + else -> { val jsonManga = webClient.httpGet("https://$domain/api/comics").parseJson().getJSONArray("comics") for (i in 0 until jsonManga.length()) { @@ -164,17 +168,6 @@ internal abstract class PizzaReaderParser( } } } - - null -> { - val jsonManga = webClient.httpGet("https://$domain/api/comics").parseJson().getJSONArray("comics") - for (i in 0 until jsonManga.length()) { - val j = jsonManga.getJSONObject(i) - val href = "/api" + j.getString("url") - manga.add( - addManga(href, j), - ) - } - } } return manga @@ -212,9 +205,6 @@ internal abstract class PizzaReaderParser( ) } - - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val fullUrl = manga.url.toAbsoluteUrl(domain) val json = webClient.httpGet(fullUrl).parseJson().getJSONObject("comic") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt index d39dc3d9..cf60ee0e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt @@ -18,22 +18,29 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, override val configKeyDomain = ConfigKey.Domain("www.brmangas.net") - override val isMultipleTagsSupported = false - override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = 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, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { append("/page/$page/") } @@ -41,7 +48,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("category/") @@ -51,7 +58,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, } } } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("/") SortOrder.UPDATED -> append("manga/") else -> append("manga/") @@ -62,37 +69,26 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, } } - - null -> { - append("manga/") - if (page > 1) { - append("page/$page/") - } - } } } val doc = webClient.httpGet(url).parseHtml() - val item = - when (filter) { + val item = when { - is MangaListFilter.Search -> { - doc.select("div.listagem div.item") - } + !filter.query.isNullOrEmpty() -> { + doc.select("div.listagem div.item") + } - is MangaListFilter.Advanced -> { - if (filter.sortOrder == SortOrder.POPULARITY && filter.tags.isEmpty()) { - doc.select("div.listagem")[1].select("div.item") // To remove the 6 mangas updated on the home page - } else { - doc.select("div.listagem div.item") - } + else -> { + if (order == SortOrder.POPULARITY && filter.tags.isEmpty()) { + doc.select("div.listagem")[1].select("div.item") // To remove the 6 mangas updated on the home page + } else { + doc.select("div.listagem div.item") } - - null -> doc.select("div.listagem div.item") - } + } return item.map { div -> val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") Manga( @@ -112,7 +108,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/lista-de-generos-de-manga/").parseHtml() return doc.select(".genres_page a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt index 460179e7..0c6a9dc9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt @@ -9,7 +9,7 @@ import java.util.* @Broken @MangaSourceParser("LERMANGA", "LerManga", "pt") -class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.LERMANGA, 24) { +internal class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.LERMANGA, 24) { override val availableSortOrders: Set = EnumSet.of( @@ -25,16 +25,19 @@ class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar override val configKeyDomain = ConfigKey.Domain("lermanga.org") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val isSearchSupported = false - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) @@ -45,13 +48,13 @@ class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar append(page.toString()) } - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/genero/") @@ -61,7 +64,7 @@ class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar append("/?orderby=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "modified&order=desc" SortOrder.UPDATED_ASC -> "modified&order=asc" SortOrder.POPULARITY -> "views&order=desc" @@ -74,8 +77,6 @@ class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar }, ) } - - null -> append("/?orderby=modified&order=desc") } } val doc = webClient.httpGet(url).parseHtml() @@ -100,7 +101,7 @@ class LerManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml().requireElementById("menu-header") return doc.select("#menu-item:contains(GÊNERO) ul li a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt index 4abefa32..f0f3d021 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt @@ -13,7 +13,8 @@ import java.util.* @Broken @MangaSourceParser("LERMANGAONLINE", "LerMangaOnline", "pt") -class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.LERMANGAONLINE, 20) { +internal class LerMangaOnline(context: MangaLoaderContext) : + PagedMangaParser(context, MangaParserSource.LERMANGAONLINE, 20) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) @@ -24,16 +25,22 @@ class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Ma keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { append("page/") append(page.toString()) @@ -43,7 +50,7 @@ class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Ma append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { filter.tags.oneOrThrowIfMany()?.let { append(it.key) append('/') @@ -55,14 +62,6 @@ class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Ma append('/') } } - - null -> { - if (page > 1) { - append("page/") - append(page.toString()) - append('/') - } - } } } @@ -91,7 +90,7 @@ class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Ma } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml().requireElementById("sub-menu") return doc.select("ul.container li a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt index fadcbe0f..8aeec24b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt @@ -15,7 +15,8 @@ import java.util.zip.ZipInputStream @Broken // Not dead but totally changed structure @MangaSourceParser("RANDOMSCANS", "LuratoonScan", "pt") -internal class LuratoonScansParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.RANDOMSCANS), +internal class LuratoonScansParser(context: MangaLoaderContext) : + SinglePageMangaParser(context, MangaParserSource.RANDOMSCANS), Interceptor { override val availableSortOrders = setOf(SortOrder.ALPHABETICAL) @@ -24,17 +25,15 @@ internal class LuratoonScansParser(context: MangaLoaderContext) : MangaParser(co override fun getRequestHeaders(): Headers = Headers.Builder().add("User-Agent", config[userAgentKey]).build() - override val isSearchSupported = false - override val isTagsExclusionSupported = false - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - if (offset > 0) { - return emptyList() - } - require(filter !is MangaListFilter.Search) { ErrorMessages.SEARCH_NOT_SUPPORTED } + override suspend fun getFilterOptions() = MangaListFilterOptions() + + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { + require(filter.query.isNullOrEmpty()) { ErrorMessages.SEARCH_NOT_SUPPORTED } val url = urlBuilder() - val tag = (filter as? MangaListFilter.Advanced)?.tags?.oneOrThrowIfMany() + val tag = filter.tags.oneOrThrowIfMany() if (tag == null) { url.addPathSegment("todas-as-obras") } else { @@ -126,8 +125,6 @@ internal class LuratoonScansParser(context: MangaLoaderContext) : MangaParser(co } } - override suspend fun getAvailableTags(): Set = emptySet() - override fun intercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) if (response.mimeType == "application/octet-stream") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt index 0b69c8eb..3c956d86 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt @@ -10,7 +10,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("MANGAONLINE", "MangaOnline.biz", "pt") -class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAONLINE, 20) { +internal class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAONLINE, 20) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) @@ -21,22 +21,28 @@ class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Manga keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search/") append(filter.query.urlEncoded()) append('/') } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/genero/") @@ -48,8 +54,6 @@ class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Manga } } - null -> append("/manga/") - } if (page > 1) { append("page/") @@ -79,7 +83,7 @@ class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, Manga } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/generos/").parseHtml() return doc.select(".wp-content p a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt index 7985c9b4..9c8438ef 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt @@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.util.* import java.util.* @MangaSourceParser("MUITOHENTAI", "MuitoHentai", "pt", ContentType.HENTAI) -class MuitoHentai(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MUITOHENTAI, 24) { +internal class MuitoHentai(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MUITOHENTAI, 24) { override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) @@ -20,22 +20,28 @@ class MuitoHentai(context: MangaLoaderContext) : PagedMangaParser(context, Manga keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { if (page > 1) return emptyList() append("/buscar-manga/?q=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("/mangas") filter.tags.oneOrThrowIfMany()?.let { @@ -47,12 +53,6 @@ class MuitoHentai(context: MangaLoaderContext) : PagedMangaParser(context, Manga append(page.toString()) append('/') } - - null -> { - append("/mangas/") - append(page.toString()) - append('/') - } } } val doc = webClient.httpGet(url).parseHtml() @@ -77,7 +77,7 @@ class MuitoHentai(context: MangaLoaderContext) : PagedMangaParser(context, Manga } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/generos-dos-mangas/").parseHtml() return doc.select("div.content a.profileSideBar").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt index 3dd0902e..a4547b68 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.site.pt import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -11,19 +11,23 @@ import java.util.* @Broken @MangaSourceParser("ONEPIECEEX", "OnePieceEx", "pt") -class OnePieceEx(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.ONEPIECEEX, 1) { +internal class OnePieceEx(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.ONEPIECEEX) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) override val configKeyDomain = ConfigKey.Domain("onepieceex.net") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities() + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - if (page > 1) return emptyList() + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { return listOf( Manga( id = generateUid("https://$domain/mangas/leitor/"), @@ -67,8 +71,6 @@ class OnePieceEx(context: MangaLoaderContext) : PagedMangaParser(context, MangaP ) } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { if (manga.url.endsWith("/leitor/")) { val chap = diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt index 9c7f3bfa..58c2c456 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.site.pt import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -13,65 +13,58 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("YUGENMANGAS", "YugenApp", "pt") -class YugenMangas(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.YUGENMANGAS, 28) { +internal class YugenMangas(context: MangaLoaderContext) : + SinglePageMangaParser(context, MangaParserSource.YUGENMANGAS) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL) override val configKeyDomain = ConfigKey.Domain("yugenmangasbr.voblog.xyz") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { + val json = when { - if (page > 1) { - return emptyList() - } + !filter.query.isNullOrEmpty() -> { - val json = - when (filter) { + val url = buildString { + append("https://api.") + append(domain) + append("/api/series/?search=") + append(filter.query.urlEncoded()) + } + webClient.httpGet(url).parseJsonArray() + } - is MangaListFilter.Search -> { + else -> { + if (order == SortOrder.UPDATED) { val url = buildString { append("https://api.") append(domain) - append("/api/series/?search=") - append(filter.query.urlEncoded()) + append("/api/latest_updates/") } webClient.httpGet(url).parseJsonArray() - } - - is MangaListFilter.Advanced -> { - - if (filter.sortOrder == SortOrder.UPDATED) { - val url = buildString { - append("https://api.") - append(domain) - append("/api/latest_updates/") - } - webClient.httpGet(url).parseJsonArray() - } else { - val url = buildString { - append("https://api.") - append(domain) - append("/api/series_novels/all_series/") - } - webClient.httpGet(url).parseJson().getJSONArray("series") - } - - } - - null -> { + } else { val url = buildString { append("https://api.") append(domain) - append("/api/latest_updates/") + append("/api/series_novels/all_series/") } - webClient.httpGet(url).parseJsonArray() + webClient.httpGet(url).parseJson().getJSONArray("series") } + } + } return json.mapJSON { j -> val slug = j.getString("slug") @@ -176,6 +169,4 @@ class YugenMangas(context: MangaLoaderContext) : PagedMangaParser(context, Manga } return pages } - - override suspend fun getAvailableTags(): Set = emptySet() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt index 28a51de2..aaa4974e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt @@ -16,25 +16,41 @@ import java.util.* internal class AComics(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.ACOMICS, pageSize = 10) { - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL, SortOrder.POPULARITY) - - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val availableSortOrders: Set = EnumSet.of( + SortOrder.UPDATED, + SortOrder.ALPHABETICAL, + SortOrder.POPULARITY, + ) override val configKeyDomain = ConfigKey.Domain("acomics.ru") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + init { paginator.firstPage = 0 searchPaginator.firstPage = 0 context.cookieJar.insertCookies(domain, "ageRestrict=18") } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage( + page: Int, + order: SortOrder, + filter: MangaListFilter, + ): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 0) { return emptyList() } @@ -42,12 +58,12 @@ internal class AComics(context: MangaLoaderContext) : append(filter.query) } - is MangaListFilter.Advanced -> { + else -> { append("/comics?ratings[]=1&ratings[]=2&ratings[]=3&ratings[]=4&ratings[]=5&ratings[]=6&skip=") - append((page * 10).toString()) + append(page * 10) append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "last_update" SortOrder.ALPHABETICAL -> "serial_name" SortOrder.POPULARITY -> "subscr_count" @@ -73,11 +89,6 @@ internal class AComics(context: MangaLoaderContext) : ) } } - - null -> { - append("/comics?ratings[]=1&ratings[]=2&ratings[]=3&ratings[]=4&ratings[]=5&ratings[]=6&sort=last_update&skip=") - append((page * 20).toString()) - } } } @@ -108,10 +119,6 @@ internal class AComics(context: MangaLoaderContext) : private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - private suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() 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 92133213..d8a946c5 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 @@ -25,20 +25,25 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont SortOrder.ALPHABETICAL, ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsCache.get().values.toSet(), + ) + override fun getRequestHeaders(): Headers = Headers.Builder() .add("User-Agent", UserAgents.KOTATSU) .build() private val tagsCache = SuspendLazy(::fetchTags) - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List { - if (query != null && page != searchPaginator.firstPage) { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + if (!filter.query.isNullOrEmpty() && page != searchPaginator.firstPage) { return emptyList() } val domain = domain @@ -46,16 +51,16 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont append("https://") append(domain) append("/manga/api/?limit=20&order=") - append(getSortKey(sortOrder)) + append(getSortKey(order)) append("&page=") append(page) - if (!tags.isNullOrEmpty()) { + if (filter.tags.isNotEmpty()) { append("&genres=") - appendAll(tags, ",") { it.key } + appendAll(filter.tags, ",") { it.key } } - if (query != null) { + if (!filter.query.isNullOrEmpty()) { append("&search=") - append(query) + append(filter.query) } } val json = webClient.httpGet(url).parseJson().getJSONArray("response") @@ -104,7 +109,6 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont ?: throw ParseException("Invalid response", url) val baseChapterUrl = manga.url + "/chapter/" val chaptersList = json.getJSONObject("chapters").getJSONArray("list") - val totalChapters = chaptersList.length() return manga.copy( tags = json.getJSONArray("genres").mapJSONToSet { MangaTag( @@ -155,10 +159,6 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont } } - override suspend fun getAvailableTags(): Set { - return tagsCache.get().values.toSet() - } - private fun getSortKey(sortOrder: SortOrder) = when (sortOrder) { SortOrder.ALPHABETICAL -> "name" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt index dfbafffb..967a3d9b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt @@ -16,9 +16,10 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("MANGA_WTF", "MangaWtf", "ru") -class MangaWtfParser( +internal class MangaWtfParser( context: MangaLoaderContext, ) : PagedMangaParser(context, MangaParserSource.MANGA_WTF, pageSize = 20) { + override val availableSortOrders: Set = EnumSet.of( SortOrder.POPULARITY, @@ -30,27 +31,30 @@ class MangaWtfParser( @InternalParsersApi override val configKeyDomain = ConfigKey.Domain("manga.wtf") - override val isTagsExclusionSupported = true + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) - override val availableStates: Set = - EnumSet.of( + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of( MangaState.UPCOMING, MangaState.PAUSED, MangaState.ONGOING, MangaState.FINISHED, - ) - - override val availableContentRating: Set = EnumSet.allOf(ContentRating::class.java) + ), + availableContentRating = EnumSet.allOf(ContentRating::class.java), + ) init { paginator.firstPage = 0 searchPaginator.firstPage = 0 } - override suspend fun getListPage( - page: Int, - filter: MangaListFilter?, - ): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = urlBuilder("api") .addPathSegment("v2") @@ -58,22 +62,16 @@ class MangaWtfParser( .addQueryParameter("page", page.toString()) .addQueryParameter("size", pageSize.toString()) .addQueryParameter("type", "COMIC") - when (filter) { - is MangaListFilter.Advanced -> { + when { + filter.query.isNullOrEmpty() -> { url.addQueryParameter( "sort", - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "updatedAt,desc" SortOrder.POPULARITY -> "viewsCount,desc" SortOrder.RATING -> "likesCount,desc" SortOrder.NEWEST -> "createdAt,desc" - SortOrder.ALPHABETICAL, - SortOrder.ALPHABETICAL_DESC, - SortOrder.UPDATED_ASC, - SortOrder.POPULARITY_ASC, - SortOrder.RATING_ASC, - SortOrder.NEWEST_ASC, - -> throw IllegalArgumentException("Unsupported ${filter.sortOrder}") + else -> throw IllegalArgumentException("Unsupported ${order}") }, ) if (filter.tags.isNotEmpty()) { @@ -110,11 +108,9 @@ class MangaWtfParser( } } - is MangaListFilter.Search -> { + else -> { url.addQueryParameter("search", filter.query) } - - null -> Unit } val ja = webClient.httpGet(url.build()).parseJsonArray() return ja.mapJSON { jo -> jo.toManga() } @@ -141,13 +137,13 @@ class MangaWtfParser( tags = jo.getJSONArray("labels").mapJSONToSet { it.toMangaTag() }, state = jo.getStringOrNull("status")?.toMangaState(), author = - jo.getJSONArray("relations").toJSONList().firstNotNullOfOrNull { - if (it.getStringOrNull("type") == "AUTHOR") { - it.getJSONObject("publisher").getStringOrNull("name") - } else { - null - } - }, + jo.getJSONArray("relations").toJSONList().firstNotNullOfOrNull { + if (it.getStringOrNull("type") == "AUTHOR") { + it.getJSONObject("publisher").getStringOrNull("name") + } else { + null + } + }, source = source, largeCoverUrl = null, description = jo.getString("description").nl2br(), @@ -172,10 +168,8 @@ class MangaWtfParser( } } - override suspend fun getAvailableTags(): Set { - val url = - urlBuilder("api") - .addPathSegment("label") + private suspend fun fetchAvailableTags(): Set { + val url = urlBuilder("api").addPathSegment("label") val json = webClient.httpGet(url.build()).parseJson() return json.getJSONArray("content").mapJSONToSet { jo -> MangaTag( @@ -215,10 +209,10 @@ class MangaWtfParser( MangaChapter( id = generateUid(jo.getString("id")), name = - jo.getStringOrNull("name") ?: buildString { - if (volume > 0) append("Том ").append(volume).append(' ') - if (number > 0) append("Глава ").append(number) else append("Без имени") - }, + jo.getStringOrNull("name") ?: buildString { + if (volume > 0) append("Том ").append(volume).append(' ') + if (number > 0) append("Глава ").append(number) else append("Без имени") + }, number = number, volume = volume, url = jo.getString("id"), 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 ad2c4cf3..65940674 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 @@ -37,6 +37,12 @@ internal class NudeMoonParser( SortOrder.RATING, ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + init { context.cookieJar.insertCookies( domain, @@ -45,43 +51,42 @@ internal class NudeMoonParser( ) } - override suspend fun getList( - offset: Int, - filter: MangaListFilter?, - ): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val domain = domain - val url = - when (filter) { - is MangaListFilter.Search -> { - if (!isAuthorized) { - throw AuthRequiredException(source) - } - "https://$domain/search?stext=${filter.query.urlEncoded()}&rowstart=$offset" + val url = when { + !filter.query.isNullOrEmpty() -> { + if (!isAuthorized) { + throw AuthRequiredException(source) } + "https://$domain/search?stext=${filter.query.urlEncoded()}&rowstart=$offset" + } - is MangaListFilter.Advanced -> { - if (filter.tags.isNotEmpty()) { - filter.tags.joinToString( - separator = "_", - prefix = "https://$domain/tags/", - postfix = "&rowstart=$offset", - transform = { it.key.urlEncoded() }, - ) - } else { - val order = when (filter.sortOrder) { - SortOrder.POPULARITY -> "views" - SortOrder.NEWEST -> "date" - SortOrder.RATING -> "like" - else -> "like" - } - "https://$domain/all_manga?$order&rowstart=$offset" + else -> { + if (filter.tags.isNotEmpty()) { + filter.tags.joinToString( + separator = "_", + prefix = "https://$domain/tags/", + postfix = "&rowstart=$offset", + transform = { it.key.urlEncoded() }, + ) + } else { + val order = when (order) { + SortOrder.POPULARITY -> "views" + SortOrder.NEWEST -> "date" + SortOrder.RATING -> "like" + else -> "like" } + "https://$domain/all_manga?$order&rowstart=$offset" } - - null -> "https://$domain/all_manga?views&rowstart=$offset" } + } + val doc = webClient.httpGet(url).parseHtml() return doc.body().select("table.news_pic2").mapNotNull { row -> val a = row.selectFirstOrThrow("a") @@ -162,7 +167,7 @@ internal class NudeMoonParser( }.toList() } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val domain = domain val doc = webClient.httpGet("https://$domain/tags").parseHtml() val root = doc.body().getElementsByAttributeValue("name", "multitags").first() 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 811067c0..35995fd4 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 @@ -59,6 +59,16 @@ internal class RemangaParser( private val regexLastUrlPath = Regex("/[^/]+/?$") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun intercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) if (response.code == TOO_MANY_REQUESTS) { @@ -69,25 +79,19 @@ internal class RemangaParser( return response } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { copyCookies() val domain = domain val urlBuilder = StringBuilder() .append("https://api.") .append(domain) - if (query != null) { + if (!filter.query.isNullOrEmpty()) { urlBuilder.append("/api/search/?query=") - .append(query.urlEncoded()) + .append(filter.query.urlEncoded()) } else { urlBuilder.append("/api/search/catalog/?ordering=") - .append(getSortKey(sortOrder)) - tags?.forEach { tag -> + .append(getSortKey(order)) + filter.tags.forEach { tag -> urlBuilder.append("&genres=") urlBuilder.append(tag.key) } @@ -218,7 +222,7 @@ internal class RemangaParser( return result } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val domain = domain val content = webClient.httpGet("https://api.$domain/api/forms/titles/?get=genres") .parseJson().getJSONObject("content").getJSONArray("genres") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt index 1266aedb..cdb6a14e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt @@ -66,10 +66,22 @@ internal abstract class GroupleParser( override val isAuthorized: Boolean get() = context.cookieJar.getCookies(domain).any { it.name == "gwt" } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + isYearRangeSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val domain = domain - val doc = when (filter) { - is MangaListFilter.Search -> webClient.httpPost( + val doc = when { + !filter.query.isNullOrEmpty() && filter.tags.isEmpty() -> webClient.httpPost( "https://$domain/search", mapOf( "q" to filter.query.urlEncoded(), @@ -78,27 +90,21 @@ internal abstract class GroupleParser( ), ) - null -> webClient.httpGet( - "https://$domain/list?sortType=${ - getSortKey(defaultSortOrder) - }&offset=${offset upBy PAGE_SIZE}", - ) - - is MangaListFilter.Advanced -> when { + else -> when { filter.tags.isEmpty() -> webClient.httpGet( "https://$domain/list?sortType=${ - getSortKey(filter.sortOrder) + getSortKey(order) }&offset=${offset upBy PAGE_SIZE}", ) filter.tags.size == 1 -> webClient.httpGet( "https://$domain/list/genre/${filter.tags.first().key}?sortType=${ - getSortKey(filter.sortOrder) + getSortKey(order) }&offset=${offset upBy PAGE_SIZE}", ) offset > 0 -> return emptyList() - else -> advancedSearch(domain, filter.tags) + else -> advancedSearch(domain, filter) } }.parseHtml().body() val root = (doc.getElementById("mangaBox") ?: doc.getElementById("mangaResults")) @@ -255,7 +261,7 @@ internal abstract class GroupleParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://${domain}/list/genres/sort_name").parseHtml() val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")?.selectFirst("table.table") ?: doc.parseFailed("Cannot find root") @@ -323,14 +329,14 @@ internal abstract class GroupleParser( else -> null } - private suspend fun advancedSearch(domain: String, tags: Set): Response { + private suspend fun advancedSearch(domain: String, filter: MangaListFilter): Response { val url = "https://$domain/search/advanced" // Step 1: map catalog genres names to advanced-search genres ids val tagsIndex = webClient.httpGet(url).parseHtml().body().selectFirst("form.search-form")?.select("div.form-group") ?.find { it.selectFirst("li.property") != null } ?: throw ParseException("Genres filter element not found", url) - val tagNames = tags.map { it.title.lowercase() } + val tagNames = filter.tags.map { it.title.lowercase() } val payload = HashMap() var foundGenres = 0 tagsIndex.select("li.property").forEach { li -> @@ -341,11 +347,11 @@ internal abstract class GroupleParser( "in" } else "" } - if (foundGenres != tags.size) { + if (foundGenres != filter.tags.size) { tagsIndex.parseFailed("Some genres are not found") } // Step 2: advanced search - payload["q"] = "" + payload["q"] = filter.query.orEmpty() payload["s_high_rate"] = "" payload["s_single"] = "" payload["s_mature"] = "" @@ -354,7 +360,11 @@ internal abstract class GroupleParser( payload["s_many_chapters"] = "" payload["s_wait_upload"] = "" payload["s_sale"] = "" - payload["years"] = "1900,2099" + payload["years"] = buildString { + append(filter.yearFrom.ifZero { 1900 }) + append(',') + append(filter.yearTo.ifZero { 2099 }) + } payload["+"] = "Искать".urlEncoded() return webClient.httpPost(url, payload) } 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 a43f51f4..42083a4c 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 @@ -23,17 +23,26 @@ internal abstract class ChanParser( SortOrder.RATING, ) - override val isTagsExclusionSupported: Boolean = true - override val authUrl: String get() = "https://${domain}" override val isAuthorized: Boolean get() = context.cookieJar.getCookies(domain).any { it.name == "dle_user_id" } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { val domain = domain - val doc = webClient.httpGet(buildUrl(offset, filter)).parseHtml() + val doc = webClient.httpGet(buildUrl(offset, order, filter)).parseHtml() val root = doc.body().selectFirst("div.main_fon")?.getElementById("content") ?: doc.parseFailed("Cannot find root") return root.select("div.content_row").mapNotNull { row -> @@ -125,7 +134,7 @@ internal abstract class ChanParser( doc.parseFailed("Pages list not found at ${chapter.url}") } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val domain = domain val doc = webClient.httpGet("https://$domain/mostfavorites&sort=manga").parseHtml() val root = doc.body().selectFirst("div.main_fon")?.getElementById("side") @@ -178,12 +187,13 @@ internal abstract class ChanParser( protected open fun buildUrl( offset: Int, - filter: MangaListFilter?, + order: SortOrder, + filter: MangaListFilter, ): HttpUrl { val builder = urlBuilder() builder.addQueryParameter("offset", offset.toString()) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { builder.addQueryParameter("do", "search") builder.addQueryParameter("subaction", "search") builder.addQueryParameter("search_start", ((offset / 40) + 1).toString()) @@ -194,7 +204,7 @@ internal abstract class ChanParser( builder.addQueryParameter("need_sort_date", "false") } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty() || filter.tagsExclude.isNotEmpty()) { builder.addPathSegment("tags") val joiner = StringUtil.StringJoiner("+") @@ -203,17 +213,17 @@ internal abstract class ChanParser( builder.addPathSegment(joiner.complete()) builder.addQueryParameter( "n", - when (filter.sortOrder) { + when (order) { SortOrder.RATING, SortOrder.POPULARITY, - -> "favdesc" + -> "favdesc" SortOrder.ALPHABETICAL -> "abcasc" else -> "" // SortOrder.NEWEST }, ) } else { - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> builder.addPathSegment("mostviews") SortOrder.ALPHABETICAL -> builder.addPathSegment("catalog") SortOrder.RATING -> builder.addPathSegment("mostfavorites") @@ -224,11 +234,6 @@ internal abstract class ChanParser( } } } - - null -> { - builder.addPathSegment("manga") - builder.addPathSegment("new") - } } return builder.build() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt index 9c9b05eb..cc5c2221 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt @@ -59,10 +59,10 @@ internal class HenChanParser(context: MangaLoaderContext) : ChanParser(context, ) } - override fun buildUrl(offset: Int, filter: MangaListFilter?): HttpUrl = when { - filter is MangaListFilter.Advanced && filter.tags.isEmpty() && filter.tagsExclude.isEmpty() -> { + override fun buildUrl(offset: Int, order: SortOrder, filter: MangaListFilter): HttpUrl = when { + filter.query.isNullOrEmpty() && filter.tags.isEmpty() && filter.tagsExclude.isEmpty() -> { val builder = urlBuilder().addQueryParameter("offset", offset.toString()) - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> { builder.addPathSegment("mostviews") builder.addQueryParameter("sort", "manga") @@ -81,15 +81,8 @@ internal class HenChanParser(context: MangaLoaderContext) : ChanParser(context, builder.build() } - filter == null -> { - val builder = urlBuilder().addQueryParameter("offset", offset.toString()) - builder.addPathSegment("manga") - builder.addPathSegment("newest") - builder.build() - } - else -> { - super.buildUrl(offset, filter) + super.buildUrl(offset, order, filter) } } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt index cddb9d13..2010bc87 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt @@ -31,10 +31,19 @@ internal abstract class LibSocialParser( ) final override val configKeyDomain = ConfigKey.Domain("lib.social") - override val availableStates: Set = EnumSet.allOf(MangaState::class.java) - override val isMultipleTagsSupported = true - override val isTagsExclusionSupported = true - override val isSearchSupported = true + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.allOf(MangaState::class.java), + ) private val statesMap = intObjectMapOf( 1, MangaState.ONGOING, @@ -57,7 +66,7 @@ internal abstract class LibSocialParser( defaultValue = null, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val urlBuilder = urlBuilder("api") .addPathSegment("api") .addPathSegment("manga") @@ -65,49 +74,41 @@ internal abstract class LibSocialParser( .addQueryParameter("fields[]", "rate") .addQueryParameter("fields[]", "rate_avg") .addQueryParameter("page", page.toString()) - when (filter) { - is MangaListFilter.Advanced -> { - for (state in filter.states) { - urlBuilder.addQueryParameter("status[]", statesMap.keyOf(state).toString()) - } - for (tag in filter.tags) { - urlBuilder.addQueryParameter("${tag.typeKey()}[]", tag.key.drop(1)) - } - for (tag in filter.tagsExclude) { - urlBuilder.addQueryParameter("${tag.typeKey()}_exclude[]", tag.key.drop(1)) - } - } - - is MangaListFilter.Search -> { - urlBuilder.addQueryParameter("q", filter.query) - } - - null -> Unit + for (state in filter.states) { + urlBuilder.addQueryParameter("status[]", statesMap.keyOf(state).toString()) + } + for (tag in filter.tags) { + urlBuilder.addQueryParameter("${tag.typeKey()}[]", tag.key.drop(1)) + } + for (tag in filter.tagsExclude) { + urlBuilder.addQueryParameter("${tag.typeKey()}_exclude[]", tag.key.drop(1)) + } + if (!filter.query.isNullOrEmpty()) { + urlBuilder.addQueryParameter("q", filter.query) } - val sortOrder = filter?.sortOrder ?: defaultSortOrder urlBuilder.addQueryParameter( "sort_by", - when (sortOrder) { + when (order) { SortOrder.UPDATED -> "last_chapter_at" SortOrder.POPULARITY -> "views" SortOrder.RATING -> "rate_avg" SortOrder.NEWEST -> "created_at" SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL_DESC, - -> "rus_name" + -> "rus_name" else -> null }, ) urlBuilder.addQueryParameter( "sort_type", - when (sortOrder) { + when (order) { SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.RATING, SortOrder.NEWEST, SortOrder.ALPHABETICAL_DESC, - -> "desc" + -> "desc" SortOrder.ALPHABETICAL -> "asc" else -> null @@ -166,7 +167,7 @@ internal abstract class LibSocialParser( } } - override suspend fun getAvailableTags(): Set = coroutineScope { + private suspend fun fetchAvailableTags(): Set = coroutineScope { val tags = async { fetchTags("tags") } val genres = async { fetchTags("genres") } tagsSetOf(tags.await(), genres.await()) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt index 5f077daa..58ff0645 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt @@ -19,8 +19,19 @@ internal abstract class ScanParser( domain: String, pageSize: Int = 0, ) : PagedMangaParser(context, source, pageSize) { + override val configKeyDomain = ConfigKey.Domain(domain) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -31,26 +42,25 @@ internal abstract class ScanParser( protected open val listUrl = "/manga" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { var query = false val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/search?q=") append(filter.query.urlEncoded()) query = true } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) append("?q=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "u" SortOrder.ALPHABETICAL -> "a" SortOrder.POPULARITY -> "p" @@ -67,12 +77,6 @@ internal abstract class ScanParser( append("&page=") append(page.toString()) } - - null -> { - append(listUrl) - append("?page=") - append(page.toString()) - } } } @@ -110,10 +114,6 @@ internal abstract class ScanParser( private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - protected suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt index 880abf9d..5e504ac4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt @@ -2,11 +2,7 @@ package org.koitharu.kotatsu.parsers.site.scan.fr import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.scan.ScanParser import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat @@ -16,7 +12,7 @@ internal class MangaFr(context: MangaLoaderContext) : ScanParser(context, MangaParserSource.MANGAFR, "www.mangafr.org") { override val listUrl = "/series" - override suspend fun getAvailableTags(): Set = emptySet() + private suspend fun fetchAvailableTags(): Set = emptySet() override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt index 520ecc8d..874f669c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt @@ -28,10 +28,6 @@ internal abstract class SinmhParser( SortOrder.POPULARITY, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - - override val isMultipleTagsSupported = false - protected open val searchUrl = "search/" protected open val listUrl = "list/" @@ -50,14 +46,23 @@ internal abstract class SinmhParser( "已完结", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') - when (filter) { - - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append(searchUrl) append("?keywords=") append(filter.query.urlEncoded()) @@ -65,7 +70,7 @@ internal abstract class SinmhParser( append(page) } - is MangaListFilter.Advanced -> { + else -> { append(listUrl) filter.tags.oneOrThrowIfMany()?.let { append(it.key) @@ -85,7 +90,7 @@ internal abstract class SinmhParser( append('/') } - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("click/") SortOrder.UPDATED -> append("update/") else -> append("/") @@ -93,13 +98,6 @@ internal abstract class SinmhParser( append(page.toString()) append('/') } - - null -> { - append(listUrl) - append("update/") - append(page.toString()) - append('/') - } } } @@ -125,7 +123,7 @@ internal abstract class SinmhParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select(".filter-item:contains(按剧情) li a:not(.active)").mapNotNullToSet { a -> val href = a.attr("href").removeSuffix('/').substringAfterLast('/') diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt index 5849423b..8e17fb33 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt @@ -14,7 +14,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("MANGAAY", "MangaAy", "tr") -class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAAY, 45) { +internal class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MANGAAY, 45) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) @@ -25,12 +25,18 @@ class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaPars keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + ) - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { return emptyList() } @@ -42,7 +48,7 @@ class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaPars ) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -70,19 +76,6 @@ class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaPars } } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/seriler") - if (page > 1) { - append("/") - append(page) - } - } - return parseMangaList(webClient.httpGet(url).parseHtml()) - } } return emptyList() @@ -135,10 +128,6 @@ class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context, MangaPars private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - private suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt index 8db23d58..4a0c4171 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.parsers.site.tr import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @@ -10,33 +10,31 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("SADSCANS", "SadScans", "tr") -internal class SadScans(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.SADSCANS) { +internal class SadScans(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.SADSCANS) { override val availableSortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) override val configKeyDomain = ConfigKey.Domain("sadscans.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - if (offset > 0) { - return emptyList() - } - + override suspend fun getList(order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append("/series") - when (filter) { - is MangaListFilter.Search -> { - append("?search=") - append(filter.query.urlEncoded()) - } - - is MangaListFilter.Advanced -> {} - null -> {} + if (!filter.query.isNullOrEmpty()) { + append("?search=") + append(filter.query.urlEncoded()) } } @@ -60,8 +58,6 @@ internal class SadScans(context: MangaLoaderContext) : MangaParser(context, Mang } } - override suspend fun getAvailableTags(): Set = emptySet() - override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt index 1f2f8b55..515695b1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt @@ -12,7 +12,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("TRWEBTOON", "TrWebtoon", "tr") -class TrWebtoon(context: MangaLoaderContext) : +internal class TrWebtoon(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.TRWEBTOON, pageSize = 21) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("trwebtoon.com") @@ -31,13 +31,19 @@ class TrWebtoon(context: MangaLoaderContext) : SortOrder.UPDATED, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override val isMultipleTagsSupported = false + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -50,9 +56,9 @@ class TrWebtoon(context: MangaLoaderContext) : return parseMangaList(webClient.httpGet(url).parseHtml()) } - is MangaListFilter.Advanced -> { + else -> { - if (filter.sortOrder == SortOrder.UPDATED) { + if (order == SortOrder.UPDATED) { if (filter.tags.isNotEmpty()) { throw IllegalArgumentException("Sort order updated + Tags or States is not supported by this source") } @@ -84,7 +90,7 @@ class TrWebtoon(context: MangaLoaderContext) : ) } append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("views&short_type=DESC") SortOrder.POPULARITY_ASC -> append("views&short_type=ASC") SortOrder.ALPHABETICAL -> append("name&short_type=ASC") @@ -97,16 +103,6 @@ class TrWebtoon(context: MangaLoaderContext) : } } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/son-eklenenler?page=") - append(page.toString()) - } - return parseMangaListUpdated(webClient.httpGet(url).parseHtml()) - } } } @@ -160,7 +156,7 @@ class TrWebtoon(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val tags = webClient.httpGet("https://$domain/webtoon-listesi").parseHtml().requireElementById("collapseExample") .select(".pt-12 a").drop(1) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/YaoiFlix.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/YaoiFlix.kt index 1c7226a7..3b082568 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/YaoiFlix.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/YaoiFlix.kt @@ -10,7 +10,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("YAOIFLIX", "YaoiFlix", "tr", ContentType.HENTAI) -class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.YAOIFLIX, 8) { +internal class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.YAOIFLIX, 8) { override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) @@ -21,14 +21,21 @@ class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar keys.add(userAgentKey) } - override val isMultipleTagsSupported = false + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) { append("/page/") append(page.toString()) @@ -37,7 +44,7 @@ class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/dizi-kategori/") @@ -58,15 +65,6 @@ class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar } } } - - null -> { - append("/tum-seriler/") - if (page > 1) { - append("page/") - append(page.toString()) - append('/') - } - } } } val doc = webClient.httpGet(url).parseHtml() @@ -91,7 +89,7 @@ class YaoiFlix(context: MangaLoaderContext) : PagedMangaParser(context, MangaPar } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.select(".tags .cat-item a").mapNotNullToSet { a -> MangaTag( 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 e45349a3..6c6a10ea 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 @@ -24,7 +24,8 @@ private const val PAGE_SIZE = 60 // NOTE High profile focus @MangaSourceParser("HENTAIUKR", "HentaiUkr", "uk", ContentType.HENTAI) -class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HENTAIUKR), Interceptor { +internal class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HENTAIUKR), + Interceptor { private val date = SimpleDateFormat("yyyy-MM-dd", Locale.US) @@ -40,6 +41,17 @@ class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaP override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("hentaiukr.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -73,38 +85,27 @@ class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaP ) } - override suspend fun getList( - offset: Int, - filter: MangaListFilter?, - ): List { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { // Get all manga val json = allManga.get().toMutableList() - when (filter) { - is MangaListFilter.Search -> { - json.retainAll { item -> - item.getString("name").contains(filter.query, ignoreCase = true) || - item.getStringOrNull("eng_name")?.contains(filter.query, ignoreCase = true) == true || - item.getStringOrNull("orig_name")?.contains(filter.query, ignoreCase = true) == true || - item.getStringOrNull("author")?.contains(filter.query, ignoreCase = true) == true || - item.getStringOrNull("team")?.contains(filter.query, ignoreCase = true) == true - } + if (!filter.query.isNullOrEmpty()) { + json.retainAll { item -> + item.getString("name").contains(filter.query, ignoreCase = true) || + item.getStringOrNull("eng_name")?.contains(filter.query, ignoreCase = true) == true || + item.getStringOrNull("orig_name")?.contains(filter.query, ignoreCase = true) == true || + item.getStringOrNull("author")?.contains(filter.query, ignoreCase = true) == true || + item.getStringOrNull("team")?.contains(filter.query, ignoreCase = true) == true } - - is MangaListFilter.Advanced -> { - if (filter.tags.isNotEmpty()) { - val ids = filter.tags.mapToSet { it.key } - json.retainAll { item -> - item.getJSONArray("tags") - .mapJSON { it.getAsString() } - .any { x -> x in ids } - } - } + } + if (filter.tags.isNotEmpty()) { + val ids = filter.tags.mapToSet { it.key } + json.retainAll { item -> + item.getJSONArray("tags") + .mapJSON { it.getAsString() } + .any { x -> x in ids } } - - null -> {} } - // Return to app return json.drop(offset).take(PAGE_SIZE).map { jo -> val id = jo.getAsLong() @@ -140,7 +141,7 @@ class HentaiUkrParser(context: MangaLoaderContext) : MangaParser(context, MangaP } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return allManga.get().flatMapTo(HashSet()) { x -> x.getJSONArray("tags").mapJSON { t -> MangaTag( 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 e712960a..f33ad13b 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 @@ -24,7 +24,7 @@ private const val HEADER_ENCODING = "Content-Encoding" private const val IMAGE_BASEURL_FALLBACK = "https://hmvolumestorage.b-cdn.net/public-resources" @MangaSourceParser("HONEYMANGA", "HoneyManga", "uk") -class HoneyMangaParser(context: MangaLoaderContext) : +internal class HoneyMangaParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.HONEYMANGA, PAGE_SIZE), Interceptor { @@ -39,6 +39,16 @@ class HoneyMangaParser(context: MangaLoaderContext) : override val configKeyDomain = ConfigKey.Domain("honey-manga.com.ua") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) @@ -86,30 +96,24 @@ class HoneyMangaParser(context: MangaLoaderContext) : ) } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val body = JSONObject() body.put("page", page) body.put("pageSize", PAGE_SIZE) val sort = JSONObject() - sort.put("sortBy", getSortKey(sortOrder)) + sort.put("sortBy", getSortKey(order)) sort.put("sortOrder", "DESC") body.put("sort", sort) val content = when { - !tags.isNullOrEmpty() -> { + filter.tags.isNotEmpty() -> { // Tags val filters = JSONArray() val tagFilter = JSONObject() tagFilter.put("filterBy", "genres") tagFilter.put("filterOperator", "ALL") val tag = JSONArray() - tags.forEach { + filter.tags.forEach { tag.put(it.title) } tagFilter.put("filterValue", tag) @@ -119,15 +123,15 @@ class HoneyMangaParser(context: MangaLoaderContext) : } - !query.isNullOrEmpty() -> { + !filter.query.isNullOrEmpty() -> { // Search when { - query.length < 3 -> throw IllegalArgumentException( + filter.query.length < 3 -> throw IllegalArgumentException( "The query must contain at least 3 characters (Запит має містити щонайменше 3 символи)", ) page == searchPaginator.firstPage -> webClient - .httpGet(searchApi + query.urlEncoded()) + .httpGet(searchApi + filter.query.urlEncoded()) .parseJsonArray() else -> JSONArray() @@ -176,7 +180,7 @@ class HoneyMangaParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { // https://data.api.honey-manga.com.ua/genres-tags/genres-list val content = webClient.httpGet(genresListApi).parseJsonArray() val tagsSet = ArraySet(content.length()) 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 9cea5b6d..4ccc9e1c 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 @@ -13,7 +13,7 @@ import java.util.* private const val DEF_BRANCH_NAME = "Основний переклад" @MangaSourceParser("MANGAINUA", "MANGA/in/UA", "uk") -class MangaInUaParser(context: MangaLoaderContext) : PagedMangaParser( +internal class MangaInUaParser(context: MangaLoaderContext) : PagedMangaParser( context = context, source = MangaParserSource.MANGAINUA, pageSize = 24, @@ -29,27 +29,28 @@ class MangaInUaParser(context: MangaLoaderContext) : PagedMangaParser( keys.add(userAgentKey) } - override val isMultipleTagsSupported: Boolean = false - private val userHashRegex by lazy { Regex("site_login_hash\\s*=\\s*\'([^\']+)\'", RegexOption.IGNORE_CASE) } - override suspend fun getListPage( - page: Int, - query: String?, - tags: Set?, - tagsExclude: Set?, - sortOrder: SortOrder, - ): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = when { - !query.isNullOrEmpty() -> ("/index.php?do=search&subaction=search&search_start=$page&full_search=1&story=$query&titleonly=3").toAbsoluteUrl( + !filter.query.isNullOrEmpty() -> ("/index.php?do=search&subaction=search&search_start=$page&full_search=1&story=${filter.query}&titleonly=3").toAbsoluteUrl( domain, ) - tags.isNullOrEmpty() -> "/mangas/page/$page".toAbsoluteUrl(domain) - tags.size == 1 -> "${tags.first().key}/page/$page" - tags.size > 1 -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_GENRES_NOT_SUPPORTED) + filter.tags.isEmpty() -> "/mangas/page/$page".toAbsoluteUrl(domain) + filter.tags.size == 1 -> "${filter.tags.first().key}/page/$page" + filter.tags.size > 1 -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_GENRES_NOT_SUPPORTED) else -> "/mangas/page/$page".toAbsoluteUrl(domain) } val doc = webClient.httpGet(url).parseHtml() @@ -161,7 +162,7 @@ class MangaInUaParser(context: MangaLoaderContext) : PagedMangaParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val domain = domain val doc = webClient.httpGet("https://$domain/mangas").parseHtml() val root = doc.body().requireElementById("menu_1").selectFirstOrThrow("div.menu__wrapper") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt index 8d1350a9..1749d8ba 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt @@ -15,7 +15,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("BLOGTRUYEN", "BlogTruyen", "vi") -class BlogTruyenParser(context: MangaLoaderContext) : +internal class BlogTruyenParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.BLOGTRUYEN, pageSize = 20) { override val configKeyDomain: ConfigKey.Domain @@ -31,24 +31,28 @@ class BlogTruyenParser(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - private val dateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.US) private var cacheTags = SuspendLazy(::fetchTags) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - return when (filter) { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = cacheTags.get().values.toSet(), + ) - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + return when { + !filter.query.isNullOrEmpty() -> { val searchUrl = "https://${domain}/timkiem/nangcao/1/0/-1/-1?txt=${filter.query.urlEncoded()}&p=$page" val searchContent = webClient.httpGet(searchUrl).parseHtml() .selectFirst("section.list-manga-bycate > div.list") parseMangaList(searchContent) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany().let { @@ -61,8 +65,6 @@ class BlogTruyenParser(context: MangaLoaderContext) : getNormalList(page) } } - - null -> getNormalList(page) } } @@ -124,10 +126,6 @@ class BlogTruyenParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { - return cacheTags.get().values.toSet() - } - private suspend fun fetchTags(): Map { val doc = webClient.httpGet("/timkiem/nangcao".toAbsoluteUrl(domain)).parseHtml() val tagItems = doc.select("li[data-id]") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVNParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVNParser.kt index 061340a7..6e2e6916 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVNParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVNParser.kt @@ -15,7 +15,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("BLOGTRUYENVN", "BlogTruyenVN", "vi") -class BlogTruyenVNParser(context: MangaLoaderContext) : +internal class BlogTruyenVNParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.BLOGTRUYENVN, pageSize = 20) { override val configKeyDomain: ConfigKey.Domain @@ -31,24 +31,28 @@ class BlogTruyenVNParser(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - private val dateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.US) private var cacheTags = SuspendLazy(::fetchTags) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - return when (filter) { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = cacheTags.get().values.toSet(), + ) - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + return when { + !filter.query.isNullOrEmpty() -> { val searchUrl = "https://${domain}/timkiem/nangcao/1/0/-1/-1?txt=${filter.query.urlEncoded()}&p=$page" val searchContent = webClient.httpGet(searchUrl).parseHtml() .selectFirst("section.list-manga-bycate > div.list") parseMangaList(searchContent) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany().let { @@ -61,8 +65,6 @@ class BlogTruyenVNParser(context: MangaLoaderContext) : getNormalList(page) } } - - null -> getNormalList(page) } } @@ -124,10 +126,6 @@ class BlogTruyenVNParser(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { - return cacheTags.get().values.toSet() - } - private suspend fun fetchTags(): Map { val doc = webClient.httpGet("/timkiem/nangcao".toAbsoluteUrl(domain)).parseHtml() val tagItems = doc.select("li[data-id]") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt index e302709a..f06c970f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt @@ -32,20 +32,25 @@ internal class CuuTruyenParser(context: MangaLoaderContext) : SortOrder.NEWEST, ) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + override fun getRequestHeaders(): Headers = Headers.Builder() .add("User-Agent", UserAgents.KOTATSU) .build() private val decryptionKey = "3141592653589793" - override suspend fun getAvailableTags(): Set = emptySet() - - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { append("/api/v2/mangas/search?q=") append(filter.query.urlEncoded()) append("&page=") @@ -53,13 +58,13 @@ internal class CuuTruyenParser(context: MangaLoaderContext) : } else -> { - val tag = (filter as? MangaListFilter.Advanced)?.tags?.oneOrThrowIfMany() + val tag = filter.tags.oneOrThrowIfMany() if (tag != null) { append("/api/v2/tags/") append(tag.key) } else { append("/api/v2/mangas") - when (filter?.sortOrder) { + when (order) { SortOrder.UPDATED -> append("/recently_updated") SortOrder.POPULARITY -> append("/top") SortOrder.NEWEST -> append("/recently_updated") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt index 2ef4f171..2ec1c3be 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt @@ -19,7 +19,7 @@ private const val PAGE_SIZE = 15 private const val SEARCH_PAGE_SIZE = 10 @MangaSourceParser("HENTAIVN", "HentaiVN", "vi", type = ContentType.HENTAI) -class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HENTAIVN) { +internal class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.HENTAIVN) { override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("hentaiayame.net") @@ -35,10 +35,19 @@ class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaPa SortOrder.NEWEST, ) - override suspend fun getList(offset: Int, filter: MangaListFilter?): List { - return when (filter) { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = getOrCreateTagMap().values.toSet(), + ) - is MangaListFilter.Search -> { + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + return when { + !filter.query.isNullOrEmpty() -> { val page = (offset / PAGE_SIZE.toFloat()).toIntUp() + 1 urlBuilder() val searchUrl = @@ -47,7 +56,7 @@ class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaPa parseMainList(docs, page) } - is MangaListFilter.Advanced -> { + else -> { val pageSize = if (filter.tags.isEmpty()) PAGE_SIZE else SEARCH_PAGE_SIZE val page = (offset / pageSize.toFloat()).toIntUp() + 1 @@ -70,19 +79,13 @@ class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaPa val docs = webClient.httpGet(url).parseHtml() return parseAdvanceSearch(docs, page) } else { - val site = if (filter.sortOrder == SortOrder.UPDATED) "/chap-moi" else "/danh-sach" + val site = if (order == SortOrder.UPDATED) "/chap-moi" else "/danh-sach" val url = "$site.html?page=$page".toAbsoluteUrl(domain) - context.cookieJar.insertCookies(domain, *getSortCookies(filter.sortOrder)) + context.cookieJar.insertCookies(domain, *getSortCookies(order)) val docs = webClient.httpGet(url).parseHtml() parseMainList(docs, page) } } - - null -> { - val page = (offset / PAGE_SIZE.toFloat()).toIntUp() + 1 - val url = "/chap-moi.html?page=$page".toAbsoluteUrl(domain) - parseMainList(webClient.httpGet(url).parseHtml(), page) - } } } @@ -146,10 +149,6 @@ class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context, MangaPa private var tagCache: ArrayMap? = null private val mutex = Mutex() - override suspend fun getAvailableTags(): Set { - return getOrCreateTagMap().values.toSet() - } - private suspend fun getOrCreateTagMap(): Map = mutex.withLock { tagCache?.let { return@withLock it } val tagMap = ArrayMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt index 4932452f..27cf622d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt @@ -20,27 +20,34 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context, SortOrder.NEWEST, SortOrder.POPULARITY, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) override val configKeyDomain = ConfigKey.Domain("lxmanga.life") - override val isMultipleTagsSupported = false - override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { val skey = "filter[name]=".urlEncoded() append("/tim-kiem?$skey") append(filter.query.urlEncoded()) @@ -48,7 +55,7 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context, append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { append("/the-loai/") @@ -74,7 +81,7 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context, } append("&sort=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("-views") SortOrder.UPDATED -> append("-updated_at") SortOrder.NEWEST -> append("-created_at") @@ -83,11 +90,6 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context, else -> append("-updated_at") } } - - null -> { - append("/danh-sach?sort=-updated_at&page=") - append(page.toString()) - } } } @@ -171,11 +173,10 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/").parseHtml() val body = doc.body() return body.select("ul.absolute.w-full a").mapToSet { a -> - MangaTag( key = a.attr("href").removeSuffix("/").substringAfterLast('/'), title = a.selectFirstOrThrow("span.text-ellipsis").text(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Truyenqq.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Truyenqq.kt index 947444ad..7dc352a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Truyenqq.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Truyenqq.kt @@ -15,74 +15,70 @@ internal class Truyenqq(context: MangaLoaderContext) : PagedMangaParser(context, override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - override val configKeyDomain = ConfigKey.Domain("truyenqqto.com") + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + override fun onCreateConfig(keys: MutableCollection>) { super.onCreateConfig(keys) keys.add(userAgentKey) } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - - val url = - when (filter) { - is MangaListFilter.Search -> { - buildString { - append("https://") - append(domain) - append("/tim-kiem/trang-$page.html") - append("?q=") - append(filter.query.urlEncoded()) - } + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val url = when { + !filter.query.isNullOrEmpty() -> { + buildString { + append("https://") + append(domain) + append("/tim-kiem/trang-$page.html") + append("?q=") + append(filter.query.urlEncoded()) } + } - is MangaListFilter.Advanced -> { - buildString { - append("https://") - append(domain) - append("/tim-kiem-nang-cao/trang-") - append(page.toString()) - append(".html?country=0&sort=") - when (filter.sortOrder) { - SortOrder.POPULARITY -> append("4") - SortOrder.UPDATED -> append("2") - SortOrder.NEWEST -> append("0") - else -> append("2") - } - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - append( - when (it) { - MangaState.ONGOING -> "0" - MangaState.FINISHED -> "1" - else -> "-1" - }, - ) - } - } else { - append("&status=-1") - } - - append("&category=") - append(filter.tags.joinToString(separator = ",") { it.key }) - append("¬category=&minchapter=0") + else -> { + buildString { + append("https://") + append(domain) + append("/tim-kiem-nang-cao/trang-") + append(page.toString()) + append(".html?country=0&sort=") + when (order) { + SortOrder.POPULARITY -> append("4") + SortOrder.UPDATED -> append("2") + SortOrder.NEWEST -> append("0") + else -> append("2") } - } - - null -> { - buildString { - append("https://") - append(domain) - append("/tim-kiem-nang-cao/trang-") - append(page.toString()) - append(".html?status=-1&country=0&sort=2&category=¬category=&minchapter=0") + if (filter.states.isNotEmpty()) { + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "0" + MangaState.FINISHED -> "1" + else -> "-1" + }, + ) + } + } else { + append("&status=-1") } + append("&category=") + append(filter.tags.joinToString(separator = ",") { it.key }) + append("¬category=&minchapter=0") } } + } val doc = webClient.httpGet(url).parseHtml() return doc.requireElementById("main_homepage").select("li").map { li -> val href = li.selectFirstOrThrow("a").attrAsRelativeUrl("href") @@ -103,7 +99,7 @@ internal class Truyenqq(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/tim-kiem-nang-cao.html").parseHtml() return doc.select(".advsearch-form div.genre-item").mapNotNullToSet { MangaTag( @@ -114,7 +110,6 @@ internal class Truyenqq(context: MangaLoaderContext) : PagedMangaParser(context, } } - override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt index bc25cc12..fe49b6be 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt @@ -12,7 +12,7 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("YURINEKO", "YuriNeko", "vi", ContentType.HENTAI) -class YurinekoParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.YURINEKO, 20) { +internal class YurinekoParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.YURINEKO, 20) { override val configKeyDomain: ConfigKey.Domain get() = ConfigKey.Domain("yurineko.net") @@ -27,25 +27,31 @@ class YurinekoParser(context: MangaLoaderContext) : PagedMangaParser(context, Ma private val apiDomain get() = "api.$domain" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val listUrl = - when (filter) { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) - is MangaListFilter.Search -> { - "/search?query=${filter.query.urlEncoded()}&page=$page" - } + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) - is MangaListFilter.Advanced -> { - if (filter.tags.isNotEmpty()) { - val tagKeys = filter.tags.joinToString(separator = ",") { it.key } - "/advancedSearch?genre=$tagKeys¬Genre=&sort=7&minChapter=1&status=0&page=$page" - } else { - "/lastest2?page=$page" - } - } + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val listUrl = when { + !filter.query.isNullOrEmpty() -> { + "/search?query=${filter.query.urlEncoded()}&page=$page" + } - null -> "/lastest2?page=$page" + else -> { + if (filter.tags.isNotEmpty()) { + val tagKeys = filter.tags.joinToString(separator = ",") { it.key } + "/advancedSearch?genre=$tagKeys¬Genre=&sort=7&minChapter=1&status=0&page=$page" + } else { + "/lastest2?page=$page" + } } + } val jsonResponse = webClient.httpGet(listUrl.toAbsoluteUrl(apiDomain)).parseJson() return jsonResponse.getJSONArray("result") .mapJSON { jo -> @@ -125,7 +131,7 @@ class YurinekoParser(context: MangaLoaderContext) : PagedMangaParser(context, Ma } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { return webClient.httpGet("https://$apiDomain/tag/find?query=") .parseJsonArray() .mapJSONToSet { jo -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt index ec427f42..16d73e09 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt @@ -24,25 +24,31 @@ internal abstract class VmpParser( override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val isMultipleTagsSupported = false - protected open val listUrl = "xxx/" protected open val geneUrl = "genero/" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + ) + init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) append('/') - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(listUrl) append("/page/") append(page.toString()) @@ -50,7 +56,7 @@ internal abstract class VmpParser( append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -65,12 +71,6 @@ internal abstract class VmpParser( append(page.toString()) } } - - null -> { - append(listUrl) - append("/page/") - append(page.toString()) - } } } @@ -96,7 +96,7 @@ internal abstract class VmpParser( } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select("div.tagcloud a").mapNotNullToSet { a -> MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt index eb454537..5cb3287e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt @@ -38,20 +38,14 @@ internal abstract class WpComicsParser( SortOrder.RATING, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - - override val isMultipleTagsSupported = false - protected open val listUrl = "/tim-truyen" protected open val datePattern = "dd/MM/yy" - init { paginator.firstPage = 1 searchPaginator.firstPage = 1 } - @JvmField protected val ongoing: Set = setOf( "Đang tiến hành", @@ -68,10 +62,20 @@ internal abstract class WpComicsParser( "完結済み", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val response = - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -90,7 +94,7 @@ internal abstract class WpComicsParser( result.getOrThrow() } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) @@ -103,12 +107,12 @@ internal abstract class WpComicsParser( } append("?sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> 0 SortOrder.POPULARITY -> 10 SortOrder.NEWEST -> 15 SortOrder.RATING -> 20 - else -> throw IllegalArgumentException("Sort order ${filter.sortOrder.name} not supported") + else -> throw IllegalArgumentException("Sort order ${order.name} not supported") }, ) filter.states.oneOrThrowIfMany()?.let { @@ -127,17 +131,6 @@ internal abstract class WpComicsParser( webClient.httpGet(url) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append("?sort=0&status=-1&page=") - append(page.toString()) - } - webClient.httpGet(url) - } } val tagMap = getOrCreateTagMap() @@ -178,7 +171,7 @@ internal abstract class WpComicsParser( } } - override suspend fun getAvailableTags(): Set { + private suspend fun fetchAvailableTags(): Set { val map = getOrCreateTagMap() val tagSet = ArraySet(map.size) for (entry in map) { @@ -288,7 +281,7 @@ internal abstract class WpComicsParser( return when { d.endsWith(" ago") || d.endsWith(" trước") - -> parseRelativeDate(date) + -> parseRelativeDate(date) d.startsWith("year") -> Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt index e3b5eacd..0a79be57 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.parsers.site.wpcomics.en +import androidx.collection.ArrayMap import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* @@ -24,20 +26,20 @@ internal class XoxoComics(context: MangaLoaderContext) : SortOrder.ALPHABETICAL, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("/search-comic?keyword=") append(filter.query.urlEncoded()) append("&page=") append(page.toString()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty()) { filter.tags.oneOrThrowIfMany()?.let { @@ -63,7 +65,7 @@ internal class XoxoComics(context: MangaLoaderContext) : append(listUrl) } - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("/popular") SortOrder.UPDATED -> append("/latest") SortOrder.NEWEST -> append("/newest") @@ -73,12 +75,6 @@ internal class XoxoComics(context: MangaLoaderContext) : append("?page=") append(page.toString()) } - - null -> { - append(listUrl) - append("/?page=") - append(page.toString()) - } } } val doc = webClient.httpGet(url).parseHtml() @@ -102,10 +98,11 @@ internal class XoxoComics(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { + override suspend fun getOrCreateTagMap(): ArrayMap = mutex.withLock { + tagCache?.let { return@withLock it } val doc = webClient.httpGet("https://$domain$listUrl").parseHtml() - return doc.select("div.genres ul li:not(.active)").mapNotNullToSet { li -> - val a = li.selectFirst("a") ?: return@mapNotNullToSet null + val list = doc.select("div.genres ul li:not(.active)").mapNotNull { li -> + val a = li.selectFirst("a") ?: return@mapNotNull null val href = a.attr("href").removeSuffix('/').substringAfterLast('/') MangaTag( key = href, @@ -113,6 +110,9 @@ internal class XoxoComics(context: MangaLoaderContext) : source = source, ) } + val result = list.associateByTo(ArrayMap(list.size)) { it.title } + tagCache = result + result } override suspend fun getDetails(manga: Manga): Manga = coroutineScope { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/ja/MangaRaw.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/ja/MangaRaw.kt index d07a7c87..25ccbb29 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/ja/MangaRaw.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/ja/MangaRaw.kt @@ -3,17 +3,9 @@ package org.koitharu.kotatsu.parsers.site.wpcomics.ja import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.exception.NotFoundException -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser -import org.koitharu.kotatsu.parsers.util.domain -import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany -import org.koitharu.kotatsu.parsers.util.parseHtml -import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.parsers.util.urlEncoded +import org.koitharu.kotatsu.parsers.util.* // Need to use 0ms.dev Proxy @@ -22,77 +14,65 @@ internal class MangaRaw(context: MangaLoaderContext) : WpComicsParser(context, MangaParserSource.MANGARAW, "mangaraw.xyz") { override val listUrl = "/search/manga" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { - val response = - when (filter) { - is MangaListFilter.Search -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append("?keyword=") - append(filter.query.urlEncoded()) - append("&page=") - append(page.toString()) - } + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val response = when { + !filter.query.isNullOrEmpty() -> { + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("?keyword=") + append(filter.query.urlEncoded()) + append("&page=") + append(page.toString()) + } - val result = runCatchingCancellable { webClient.httpGet(url) } - val exception = result.exceptionOrNull() - if (exception is NotFoundException) { - return emptyList() - } - result.getOrThrow() + val result = runCatchingCancellable { webClient.httpGet(url) } + val exception = result.exceptionOrNull() + if (exception is NotFoundException) { + return emptyList() } + result.getOrThrow() + } - is MangaListFilter.Advanced -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append("?sort=") + else -> { + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("?sort=") + append( + when (order) { + SortOrder.UPDATED -> 0 + SortOrder.POPULARITY -> 10 + SortOrder.NEWEST -> 15 + SortOrder.RATING -> 20 + else -> throw IllegalArgumentException("Sort order ${order.name} not supported") + }, + ) + if (filter.tags.isNotEmpty()) { + append("&genre=") + filter.tags.oneOrThrowIfMany()?.let { + append(it.key) + } + } + filter.states.oneOrThrowIfMany()?.let { + append("&status=") append( - when (filter.sortOrder) { - SortOrder.UPDATED -> 0 - SortOrder.POPULARITY -> 10 - SortOrder.NEWEST -> 15 - SortOrder.RATING -> 20 - else -> throw IllegalArgumentException("Sort order ${filter.sortOrder.name} not supported") + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "2" + else -> "-1" }, ) - if (filter.tags.isNotEmpty()) { - append("&genre=") - filter.tags.oneOrThrowIfMany()?.let { - append(it.key) - } - } - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - append( - when (it) { - MangaState.ONGOING -> "1" - MangaState.FINISHED -> "2" - else -> "-1" - }, - ) - } - append("&page=") - append(page.toString()) } - - webClient.httpGet(url) + append("&page=") + append(page.toString()) } - null -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append("?genres=¬genres=&gender=-1&status=-1&minchapter=1&sort=0&page=") - append(page.toString()) - } - webClient.httpGet(url) - } + webClient.httpGet(url) } + } val tagMap = getOrCreateTagMap() return parseMangaList(response.parseHtml(), tagMap) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenHE.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenHE.kt index b4ed1b0c..4aa10f2b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenHE.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenHE.kt @@ -5,25 +5,17 @@ import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.exception.NotFoundException -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.util.* -import java.util.EnumSet +import java.util.* @MangaSourceParser("NETTRUYENHE", "NetTruyenHE", "vi") internal class NetTruyenHE(context: MangaLoaderContext) : WpComicsParser(context, MangaParserSource.NETTRUYENHE, "nettruyenhe.com", 20) { - override val isMultipleTagsSupported = true - override val isTagsExclusionSupported = true override val listUrl = "/tim-kiem-nang-cao" - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) + override val availableSortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, @@ -33,10 +25,20 @@ internal class NetTruyenHE(context: MangaLoaderContext) : SortOrder.ALPHABETICAL_DESC, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val response = - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -56,7 +58,7 @@ internal class NetTruyenHE(context: MangaLoaderContext) : result.getOrThrow() } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) @@ -93,7 +95,7 @@ internal class NetTruyenHE(context: MangaLoaderContext) : append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "latest-updated" SortOrder.POPULARITY -> "views" SortOrder.NEWEST -> "new" @@ -107,19 +109,6 @@ internal class NetTruyenHE(context: MangaLoaderContext) : webClient.httpGet(url) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append('/') - append(page.toString()) - append('/') - append("?genres=¬Genres=&sex=All&status=&chapter_count=0&sort=latest-updated") - } - webClient.httpGet(url) - } } val tagMap = getOrCreateTagMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenLL.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenLL.kt index 09c458b6..0529e4c6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenLL.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenLL.kt @@ -5,12 +5,7 @@ import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.exception.NotFoundException -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.util.* import java.util.EnumSet @@ -21,11 +16,18 @@ import org.koitharu.kotatsu.parsers.Broken internal class NetTruyenLL(context: MangaLoaderContext) : WpComicsParser(context, MangaParserSource.NETTRUYENLL, "nettruyenll.com", 20) { - override val isMultipleTagsSupported = true - override val isTagsExclusionSupported = true override val listUrl = "/tim-kiem-nang-cao" - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + ) + override val availableSortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, @@ -35,10 +37,10 @@ internal class NetTruyenLL(context: MangaLoaderContext) : SortOrder.ALPHABETICAL_DESC, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val response = - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -58,7 +60,7 @@ internal class NetTruyenLL(context: MangaLoaderContext) : result.getOrThrow() } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) @@ -95,7 +97,7 @@ internal class NetTruyenLL(context: MangaLoaderContext) : append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "latest-updated" SortOrder.POPULARITY -> "views" SortOrder.NEWEST -> "new" @@ -109,19 +111,6 @@ internal class NetTruyenLL(context: MangaLoaderContext) : webClient.httpGet(url) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append('/') - append(page.toString()) - append('/') - append("?genres=¬Genres=&sex=All&status=&chapter_count=0&sort=latest-updated") - } - webClient.httpGet(url) - } } val tagMap = getOrCreateTagMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenSSR.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenSSR.kt index cec49745..23f75bb4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenSSR.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenSSR.kt @@ -5,12 +5,7 @@ import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.exception.NotFoundException -import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState -import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.Broken @@ -21,11 +16,8 @@ import java.util.EnumSet internal class NetTruyenSSR(context: MangaLoaderContext) : WpComicsParser(context, MangaParserSource.NETTRUYENSSR, "nettruyenssr.com", 20) { - override val isMultipleTagsSupported = true - override val isTagsExclusionSupported = true override val listUrl = "/tim-kiem-nang-cao" - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED) + override val availableSortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, @@ -35,10 +27,20 @@ internal class NetTruyenSSR(context: MangaLoaderContext) : SortOrder.ALPHABETICAL_DESC, ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.PAUSED, MangaState.ABANDONED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val response = - when (filter) { - is MangaListFilter.Search -> { + when { + !filter.query.isNullOrEmpty() -> { val url = buildString { append("https://") append(domain) @@ -58,7 +60,7 @@ internal class NetTruyenSSR(context: MangaLoaderContext) : result.getOrThrow() } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) @@ -95,7 +97,7 @@ internal class NetTruyenSSR(context: MangaLoaderContext) : append("&sort=") append( - when (filter.sortOrder) { + when (order) { SortOrder.UPDATED -> "latest-updated" SortOrder.POPULARITY -> "views" SortOrder.NEWEST -> "new" @@ -109,19 +111,6 @@ internal class NetTruyenSSR(context: MangaLoaderContext) : webClient.httpGet(url) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append(listUrl) - append('/') - append(page.toString()) - append('/') - append("?genres=¬Genres=&sex=All&status=&chapter_count=0&sort=latest-updated") - } - webClient.httpGet(url) - } } val tagMap = getOrCreateTagMap() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt index df66b316..0b8beec7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt @@ -30,15 +30,15 @@ internal abstract class ZeistMangaParser( keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED) - protected open val datePattern = "yyyy-MM-dd" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + @JvmField protected val ongoing: Set = hashSetOf( "ongoing", @@ -82,16 +82,21 @@ internal abstract class ZeistMangaParser( protected open val mangaCategory: String = "Series" - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val startIndex = maxMangaResults * (page - 1) + 1 val url = buildString { append("https://") append(domain) append("/feeds/posts/default/-/") - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append(mangaCategory) append("?alt=json&orderby=published&max-results=") append((maxMangaResults + 1).toString()) @@ -103,7 +108,7 @@ internal abstract class ZeistMangaParser( append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { @@ -132,14 +137,6 @@ internal abstract class ZeistMangaParser( append("&start-index=") append(startIndex.toString()) } - - null -> { - append(mangaCategory) - append("?alt=json&orderby=published&max-results=") - append((maxMangaResults + 1).toString()) - append("&start-index=") - append(startIndex.toString()) - } } } @@ -183,7 +180,7 @@ internal abstract class ZeistMangaParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt index 35ae299d..57fa20aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt @@ -17,7 +17,7 @@ internal class MangaAiLand(context: MangaLoaderContext) : override val sateFinished: String = "مكتملة" override val sateAbandoned: String = "متوقفة" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaHub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaHub.kt index ee37fee5..0531544e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaHub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaHub.kt @@ -14,13 +14,14 @@ import java.util.* internal class MangaHub(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.MANGAHUB_LINK, "www.mangahub.link") { - override val availableStates: Set = - EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - override val sateOngoing: String = "مستمر" override val sateFinished: String = "مكتمل" - override suspend fun getAvailableTags(): Set { + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("Genre").select("div.items-center").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt index 1a0a4178..7d690171 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt @@ -15,7 +15,7 @@ import org.koitharu.kotatsu.parsers.util.requireElementById internal class KlManhua(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.KLMANHUA, "klmanhua.blogspot.com") { - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt index 89f91daa..9d370097 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt @@ -12,7 +12,7 @@ import org.koitharu.kotatsu.parsers.util.* internal class Mikoroku(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.MIKOROKU, "www.mikoroku.web.id") { - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("Genre").select("div.items-center").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt index 57be769e..69feebd1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt @@ -15,7 +15,7 @@ internal class ShiyuraSub(context: MangaLoaderContext) : override val selectTags = ".leading-8 div.my-5.gap-2 a" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.select("div.list-label-widget-content ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt index b9e50938..2c5b8176 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt @@ -13,7 +13,8 @@ import org.koitharu.kotatsu.parsers.util.* @MangaSourceParser("TOONCUBUS", "ToonCubus", "id", ContentType.HENTAI) internal class ToonCubus(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.TOONCUBUS, "www.tooncubus.top") { - override suspend fun getAvailableTags(): Set { + + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/p/genre-list.html").parseHtml() return doc.select(".dzdes-genre ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt index dccc99fc..99a25337 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt @@ -17,7 +17,7 @@ internal class AnimeXNovel(context: MangaLoaderContext) : override val sateFinished: String = "Completo" override val sateAbandoned: String = "Dropado" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt index 1389e8ab..7d1a5776 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt @@ -14,7 +14,7 @@ import org.koitharu.kotatsu.parsers.util.requireElementById internal class GuildaTierDraw(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.GUILDATIERDRAW, "www.guildatierdraw.top") { - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList2").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/RaysScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/RaysScan.kt index 2e120fbc..6a6bb40c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/RaysScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/RaysScan.kt @@ -3,15 +3,18 @@ package org.koitharu.kotatsu.parsers.site.zeistmanga.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser @MangaSourceParser("RAYSSCAN", "RaysScan", "pt") internal class RaysScan(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.RAYSSCAN, "raysscan.blogspot.com") { - override val availableStates: Set = emptySet() - override suspend fun getAvailableTags(): Set = emptySet() + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + ) + + override suspend fun fetchAvailableTags(): Set = emptySet() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TemakiMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TemakiMangas.kt index 1d212030..94292752 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TemakiMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TemakiMangas.kt @@ -3,15 +3,18 @@ package org.koitharu.kotatsu.parsers.site.zeistmanga.pt import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser @MangaSourceParser("TEMAKIMANGAS", "TemakiMangas", "pt") internal class TemakiMangas(context: MangaLoaderContext) : ZeistMangaParser(context, MangaParserSource.TEMAKIMANGAS, "www.temakimangas.xyz") { - override val availableStates: Set = emptySet() - override suspend fun getAvailableTags(): Set = emptySet() + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = emptySet(), + ) + + override suspend fun fetchAvailableTags(): Set = emptySet() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt index aafeb176..df04089c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt @@ -17,7 +17,7 @@ internal class EpikMan(context: MangaLoaderContext) : override val sateFinished = "Tamamlandı" override val mangaCategory = "Seri" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/HyperionScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/HyperionScans.kt index 7507a09a..72471c5f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/HyperionScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/HyperionScans.kt @@ -17,7 +17,7 @@ internal class HyperionScans(context: MangaLoaderContext) : override val sateFinished: String = "Tamamlandı" override val sateAbandoned: String = "Güncel" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt index f27dd7b9..03ffd999 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt @@ -18,7 +18,7 @@ internal class Mikrokosmosfb(context: MangaLoaderContext) : override val sateFinished: String = "Tamamlandı" override val sateAbandoned: String = "Güncel" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain").parseHtml() val tags = doc.selectFirstOrThrow("script:containsData(label: )").data() .substringAfter("label: [").substringBefore("]").replace("\"", "").split(", ") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt index f1341631..a5ad5d90 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt @@ -20,7 +20,7 @@ internal class SnscoeurTurkey(context: MangaLoaderContext) : override val mangaCategory = "Seriler" - override suspend fun getAvailableTags(): Set { + override suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/p/gelismis-arama.html").parseHtml() return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet { MangaTag( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt index 0467eb45..3714c145 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt @@ -19,8 +19,6 @@ internal class Baozimh(context: MangaLoaderContext) : override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - override val configKeyDomain = ConfigKey.Domain("www.baozimh.com") override fun onCreateConfig(keys: MutableCollection>) { @@ -28,14 +26,21 @@ internal class Baozimh(context: MangaLoaderContext) : keys.add(userAgentKey) } - override val isMultipleTagsSupported = false - private val tagsMap = SuspendLazy(::parseTags) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsMap.get().values.toSet(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) - when (filter) { - is MangaListFilter.Search -> { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { if (page > 1) return emptyList() val url = buildString { append("https://") @@ -46,7 +51,7 @@ internal class Baozimh(context: MangaLoaderContext) : return parseMangaListSearch(webClient.httpGet(url).parseHtml()) } - is MangaListFilter.Advanced -> { + else -> { val url = buildString { append("https://") append(domain) @@ -82,16 +87,6 @@ internal class Baozimh(context: MangaLoaderContext) : return parseMangaList(webClient.httpGet(url).parseJson().getJSONArray("items")) } - - null -> { - val url = buildString { - append("https://") - append(domain) - append("/api/bzmhq/amp_comic_list?filter=*®ion=all&type=all&state=all&limit=36&page=") - append(page.toString()) - } - return parseMangaList(webClient.httpGet(url).parseJson().getJSONArray("items")) - } } } @@ -135,10 +130,6 @@ internal class Baozimh(context: MangaLoaderContext) : } } - override suspend fun getAvailableTags(): Set { - return tagsMap.get().values.toSet() - } - private suspend fun parseTags(): Map { val tagElements = webClient.httpGet("https://$domain/classify").parseHtml() .select("div.nav")[3].select("a.item:not(.active)") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt index b1d04e23..695f0ce5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt @@ -35,11 +35,19 @@ internal abstract class ZMangaParser( SortOrder.ALPHABETICAL_DESC, ) - override val availableStates: Set = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED) - protected open val listUrl = "advanced-search/" protected open val datePattern = "MMMM d, yyyy" + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) init { paginator.firstPage = 1 @@ -58,7 +66,7 @@ internal abstract class ZMangaParser( "Completed", ) - override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) @@ -70,17 +78,17 @@ internal abstract class ZMangaParser( append('/') } - when (filter) { + when { - is MangaListFilter.Search -> { + !filter.query.isNullOrEmpty() -> { append("&title=") append(filter.query.urlEncoded()) } - is MangaListFilter.Advanced -> { + else -> { append("?order=") - when (filter.sortOrder) { + when (order) { SortOrder.POPULARITY -> append("popular") SortOrder.UPDATED -> append("update") SortOrder.ALPHABETICAL -> append("title") @@ -108,8 +116,6 @@ internal abstract class ZMangaParser( ) } } - - null -> append("?order=update") } } @@ -141,7 +147,7 @@ internal abstract class ZMangaParser( } } - override suspend fun getAvailableTags(): Set { + protected open suspend fun fetchAvailableTags(): Set { val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() return doc.select("tr.gnrx div.custom-control").mapNotNullToSet { checkbox -> val key = checkbox.selectFirstOrThrow("input").attr("value") ?: return@mapNotNullToSet null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Assert.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Assert.kt index fbf6b9af..35212e30 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Assert.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Assert.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.parsers.util -fun T?.assertNotNull(name: String): T? { +internal fun T?.assertNotNull(name: String): T? { assert(this != null) { "Value $name is null" } return this -} \ No newline at end of file +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Chapters.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Chapters.kt index 4211ad09..eb56ac0a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Chapters.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Chapters.kt @@ -4,7 +4,7 @@ import org.koitharu.kotatsu.parsers.InternalParsersApi import org.koitharu.kotatsu.parsers.model.MangaChapter @InternalParsersApi -inline fun List.mapChapters( +public inline fun List.mapChapters( reversed: Boolean = false, transform: (index: Int, T) -> MangaChapter?, ): List { @@ -20,7 +20,7 @@ inline fun List.mapChapters( } @InternalParsersApi -inline fun List.flatMapChapters( +public inline fun List.flatMapChapters( reversed: Boolean = false, transform: (T) -> Iterable, ): List { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Coroutines.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Coroutines.kt index fffb94f2..0a902c1a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Coroutines.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Coroutines.kt @@ -7,11 +7,11 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlin.coroutines.cancellation.CancellationException -fun Iterable.cancelAll(cause: CancellationException? = null) { +public fun Iterable.cancelAll(cause: CancellationException? = null) { forEach { it.cancel(cause) } } -suspend fun Iterable>.awaitFirst(): T { +public suspend fun Iterable>.awaitFirst(): T { return channelFlow { for (deferred in this@awaitFirst) { launch { @@ -21,7 +21,7 @@ suspend fun Iterable>.awaitFirst(): T { }.first().also { this@awaitFirst.cancelAll() } } -suspend fun Collection>.awaitFirst(condition: (T) -> Boolean): T { +public suspend fun Collection>.awaitFirst(condition: (T) -> Boolean): T { return channelFlow { for (deferred in this@awaitFirst) { launch { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Enum.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Enum.kt index 2ff419fa..56e2c7d4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Enum.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Enum.kt @@ -4,10 +4,10 @@ package org.koitharu.kotatsu.parsers.util import kotlin.enums.EnumEntries -fun > EnumEntries.names() = Array(size) { i -> +public fun > EnumEntries.names() = Array(size) { i -> get(i).name } -fun > EnumEntries.find(name: String): E? { +public fun > EnumEntries.find(name: String): E? { return find { x -> x.name == name } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt index 80d12b7c..96b61b1d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt @@ -6,12 +6,12 @@ import org.koitharu.kotatsu.parsers.model.Favicons import org.koitharu.kotatsu.parsers.network.WebClient import org.koitharu.kotatsu.parsers.util.json.mapJSON -class FaviconParser( +public class FaviconParser( private val webClient: WebClient, private val domain: String, ) { - suspend fun parseFavicons(): Favicons { + public suspend fun parseFavicons(): Favicons { val url = "https://$domain" val doc = webClient.httpGet(url).parseHtml() val result = HashSet() 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 79fb7bc0..845c5e29 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt @@ -51,30 +51,34 @@ fun Element.parseFailed(message: String? = null): Nothing { } @InternalParsersApi -fun Set?.oneOrThrowIfMany(): MangaTag? { - return when { - isNullOrEmpty() -> null - size == 1 -> first() - else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_GENRES_NOT_SUPPORTED) - } -} +fun Set?.oneOrThrowIfMany(): MangaTag? = oneOrThrowIfMany( + ErrorMessages.FILTER_MULTIPLE_GENRES_NOT_SUPPORTED, +) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): MangaState? { - return when { - isNullOrEmpty() -> null - size == 1 -> first() - else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_STATES_NOT_SUPPORTED) - } -} +fun Set?.oneOrThrowIfMany(): MangaState? = oneOrThrowIfMany( + ErrorMessages.FILTER_MULTIPLE_STATES_NOT_SUPPORTED, +) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): ContentRating? { - return when { - isNullOrEmpty() -> null - size == 1 -> first() - else -> throw IllegalArgumentException(ErrorMessages.FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED) - } +fun Set?.oneOrThrowIfMany(): ContentType? = oneOrThrowIfMany( + ErrorMessages.FILTER_MULTIPLE_CONTENT_TYPES_NOT_SUPPORTED, +) + +@InternalParsersApi +fun Set?.oneOrThrowIfMany(): Demographic? = oneOrThrowIfMany( + ErrorMessages.FILTER_MULTIPLE_DEMOGRAPHICS_NOT_SUPPORTED, +) + +@InternalParsersApi +fun Set?.oneOrThrowIfMany(): ContentRating? = oneOrThrowIfMany( + ErrorMessages.FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED, +) + +private fun Set?.oneOrThrowIfMany(msg: String): T? = when { + isNullOrEmpty() -> null + size == 1 -> first() + else -> throw IllegalArgumentException(msg) } val MangaParser.domain: String diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt index 4eacc6d7..464ce78c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt @@ -5,6 +5,7 @@ package org.koitharu.kotatsu.parsers.util import java.text.DecimalFormat import java.text.NumberFormat import java.util.* +import kotlin.math.absoluteValue fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = ' '): String { val formatter = NumberFormat.getInstance(Locale.US) as DecimalFormat @@ -22,7 +23,7 @@ fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = return when (this) { is Float, is Double, - -> formatter.format(this.toDouble()) + -> formatter.format(this.toDouble()) else -> formatter.format(this.toLong()) } @@ -30,7 +31,7 @@ fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = fun Float.toIntUp(): Int { val intValue = toInt() - return if (this == intValue.toFloat()) { + return if ((this - intValue.toFloat()).absoluteValue <= 0.00001) { intValue } else { intValue + 1 @@ -54,3 +55,9 @@ fun Number.formatSimple(): String { raw } } + +inline fun Int.ifZero(defaultVale: () -> Int): Int = if (this == 0) { + defaultVale() +} else { + this +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt index 516a5b9c..c118a298 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt @@ -6,6 +6,8 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.SortOrder class RelatedMangaFinder( private val parsers: Collection, @@ -34,7 +36,7 @@ class RelatedMangaFinder( } val results = words.map { keyword -> scope.async { - val result = parser.getList(0, keyword) + val result = parser.getList(0, SortOrder.RELEVANCE, MangaListFilter(query = keyword)) result.filter { it.id != seed.id && it.containKeyword(keyword) } } }.awaitAll() diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt index bdb66622..33819830 100644 --- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt +++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt @@ -19,19 +19,22 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|list|{0}") @MangaSources - fun list(source: MangaParserSource) = runTest(timeout = timeout) { + fun list(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, null) + val list = parser.getList(0, parser.defaultSortOrder, MangaListFilter.EMPTY) checkMangaList(list, "list") assert(list.all { it.source == source }) } @ParameterizedTest(name = "{index}|pagination|{0}") @MangaSources - fun pagination(source: MangaParserSource) = runTest(timeout = timeout) { + fun pagination(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val page1 = parser.getList(0, filter = null) - val page2 = parser.getList(page1.size, filter = null) + if (parser is SinglePageMangaParser) { + return@runTest + } + val page1 = parser.getList(0, parser.defaultSortOrder, MangaListFilter.EMPTY) + val page2 = parser.getList(page1.size, parser.defaultSortOrder, MangaListFilter.EMPTY) if (parser is PagedMangaParser) { assert(parser.pageSize >= page1.size) { "Page size is ${page1.size} but ${parser.pageSize} expected" @@ -48,24 +51,18 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|search|{0}") @MangaSources - fun search(source: MangaParserSource) = runTest(timeout = timeout) { + fun search(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) val subject = parser.getList( offset = 0, - filter = MangaListFilter.Advanced( - sortOrder = SortOrder.POPULARITY, - tags = emptySet(), - locale = null, - states = emptySet(), - tagsExclude = emptySet(), - contentRating = emptySet(), - ), + order = SortOrder.POPULARITY, + filter = MangaListFilter.EMPTY, ).minByOrNull { it.title.length } ?: error("No manga found") val query = subject.title check(query.isNotBlank()) { "Manga title '$query' is blank" } - val list = parser.getList(0, MangaListFilter.Search(query)) + val list = parser.getList(0, SortOrder.RELEVANCE, MangaListFilter(query = query)) assert(list.isNotEmpty()) { "Empty search results by \"$query\"" } assert(list.singleOrNull { it.url == subject.url && it.id == subject.id } != null) { "Single subject '${subject.title} (${subject.publicUrl})' not found in search results" @@ -76,9 +73,9 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|tags|{0}") @MangaSources - fun tags(source: MangaParserSource) = runTest(timeout = timeout) { + fun tags(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val tags = parser.getAvailableTags() + val tags = parser.getFilterOptions().availableTags assert(tags.isNotEmpty()) { "No tags found" } val keys = tags.map { it.key } assert(keys.isDistinct()) @@ -95,9 +92,8 @@ internal class MangaParserTest { val tag = tags.last() val list = parser.getList( offset = 0, - MangaListFilter.Advanced.Builder(parser.defaultSortOrder) - .tags(setOf(tag)) - .build(), + order = parser.defaultSortOrder, + filter = MangaListFilter(tags = setOf(tag)), ) checkMangaList(list, "${tag.title} (${tag.key})") assert(list.all { it.source == source }) @@ -105,36 +101,30 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|tags_multiple|{0}") @MangaSources - fun tagsMultiple(source: MangaParserSource) = runTest(timeout = timeout) { + fun tagsMultiple(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - if (!parser.isMultipleTagsSupported) return@runTest - val tags = parser.getAvailableTags().shuffled().take(2).toSet() + if (!parser.filterCapabilities.isMultipleTagsSupported) return@runTest + val tags = parser.getFilterOptions().availableTags.shuffled().take(2).toSet() - val filter = MangaListFilter.Advanced.Builder(parser.availableSortOrders.first()) - .tags(tags) - .build() - val list = parser.getList(0, filter) + val filter = MangaListFilter(tags = tags) + val list = parser.getList(0, parser.defaultSortOrder, filter) checkMangaList(list, "${tags.joinToString { it.title }} (${tags.joinToString { it.key }})") assert(list.all { it.source == source }) } @ParameterizedTest(name = "{index}|locale|{0}") @MangaSources - fun locale(source: MangaParserSource) = runTest(timeout = timeout) { + fun locale(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val locales = parser.getAvailableLocales() + val locales = parser.getFilterOptions().availableLocales if (locales.isEmpty()) { return@runTest } - val filter = MangaListFilter.Advanced( - sortOrder = parser.availableSortOrders.first(), - tags = setOf(), - tagsExclude = setOf(), + val filter = MangaListFilter( locale = locales.random(), - states = setOf(), - contentRating = setOf(), + originalLocale = locales.random(), ) - val list = parser.getList(offset = 0, filter) + val list = parser.getList(offset = 0, order = parser.defaultSortOrder, filter) checkMangaList(list, filter.locale.toString()) assert(list.all { it.source == source }) } @@ -142,9 +132,9 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|details|{0}") @MangaSources - fun details(source: MangaParserSource) = runTest(timeout = timeout) { + fun details(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, null) + val list = parser.getList(0, parser.defaultSortOrder, MangaListFilter.EMPTY) val manga = list[3] parser.getDetails(manga).apply { assert(!chapters.isNullOrEmpty()) { "Chapters are null or empty" } @@ -172,9 +162,9 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|pages|{0}") @MangaSources - fun pages(source: MangaParserSource) = runTest(timeout = timeout) { + fun pages(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) - val list = parser.getList(0, null) + val list = parser.getList(0, parser.defaultSortOrder, MangaListFilter.EMPTY) val manga = list.first() val chapter = parser.getDetails(manga).chapters?.firstOrNull() ?: error("Chapter is null at ${manga.publicUrl}") val pages = parser.getPages(chapter) @@ -196,7 +186,7 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|favicon|{0}") @MangaSources - fun favicon(source: MangaParserSource) = runTest(timeout = timeout) { + fun favicon(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) val favicons = parser.getFavicons() val types = setOf("png", "svg", "ico", "gif", "jpg", "jpeg") @@ -212,7 +202,7 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|domain|{0}") @MangaSources - fun domain(source: MangaParserSource) = runTest(timeout = timeout) { + fun domain(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) val defaultDomain = parser.domain val url = HttpUrl.Builder().host(defaultDomain).scheme("https").toString() @@ -228,7 +218,7 @@ internal class MangaParserTest { @ParameterizedTest(name = "{index}|authorization|{0}") @MangaSources @Disabled - fun authorization(source: MangaParserSource) = runTest(timeout = timeout) { + fun authorization(source: MangaParserSource) = runTest(timeout = timeout) { val parser = context.newParserInstance(source) if (parser is MangaParserAuthProvider) { val username = parser.getUsername()