Change configuration behavior

pull/6/head
Koitharu 4 years ago
parent 3589bee5f9
commit 6ab5743f66
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -5,6 +5,7 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject import org.json.JSONObject
import org.jsoup.HttpStatusException import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.exception.GraphQLException import org.koitharu.kotatsu.parsers.exception.GraphQLException
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.await

@ -1,19 +1,18 @@
package org.koitharu.kotatsu.parsers package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
abstract class MangaParser { abstract class MangaParser(val source: MangaSource) {
protected abstract val context: MangaLoaderContext protected abstract val context: MangaLoaderContext
abstract val source: MangaSource
abstract val sortOrders: Set<SortOrder> abstract val sortOrders: Set<SortOrder>
abstract val defaultDomain: String val config by lazy { context.getConfig(source) }
protected val config by lazy { context.getConfig(source) } protected abstract val configKeyDomain: ConfigKey.Domain
abstract suspend fun getList( abstract suspend fun getList(
offset: Int, offset: Int,
@ -32,10 +31,14 @@ abstract class MangaParser {
open fun getFaviconUrl() = "https://${getDomain()}/favicon.ico" open fun getFaviconUrl() = "https://${getDomain()}/favicon.ico"
open fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
keys.add(configKeyDomain)
}
/* Utils */ /* Utils */
protected fun getDomain(): String { fun getDomain(): String {
return config.getDomain(defaultDomain) return config[configKeyDomain]
} }
protected fun generateUid(url: String): Long { protected fun generateUid(url: String): Long {
@ -60,25 +63,19 @@ abstract class MangaParser {
protected fun String.withDomain(subdomain: String? = null) = when { protected fun String.withDomain(subdomain: String? = null) = when {
this.startsWith("//") -> buildString { this.startsWith("//") -> buildString {
append("http") append("https")
if (config.isSslEnabled(true)) {
append('s')
}
append(":") append(":")
append(this@withDomain) append(this@withDomain)
} }
this.startsWith("/") -> buildString { this.startsWith("/") -> buildString {
append("http") append("https")
if (config.isSslEnabled(true)) {
append('s')
}
append("://") append("://")
if (subdomain != null) { if (subdomain != null) {
append(subdomain) append(subdomain)
append('.') append('.')
append(config.getDomain(defaultDomain).removePrefix("www.")) append(getDomain().removePrefix("www."))
} else { } else {
append(config.getDomain(defaultDomain)) append(getDomain())
} }
append(this@withDomain) append(this@withDomain)
} }

@ -1,6 +0,0 @@
package org.koitharu.kotatsu.parsers
interface MangaSourceConfig {
fun getDomain(defaultValue: String): String
fun isSslEnabled(defaultValue: Boolean): Boolean
}

@ -0,0 +1,13 @@
package org.koitharu.kotatsu.parsers.config
sealed class ConfigKey<T>(
val key: String,
) {
abstract val defaultValue: T
class Domain(
override val defaultValue: String,
val presetValues: Array<String>?,
) : ConfigKey<String>("domain")
}

@ -0,0 +1,6 @@
package org.koitharu.kotatsu.parsers.config
interface MangaSourceConfig {
operator fun <T> get(key: ConfigKey<T>): T
}

@ -0,0 +1,3 @@
package org.koitharu.kotatsu.parsers.model
internal const val RATING_UNKNOWN = -1f

@ -1,25 +1,98 @@
package org.koitharu.kotatsu.parsers.model package org.koitharu.kotatsu.parsers.model
data class Manga( class Manga(
val id: Long, val id: Long,
val title: String, val title: String,
val altTitle: String? = null, val altTitle: String?,
val url: String, // relative url for internal use val url: String, // relative url for internal use
val publicUrl: String, val publicUrl: String,
val rating: Float = NO_RATING, // normalized value [0..1] or -1 val rating: Float, // normalized value [0..1] or -1
val isNsfw: Boolean = false, val isNsfw: Boolean,
val coverUrl: String, val coverUrl: String,
val tags: Set<MangaTag>,
val state: MangaState?,
val author: String?,
val largeCoverUrl: String? = null, val largeCoverUrl: String? = null,
val description: String? = null, // HTML val description: String? = null, // HTML
val tags: Set<MangaTag> = emptySet(),
val state: MangaState? = null,
val author: String? = null,
val chapters: List<MangaChapter>? = null, val chapters: List<MangaChapter>? = null,
val source: MangaSource, val source: MangaSource,
) { ) {
companion object { val hasRating: Boolean
get() = rating in 0f..1f
const val NO_RATING = -1f internal fun copy(
title: String = this.title,
altTitle: String? = this.altTitle,
publicUrl: String = this.publicUrl,
rating: Float = this.rating,
isNsfw: Boolean = this.isNsfw,
coverUrl: String = this.coverUrl,
tags: Set<MangaTag> = this.tags,
state: MangaState? = this.state,
author: String? = this.author,
largeCoverUrl: String? = this.largeCoverUrl,
description: String? = this.description,
chapters: List<MangaChapter>? = this.chapters,
) = Manga(
id = id,
title = title,
altTitle = altTitle,
url = url,
publicUrl = publicUrl,
rating = rating,
isNsfw = isNsfw,
coverUrl = coverUrl,
tags = tags,
state = state,
author = author,
largeCoverUrl = largeCoverUrl,
description = description,
chapters = chapters,
source = source,
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Manga
if (id != other.id) return false
if (title != other.title) return false
if (altTitle != other.altTitle) return false
if (url != other.url) return false
if (publicUrl != other.publicUrl) return false
if (rating != other.rating) return false
if (isNsfw != other.isNsfw) return false
if (coverUrl != other.coverUrl) return false
if (tags != other.tags) return false
if (state != other.state) return false
if (author != other.author) return false
if (largeCoverUrl != other.largeCoverUrl) return false
if (description != other.description) return false
if (chapters != other.chapters) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + (altTitle?.hashCode() ?: 0)
result = 31 * result + url.hashCode()
result = 31 * result + publicUrl.hashCode()
result = 31 * result + rating.hashCode()
result = 31 * result + isNsfw.hashCode()
result = 31 * result + coverUrl.hashCode()
result = 31 * result + tags.hashCode()
result = 31 * result + (state?.hashCode() ?: 0)
result = 31 * result + (author?.hashCode() ?: 0)
result = 31 * result + (largeCoverUrl?.hashCode() ?: 0)
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + (chapters?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
return result
} }
} }

@ -1,6 +1,6 @@
package org.koitharu.kotatsu.parsers.model package org.koitharu.kotatsu.parsers.model
data class MangaChapter( class MangaChapter(
val id: Long, val id: Long,
val name: String, val name: String,
val number: Int, val number: Int,
@ -14,4 +14,36 @@ data class MangaChapter(
override fun compareTo(other: MangaChapter): Int { override fun compareTo(other: MangaChapter): Int {
return number.compareTo(other.number) return number.compareTo(other.number)
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaChapter
if (id != other.id) return false
if (name != other.name) return false
if (number != other.number) return false
if (url != other.url) return false
if (scanlator != other.scanlator) return false
if (uploadDate != other.uploadDate) return false
if (branch != other.branch) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + number
result = 31 * result + url.hashCode()
result = 31 * result + (scanlator?.hashCode() ?: 0)
result = 31 * result + uploadDate.hashCode()
result = 31 * result + (branch?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
return result
}
} }

@ -1,9 +1,34 @@
package org.koitharu.kotatsu.parsers.model package org.koitharu.kotatsu.parsers.model
data class MangaPage( class MangaPage(
val id: Long, val id: Long,
val url: String, val url: String,
val referer: String, val referer: String,
val preview: String?, val preview: String?,
val source: MangaSource, val source: MangaSource,
) ) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaPage
if (id != other.id) return false
if (url != other.url) return false
if (referer != other.referer) return false
if (preview != other.preview) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + url.hashCode()
result = 31 * result + referer.hashCode()
result = 31 * result + (preview?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
return result
}
}

@ -1,7 +1,28 @@
package org.koitharu.kotatsu.parsers.model package org.koitharu.kotatsu.parsers.model
data class MangaTag( class MangaTag(
val title: String, val title: String,
val key: String, val key: String,
val source: MangaSource, val source: MangaSource,
) ) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaTag
if (title != other.title) return false
if (key != other.key) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + key.hashCode()
result = 31 * result + source.hashCode()
return result
}
}

@ -5,17 +5,16 @@ import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.stringIterator import org.koitharu.kotatsu.parsers.util.json.stringIterator
import java.util.* import java.util.*
internal class AnibelParser(override val context: MangaLoaderContext) : MangaParser() { internal class AnibelParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.ANIBEL) {
override val source = MangaSource.ANIBEL override val configKeyDomain = ConfigKey.Domain("anibel.net", null)
override val defaultDomain = "anibel.net"
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST, SortOrder.NEWEST,
@ -73,6 +72,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar
.withDomain("cdn") + "?width=200&height=280", .withDomain("cdn") + "?width=200&height=280",
altTitle = title.getString("alt").takeUnless(String::isEmpty), altTitle = title.getString("alt").takeUnless(String::isEmpty),
author = null, author = null,
isNsfw = false,
rating = jo.getDouble("rating").toFloat() / 10f, rating = jo.getDouble("rating").toFloat() / 10f,
url = href, url = href,
publicUrl = "https://${getDomain()}/$href", publicUrl = "https://${getDomain()}/$href",
@ -213,7 +213,8 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar
.withDomain("cdn") + "?width=200&height=280", .withDomain("cdn") + "?width=200&height=280",
altTitle = title.getString("en").takeUnless(String::isEmpty), altTitle = title.getString("en").takeUnless(String::isEmpty),
author = null, author = null,
rating = Manga.NO_RATING, isNsfw = false,
rating = RATING_UNKNOWN,
url = href, url = href,
publicUrl = "https://${getDomain()}/$href", publicUrl = "https://${getDomain()}/$href",
tags = emptySet(), tags = emptySet(),

@ -6,6 +6,7 @@ import org.json.JSONObject
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@ -18,9 +19,7 @@ import javax.crypto.spec.SecretKeySpec
private const val PAGE_SIZE = 60 private const val PAGE_SIZE = 60
private const val PAGE_SIZE_SEARCH = 20 private const val PAGE_SIZE_SEARCH = 20
internal class BatoToParser(override val context: MangaLoaderContext) : MangaParser() { internal class BatoToParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.BATOTO) {
override val source = MangaSource.BATOTO
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST, SortOrder.NEWEST,
@ -29,7 +28,10 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
) )
override val defaultDomain: String = "bato.to" override val configKeyDomain = ConfigKey.Domain(
"bato.to",
arrayOf("bato.to", "mto.to", "mangatoto.com", "battwo.com", "batotwo.com", "comiko.net", "batotoo.com"),
)
override suspend fun getList( override suspend fun getList(
offset: Int, offset: Int,
@ -193,7 +195,7 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
altTitle = div.selectFirst(".item-alias")?.text()?.takeUnless { it == title }, altTitle = div.selectFirst(".item-alias")?.text()?.takeUnless { it == title },
url = href, url = href,
publicUrl = a.absUrl("href"), publicUrl = a.absUrl("href"),
rating = Manga.NO_RATING, rating = RATING_UNKNOWN,
isNsfw = false, isNsfw = false,
coverUrl = div.selectFirst("img[src]")?.absUrl("src").orEmpty(), coverUrl = div.selectFirst("img[src]")?.absUrl("src").orEmpty(),
largeCoverUrl = null, largeCoverUrl = null,

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
internal abstract class ChanParser : MangaParser() { internal abstract class ChanParser(source: MangaSource) : MangaParser(source) {
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST, SortOrder.NEWEST,
@ -64,6 +64,9 @@ internal abstract class ChanParser : MangaParser() {
) )
} }
}.getOrNull().orEmpty(), }.getOrNull().orEmpty(),
rating = RATING_UNKNOWN,
state = null,
isNsfw = false,
source = source, source = source,
) )
} }

@ -6,6 +6,7 @@ import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.JSONIterator import org.koitharu.kotatsu.parsers.util.json.JSONIterator
@ -22,10 +23,10 @@ import java.util.*
private const val PAGE_SIZE = 20 private const val PAGE_SIZE = 20
private const val CHAPTERS_LIMIT = 99999 private const val CHAPTERS_LIMIT = 99999
internal class ComickFunParser(override val context: MangaLoaderContext) : MangaParser() { internal class ComickFunParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.COMICK_FUN) {
override val configKeyDomain = ConfigKey.Domain("comick.fun", null)
override val defaultDomain = "comick.fun"
override val source = MangaSource.COMICK_FUN
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY, SortOrder.POPULARITY,
SortOrder.UPDATED, SortOrder.UPDATED,

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -10,11 +11,9 @@ import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import java.util.* import java.util.*
internal class DesuMeParser(override val context: MangaLoaderContext) : MangaParser() { internal class DesuMeParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.DESUME) {
override val source = MangaSource.DESUME override val configKeyDomain = ConfigKey.Domain("desu.me", null)
override val defaultDomain = "desu.me"
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED, SortOrder.UPDATED,
@ -71,6 +70,9 @@ internal class DesuMeParser(override val context: MangaLoaderContext) : MangaPar
}, },
rating = jo.getDouble("score").toFloat().coerceIn(0f, 1f), rating = jo.getDouble("score").toFloat().coerceIn(0f, 1f),
id = generateUid(id), id = generateUid(id),
isNsfw = false,
tags = emptySet(),
author = null,
description = jo.getString("description"), description = jo.getString("description"),
) )
} }

@ -4,6 +4,7 @@ import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
@ -14,16 +15,16 @@ import kotlin.math.pow
private const val DOMAIN_UNAUTHORIZED = "e-hentai.org" private const val DOMAIN_UNAUTHORIZED = "e-hentai.org"
private const val DOMAIN_AUTHORIZED = "exhentai.org" private const val DOMAIN_AUTHORIZED = "exhentai.org"
internal class ExHentaiParser(override val context: MangaLoaderContext) : MangaParser(), MangaParserAuthProvider { internal class ExHentaiParser(
override val context: MangaLoaderContext,
override val source = MangaSource.EXHENTAI ) : MangaParser(MangaSource.EXHENTAI), MangaParserAuthProvider {
override val sortOrders: Set<SortOrder> = Collections.singleton( override val sortOrders: Set<SortOrder> = Collections.singleton(
SortOrder.NEWEST, SortOrder.NEWEST,
) )
override val defaultDomain: String override val configKeyDomain: ConfigKey.Domain
get() = if (isAuthorized) DOMAIN_AUTHORIZED else DOMAIN_UNAUTHORIZED get() = ConfigKey.Domain(if (isAuthorized) DOMAIN_AUTHORIZED else DOMAIN_UNAUTHORIZED, null)
override val authUrl: String override val authUrl: String
get() = "https://${getDomain()}/bounce_login.php" get() = "https://${getDomain()}/bounce_login.php"
@ -118,7 +119,7 @@ internal class ExHentaiParser(override val context: MangaLoaderContext) : MangaP
altTitle = null, altTitle = null,
url = href, url = href,
publicUrl = a.absUrl("href"), publicUrl = a.absUrl("href"),
rating = td2.selectFirst("div.ir")?.parseRating() ?: Manga.NO_RATING, rating = td2.selectFirst("div.ir")?.parseRating() ?: RATING_UNKNOWN,
isNsfw = true, isNsfw = true,
coverUrl = td1.selectFirst("img")?.absUrl("src").orEmpty(), coverUrl = td1.selectFirst("img")?.absUrl("src").orEmpty(),
tags = setOfNotNull(mainTag), tags = setOfNotNull(mainTag),
@ -239,7 +240,7 @@ internal class ExHentaiParser(override val context: MangaLoaderContext) : MangaP
p1 += 8 p1 += 8
} }
(80 - p1) / 80f (80 - p1) / 80f
}.getOrDefault(Manga.NO_RATING) }.getOrDefault(RATING_UNKNOWN)
} }
private fun String.cleanupTitle(): String { private fun String.cleanupTitle(): String {

@ -14,7 +14,7 @@ private const val PAGE_SIZE = 70
private const val PAGE_SIZE_SEARCH = 50 private const val PAGE_SIZE_SEARCH = 50
private const val NSFW_ALERT = "сексуальные сцены" private const val NSFW_ALERT = "сексуальные сцены"
internal abstract class GroupleParser : MangaParser() { internal abstract class GroupleParser(source: MangaSource) : MangaParser(source) {
private val headers = Headers.Builder() private val headers = Headers.Builder()
.add("User-Agent", "readmangafun") .add("User-Agent", "readmangafun")
@ -91,8 +91,9 @@ internal abstract class GroupleParser : MangaParser() {
?.substringBefore(' ') ?.substringBefore(' ')
?.toFloatOrNull() ?.toFloatOrNull()
?.div(10f) ?.div(10f)
}.getOrNull() ?: Manga.NO_RATING, }.getOrNull() ?: RATING_UNKNOWN,
author = tileInfo?.selectFirst("a.person-link")?.text(), author = tileInfo?.selectFirst("a.person-link")?.text(),
isNsfw = false,
tags = runCatching { tags = runCatching {
tileInfo?.select("a.element-link") tileInfo?.select("a.element-link")
?.mapToSet { ?.mapToSet {

@ -1,16 +1,16 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.parsers.util.toTitleCase
internal class HenChanParser(override val context: MangaLoaderContext) : ChanParser() { internal class HenChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.HENCHAN) {
override val defaultDomain = "hentaichan.live" override val configKeyDomain = ConfigKey.Domain("hentaichan.live", null)
override val source = MangaSource.HENCHAN
override suspend fun getList( override suspend fun getList(
offset: Int, offset: Int,

@ -2,13 +2,11 @@ package org.koitharu.kotatsu.parsers.site
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
internal class HentaiLibParser(context: MangaLoaderContext) : MangaLibParser(context) { internal class HentaiLibParser(context: MangaLoaderContext) : MangaLibParser(context, MangaSource.HENTAILIB) {
override val defaultDomain = "hentailib.me"
override val source = MangaSource.HENTAILIB
override val configKeyDomain = ConfigKey.Domain("hentailib.me", null)
override fun isNsfw(doc: Document) = true override fun isNsfw(doc: Document) = true
} }

@ -1,10 +1,10 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
internal class MangaChanParser(override val context: MangaLoaderContext) : ChanParser() { internal class MangaChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.MANGACHAN) {
override val defaultDomain = "manga-chan.me" override val configKeyDomain = ConfigKey.Domain("manga-chan.me", null)
override val source = MangaSource.MANGACHAN
} }

@ -5,6 +5,7 @@ import kotlinx.coroutines.coroutineScope
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.* import org.koitharu.kotatsu.parsers.util.json.*
@ -16,10 +17,9 @@ private const val CONTENT_RATING =
"contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic" "contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic"
private const val LOCALE_FALLBACK = "en" private const val LOCALE_FALLBACK = "en"
internal class MangaDexParser(override val context: MangaLoaderContext) : MangaParser() { internal class MangaDexParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGADEX) {
override val source = MangaSource.MANGADEX override val configKeyDomain = ConfigKey.Domain("mangadex.org", null)
override val defaultDomain = "mangadex.org"
override val sortOrders: EnumSet<SortOrder> = EnumSet.of( override val sortOrders: EnumSet<SortOrder> = EnumSet.of(
SortOrder.UPDATED, SortOrder.UPDATED,
@ -86,7 +86,7 @@ internal class MangaDexParser(override val context: MangaLoaderContext) : MangaP
altTitle = attrs.optJSONObject("altTitles")?.selectByLocale(), altTitle = attrs.optJSONObject("altTitles")?.selectByLocale(),
url = id, url = id,
publicUrl = "https://$domain/title/$id", publicUrl = "https://$domain/title/$id",
rating = Manga.NO_RATING, rating = RATING_UNKNOWN,
isNsfw = attrs.getStringOrNull("contentRating") == "erotica", isNsfw = attrs.getStringOrNull("contentRating") == "erotica",
coverUrl = cover?.plus(".256.jpg").orEmpty(), coverUrl = cover?.plus(".256.jpg").orEmpty(),
largeCoverUrl = cover, largeCoverUrl = cover,
@ -114,7 +114,7 @@ internal class MangaDexParser(override val context: MangaLoaderContext) : MangaP
} }
} }
override suspend fun getDetails(manga: Manga): Manga = coroutineScope<Manga> { override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val domain = getDomain() val domain = getDomain()
val attrsDeferred = async { val attrsDeferred = async {
context.httpGet( context.httpGet(
@ -169,11 +169,11 @@ internal class MangaDexParser(override val context: MangaLoaderContext) : MangaP
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val domain = getDomain() val domain = getDomain()
val chapter = context.httpGet("https://api.$domain/at-home/server/${chapter.url}?forcePort443=false") val chapterJson = context.httpGet("https://api.$domain/at-home/server/${chapter.url}?forcePort443=false")
.parseJson() .parseJson()
.getJSONObject("chapter") .getJSONObject("chapter")
val pages = chapter.getJSONArray("data") val pages = chapterJson.getJSONArray("data")
val prefix = "https://uploads.$domain/data/${chapter.getString("hash")}/" val prefix = "https://uploads.$domain/data/${chapterJson.getString("hash")}/"
val referer = "https://$domain/" val referer = "https://$domain/"
return List(pages.length()) { i -> return List(pages.length()) { i ->
val url = prefix + pages.getString(i) val url = prefix + pages.getString(i)

@ -7,6 +7,7 @@ import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
@ -17,11 +18,12 @@ import org.koitharu.kotatsu.parsers.util.json.mapJSON
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
internal open class MangaLibParser(override val context: MangaLoaderContext) : MangaParser(), MangaParserAuthProvider { internal open class MangaLibParser(
override val context: MangaLoaderContext,
source: MangaSource = MangaSource.MANGALIB,
) : MangaParser(source), MangaParserAuthProvider {
override val defaultDomain = "mangalib.me" override val configKeyDomain = ConfigKey.Domain("mangalib.me", null)
override val source = MangaSource.MANGALIB
override val authUrl: String override val authUrl: String
get() = "https://${getDomain()}/login" get() = "https://${getDomain()}/login"
@ -69,11 +71,12 @@ internal open class MangaLibParser(override val context: MangaLoaderContext) : M
coverUrl = a.absUrl("data-src"), coverUrl = a.absUrl("data-src"),
altTitle = null, altTitle = null,
author = null, author = null,
rating = Manga.NO_RATING, rating = RATING_UNKNOWN,
url = href, url = href,
publicUrl = href.inContextOf(a), publicUrl = href.inContextOf(a),
tags = emptySet(), tags = emptySet(),
state = null, state = null,
isNsfw = false,
source = source, source = source,
) )
} }
@ -269,8 +272,9 @@ internal open class MangaLibParser(override val context: MangaLoaderContext) : M
author = null, author = null,
tags = emptySet(), tags = emptySet(),
rating = jo.getString("rate_avg") rating = jo.getString("rate_avg")
.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING, .toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
state = null, state = null,
isNsfw = false,
source = source, source = source,
coverUrl = covers.getString("thumbnail"), coverUrl = covers.getString("thumbnail"),
largeCoverUrl = covers.getString("default"), largeCoverUrl = covers.getString("default"),

@ -2,17 +2,16 @@ package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaParser() { internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGAOWL) {
override val source = MangaSource.MANGAOWL override val configKeyDomain = ConfigKey.Domain("mangaowls.com", null)
override val defaultDomain = "mangaowls.com"
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY, SortOrder.POPULARITY,
@ -62,8 +61,11 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
?.text() ?.text()
?.toFloatOrNull() ?.toFloatOrNull()
?.div(10f) ?.div(10f)
}.getOrNull() ?: Manga.NO_RATING, }.getOrNull() ?: RATING_UNKNOWN,
url = href, url = href,
isNsfw = false,
tags = emptySet(),
state = null,
publicUrl = href.withDomain(), publicUrl = href.withDomain(),
source = source, source = source,
) )
@ -79,7 +81,7 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
val trElement = val trElement =
doc.getElementsByTag("script").find { trRegex.find(it.data()) != null } ?: parseFailed("Oops, tr not found") doc.getElementsByTag("script").find { trRegex.find(it.data()) != null } ?: parseFailed("Oops, tr not found")
val tr = trRegex.find(trElement.data())!!.groups[1]!!.value val tr = trRegex.find(trElement.data())!!.groups[1]!!.value
val s = context.encodeBase64(defaultDomain.toByteArray()) val s = context.encodeBase64(getDomain().toByteArray())
return manga.copy( return manga.copy(
description = info.selectFirst(".description")?.html(), description = info.selectFirst(".description")?.html(),
largeCoverUrl = info.select("img").first()?.let { img -> largeCoverUrl = info.select("img").first()?.let { img ->

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -9,11 +10,9 @@ import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
internal class MangaTownParser(override val context: MangaLoaderContext) : MangaParser() { internal class MangaTownParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGATOWN) {
override val source = MangaSource.MANGATOWN override val configKeyDomain = ConfigKey.Domain("www.mangatown.com", null)
override val defaultDomain = "www.mangatown.com"
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
@ -69,7 +68,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
source = MangaSource.MANGATOWN, source = MangaSource.MANGATOWN,
altTitle = null, altTitle = null,
rating = li.selectFirst("p.score")?.selectFirst("b") rating = li.selectFirst("p.score")?.selectFirst("b")
?.ownText()?.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING, ?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
author = views.findText { x -> x.startsWith("Author:") }?.substringAfter(':') author = views.findText { x -> x.startsWith("Author:") }?.substringAfter(':')
?.trim(), ?.trim(),
state = when (status) { state = when (status) {
@ -85,6 +84,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
) )
}.orEmpty(), }.orEmpty(),
url = href, url = href,
isNsfw = false,
publicUrl = href.inContextOf(a), publicUrl = href.inContextOf(a),
) )
} }

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -11,11 +12,9 @@ import java.util.*
private const val PAGE_SIZE = 12 private const val PAGE_SIZE = 12
internal class MangareadParser(override val context: MangaLoaderContext) : MangaParser() { internal class MangareadParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGAREAD) {
override val source = MangaSource.MANGAREAD override val configKeyDomain = ConfigKey.Domain("www.mangaread.org", null)
override val defaultDomain = "www.mangaread.org"
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED, SortOrder.UPDATED,
@ -56,6 +55,7 @@ internal class MangareadParser(override val context: MangaLoaderContext) : Manga
publicUrl = href.inContextOf(div), publicUrl = href.inContextOf(div),
coverUrl = div.selectFirst("img")?.absUrl("data-src").orEmpty(), coverUrl = div.selectFirst("img")?.absUrl("data-src").orEmpty(),
title = summary?.selectFirst("h3")?.text().orEmpty(), title = summary?.selectFirst("h3")?.text().orEmpty(),
altTitle = null,
rating = div.selectFirst("span.total_votes")?.ownText() rating = div.selectFirst("span.total_votes")?.ownText()
?.toFloatOrNull()?.div(5f) ?: -1f, ?.toFloatOrNull()?.div(5f) ?: -1f,
tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapToSet { a -> tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
@ -75,6 +75,7 @@ internal class MangareadParser(override val context: MangaLoaderContext) : Manga
else -> null else -> null
}, },
source = MangaSource.MANGAREAD, source = MangaSource.MANGAREAD,
isNsfw = false,
) )
} }
} }

@ -1,10 +1,10 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
internal class MintMangaParser(override val context: MangaLoaderContext) : GroupleParser() { internal class MintMangaParser(override val context: MangaLoaderContext) : GroupleParser(MangaSource.MINTMANGA) {
override val source = MangaSource.MINTMANGA override val configKeyDomain = ConfigKey.Domain("mintmanga.live", null)
override val defaultDomain: String = "mintmanga.live"
} }

@ -4,6 +4,7 @@ import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -14,9 +15,11 @@ private const val PAGE_SIZE = 26
internal abstract class NineMangaParser( internal abstract class NineMangaParser(
final override val context: MangaLoaderContext, final override val context: MangaLoaderContext,
final override val source: MangaSource, source: MangaSource,
final override val defaultDomain: String, defaultDomain: String,
) : MangaParser() { ) : MangaParser(source) {
override val configKeyDomain = ConfigKey.Domain(defaultDomain, null)
init { init {
context.cookieJar.insertCookies(getDomain(), "ninemanga_template_desk=yes") context.cookieJar.insertCookies(getDomain(), "ninemanga_template_desk=yes")
@ -77,8 +80,9 @@ internal abstract class NineMangaParser(
title = dd?.selectFirst("a.bookname")?.text()?.toCamelCase().orEmpty(), title = dd?.selectFirst("a.bookname")?.text()?.toCamelCase().orEmpty(),
altTitle = null, altTitle = null,
coverUrl = node.selectFirst("img")?.absUrl("src").orEmpty(), coverUrl = node.selectFirst("img")?.absUrl("src").orEmpty(),
rating = Manga.NO_RATING, rating = RATING_UNKNOWN,
author = null, author = null,
isNsfw = false,
tags = emptySet(), tags = emptySet(),
state = null, state = null,
source = source, source = source,

@ -4,6 +4,7 @@ import androidx.collection.SparseArrayCompat
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
@ -12,10 +13,11 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
internal class NudeMoonParser(override val context: MangaLoaderContext) : MangaParser(), MangaParserAuthProvider { internal class NudeMoonParser(
override val context: MangaLoaderContext,
) : MangaParser(MangaSource.NUDEMOON), MangaParserAuthProvider {
override val source = MangaSource.NUDEMOON override val configKeyDomain = ConfigKey.Domain("nude-moon.net", null)
override val defaultDomain = "nude-moon.net"
override val authUrl: String override val authUrl: String
get() = "https://${getDomain()}/index.php" get() = "https://${getDomain()}/index.php"
@ -89,7 +91,7 @@ internal class NudeMoonParser(override val context: MangaLoaderContext) : MangaP
}.orEmpty(), }.orEmpty(),
source = source, source = source,
publicUrl = a.absUrl("href"), publicUrl = a.absUrl("href"),
rating = Manga.NO_RATING, rating = RATING_UNKNOWN,
isNsfw = true, isNsfw = true,
description = row.selectFirst("div.description")?.html(), description = row.selectFirst("div.description")?.html(),
state = null, state = null,

@ -1,10 +1,10 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
internal class ReadmangaParser(override val context: MangaLoaderContext) : GroupleParser() { internal class ReadmangaParser(override val context: MangaLoaderContext) : GroupleParser(MangaSource.READMANGA_RU) {
override val defaultDomain = "readmanga.io" override val configKeyDomain = ConfigKey.Domain("readmanga.io", null)
override val source = MangaSource.READMANGA_RU
} }

@ -7,6 +7,7 @@ import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -23,11 +24,11 @@ private const val PAGE_SIZE = 30
private const val STATUS_ONGOING = 1 private const val STATUS_ONGOING = 1
private const val STATUS_FINISHED = 0 private const val STATUS_FINISHED = 0
internal class RemangaParser(override val context: MangaLoaderContext) : MangaParser(), MangaParserAuthProvider { internal class RemangaParser(
override val context: MangaLoaderContext,
) : MangaParser(MangaSource.REMANGA), MangaParserAuthProvider {
override val source = MangaSource.REMANGA override val configKeyDomain = ConfigKey.Domain("remanga.org", null)
override val defaultDomain = "remanga.org"
override val authUrl: String override val authUrl: String
get() = "https://${getDomain()}/user/login" get() = "https://${getDomain()}/user/login"
@ -85,10 +86,12 @@ internal class RemangaParser(override val context: MangaLoaderContext) : MangaPa
publicUrl = "https://$domain$url", publicUrl = "https://$domain$url",
title = jo.getString("rus_name"), title = jo.getString("rus_name"),
altTitle = jo.getString("en_name"), altTitle = jo.getString("en_name"),
rating = jo.getString("avg_rating").toFloatOrNull()?.div(10f) ?: Manga.NO_RATING, rating = jo.getString("avg_rating").toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,
coverUrl = "https://api.$domain${img.getString("mid")}", coverUrl = "https://api.$domain${img.getString("mid")}",
largeCoverUrl = "https://api.$domain${img.getString("high")}", largeCoverUrl = "https://api.$domain${img.getString("high")}",
author = null, author = null,
isNsfw = false,
state = null,
tags = jo.optJSONArray("genres")?.mapJSONToSet { g -> tags = jo.optJSONArray("genres")?.mapJSONToSet { g ->
MangaTag( MangaTag(
title = g.getString("name").toTitleCase(), title = g.getString("name").toTitleCase(),

@ -1,10 +1,10 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
internal class SelfMangaParser(override val context: MangaLoaderContext) : GroupleParser() { internal class SelfMangaParser(override val context: MangaLoaderContext) : GroupleParser(MangaSource.SELFMANGA) {
override val defaultDomain = "selfmanga.live" override val configKeyDomain = ConfigKey.Domain("selfmanga.live", null)
override val source = MangaSource.SELFMANGA
} }

@ -1,6 +1,7 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
@ -8,10 +9,9 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.relUrl import org.koitharu.kotatsu.parsers.util.relUrl
internal class YaoiChanParser(override val context: MangaLoaderContext) : ChanParser() { internal class YaoiChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.YAOICHAN) {
override val source = MangaSource.YAOICHAN override val configKeyDomain = ConfigKey.Domain("yaoi-chan.me", null)
override val defaultDomain = "yaoi-chan.me"
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.withDomain()).parseHtml() val doc = context.httpGet(manga.url.withDomain()).parseHtml()

@ -32,7 +32,7 @@ class InMemoryCookieJar : CookieJar {
if (line.isBlank() || line.startsWith("# ")) { if (line.isBlank() || line.startsWith("# ")) {
continue continue
} }
val (host, includeSubdomains, path, secure, expire, name, value) = line.split(Regex("\\s+")) val (host, _, path, secure, expire, name, value) = line.split(Regex("\\s+"))
val domain = host.removePrefix("#HttpOnly_").trimStart('.') val domain = host.removePrefix("#HttpOnly_").trimStart('.')
val httpOnly = host.startsWith("#HttpOnly_") val httpOnly = host.startsWith("#HttpOnly_")
val cookie = Cookie.Builder() val cookie = Cookie.Builder()

@ -4,6 +4,7 @@ import com.koushikdutta.quack.QuackContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.await
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit

@ -1,8 +1,9 @@
package org.koitharu.kotatsu.parsers package org.koitharu.kotatsu.parsers
internal class SourceConfigMock : MangaSourceConfig { import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
override fun getDomain(defaultValue: String): String = defaultValue internal class SourceConfigMock : MangaSourceConfig {
override fun isSslEnabled(defaultValue: Boolean): Boolean = defaultValue override fun <T> get(key: ConfigKey<T>): T = key.defaultValue
} }
Loading…
Cancel
Save