Reformat code
This commit is contained in:
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -3,4 +3,4 @@
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.22" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -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")
|
||||
@@ -596,7 +596,7 @@ internal abstract class MangaReaderParser(
|
||||
override val listUrl: String
|
||||
get() = "/manga"
|
||||
override val tableMode: Boolean
|
||||
get() = false
|
||||
get() = false
|
||||
|
||||
override val chapterDateFormat: SimpleDateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRENCH)
|
||||
|
||||
@@ -611,8 +611,8 @@ internal abstract class MangaReaderParser(
|
||||
.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MangaSourceParser("KOMIKLOKAL", "KomikLokal", "id")
|
||||
class KomikLokalParser(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaSource.KOMIKLOKAL, pageSize = 20, searchPageSize = 10) {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -43,4 +44,4 @@ infix fun Int.upBy(step: Int): Int {
|
||||
} else {
|
||||
this - mod + step
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>()
|
||||
@@ -22,4 +22,4 @@ class Paginator constructor(private val initialPageSize: Int) {
|
||||
fun onListReceived(offset: Int, page: Int, count: Int) {
|
||||
pages[offset + count] = if (count > 0) page + 1 else page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user