Refactor getList function

pull/19/head
Koitharu 4 years ago
parent 22baf09cc3
commit d724460332
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -73,8 +73,6 @@ class ParserProcessor(
"""
package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.model.MangaSource
enum class MangaSource(
val title: String,
val locale: String?,

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import java.util.*
abstract class MangaParser(val source: MangaSource) {
abstract class MangaParser @InternalParsersApi constructor(val source: MangaSource) {
protected abstract val context: MangaLoaderContext
@ -27,6 +27,15 @@ abstract class MangaParser(val source: MangaSource) {
*/
protected abstract val configKeyDomain: ConfigKey.Domain
/**
* Used as fallback if value of `sortOrder` passed to [getList] is null
*/
protected open val defaultSortOrder: SortOrder
get() {
val supported = sortOrders
return SortOrder.values().first { it in supported }
}
/**
* Parse list of manga by specified criteria
*
@ -36,13 +45,36 @@ abstract class MangaParser(val source: MangaSource) {
* @param tags genres for filtering, values from [getTags] and [Manga.tags]. May be null or empty
* @param sortOrder one of [sortOrders] or null for default value
*/
@InternalParsersApi
abstract suspend fun getList(
offset: Int,
query: String? = null,
tags: Set<MangaTag>? = null,
sortOrder: SortOrder? = null,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga>
/**
* Parse list of manga with search by text query
*
* @param offset starting from 0 and used for pagination.
* @param query search query
*/
suspend fun getList(offset: Int, query: String): List<Manga> {
return getList(offset, query, null, defaultSortOrder)
}
/**
* 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 [getTags] and [Manga.tags]. May be null or empty
* @param sortOrder one of [sortOrders] or null for default value
*/
suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga> {
return getList(offset, null, tags, sortOrder ?: defaultSortOrder)
}
/**
* Parse details for [Manga]: chapters list, description, large cover, etc.
* Must return the same manga, may change any fields excepts id, url and source
@ -121,12 +153,29 @@ abstract class MangaParser(val source: MangaSource) {
/**
* Convert relative url to an absolute using [getDomain]
*/
protected fun String.withDomain(subdomain: String? = null): String {
var domain = getDomain()
if (subdomain != null) {
domain = subdomain + "." + domain.removePrefix("www.")
}
return toAbsoluteUrl(domain)
@Deprecated(
message = "Use toAbsoluteUrl() instead",
replaceWith = ReplaceWith(
"toAbsoluteUrl(getDomain(), subdomain)",
"org.koitharu.kotatsu.parsers.util.toAbsoluteUrl",
),
)
protected fun String.withDomain(subdomain: String): String {
return toAbsoluteUrl(getDomain(), subdomain)
}
/**
* Convert relative url to an absolute using [getDomain]
*/
@Deprecated(
message = "Use toAbsoluteUrl() instead",
replaceWith = ReplaceWith(
"toAbsoluteUrl(getDomain())",
"org.koitharu.kotatsu.parsers.util.toAbsoluteUrl",
),
)
protected fun String.withDomain(): String {
return toAbsoluteUrl(getDomain())
}
@InternalParsersApi

@ -30,7 +30,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
if (!query.isNullOrEmpty()) {
return if (offset == 0) {

@ -39,7 +39,7 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
if (!query.isNullOrEmpty()) {
return search(offset, query)
@ -52,7 +52,6 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
append(getDomain())
append("/browse?sort=")
when (sortOrder) {
null,
SortOrder.UPDATED,
-> append("update.za")
SortOrder.POPULARITY -> append("views_a.za")

@ -19,7 +19,7 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) {
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = when {
@ -143,16 +143,16 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) {
}
}
private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.ALPHABETICAL -> "catalog"
SortOrder.POPULARITY -> "mostfavorites"
SortOrder.NEWEST -> "manga/new"
else -> "mostfavorites"
}
private fun getSortKey2(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getSortKey2(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.ALPHABETICAL -> "abcasc"
SortOrder.POPULARITY -> "favdesc"
SortOrder.NEWEST -> "datedesc"

@ -39,7 +39,7 @@ internal class ComickFunParser(override val context: MangaLoaderContext) : Manga
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = buildString {

@ -28,7 +28,7 @@ internal class DesuMeParser(override val context: MangaLoaderContext) : MangaPar
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
if (query != null && offset != 0) {
return emptyList()
@ -149,7 +149,7 @@ internal class DesuMeParser(override val context: MangaLoaderContext) : MangaPar
}
}
private fun getSortKey(sortOrder: SortOrder?) =
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.ALPHABETICAL -> "name"
SortOrder.POPULARITY -> "popular"

@ -61,7 +61,7 @@ internal class ExHentaiParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val page = (offset / 25f).toIntUp()
var search = query?.urlEncoded().orEmpty()

@ -5,7 +5,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response
import org.json.JSONArray
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.mapJSON
@ -34,7 +33,7 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) :
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val doc = when {
@ -48,17 +47,13 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) :
)
tags.isNullOrEmpty() -> context.httpGet(
"https://$domain/list?sortType=${
getSortKey(
sortOrder,
)
getSortKey(sortOrder)
}&offset=${offset upBy PAGE_SIZE}",
headers,
)
tags.size == 1 -> context.httpGet(
"https://$domain/list/genre/${tags.first().key}?sortType=${
getSortKey(
sortOrder,
)
getSortKey(sortOrder)
}&offset=${offset upBy PAGE_SIZE}",
headers,
)
@ -234,14 +229,13 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) :
}
}
private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.ALPHABETICAL -> "name"
SortOrder.POPULARITY -> "rate"
SortOrder.UPDATED -> "updated"
SortOrder.NEWEST -> "created"
SortOrder.RATING -> "votes"
null -> "updated"
}
private suspend fun advancedSearch(domain: String, tags: Set<MangaTag>): Response {

@ -21,7 +21,7 @@ internal class HenChanParser(override val context: MangaLoaderContext) : ChanPar
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
return super.getList(offset, query, tags, sortOrder).map {
it.copy(

@ -34,7 +34,7 @@ internal abstract class MadaraParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val tag = when {
tags.isNullOrEmpty() -> null

@ -35,7 +35,7 @@ internal class MangaDexParser(override val context: MangaLoaderContext) : MangaP
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = buildString {
@ -60,7 +60,6 @@ internal class MangaDexParser(override val context: MangaLoaderContext) : MangaP
append("&order")
append(
when (sortOrder) {
null,
SortOrder.UPDATED,
-> "[latestUploadedChapter]=desc"
SortOrder.ALPHABETICAL -> "[title]=asc"

@ -23,7 +23,7 @@ class MangaInUaParser(override val context: MangaLoaderContext) : MangaParser(Ma
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val page = (offset / 24f).toIntUp().inc()
val searchPage = (offset / 10f).toIntUp().inc()

@ -41,7 +41,7 @@ internal open class MangaLibParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
if (!query.isNullOrEmpty()) {
return if (offset == 0) search(query) else emptyList()

@ -27,7 +27,7 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val page = (offset / 36f).toIntUp().inc()
val link = buildString {
@ -165,16 +165,16 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
}
}
private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.POPULARITY -> "popular"
SortOrder.NEWEST -> "new_release"
SortOrder.UPDATED -> "lastest"
else -> "lastest"
}
private fun getAlternativeSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getAlternativeSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.POPULARITY -> "0"
SortOrder.NEWEST -> "2"
SortOrder.UPDATED -> "3"

@ -29,7 +29,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val sortKey = when (sortOrder) {
SortOrder.ALPHABETICAL -> "?name.az"

@ -38,7 +38,7 @@ internal abstract class NineMangaParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val page = (offset / PAGE_SIZE.toFloat()).toIntUp() + 1
val url = buildString {

@ -48,7 +48,7 @@ internal class NudeMoonParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = when {
@ -206,8 +206,8 @@ internal class NudeMoonParser(
return "https://${getDomain()}/favicon.jpg"
}
private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.POPULARITY -> "views"
SortOrder.NEWEST -> "date"
SortOrder.RATING -> "like"

@ -57,7 +57,7 @@ internal class RemangaParser(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?,
sortOrder: SortOrder,
): List<Manga> {
copyCookies()
val domain = getDomain()

@ -105,15 +105,24 @@ fun String.toRelativeUrl(domain: String): String {
}
/**
* Convert url to absolute with specified [domain]
* Convert url to absolute with specified domain
* @return an absolute url with [domain] if this is relative
*/
fun String.toAbsoluteUrl(domain: String): String = when {
this.startsWith("//") -> "https:$this"
this.startsWith("/") -> "https://$domain$this"
this.startsWith('/') -> "https://$domain$this"
else -> this
}
/**
* Convert url to absolute with specified domain and subdomain
* @return an absolute url with [subdomain].[domain] if this is relative
*/
fun String.toAbsoluteUrl(domain: String, subdomain: String): String {
if (!this.startsWith('/')) return this
return toAbsoluteUrl(subdomain + "." + domain.removePrefix("www."))
}
@Deprecated(
message = "",
level = DeprecationLevel.ERROR,

@ -25,7 +25,7 @@ internal class MangaParserTest {
@MangaSources
fun list(source: MangaSource) = runTest {
val parser = source.newParser(context)
val list = parser.getList(20, query = null, sortOrder = SortOrder.POPULARITY, tags = null)
val list = parser.getList(20, sortOrder = SortOrder.POPULARITY, tags = null)
checkMangaList(list, "list")
assert(list.all { it.source == source })
}
@ -34,12 +34,12 @@ internal class MangaParserTest {
@MangaSources
fun search(source: MangaSource) = runTest {
val parser = source.newParser(context)
val subject = parser.getList(20, query = null, sortOrder = SortOrder.POPULARITY, tags = null).minByOrNull {
val subject = parser.getList(20, sortOrder = SortOrder.POPULARITY, tags = null).minByOrNull {
it.title.length
} ?: error("No manga found")
val query = subject.title
check(query.isNotBlank()) { "Manga title '$query' is blank" }
val list = parser.getList(offset = 0, query, sortOrder = null, tags = null)
val list = parser.getList(0, query)
assert(list.singleOrNull { it.url == subject.url && it.id == subject.id } != null) {
"Single subject '${subject.title} (${subject.publicUrl})' not found in search results"
}
@ -62,7 +62,7 @@ internal class MangaParserTest {
assert(tags.all { it.source == source })
val tag = tags.last()
val list = parser.getList(offset = 0, tags = setOf(tag), query = null, sortOrder = null)
val list = parser.getList(offset = 0, tags = setOf(tag), sortOrder = null)
checkMangaList(list, "${tag.title} (${tag.key})")
assert(list.all { it.source == source })
}
@ -71,7 +71,7 @@ internal class MangaParserTest {
@MangaSources
fun details(source: MangaSource) = runTest {
val parser = source.newParser(context)
val list = parser.getList(20, query = null, sortOrder = SortOrder.POPULARITY, tags = null)
val list = parser.getList(20, sortOrder = SortOrder.POPULARITY, tags = null)
val manga = list[3]
parser.getDetails(manga).apply {
assert(!chapters.isNullOrEmpty()) { "Chapters are null or empty" }
@ -100,7 +100,7 @@ internal class MangaParserTest {
@MangaSources
fun pages(source: MangaSource) = runTest {
val parser = source.newParser(context)
val list = parser.getList(20, query = null, sortOrder = SortOrder.POPULARITY, tags = null)
val list = parser.getList(20, sortOrder = SortOrder.POPULARITY, tags = null)
val manga = list.first()
val chapter = parser.getDetails(manga).chapters?.firstOrNull() ?: error("Chapter is null")
val pages = parser.getPages(chapter)

Loading…
Cancel
Save