diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 9a55c2de1..fbd3b0a1b 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -3,4 +3,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt
index 676dae332..0cb691366 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/InternalParsersApi.kt
@@ -11,4 +11,4 @@ package org.koitharu.kotatsu.parsers
@SinceKotlin("1.3")
@RequiresOptIn
@MustBeDocumented
-annotation class InternalParsersApi()
\ No newline at end of file
+annotation class InternalParsersApi
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt
index fc661ccef..dc6ce96f2 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaLoaderContext.kt
@@ -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)
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/AstralManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/AstralManga.kt
index 43411e6ac..88520796d 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/AstralManga.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/AstralManga.kt
@@ -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) :
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Atlantisscan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Atlantisscan.kt
index 4645a1d46..38287ce0b 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Atlantisscan.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Atlantisscan.kt
@@ -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")
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/FrScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/FrScan.kt
index bb836c46d..86f46179e 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/FrScan.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/FrScan.kt
@@ -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")
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaiteca.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaiteca.kt
index 723a05ebb..a92c7ba34 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaiteca.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaiteca.kt
@@ -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) :
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaizone.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaizone.kt
index ff1b86f17..3fcd44bff 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaizone.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hentaizone.kt
@@ -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")
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/HhentaiFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/HhentaiFr.kt
index c079acb63..1b2ce0414 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/HhentaiFr.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/HhentaiFr.kt
@@ -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.*
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hipercool.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hipercool.kt
index e5168d3ae..6944ac47f 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hipercool.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Hipercool.kt
@@ -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.*
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt
index f6a5b13f6..077a4c99d 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt
@@ -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
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangaScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangaScantrad.kt
index 3c871d341..0c2f8eb73 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangaScantrad.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangaScantrad.kt
@@ -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")
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangalinkParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangalinkParser.kt
index 25404f35c..fdbe63690 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangalinkParser.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangalinkParser.kt
@@ -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(),
+ )
+ }
}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansFr.kt
index 931912c31..1f39412d6 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansFr.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansFr.kt
@@ -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")
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansParser.kt
index d613607d1..ad8d5be51 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansParser.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ReaperScansParser.kt
@@ -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): 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): 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 {
- 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(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 {
+ 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(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,
+ )
+ }
+ }
}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ScantradVf.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ScantradVf.kt
index ee12fc4a6..d7a875547 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ScantradVf.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ScantradVf.kt
@@ -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) :
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt
index 012e8ad6e..7211c931e 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt
@@ -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) {
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt
index cb8a6a3d7..60e5a26e5 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/FaviconParser.kt
@@ -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()
- 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()
+ 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 {
- 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 {
+ 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"
+ }
+ }
+ }
}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt
index 1e20c9e0c..181745d31 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt
@@ -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?.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)
}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt
index 3c5abc9b1..99a8b2826 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt
@@ -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
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt
index df363e703..c4f37074a 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt
@@ -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
}
diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt
index 5e41bd80c..ab313c53c 100644
--- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt
+++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt
@@ -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()
@@ -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
}
-}
\ No newline at end of file
+}