Adjust nullability in parsers

This commit is contained in:
Koitharu
2021-07-14 07:01:11 +03:00
parent 86be393335
commit 6a3421df8a
11 changed files with 105 additions and 84 deletions

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.core.parser
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.MangaTag
@@ -75,4 +76,8 @@ abstract class RemoteMangaRepository(
h = 31 * h + id
return h
}
protected fun parseFailed(message: String? = null): Nothing {
throw ParseException(message)
}
}

View File

@@ -32,30 +32,33 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
else -> "/manga?page=$page".withDomain()
}
val doc = loaderContext.httpGet(link).parseHtml()
val root = doc.body().select("div.manga-block") ?: throw ParseException("Cannot find root")
val root = doc.body().select("div.manga-block") ?: parseFailed("Cannot find root")
val items = root.select("div.anime-card")
return items.mapNotNull { card ->
val href = card.selectFirst("a").attr("href")
val href = card.selectFirst("a")?.attr("href") ?: return@mapNotNull null
val status = card.select("tr")[2].text()
val fullTitle = card.selectFirst("h1.anime-card-title").text()
.substringBeforeLast('[')
val fullTitle = card.selectFirst("h1.anime-card-title")?.text()
?.substringBeforeLast('[') ?: return@mapNotNull null
val titleParts = fullTitle.splitTwoParts('/')
Manga(
id = generateUid(href),
title = titleParts?.first?.trim() ?: fullTitle,
coverUrl = card.selectFirst("img").attr("data-src").withDomain(),
coverUrl = card.selectFirst("img")?.attr("data-src")
?.withDomain().orEmpty(),
altTitle = titleParts?.second?.trim(),
author = null,
rating = Manga.NO_RATING,
url = href,
publicUrl = href.withDomain(),
tags = card.select("p.tupe.tag")?.select("a")?.mapNotNullToSet tags@{ x ->
tags = card.select("p.tupe.tag").select("a").mapNotNullToSet tags@{ x ->
MangaTag(
title = x.text(),
key = x.attr("href")?.substringAfterLast("=") ?: return@tags null,
key = x.attr("href").ifEmpty {
return@mapNotNull null
}.substringAfterLast("="),
source = source
)
}.orEmpty(),
},
state = when (status) {
"выпускаецца" -> MangaState.ONGOING
"завершанае" -> MangaState.FINISHED
@@ -68,16 +71,17 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
override suspend fun getDetails(manga: Manga): Manga {
val doc = loaderContext.httpGet(manga.publicUrl).parseHtml()
val root = doc.body().select("div.container") ?: throw ParseException("Cannot find root")
val root = doc.body().select("div.container") ?: parseFailed("Cannot find root")
return manga.copy(
description = root.select("div.manga-block.grid-12")[2].select("p").text(),
chapters = root.select("ul.series").flatMap { table ->
table.select("li")
}.map { it.selectFirst("a") }.mapIndexedNotNull { i, a ->
val href = a.select("a").first().attr("href").toRelativeUrl(getDomain())
val href = a?.select("a")?.first()?.attr("href")
?.toRelativeUrl(getDomain()) ?: return@mapIndexedNotNull null
MangaChapter(
id = generateUid(href),
name = a.select("a").first().text(),
name = a.selectFirst("a")?.text().orEmpty(),
number = i + 1,
url = href,
source = source
@@ -112,16 +116,17 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
)
}
}
throw ParseException("Pages list not found at ${chapter.url.withDomain()}")
parseFailed("Pages list not found at ${chapter.url.withDomain()}")
}
override suspend fun getTags(): Set<MangaTag> {
val doc = loaderContext.httpGet("https://${getDomain()}/manga").parseHtml()
val root = doc.body().select("div#tabs-genres").select("ul#list.ul-three-colums")
return root.select("p.menu-tags.tupe").mapToSet { a ->
return root.select("p.menu-tags.tupe").mapToSet { p ->
val a = p.selectFirst("a") ?: parseFailed("a is null")
MangaTag(
title = a.select("a").text().capitalize(Locale.ROOT),
key = a.select("a").attr("data-name"),
title = a.text().toCamelCase(),
key = a.attr("data-name"),
source = source
)
}
@@ -130,30 +135,33 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
private suspend fun search(query: String): List<Manga> {
val domain = getDomain()
val doc = loaderContext.httpGet("https://$domain/search?q=$query").parseHtml()
val root = doc.body().select("div.manga-block").select("article.tab-2") ?: throw ParseException("Cannot find root")
val root = doc.body().select("div.manga-block").select("article.tab-2") ?: parseFailed("Cannot find root")
val items = root.select("div.anime-card")
return items.mapNotNull { card ->
val href = card.select("a").attr("href")
val status = card.select("tr")[2].text()
val fullTitle = card.selectFirst("h1.anime-card-title").text()
.substringBeforeLast('[')
val fullTitle = card.selectFirst("h1.anime-card-title")?.text()
?.substringBeforeLast('[') ?: return@mapNotNull null
val titleParts = fullTitle.splitTwoParts('/')
Manga(
id = generateUid(href),
title = titleParts?.first?.trim() ?: fullTitle,
coverUrl = card.selectFirst("img").attr("src").withDomain(),
coverUrl = card.selectFirst("img")?.attr("src")
?.withDomain().orEmpty(),
altTitle = titleParts?.second?.trim(),
author = null,
rating = Manga.NO_RATING,
url = href,
publicUrl = href.withDomain(),
tags = card.select("p.tupe.tag")?.select("a")?.mapNotNullToSet tags@{ x ->
tags = card.select("p.tupe.tag").select("a").mapNotNullToSet tags@{ x ->
MangaTag(
title = x.text(),
key = x.attr("href")?.substringAfterLast("=") ?: return@tags null,
key = x.attr("href").ifEmpty {
return@mapNotNull null
}.substringAfterLast("="),
source = source
)
}.orEmpty(),
},
state = when (status) {
"выпускаецца" -> MangaState.ONGOING
"завершанае" -> MangaState.FINISHED

View File

@@ -35,7 +35,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
else -> "https://$domain/${getSortKey(sortOrder)}?offset=$offset"
}
val doc = loaderContext.httpGet(url).parseHtml()
val root = doc.body().selectFirst("div.main_fon").getElementById("content")
val root = doc.body().selectFirst("div.main_fon")?.getElementById("content")
?: throw ParseException("Cannot find root")
return root.select("div.content_row").mapNotNull { row ->
val a = row.selectFirst("div.manga_row1")?.selectFirst("h2")?.selectFirst("a")
@@ -78,7 +78,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
chapters = root.select("table.table_cha").flatMap { table ->
table.select("div.manga2")
}.map { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
val href = a.relUrl("href")
val href = a?.relUrl("href") ?: return@mapIndexedNotNull null
MangaChapter(
id = generateUid(href),
name = a.text().trim(),
@@ -123,12 +123,12 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
override suspend fun getTags(): Set<MangaTag> {
val domain = getDomain()
val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml()
val root = doc.body().selectFirst("div.main_fon").getElementById("side")
.select("ul").last()
val root = doc.body().selectFirst("div.main_fon")?.getElementById("side")
?.select("ul")?.last() ?: throw ParseException("Cannot find root")
return root.select("li.sidetag").mapToSet { li ->
val a = li.children().last()
val a = li.children().last() ?: throw ParseException("a is null")
MangaTag(
title = a.text().capitalize(),
title = a.text().toCamelCase(),
key = a.attr("href").substringAfterLast('/'),
source = source
)

View File

@@ -6,7 +6,6 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.utils.ext.*
import java.util.*
import kotlin.collections.ArrayList
class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
@@ -122,12 +121,13 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
override suspend fun getTags(): Set<MangaTag> {
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
val root = doc.body().getElementById("animeFilter").selectFirst(".catalog-genres")
val root = doc.body().getElementById("animeFilter")
?.selectFirst(".catalog-genres") ?: throw ParseException("Root not found")
return root.select("li").mapToSet {
MangaTag(
source = source,
key = it.selectFirst("input").attr("data-genre"),
title = it.selectFirst("label").text()
key = it.selectFirst("input")?.attr("data-genre") ?: parseFailed(),
title = it.selectFirst("label")?.text() ?: parseFailed()
)
}
}

View File

@@ -57,7 +57,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
if (descDiv.selectFirst("i.fa-user") != null) {
return@mapNotNull null //skip author
}
val href = imgDiv.selectFirst("a").attr("href")?.inContextOf(node)
val href = imgDiv.selectFirst("a")?.attr("href")?.inContextOf(node)
if (href == null || href.toHttpUrl().host != baseHost) {
return@mapNotNull null // skip external links
}
@@ -161,11 +161,11 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
override suspend fun getTags(): Set<MangaTag> {
val doc = loaderContext.httpGet("https://${getDomain()}/list/genres/sort_name").parseHtml()
val root = doc.body().getElementById("mangaBox").selectFirst("div.leftContent")
.selectFirst("table.table")
val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")
?.selectFirst("table.table") ?: parseFailed("Cannot find root")
return root.select("a.element-link").mapToSet { a ->
MangaTag(
title = a.text().capitalize(),
title = a.text().toCamelCase(),
key = a.attr("href").substringAfterLast('/'),
source = source
)

View File

@@ -36,7 +36,7 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.mapToSet {
val a = it.children().last()
val a = it.children().last() ?: parseFailed("Invalid tag")
MangaTag(
title = a.text(),
key = a.attr("href").substringAfterLast('/'),

View File

@@ -10,7 +10,6 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.utils.ext.*
import java.util.*
import kotlin.collections.ArrayList
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
RemoteMangaRepository(loaderContext) {
@@ -51,13 +50,14 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
}
val doc = loaderContext.httpGet(url).parseHtml()
val root = doc.body().getElementById("manga-list") ?: throw ParseException("Root not found")
val items = root.selectFirst("div.media-cards-grid").select("div.media-card-wrap")
val items = root.selectFirst("div.media-cards-grid")?.select("div.media-card-wrap")
?: return emptyList()
return items.mapNotNull { card ->
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
val href = a.relUrl("href")
Manga(
id = generateUid(href),
title = card.selectFirst("h3").text(),
title = card.selectFirst("h3")?.text().orEmpty(),
coverUrl = a.absUrl("data-src"),
altTitle = null,
author = null,
@@ -98,10 +98,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
append(item.getInt("chapter_volume"))
append("/c")
append(item.getString("chapter_number"))
@Suppress("BlockingMethodInNonBlockingContext") // lint issue
append('/')
append(item.optString("chapter_string"))
}
var name = item.getString("chapter_name")
var name = item.getStringOrNull("chapter_name")
if (name.isNullOrBlank() || name == "null") {
name = "Том " + item.getInt("chapter_volume") +
" Глава " + item.getString("chapter_number")
@@ -128,17 +129,17 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
rating = root.selectFirst("div.media-stats-item__score")
?.selectFirst("span")
?.text()?.toFloatOrNull()?.div(5f) ?: manga.rating,
author = info.getElementsMatchingOwnText("Автор").firstOrNull()
author = info?.getElementsMatchingOwnText("Автор")?.firstOrNull()
?.nextElementSibling()?.text() ?: manga.author,
tags = info.selectFirst("div.media-tags")
tags = info?.selectFirst("div.media-tags")
?.select("a.media-tag-item")?.mapToSet { a ->
MangaTag(
title = a.text().capitalize(),
title = a.text().toCamelCase(),
key = a.attr("href").substringAfterLast('='),
source = source
)
} ?: manga.tags,
description = info.selectFirst("div.media-description__text")?.html(),
description = info?.selectFirst("div.media-description__text")?.html(),
chapters = chapters
)
}
@@ -146,11 +147,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.withDomain()
val doc = loaderContext.httpGet(fullUrl).parseHtml()
if (doc.location()?.endsWith("/register") == true) {
if (doc.location().endsWith("/register")) {
throw AuthRequiredException("/login".inContextOf(doc))
}
val scripts = doc.head().select("script")
val pg = doc.body().getElementById("pg").html()
val pg = (doc.body().getElementById("pg")?.html() ?: parseFailed("Element #pg not found"))
.substringAfter('=')
.substringBeforeLast(';')
val pages = JSONArray(pg)
@@ -196,7 +197,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
result += MangaTag(
source = source,
key = x.getInt("id").toString(),
title = x.getString("name").capitalize()
title = x.getString("name").toCamelCase()
)
}
return result

View File

@@ -51,14 +51,15 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
?: throw ParseException("Root not found")
return root.select("li").mapNotNull { li ->
val a = li.selectFirst("a.manga_cover")
val href = a.relUrl("href")
val href = a?.relUrl("href")
?: return@mapNotNull null
val views = li.select("p.view")
val status = views.findOwnText { x -> x.startsWith("Status:") }
?.substringAfter(':')?.trim()?.toLowerCase(Locale.ROOT)
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
Manga(
id = generateUid(href),
title = a.attr("title"),
coverUrl = a.selectFirst("img").absUrl("src"),
coverUrl = a.selectFirst("img")?.absUrl("src").orEmpty(),
source = MangaSource.MANGATOWN,
altTitle = null,
rating = li.selectFirst("p.score")?.selectFirst("b")
@@ -87,11 +88,11 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
val root = doc.body().selectFirst("section.main")
?.selectFirst("div.article_content") ?: throw ParseException("Cannot find root")
val info = root.selectFirst("div.detail_info").selectFirst("ul")
val info = root.selectFirst("div.detail_info")?.selectFirst("ul")
val chaptersList = root.selectFirst("div.chapter_content")
?.selectFirst("ul.chapter_list")?.select("li")?.asReversed()
return manga.copy(
tags = manga.tags + info.select("li").find { x ->
tags = manga.tags + info?.select("li")?.find { x ->
x.selectFirst("b")?.ownText() == "Genre(s):"
}?.select("a")?.mapNotNull { a ->
MangaTag(
@@ -100,9 +101,10 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
source = MangaSource.MANGATOWN
)
}.orEmpty(),
description = info.getElementById("show")?.ownText(),
description = info?.getElementById("show")?.ownText(),
chapters = chaptersList?.mapIndexedNotNull { i, li ->
val href = li.selectFirst("a").relUrl("href")
val href = li.selectFirst("a")?.relUrl("href")
?: return@mapIndexedNotNull null
val name = li.select("span").filter { it.className().isEmpty() }
.joinToString(" - ") { it.text() }.trim()
MangaChapter(
@@ -110,7 +112,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
url = href,
source = MangaSource.MANGATOWN,
number = i + 1,
name = if (name.isEmpty()) "${manga.title} - ${i + 1}" else name
name = name.ifEmpty { "${manga.title} - ${i + 1}" }
)
}
)
@@ -121,7 +123,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
val doc = loaderContext.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirst("div.page_select")
?: throw ParseException("Cannot find root")
return root.selectFirst("select").select("option").mapNotNull {
return root.selectFirst("select")?.select("option")?.mapNotNull {
val href = it.relUrl("value")
if (href.endsWith("featured.html")) {
return@mapNotNull null
@@ -132,20 +134,20 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
referer = fullUrl,
source = MangaSource.MANGATOWN
)
}
} ?: parseFailed("Pages list not found")
}
override suspend fun getPageUrl(page: MangaPage): String {
val doc = loaderContext.httpGet(page.url.withDomain()).parseHtml()
return doc.getElementById("image").absUrl("src")
return doc.getElementById("image")?.absUrl("src") ?: parseFailed("Image not found")
}
override suspend fun getTags(): Set<MangaTag> {
val doc = loaderContext.httpGet("/directory/".withDomain()).parseHtml()
val root = doc.body().selectFirst("aside.right")
.getElementsContainingOwnText("Genres")
.first()
.nextElementSibling()
?.getElementsContainingOwnText("Genres")
?.first()
?.nextElementSibling() ?: parseFailed("Root not found")
return root.select("li").mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val key = a.attr("href").parseTagKey()

View File

@@ -43,25 +43,26 @@ class MangareadRepository(
payload
).parseHtml()
return doc.select("div.row.c-tabs-item__content").map { div ->
val href = div.selectFirst("a").relUrl("href")
val href = div.selectFirst("a")?.relUrl("href")
?: parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary")
Manga(
id = generateUid(href),
url = href,
publicUrl = href.inContextOf(div),
coverUrl = div.selectFirst("img").absUrl("src"),
title = summary.selectFirst("h3").text(),
coverUrl = div.selectFirst("img")?.absUrl("src").orEmpty(),
title = summary?.selectFirst("h3")?.text().orEmpty(),
rating = div.selectFirst("span.total_votes")?.ownText()
?.toFloatOrNull()?.div(5f) ?: -1f,
tags = summary.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
title = a.text(),
source = MangaSource.MANGAREAD
)
}.orEmpty(),
author = summary.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
state = when (summary.selectFirst(".mg_status")?.selectFirst(".summary-content")
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")
?.ownText()?.trim()) {
"OnGoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
@@ -75,9 +76,9 @@ class MangareadRepository(
override suspend fun getTags(): Set<MangaTag> {
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
val root = doc.body().selectFirst("header")
.selectFirst("ul.second-menu")
?.selectFirst("ul.second-menu") ?: parseFailed("Root not found")
return root.select("li").mapNotNullToSet { li ->
val a = li.selectFirst("a")
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val href = a.attr("href").removeSuffix("/")
.substringAfterLast("genres/", "")
if (href.isEmpty()) {
@@ -127,10 +128,12 @@ class MangareadRepository(
?.joinToString { it.html() },
chapters = doc2.select("li").asReversed().mapIndexed { i, li ->
val a = li.selectFirst("a")
val href = a.relUrl("href")
val href = a?.relUrl("href").orEmpty().ifEmpty {
parseFailed("Link is missing")
}
MangaChapter(
id = generateUid(href),
name = a.ownText(),
name = a!!.ownText(),
number = i + 1,
url = href,
source = MangaSource.MANGAREAD
@@ -147,7 +150,7 @@ class MangareadRepository(
?: throw ParseException("Root not found")
return root.select("div.page-break").map { div ->
val img = div.selectFirst("img")
val url = img.relUrl("src")
val url = img?.relUrl("src") ?: parseFailed("Page image not found")
MangaPage(
id = generateUid(url),
url = url,

View File

@@ -56,22 +56,23 @@ abstract class NineMangaRepository(
?: throw ParseException("Cannot find root")
val baseHost = root.baseUri().toHttpUrl().host
return root.select("li").map { node ->
val href = node.selectFirst("a").absUrl("href")
val href = node.selectFirst("a")?.absUrl("href")
?: parseFailed("Link not found")
val relUrl = href.toRelativeUrl(baseHost)
val dd = node.selectFirst("dd")
Manga(
id = generateUid(relUrl),
url = relUrl,
publicUrl = href,
title = dd.selectFirst("a.bookname").text().toCamelCase(),
title = dd?.selectFirst("a.bookname")?.text()?.toCamelCase().orEmpty(),
altTitle = null,
coverUrl = node.selectFirst("img").absUrl("src"),
coverUrl = node.selectFirst("img")?.absUrl("src").orEmpty(),
rating = Manga.NO_RATING,
author = null,
tags = emptySet(),
state = null,
source = source,
description = dd.selectFirst("p").html(),
description = dd?.selectFirst("p")?.html(),
)
}
}
@@ -86,7 +87,7 @@ abstract class NineMangaRepository(
val infoRoot = root.selectFirst("div.bookintro")
?: throw ParseException("Cannot find info")
return manga.copy(
tags = infoRoot.getElementsByAttributeValue("itemprop", "genre")?.first()
tags = infoRoot.getElementsByAttributeValue("itemprop", "genre").first()
?.select("a")?.mapToSet { a ->
MangaTag(
title = a.text(),
@@ -94,13 +95,13 @@ abstract class NineMangaRepository(
source = source,
)
}.orEmpty(),
author = infoRoot.getElementsByAttributeValue("itemprop", "author")?.first()?.text(),
description = infoRoot.getElementsByAttributeValue("itemprop", "description")?.first()
author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.text(),
description = infoRoot.getElementsByAttributeValue("itemprop", "description").first()
?.html()?.substringAfter("</b>"),
chapters = root.selectFirst("div.chapterbox")?.selectFirst("ul")
?.select("li")?.asReversed()?.mapIndexed { i, li ->
val a = li.selectFirst("a")
val href = a.relUrl("href")
val href = a?.relUrl("href") ?: parseFailed("Link not found")
MangaChapter(
id = generateUid(href),
name = a.text(),
@@ -138,14 +139,14 @@ abstract class NineMangaRepository(
val doc = loaderContext.httpGet("https://${getDomain()}/category/", PREDEFINED_HEADERS)
.parseHtml()
val root = doc.body().selectFirst("ul.genreidex")
return root.select("li").mapToSet { li ->
val a = li.selectFirst("a")
return root?.select("li")?.mapToSet { li ->
val a = li.selectFirst("a") ?: parseFailed("Link not found")
MangaTag(
title = a.text(),
key = a.attr("href").substringBetweenLast("/", "."),
source = source
)
}
} ?: parseFailed("Root not found")
}
class English(loaderContext: MangaLoaderContext) : NineMangaRepository(

View File

@@ -6,6 +6,7 @@ import java.math.BigInteger
import java.net.URLEncoder
import java.security.MessageDigest
import java.util.*
import kotlin.contracts.contract
import kotlin.math.min
fun String.longHashCode(): Long {