Reformat code

Koitharu 3 years ago
parent 16698201f4
commit 553a6c709a
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -11,4 +11,4 @@ package org.koitharu.kotatsu.parsers
@SinceKotlin("1.3")
@RequiresOptIn
@MustBeDocumented
annotation class InternalParsersApi()
annotation class InternalParsersApi

@ -12,7 +12,7 @@ abstract class MangaLoaderContext {
abstract val cookieJar: CookieJar
fun newParserInstance(source: MangaSource): MangaParser = source.newParser(this)
fun newParserInstance(source: MangaSource): MangaParser = this.newParserInstance(source)
open fun encodeBase64(data: ByteArray): String = Base64.getEncoder().encodeToString(data)

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.assertNotNull
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
@MangaSourceParser("ASTRALMANGA", "AstralManga", "fr")
internal class AstralManga(context: MangaLoaderContext) :

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.assertNotNull
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
@MangaSourceParser("ATLANTISSCAN", "Atlantisscan", "pt")

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.assertNotNull
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import java.util.*
@MangaSourceParser("FRSCAN", "FrScan", "fr")

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.assertNotNull
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
@MangaSourceParser("HENTAITECA", "Hentaiteca", "pt")
internal class Hentaiteca(context: MangaLoaderContext) :

@ -4,11 +4,13 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
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.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
import java.time.LocalDate;
@MangaSourceParser("HENTAIZONE", "Hentaizone", "fr")

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.domain
import org.koitharu.kotatsu.parsers.util.insertCookies
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import java.util.*

@ -3,8 +3,13 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import java.util.*

@ -193,7 +193,7 @@ internal abstract class MadaraParser(
parseRelativeDate(date)
}
// Handle translated 'ago' in french.
date.startsWith("il y a", ignoreCase = true)-> {
date.startsWith("il y a", ignoreCase = true) -> {
parseRelativeDate(date)
}
// Handle 'yesterday' and 'today', using midnight

@ -3,8 +3,14 @@ package org.koitharu.kotatsu.parsers.site.madara
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.assertNotNull
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import java.util.*
@MangaSourceParser("MANGA_SCANTRAD", "Manga Scantrad", "fr")

@ -12,35 +12,35 @@ import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("MANGALINK_AR", "Mangalink", "ar")
internal class MangalinkParser(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.MANGALINK_AR, "mangalink.online") {
MadaraParser(context, MangaSource.MANGALINK_AR, "mangalink.online") {
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val chaptersDeferred = async { getChapters(manga, doc) }
val root = doc.body().selectFirst("div.profile-manga")
?.selectFirst("div.summary_content")
?.selectFirst("div.post-content")
?: throw ParseException("Root not found", fullUrl)
val root2 = doc.body().selectFirst("div.content-area")
?.selectFirst("div.c-page")
?: throw ParseException("Root2 not found", fullUrl)
manga.copy(
tags = root.selectFirst("div.genres-content")?.select("a")
?.mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
title = a.text().toTitleCase(),
source = source,
)
} ?: manga.tags,
description = root2.selectFirst("div.description-summary")
?.selectFirst("div.summary__content")
?.select("p")
?.filterNot { it.ownText().startsWith("A brief description") }
?.joinToString { it.html() },
chapters = chaptersDeferred.await(),
)
}
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val chaptersDeferred = async { getChapters(manga, doc) }
val root = doc.body().selectFirst("div.profile-manga")
?.selectFirst("div.summary_content")
?.selectFirst("div.post-content")
?: throw ParseException("Root not found", fullUrl)
val root2 = doc.body().selectFirst("div.content-area")
?.selectFirst("div.c-page")
?: throw ParseException("Root2 not found", fullUrl)
manga.copy(
tags = root.selectFirst("div.genres-content")?.select("a")
?.mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
title = a.text().toTitleCase(),
source = source,
)
} ?: manga.tags,
description = root2.selectFirst("div.description-summary")
?.selectFirst("div.summary__content")
?.select("p")
?.filterNot { it.ownText().startsWith("A brief description") }
?.joinToString { it.html() },
chapters = chaptersDeferred.await(),
)
}
}

@ -4,8 +4,9 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
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.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("REAPERSCANS_FR", "ReaperScansFr", "fr")

