|
|
|
@ -1,10 +1,9 @@
|
|
|
|
package org.koitharu.kotatsu.parsers
|
|
|
|
package org.koitharu.kotatsu.parsers
|
|
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.CallSuper
|
|
|
|
import androidx.annotation.CallSuper
|
|
|
|
import kotlinx.coroutines.async
|
|
|
|
|
|
|
|
import kotlinx.coroutines.coroutineScope
|
|
|
|
|
|
|
|
import okhttp3.Headers
|
|
|
|
import okhttp3.Headers
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
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.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.network.OkHttpWebClient
|
|
|
|
import org.koitharu.kotatsu.parsers.network.OkHttpWebClient
|
|
|
|
import org.koitharu.kotatsu.parsers.network.WebClient
|
|
|
|
import org.koitharu.kotatsu.parsers.network.WebClient
|
|
|
|
@ -14,9 +13,9 @@ import org.koitharu.kotatsu.parsers.util.domain
|
|
|
|
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
|
|
|
|
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
|
|
|
|
import java.util.*
|
|
|
|
import java.util.*
|
|
|
|
|
|
|
|
|
|
|
|
abstract class MangaParser @InternalParsersApi constructor(
|
|
|
|
public abstract class MangaParser @InternalParsersApi constructor(
|
|
|
|
@property:InternalParsersApi val context: MangaLoaderContext,
|
|
|
|
@property:InternalParsersApi public val context: MangaLoaderContext,
|
|
|
|
val source: MangaParserSource,
|
|
|
|
public val source: MangaParserSource,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -24,43 +23,16 @@ abstract class MangaParser @InternalParsersApi constructor(
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* For better performance use [EnumSet] for more than one item.
|
|
|
|
* For better performance use [EnumSet] for more than one item.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
abstract val availableSortOrders: Set<SortOrder>
|
|
|
|
public abstract val availableSortOrders: Set<SortOrder>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public abstract val filterCapabilities: MangaListFilterCapabilities
|
|
|
|
* Supported [MangaState] variants for filtering. May be empty.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* For better performance use [EnumSet] for more than one item.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Deprecated("")
|
|
|
|
|
|
|
|
internal open val availableStates: Set<MangaState>
|
|
|
|
|
|
|
|
get() = emptySet()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open val filterCapabilities: MangaListFilterCapabilities
|
|
|
|
|
|
|
|
get() = MangaListFilterCapabilities(
|
|
|
|
|
|
|
|
isMultipleTagsSupported = isMultipleTagsSupported,
|
|
|
|
|
|
|
|
isTagsExclusionSupported = isTagsExclusionSupported,
|
|
|
|
|
|
|
|
isSearchSupported = true,
|
|
|
|
|
|
|
|
isSearchWithFiltersSupported = false,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public val config: MangaSourceConfig by lazy { context.getConfig(source) }
|
|
|
|
* Whether parser supports filtering by more than one tag
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Deprecated("Use getListFilterCapabilities instead")
|
|
|
|
|
|
|
|
internal open val isMultipleTagsSupported: Boolean = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public open val sourceLocale: Locale
|
|
|
|
* Whether parser supports tagsExclude field in filter
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Deprecated("Use getListFilterCapabilities instead")
|
|
|
|
|
|
|
|
internal open val isTagsExclusionSupported: Boolean = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val config by lazy { context.getConfig(source) }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open val sourceLocale: Locale
|
|
|
|
|
|
|
|
get() = if (source.locale.isEmpty()) Locale.ROOT else Locale(source.locale)
|
|
|
|
get() = if (source.locale.isEmpty()) Locale.ROOT else Locale(source.locale)
|
|
|
|
|
|
|
|
|
|
|
|
protected val isNsfwSource = source.contentType == ContentType.HENTAI
|
|
|
|
protected val isNsfwSource: Boolean = source.contentType == ContentType.HENTAI
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Provide default domain and available alternatives, if any.
|
|
|
|
* Provide default domain and available alternatives, if any.
|
|
|
|
@ -68,18 +40,18 @@ abstract class MangaParser @InternalParsersApi constructor(
|
|
|
|
* Never hardcode domain in requests, use [domain] instead.
|
|
|
|
* Never hardcode domain in requests, use [domain] instead.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@InternalParsersApi
|
|
|
|
@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])
|
|
|
|
.add("User-Agent", config[userAgentKey])
|
|
|
|
.build()
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Used as fallback if value of `order` passed to [getList] is null
|
|
|
|
* Used as fallback if value of `order` passed to [getList] is null
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
open val defaultSortOrder: SortOrder
|
|
|
|
public open val defaultSortOrder: SortOrder
|
|
|
|
get() {
|
|
|
|
get() {
|
|
|
|
val supported = availableSortOrders
|
|
|
|
val supported = availableSortOrders
|
|
|
|
return SortOrder.entries.first { it in supported }
|
|
|
|
return SortOrder.entries.first { it in supported }
|
|
|
|
@ -96,55 +68,41 @@ abstract class MangaParser @InternalParsersApi constructor(
|
|
|
|
* @param order one of [availableSortOrders] or [defaultSortOrder] for default value
|
|
|
|
* @param order one of [availableSortOrders] or [defaultSortOrder] for default value
|
|
|
|
* @param filter is a set of filter rules
|
|
|
|
* @param filter is a set of filter rules
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
abstract suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilterV2): List<Manga>
|
|
|
|
public abstract suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilterV2): List<Manga>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Parse details for [Manga]: chapters list, description, large cover, etc.
|
|
|
|
* Parse details for [Manga]: chapters list, description, large cover, etc.
|
|
|
|
* Must return the same manga, may change any fields excepts id, url and source
|
|
|
|
* Must return the same manga, may change any fields excepts id, url and source
|
|
|
|
* @see Manga.copy
|
|
|
|
* @see Manga.copy
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
abstract suspend fun getDetails(manga: Manga): Manga
|
|
|
|
public abstract suspend fun getDetails(manga: Manga): Manga
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Parse pages list for specified chapter.
|
|
|
|
* Parse pages list for specified chapter.
|
|
|
|
* @see MangaPage for details
|
|
|
|
* @see MangaPage for details
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
abstract suspend fun getPages(chapter: MangaChapter): List<MangaPage>
|
|
|
|
public abstract suspend fun getPages(chapter: MangaChapter): List<MangaPage>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Fetch direct link to the page image.
|
|
|
|
* Fetch direct link to the page image.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
internal open suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain)
|
|
|
|
internal open suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public abstract suspend fun getFilterOptions(): MangaListFilterOptions
|
|
|
|
* Fetch available tags (genres) for source
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Deprecated("Use getListFilterDatasets instead")
|
|
|
|
|
|
|
|
internal open suspend fun getAvailableTags(): Set<MangaTag> = emptySet()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open suspend fun getFilterOptions(): MangaListFilterOptions = coroutineScope {
|
|
|
|
|
|
|
|
val tagsDeferred = async { getAvailableTags() }
|
|
|
|
|
|
|
|
MangaListFilterOptions(
|
|
|
|
|
|
|
|
availableTags = tagsDeferred.await(),
|
|
|
|
|
|
|
|
availableStates = availableStates,
|
|
|
|
|
|
|
|
availableContentRating = emptySet(),
|
|
|
|
|
|
|
|
availableLocales = emptySet(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Parse favicons from the main page of the source`s website
|
|
|
|
* 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()
|
|
|
|
return FaviconParser(webClient, domain).parseFavicons()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@CallSuper
|
|
|
|
@CallSuper
|
|
|
|
open fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
|
|
|
|
public open fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
|
|
|
|
keys.add(configKeyDomain)
|
|
|
|
keys.add(configKeyDomain)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
open suspend fun getRelatedManga(seed: Manga): List<Manga> {
|
|
|
|
public open suspend fun getRelatedManga(seed: Manga): List<Manga> {
|
|
|
|
return RelatedMangaFinder(listOf(this)).invoke(seed)
|
|
|
|
return RelatedMangaFinder(listOf(this)).invoke(seed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|