@ -9,78 +9,78 @@ import java.util.*
@MangaSourceParser("REAPER_SCANS_ID", "ReaperScansID", "in")
internal class ReaperScansParser(context: MangaLoaderContext) :
Madara6Parser(context, MangaSource.REAPER_SCANS_ID, "reaperscans.id") {
Madara6Parser(context, MangaSource.REAPER_SCANS_ID, "reaperscans.id") {
override val datePattern = "MMMM dd, yyyy"
override val tagPrefix = "genre/"
override val sourceLocale: Locale = Locale.ENGLISH
override val datePattern = "MMMM dd, yyyy"
override val tagPrefix = "genre/"
override val sourceLocale: Locale = Locale.ENGLISH
override fun String.asMangaState(): MangaState? = when (this) {
"OnGoing",
"Upcoming",
-> MangaState.ONGOING
override fun String.asMangaState(): MangaState? = when (this) {
"OnGoing",
"Upcoming",
-> MangaState.ONGOING
"Completed",
"Dropped",
-> MangaState.FINISHED
"Completed",
"Dropped",
-> MangaState.FINISHED
else -> null
}
else -> null
}
override fun parseDetails(manga: Manga, body: Element, chapters: List<MangaChapter>): Manga {
val root = body.selectFirstOrThrow(".site-content")
val postContent = root.requireElementById("nav-info")
val tags = postContent.getElementsContainingOwnText("Gênero")
.firstOrNull()?.tableValue()
?.getElementsByAttributeValueContaining("href", tagPrefix)
?.mapToSet { a -> a.asMangaTag() } ?: manga.tags
return manga.copy(
rating = postContent.selectFirstOrThrow(".post-rating")
.selectFirstOrThrow(".total_votes").text().toFloat() / 5f,
largeCoverUrl = root.selectFirst(".summary_image")
?.selectFirst("img[data-src]")
?.attrAsAbsoluteUrlOrNull("data-src")
.assertNotNull("largeCoverUrl"),
description = root.requireElementById("nav-profile")
.selectFirstOrThrow(".description-summary")
.firstElementChild()?.html(),
author = postContent.getElementsContainingOwnText("Author(s)")
.firstOrNull()?.tableValue()?.text()?.trim(),
altTitle = postContent.getElementsContainingOwnText("Alternative")
.firstOrNull()?.tableValue()?.text()?.trim(),
state = postContent.getElementsContainingOwnText("Status")
.firstOrNull()?.tableValue()?.text()?.asMangaState(),
tags = tags,
isNsfw = body.hasClass("adult-content"),
chapters = chapters,
)
}
override fun parseDetails(manga: Manga, body: Element, chapters: List<MangaChapter>): Manga {
val root = body.selectFirstOrThrow(".site-content")
val postContent = root.requireElementById("nav-info")
val tags = postContent.getElementsContainingOwnText("Gênero")
.firstOrNull()?.tableValue()
?.getElementsByAttributeValueContaining("href", tagPrefix)
?.mapToSet { a -> a.asMangaTag() } ?: manga.tags
return manga.copy(
rating = postContent.selectFirstOrThrow(".post-rating")
.selectFirstOrThrow(".total_votes").text().toFloat() / 5f,
largeCoverUrl = root.selectFirst(".summary_image")
?.selectFirst("img[data-src]")
?.attrAsAbsoluteUrlOrNull("data-src")
.assertNotNull("largeCoverUrl"),
description = root.requireElementById("nav-profile")
.selectFirstOrThrow(".description-summary")
.firstElementChild()?.html(),
author = postContent.getElementsContainingOwnText("Author(s)")
.firstOrNull()?.tableValue()?.text()?.trim(),
altTitle = postContent.getElementsContainingOwnText("Alternative")
.firstOrNull()?.tableValue()?.text()?.trim(),
state = postContent.getElementsContainingOwnText("Status")
.firstOrNull()?.tableValue()?.text()?.asMangaState(),
tags = tags,
isNsfw = body.hasClass("adult-content"),
chapters = chapters,
)
}
override suspend fun getTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://${domain}/semua-komik/").parseHtml()
val body = doc.body()
val root1 = body.selectFirst("header")?.selectFirst("ul.second-menu")
val root2 = body.selectFirst("div.genres_wrap")?.selectFirst("ul.list-unstyled")
if (root1 == null && root2 == null) {
doc.parseFailed("Root not found")
}
val list = root1?.select("li").orEmpty() + root2?.select("li").orEmpty()
val keySet = HashSet<String>(list.size)
return list.mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val href = a.attr("href").removeSuffix("/")
.substringAfterLast(tagPrefix, "")
if (href.isEmpty() || !keySet.add(href)) {
return@mapNotNullToSet null
}
MangaTag(
key = href,
title = a.ownText().trim().ifEmpty {
a.selectFirst(".menu-image-title")?.text()?.trim() ?: return@mapNotNullToSet null
}.toTitleCase(),
source = source,
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://${domain}/semua-komik/").parseHtml()
val body = doc.body()
val root1 = body.selectFirst("header")?.selectFirst("ul.second-menu")
val root2 = body.selectFirst("div.genres_wrap")?.selectFirst("ul.list-unstyled")
if (root1 == null && root2 == null) {
doc.parseFailed("Root not found")
}
val list = root1?.select("li").orEmpty() + root2?.select("li").orEmpty()
val keySet = HashSet<String>(list.size)
return list.mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val href = a.attr("href").removeSuffix("/")
.substringAfterLast(tagPrefix, "")
if (href.isEmpty() || !keySet.add(href)) {
return@mapNotNullToSet null
}
MangaTag(
key = href,
title = a.ownText().trim().ifEmpty {
a.selectFirst(".menu-image-title")?.text()?.trim() ?: return@mapNotNullToSet null
}.toTitleCase(),
source = source,
)
}
}
}

@ -5,7 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@MangaSourceParser("SCANTRADVF", "ScantradVf", "fr")
internal class ScantradVf(context: MangaLoaderContext) :

@ -535,7 +535,7 @@ internal abstract class MangaReaderParser(
}
@MangaSourceParser("PHENIXSCANS", "Phenixscans", "fr")
class PhenixscansParser(context: MangaLoaderContext) :
class PhenixscansParser(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.PHENIXSCANS, pageSize = 20, searchPageSize = 10) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("phenixscans.fr")
@ -561,7 +561,7 @@ internal abstract class MangaReaderParser(
}
@MangaSourceParser("EPSILONSCAN", "Epsilonscan", "fr")
class EpsilonscanParser(context: MangaLoaderContext) :
class EpsilonscanParser(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.EPSILONSCAN, pageSize = 20, searchPageSize = 10) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("epsilonscan.fr")
@ -570,7 +570,7 @@ internal abstract class MangaReaderParser(
get() = "/manga"
override val tableMode: Boolean
get() = false
override val isNsfwSource: Boolean = true
override val isNsfwSource: Boolean = true
override val chapterDateFormat: SimpleDateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRENCH)
@ -588,7 +588,7 @@ internal abstract class MangaReaderParser(
}
@MangaSourceParser("LEGACY_SCANS", "Legacy Scans", "fr")
class LegacyScansParser(context: MangaLoaderContext) :
class LegacyScansParser(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.LEGACY_SCANS, pageSize = 20, searchPageSize = 10) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("legacy-scans.com")

@ -7,86 +7,86 @@ import org.koitharu.kotatsu.parsers.network.WebClient
import org.koitharu.kotatsu.parsers.util.json.mapJSON
class FaviconParser(
private val webClient: WebClient,
private val domain: String,
private val webClient: WebClient,
private val domain: String,
) {
suspend fun parseFavicons(): Favicons {
val url = "https://$domain"
val doc = webClient.httpGet(url).parseHtml()
val result = HashSet<Favicon>()
val manifestLink = doc.getElementsByAttributeValue("rel", "manifest").firstOrNull()
?.attrAsAbsoluteUrlOrNull("href")
if (manifestLink != null) {
result += parseManifest(manifestLink)
}
val links = doc.getElementsByAttributeValueContaining("rel", "icon")
links.mapNotNullTo(result) { link ->
parseLink(link)
}
if (result.isEmpty()) {
result.add(createFallback())
}
return Favicons(result, url)
}
suspend fun parseFavicons(): Favicons {
val url = "https://$domain"
val doc = webClient.httpGet(url).parseHtml()
val result = HashSet<Favicon>()
val manifestLink = doc.getElementsByAttributeValue("rel", "manifest").firstOrNull()
?.attrAsAbsoluteUrlOrNull("href")
if (manifestLink != null) {
result += parseManifest(manifestLink)
}
val links = doc.getElementsByAttributeValueContaining("rel", "icon")
links.mapNotNullTo(result) { link ->
parseLink(link)
}
if (result.isEmpty()) {
result.add(createFallback())
}
return Favicons(result, url)
}
private fun parseLink(link: Element): Favicon? {
val href = link.attrAsAbsoluteUrlOrNull("href")
if (href == null || href.endsWith('/')) {
return null
}
val sizes = link.attr("sizes")
return Favicon(
url = href,
size = parseSize(sizes),
rel = link.attrOrNull("rel"),
)
}
private fun parseLink(link: Element): Favicon? {
val href = link.attrAsAbsoluteUrlOrNull("href")
if (href == null || href.endsWith('/')) {
return null
}
val sizes = link.attr("sizes")
return Favicon(
url = href,
size = parseSize(sizes),
rel = link.attrOrNull("rel"),
)
}
private fun parseSize(sizes: String): Int {
if (sizes.isEmpty() || sizes == "any") {
return 0
}
return sizes.substringBefore(' ')
.split('x', 'X', '*')
.firstNotNullOfOrNull { it.toIntOrNull() }
?: 0
}
private fun parseSize(sizes: String): Int {
if (sizes.isEmpty() || sizes == "any") {
return 0
}
return sizes.substringBefore(' ')
.split('x', 'X', '*')
.firstNotNullOfOrNull { it.toIntOrNull() }
?: 0
}
private suspend fun parseManifest(url: String): List<Favicon> {
val json = webClient.httpGet(url).parseJson()
val icons = json.getJSONArray("icons")
return icons.mapJSON { jo ->
Favicon(
url = jo.getString("src").resolveLink(),
size = parseSize(jo.getString("sizes")),
rel = null,
)
}
}
private suspend fun parseManifest(url: String): List<Favicon> {
val json = webClient.httpGet(url).parseJson()
val icons = json.getJSONArray("icons")
return icons.mapJSON { jo ->
Favicon(
url = jo.getString("src").resolveLink(),
size = parseSize(jo.getString("sizes")),
rel = null,
)
}
}
private fun createFallback(): Favicon {
val href = "https://$domain/favicon.ico"
return Favicon(
url = href,
size = 0,
rel = null,
)
}
private fun createFallback(): Favicon {
val href = "https://$domain/favicon.ico"
return Favicon(
url = href,
size = 0,
rel = null,
)
}
private fun String.resolveLink(): String {
return when {
startsWith("http:") || startsWith("https:") -> {
this
}
private fun String.resolveLink(): String {
return when {
startsWith("http:") || startsWith("https:") -> {
this
}
startsWith('/') -> {
"https://$domain$this"
}
startsWith('/') -> {
"https://$domain$this"
}
else -> {
"https://$domain/$this"
}
}
}
else -> {
"https://$domain/$this"
}
}
}
}

@ -20,14 +20,14 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
*/
@InternalParsersApi
fun MangaParser.generateUid(url: String): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
url.forEach { c ->
h = 31 * h + c.code
}
return h
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
url.forEach { c ->
h = 31 * h + c.code
}
return h
}
/**
@ -39,40 +39,40 @@ fun MangaParser.generateUid(url: String): Long {
*/
@InternalParsersApi
fun MangaParser.generateUid(id: Long): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
h = 31 * h + id
return h
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
h = 31 * h + id
return h
}
@InternalParsersApi
fun Element.parseFailed(message: String? = null): Nothing {
throw ParseException(message, ownerDocument()?.location() ?: baseUri(), null)
throw ParseException(message, ownerDocument()?.location() ?: baseUri(), null)
}
@InternalParsersApi
fun Set<MangaTag>?.oneOrThrowIfMany(): MangaTag? {
return when {
isNullOrEmpty() -> null
size == 1 -> first()
else -> throw IllegalArgumentException("Multiple genres are not supported by this source")
}
return when {
isNullOrEmpty() -> null
size == 1 -> first()
else -> throw IllegalArgumentException("Multiple genres are not supported by this source")
}
}
val MangaParser.domain: String
get() {
return config[configKeyDomain]
}
get() {
return config[configKeyDomain]
}
fun MangaParser.getDomain(subdomain: String): String {
val domain = domain
return subdomain + "." + domain.removePrefix("www.")
val domain = domain
return subdomain + "." + domain.removePrefix("www.")
}
fun MangaParser.urlBuilder(): HttpUrl.Builder {
return HttpUrl.Builder()
.scheme("https")
.host(domain)
return HttpUrl.Builder()
.scheme("https")
.host(domain)
}

@ -23,6 +23,7 @@ fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? =
is Float,
is Double,
-> formatter.format(this.toDouble())
else -> formatter.format(this.toLong())
}
}

@ -8,22 +8,22 @@ import okhttp3.Headers
import okhttp3.Response
suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation ->
val callback = ContinuationCallCallback(this, continuation)
enqueue(callback)
continuation.invokeOnCancellation(callback)
val callback = ContinuationCallCallback(this, continuation)
enqueue(callback)
continuation.invokeOnCancellation(callback)
}
val Response.mimeType: String?
get() = body?.contentType()?.run { "$type/$subtype" }
get() = body?.contentType()?.run { "$type/$subtype" }
val Response.contentDisposition: String?
get() = header("Content-Disposition")
get() = header("Content-Disposition")
fun Headers.Builder.mergeWith(other: Headers, replaceExisting: Boolean): Headers.Builder {
for ((name, value) in other) {
if (replaceExisting || this[name] == null) {
this[name] = value
}
}
return this
for ((name, value) in other) {
if (replaceExisting || this[name] == null) {
this[name] = value
}
}
return this
}

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.util
import androidx.collection.SparseArrayCompat
import androidx.collection.set
class Paginator constructor(private val initialPageSize: Int) {
class Paginator(private val initialPageSize: Int) {
var firstPage = 1
private var pages = SparseArrayCompat<Int>()

Loading…
Cancel
Save