Adjust nullability in parsers
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.core.parser
|
package org.koitharu.kotatsu.core.parser
|
||||||
|
|
||||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
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.MangaPage
|
||||||
import org.koitharu.kotatsu.core.model.MangaSource
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
import org.koitharu.kotatsu.core.model.MangaTag
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
@@ -75,4 +76,8 @@ abstract class RemoteMangaRepository(
|
|||||||
h = 31 * h + id
|
h = 31 * h + id
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun parseFailed(message: String? = null): Nothing {
|
||||||
|
throw ParseException(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -32,30 +32,33 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
else -> "/manga?page=$page".withDomain()
|
else -> "/manga?page=$page".withDomain()
|
||||||
}
|
}
|
||||||
val doc = loaderContext.httpGet(link).parseHtml()
|
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")
|
val items = root.select("div.anime-card")
|
||||||
return items.mapNotNull { 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 status = card.select("tr")[2].text()
|
||||||
val fullTitle = card.selectFirst("h1.anime-card-title").text()
|
val fullTitle = card.selectFirst("h1.anime-card-title")?.text()
|
||||||
.substringBeforeLast('[')
|
?.substringBeforeLast('[') ?: return@mapNotNull null
|
||||||
val titleParts = fullTitle.splitTwoParts('/')
|
val titleParts = fullTitle.splitTwoParts('/')
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = titleParts?.first?.trim() ?: fullTitle,
|
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(),
|
altTitle = titleParts?.second?.trim(),
|
||||||
author = null,
|
author = null,
|
||||||
rating = Manga.NO_RATING,
|
rating = Manga.NO_RATING,
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.withDomain(),
|
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(
|
MangaTag(
|
||||||
title = x.text(),
|
title = x.text(),
|
||||||
key = x.attr("href")?.substringAfterLast("=") ?: return@tags null,
|
key = x.attr("href").ifEmpty {
|
||||||
|
return@mapNotNull null
|
||||||
|
}.substringAfterLast("="),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}.orEmpty(),
|
},
|
||||||
state = when (status) {
|
state = when (status) {
|
||||||
"выпускаецца" -> MangaState.ONGOING
|
"выпускаецца" -> MangaState.ONGOING
|
||||||
"завершанае" -> MangaState.FINISHED
|
"завершанае" -> MangaState.FINISHED
|
||||||
@@ -68,16 +71,17 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
|
|
||||||
override suspend fun getDetails(manga: Manga): Manga {
|
override suspend fun getDetails(manga: Manga): Manga {
|
||||||
val doc = loaderContext.httpGet(manga.publicUrl).parseHtml()
|
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(
|
return manga.copy(
|
||||||
description = root.select("div.manga-block.grid-12")[2].select("p").text(),
|
description = root.select("div.manga-block.grid-12")[2].select("p").text(),
|
||||||
chapters = root.select("ul.series").flatMap { table ->
|
chapters = root.select("ul.series").flatMap { table ->
|
||||||
table.select("li")
|
table.select("li")
|
||||||
}.map { it.selectFirst("a") }.mapIndexedNotNull { i, a ->
|
}.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(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.select("a").first().text(),
|
name = a.selectFirst("a")?.text().orEmpty(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
source = source
|
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> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val doc = loaderContext.httpGet("https://${getDomain()}/manga").parseHtml()
|
val doc = loaderContext.httpGet("https://${getDomain()}/manga").parseHtml()
|
||||||
val root = doc.body().select("div#tabs-genres").select("ul#list.ul-three-colums")
|
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(
|
MangaTag(
|
||||||
title = a.select("a").text().capitalize(Locale.ROOT),
|
title = a.text().toCamelCase(),
|
||||||
key = a.select("a").attr("data-name"),
|
key = a.attr("data-name"),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -130,30 +135,33 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
private suspend fun search(query: String): List<Manga> {
|
private suspend fun search(query: String): List<Manga> {
|
||||||
val domain = getDomain()
|
val domain = getDomain()
|
||||||
val doc = loaderContext.httpGet("https://$domain/search?q=$query").parseHtml()
|
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")
|
val items = root.select("div.anime-card")
|
||||||
return items.mapNotNull { card ->
|
return items.mapNotNull { card ->
|
||||||
val href = card.select("a").attr("href")
|
val href = card.select("a").attr("href")
|
||||||
val status = card.select("tr")[2].text()
|
val status = card.select("tr")[2].text()
|
||||||
val fullTitle = card.selectFirst("h1.anime-card-title").text()
|
val fullTitle = card.selectFirst("h1.anime-card-title")?.text()
|
||||||
.substringBeforeLast('[')
|
?.substringBeforeLast('[') ?: return@mapNotNull null
|
||||||
val titleParts = fullTitle.splitTwoParts('/')
|
val titleParts = fullTitle.splitTwoParts('/')
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = titleParts?.first?.trim() ?: fullTitle,
|
title = titleParts?.first?.trim() ?: fullTitle,
|
||||||
coverUrl = card.selectFirst("img").attr("src").withDomain(),
|
coverUrl = card.selectFirst("img")?.attr("src")
|
||||||
|
?.withDomain().orEmpty(),
|
||||||
altTitle = titleParts?.second?.trim(),
|
altTitle = titleParts?.second?.trim(),
|
||||||
author = null,
|
author = null,
|
||||||
rating = Manga.NO_RATING,
|
rating = Manga.NO_RATING,
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.withDomain(),
|
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(
|
MangaTag(
|
||||||
title = x.text(),
|
title = x.text(),
|
||||||
key = x.attr("href")?.substringAfterLast("=") ?: return@tags null,
|
key = x.attr("href").ifEmpty {
|
||||||
|
return@mapNotNull null
|
||||||
|
}.substringAfterLast("="),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}.orEmpty(),
|
},
|
||||||
state = when (status) {
|
state = when (status) {
|
||||||
"выпускаецца" -> MangaState.ONGOING
|
"выпускаецца" -> MangaState.ONGOING
|
||||||
"завершанае" -> MangaState.FINISHED
|
"завершанае" -> MangaState.FINISHED
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
else -> "https://$domain/${getSortKey(sortOrder)}?offset=$offset"
|
else -> "https://$domain/${getSortKey(sortOrder)}?offset=$offset"
|
||||||
}
|
}
|
||||||
val doc = loaderContext.httpGet(url).parseHtml()
|
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")
|
?: throw ParseException("Cannot find root")
|
||||||
return root.select("div.content_row").mapNotNull { row ->
|
return root.select("div.content_row").mapNotNull { row ->
|
||||||
val a = row.selectFirst("div.manga_row1")?.selectFirst("h2")?.selectFirst("a")
|
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 ->
|
chapters = root.select("table.table_cha").flatMap { table ->
|
||||||
table.select("div.manga2")
|
table.select("div.manga2")
|
||||||
}.map { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
}.map { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
||||||
val href = a.relUrl("href")
|
val href = a?.relUrl("href") ?: return@mapIndexedNotNull null
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text().trim(),
|
name = a.text().trim(),
|
||||||
@@ -123,12 +123,12 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
override suspend fun getTags(): Set<MangaTag> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val domain = getDomain()
|
val domain = getDomain()
|
||||||
val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml()
|
val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml()
|
||||||
val root = doc.body().selectFirst("div.main_fon").getElementById("side")
|
val root = doc.body().selectFirst("div.main_fon")?.getElementById("side")
|
||||||
.select("ul").last()
|
?.select("ul")?.last() ?: throw ParseException("Cannot find root")
|
||||||
return root.select("li.sidetag").mapToSet { li ->
|
return root.select("li.sidetag").mapToSet { li ->
|
||||||
val a = li.children().last()
|
val a = li.children().last() ?: throw ParseException("a is null")
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text().capitalize(),
|
title = a.text().toCamelCase(),
|
||||||
key = a.attr("href").substringAfterLast('/'),
|
key = a.attr("href").substringAfterLast('/'),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import org.koitharu.kotatsu.core.model.*
|
|||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
||||||
|
|
||||||
@@ -122,12 +121,13 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
|
|
||||||
override suspend fun getTags(): Set<MangaTag> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
|
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 {
|
return root.select("li").mapToSet {
|
||||||
MangaTag(
|
MangaTag(
|
||||||
source = source,
|
source = source,
|
||||||
key = it.selectFirst("input").attr("data-genre"),
|
key = it.selectFirst("input")?.attr("data-genre") ?: parseFailed(),
|
||||||
title = it.selectFirst("label").text()
|
title = it.selectFirst("label")?.text() ?: parseFailed()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
if (descDiv.selectFirst("i.fa-user") != null) {
|
if (descDiv.selectFirst("i.fa-user") != null) {
|
||||||
return@mapNotNull null //skip author
|
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) {
|
if (href == null || href.toHttpUrl().host != baseHost) {
|
||||||
return@mapNotNull null // skip external links
|
return@mapNotNull null // skip external links
|
||||||
}
|
}
|
||||||
@@ -161,11 +161,11 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
|
|
||||||
override suspend fun getTags(): Set<MangaTag> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val doc = loaderContext.httpGet("https://${getDomain()}/list/genres/sort_name").parseHtml()
|
val doc = loaderContext.httpGet("https://${getDomain()}/list/genres/sort_name").parseHtml()
|
||||||
val root = doc.body().getElementById("mangaBox").selectFirst("div.leftContent")
|
val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")
|
||||||
.selectFirst("table.table")
|
?.selectFirst("table.table") ?: parseFailed("Cannot find root")
|
||||||
return root.select("a.element-link").mapToSet { a ->
|
return root.select("a.element-link").mapToSet { a ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text().capitalize(),
|
title = a.text().toCamelCase(),
|
||||||
key = a.attr("href").substringAfterLast('/'),
|
key = a.attr("href").substringAfterLast('/'),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
|
|||||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||||
tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.mapToSet {
|
tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.mapToSet {
|
||||||
val a = it.children().last()
|
val a = it.children().last() ?: parseFailed("Invalid tag")
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text(),
|
title = a.text(),
|
||||||
key = a.attr("href").substringAfterLast('/'),
|
key = a.attr("href").substringAfterLast('/'),
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.koitharu.kotatsu.core.model.*
|
|||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||||
RemoteMangaRepository(loaderContext) {
|
RemoteMangaRepository(loaderContext) {
|
||||||
@@ -51,13 +50,14 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
}
|
}
|
||||||
val doc = loaderContext.httpGet(url).parseHtml()
|
val doc = loaderContext.httpGet(url).parseHtml()
|
||||||
val root = doc.body().getElementById("manga-list") ?: throw ParseException("Root not found")
|
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 ->
|
return items.mapNotNull { card ->
|
||||||
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
|
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.relUrl("href")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = card.selectFirst("h3").text(),
|
title = card.selectFirst("h3")?.text().orEmpty(),
|
||||||
coverUrl = a.absUrl("data-src"),
|
coverUrl = a.absUrl("data-src"),
|
||||||
altTitle = null,
|
altTitle = null,
|
||||||
author = null,
|
author = null,
|
||||||
@@ -98,10 +98,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
append(item.getInt("chapter_volume"))
|
append(item.getInt("chapter_volume"))
|
||||||
append("/c")
|
append("/c")
|
||||||
append(item.getString("chapter_number"))
|
append(item.getString("chapter_number"))
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext") // lint issue
|
||||||
append('/')
|
append('/')
|
||||||
append(item.optString("chapter_string"))
|
append(item.optString("chapter_string"))
|
||||||
}
|
}
|
||||||
var name = item.getString("chapter_name")
|
var name = item.getStringOrNull("chapter_name")
|
||||||
if (name.isNullOrBlank() || name == "null") {
|
if (name.isNullOrBlank() || name == "null") {
|
||||||
name = "Том " + item.getInt("chapter_volume") +
|
name = "Том " + item.getInt("chapter_volume") +
|
||||||
" Глава " + item.getString("chapter_number")
|
" Глава " + item.getString("chapter_number")
|
||||||
@@ -128,17 +129,17 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
rating = root.selectFirst("div.media-stats-item__score")
|
rating = root.selectFirst("div.media-stats-item__score")
|
||||||
?.selectFirst("span")
|
?.selectFirst("span")
|
||||||
?.text()?.toFloatOrNull()?.div(5f) ?: manga.rating,
|
?.text()?.toFloatOrNull()?.div(5f) ?: manga.rating,
|
||||||
author = info.getElementsMatchingOwnText("Автор").firstOrNull()
|
author = info?.getElementsMatchingOwnText("Автор")?.firstOrNull()
|
||||||
?.nextElementSibling()?.text() ?: manga.author,
|
?.nextElementSibling()?.text() ?: manga.author,
|
||||||
tags = info.selectFirst("div.media-tags")
|
tags = info?.selectFirst("div.media-tags")
|
||||||
?.select("a.media-tag-item")?.mapToSet { a ->
|
?.select("a.media-tag-item")?.mapToSet { a ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text().capitalize(),
|
title = a.text().toCamelCase(),
|
||||||
key = a.attr("href").substringAfterLast('='),
|
key = a.attr("href").substringAfterLast('='),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
} ?: manga.tags,
|
} ?: manga.tags,
|
||||||
description = info.selectFirst("div.media-description__text")?.html(),
|
description = info?.selectFirst("div.media-description__text")?.html(),
|
||||||
chapters = chapters
|
chapters = chapters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -146,11 +147,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
val fullUrl = chapter.url.withDomain()
|
val fullUrl = chapter.url.withDomain()
|
||||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||||
if (doc.location()?.endsWith("/register") == true) {
|
if (doc.location().endsWith("/register")) {
|
||||||
throw AuthRequiredException("/login".inContextOf(doc))
|
throw AuthRequiredException("/login".inContextOf(doc))
|
||||||
}
|
}
|
||||||
val scripts = doc.head().select("script")
|
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('=')
|
.substringAfter('=')
|
||||||
.substringBeforeLast(';')
|
.substringBeforeLast(';')
|
||||||
val pages = JSONArray(pg)
|
val pages = JSONArray(pg)
|
||||||
@@ -196,7 +197,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
result += MangaTag(
|
result += MangaTag(
|
||||||
source = source,
|
source = source,
|
||||||
key = x.getInt("id").toString(),
|
key = x.getInt("id").toString(),
|
||||||
title = x.getString("name").capitalize()
|
title = x.getString("name").toCamelCase()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -51,14 +51,15 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
?: throw ParseException("Root not found")
|
?: throw ParseException("Root not found")
|
||||||
return root.select("li").mapNotNull { li ->
|
return root.select("li").mapNotNull { li ->
|
||||||
val a = li.selectFirst("a.manga_cover")
|
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 views = li.select("p.view")
|
||||||
val status = views.findOwnText { x -> x.startsWith("Status:") }
|
val status = views.findOwnText { x -> x.startsWith("Status:") }
|
||||||
?.substringAfter(':')?.trim()?.toLowerCase(Locale.ROOT)
|
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = a.attr("title"),
|
title = a.attr("title"),
|
||||||
coverUrl = a.selectFirst("img").absUrl("src"),
|
coverUrl = a.selectFirst("img")?.absUrl("src").orEmpty(),
|
||||||
source = MangaSource.MANGATOWN,
|
source = MangaSource.MANGATOWN,
|
||||||
altTitle = null,
|
altTitle = null,
|
||||||
rating = li.selectFirst("p.score")?.selectFirst("b")
|
rating = li.selectFirst("p.score")?.selectFirst("b")
|
||||||
@@ -87,11 +88,11 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||||
val root = doc.body().selectFirst("section.main")
|
val root = doc.body().selectFirst("section.main")
|
||||||
?.selectFirst("div.article_content") ?: throw ParseException("Cannot find root")
|
?.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")
|
val chaptersList = root.selectFirst("div.chapter_content")
|
||||||
?.selectFirst("ul.chapter_list")?.select("li")?.asReversed()
|
?.selectFirst("ul.chapter_list")?.select("li")?.asReversed()
|
||||||
return manga.copy(
|
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):"
|
x.selectFirst("b")?.ownText() == "Genre(s):"
|
||||||
}?.select("a")?.mapNotNull { a ->
|
}?.select("a")?.mapNotNull { a ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
@@ -100,9 +101,10 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
source = MangaSource.MANGATOWN
|
source = MangaSource.MANGATOWN
|
||||||
)
|
)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
description = info.getElementById("show")?.ownText(),
|
description = info?.getElementById("show")?.ownText(),
|
||||||
chapters = chaptersList?.mapIndexedNotNull { i, li ->
|
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() }
|
val name = li.select("span").filter { it.className().isEmpty() }
|
||||||
.joinToString(" - ") { it.text() }.trim()
|
.joinToString(" - ") { it.text() }.trim()
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
@@ -110,7 +112,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
url = href,
|
url = href,
|
||||||
source = MangaSource.MANGATOWN,
|
source = MangaSource.MANGATOWN,
|
||||||
number = i + 1,
|
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 doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||||
val root = doc.body().selectFirst("div.page_select")
|
val root = doc.body().selectFirst("div.page_select")
|
||||||
?: throw ParseException("Cannot find root")
|
?: throw ParseException("Cannot find root")
|
||||||
return root.selectFirst("select").select("option").mapNotNull {
|
return root.selectFirst("select")?.select("option")?.mapNotNull {
|
||||||
val href = it.relUrl("value")
|
val href = it.relUrl("value")
|
||||||
if (href.endsWith("featured.html")) {
|
if (href.endsWith("featured.html")) {
|
||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
@@ -132,20 +134,20 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
referer = fullUrl,
|
referer = fullUrl,
|
||||||
source = MangaSource.MANGATOWN
|
source = MangaSource.MANGATOWN
|
||||||
)
|
)
|
||||||
}
|
} ?: parseFailed("Pages list not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getPageUrl(page: MangaPage): String {
|
override suspend fun getPageUrl(page: MangaPage): String {
|
||||||
val doc = loaderContext.httpGet(page.url.withDomain()).parseHtml()
|
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> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val doc = loaderContext.httpGet("/directory/".withDomain()).parseHtml()
|
val doc = loaderContext.httpGet("/directory/".withDomain()).parseHtml()
|
||||||
val root = doc.body().selectFirst("aside.right")
|
val root = doc.body().selectFirst("aside.right")
|
||||||
.getElementsContainingOwnText("Genres")
|
?.getElementsContainingOwnText("Genres")
|
||||||
.first()
|
?.first()
|
||||||
.nextElementSibling()
|
?.nextElementSibling() ?: parseFailed("Root not found")
|
||||||
return root.select("li").mapNotNullToSet { li ->
|
return root.select("li").mapNotNullToSet { li ->
|
||||||
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
|
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
|
||||||
val key = a.attr("href").parseTagKey()
|
val key = a.attr("href").parseTagKey()
|
||||||
|
|||||||
@@ -43,25 +43,26 @@ class MangareadRepository(
|
|||||||
payload
|
payload
|
||||||
).parseHtml()
|
).parseHtml()
|
||||||
return doc.select("div.row.c-tabs-item__content").map { div ->
|
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")
|
val summary = div.selectFirst(".tab-summary")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.inContextOf(div),
|
publicUrl = href.inContextOf(div),
|
||||||
coverUrl = div.selectFirst("img").absUrl("src"),
|
coverUrl = div.selectFirst("img")?.absUrl("src").orEmpty(),
|
||||||
title = summary.selectFirst("h3").text(),
|
title = summary?.selectFirst("h3")?.text().orEmpty(),
|
||||||
rating = div.selectFirst("span.total_votes")?.ownText()
|
rating = div.selectFirst("span.total_votes")?.ownText()
|
||||||
?.toFloatOrNull()?.div(5f) ?: -1f,
|
?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||||
tags = summary.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
|
tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
|
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
|
||||||
title = a.text(),
|
title = a.text(),
|
||||||
source = MangaSource.MANGAREAD
|
source = MangaSource.MANGAREAD
|
||||||
)
|
)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
author = summary.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
|
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
|
||||||
state = when (summary.selectFirst(".mg_status")?.selectFirst(".summary-content")
|
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")
|
||||||
?.ownText()?.trim()) {
|
?.ownText()?.trim()) {
|
||||||
"OnGoing" -> MangaState.ONGOING
|
"OnGoing" -> MangaState.ONGOING
|
||||||
"Completed" -> MangaState.FINISHED
|
"Completed" -> MangaState.FINISHED
|
||||||
@@ -75,9 +76,9 @@ class MangareadRepository(
|
|||||||
override suspend fun getTags(): Set<MangaTag> {
|
override suspend fun getTags(): Set<MangaTag> {
|
||||||
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
|
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
|
||||||
val root = doc.body().selectFirst("header")
|
val root = doc.body().selectFirst("header")
|
||||||
.selectFirst("ul.second-menu")
|
?.selectFirst("ul.second-menu") ?: parseFailed("Root not found")
|
||||||
return root.select("li").mapNotNullToSet { li ->
|
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("/")
|
val href = a.attr("href").removeSuffix("/")
|
||||||
.substringAfterLast("genres/", "")
|
.substringAfterLast("genres/", "")
|
||||||
if (href.isEmpty()) {
|
if (href.isEmpty()) {
|
||||||
@@ -127,10 +128,12 @@ class MangareadRepository(
|
|||||||
?.joinToString { it.html() },
|
?.joinToString { it.html() },
|
||||||
chapters = doc2.select("li").asReversed().mapIndexed { i, li ->
|
chapters = doc2.select("li").asReversed().mapIndexed { i, li ->
|
||||||
val a = li.selectFirst("a")
|
val a = li.selectFirst("a")
|
||||||
val href = a.relUrl("href")
|
val href = a?.relUrl("href").orEmpty().ifEmpty {
|
||||||
|
parseFailed("Link is missing")
|
||||||
|
}
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.ownText(),
|
name = a!!.ownText(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
source = MangaSource.MANGAREAD
|
source = MangaSource.MANGAREAD
|
||||||
@@ -147,7 +150,7 @@ class MangareadRepository(
|
|||||||
?: throw ParseException("Root not found")
|
?: throw ParseException("Root not found")
|
||||||
return root.select("div.page-break").map { div ->
|
return root.select("div.page-break").map { div ->
|
||||||
val img = div.selectFirst("img")
|
val img = div.selectFirst("img")
|
||||||
val url = img.relUrl("src")
|
val url = img?.relUrl("src") ?: parseFailed("Page image not found")
|
||||||
MangaPage(
|
MangaPage(
|
||||||
id = generateUid(url),
|
id = generateUid(url),
|
||||||
url = url,
|
url = url,
|
||||||
|
|||||||
@@ -56,22 +56,23 @@ abstract class NineMangaRepository(
|
|||||||
?: throw ParseException("Cannot find root")
|
?: throw ParseException("Cannot find root")
|
||||||
val baseHost = root.baseUri().toHttpUrl().host
|
val baseHost = root.baseUri().toHttpUrl().host
|
||||||
return root.select("li").map { node ->
|
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 relUrl = href.toRelativeUrl(baseHost)
|
||||||
val dd = node.selectFirst("dd")
|
val dd = node.selectFirst("dd")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(relUrl),
|
id = generateUid(relUrl),
|
||||||
url = relUrl,
|
url = relUrl,
|
||||||
publicUrl = href,
|
publicUrl = href,
|
||||||
title = dd.selectFirst("a.bookname").text().toCamelCase(),
|
title = dd?.selectFirst("a.bookname")?.text()?.toCamelCase().orEmpty(),
|
||||||
altTitle = null,
|
altTitle = null,
|
||||||
coverUrl = node.selectFirst("img").absUrl("src"),
|
coverUrl = node.selectFirst("img")?.absUrl("src").orEmpty(),
|
||||||
rating = Manga.NO_RATING,
|
rating = Manga.NO_RATING,
|
||||||
author = null,
|
author = null,
|
||||||
tags = emptySet(),
|
tags = emptySet(),
|
||||||
state = null,
|
state = null,
|
||||||
source = source,
|
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")
|
val infoRoot = root.selectFirst("div.bookintro")
|
||||||
?: throw ParseException("Cannot find info")
|
?: throw ParseException("Cannot find info")
|
||||||
return manga.copy(
|
return manga.copy(
|
||||||
tags = infoRoot.getElementsByAttributeValue("itemprop", "genre")?.first()
|
tags = infoRoot.getElementsByAttributeValue("itemprop", "genre").first()
|
||||||
?.select("a")?.mapToSet { a ->
|
?.select("a")?.mapToSet { a ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text(),
|
title = a.text(),
|
||||||
@@ -94,13 +95,13 @@ abstract class NineMangaRepository(
|
|||||||
source = source,
|
source = source,
|
||||||
)
|
)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
author = infoRoot.getElementsByAttributeValue("itemprop", "author")?.first()?.text(),
|
author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.text(),
|
||||||
description = infoRoot.getElementsByAttributeValue("itemprop", "description")?.first()
|
description = infoRoot.getElementsByAttributeValue("itemprop", "description").first()
|
||||||
?.html()?.substringAfter("</b>"),
|
?.html()?.substringAfter("</b>"),
|
||||||
chapters = root.selectFirst("div.chapterbox")?.selectFirst("ul")
|
chapters = root.selectFirst("div.chapterbox")?.selectFirst("ul")
|
||||||
?.select("li")?.asReversed()?.mapIndexed { i, li ->
|
?.select("li")?.asReversed()?.mapIndexed { i, li ->
|
||||||
val a = li.selectFirst("a")
|
val a = li.selectFirst("a")
|
||||||
val href = a.relUrl("href")
|
val href = a?.relUrl("href") ?: parseFailed("Link not found")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text(),
|
name = a.text(),
|
||||||
@@ -138,14 +139,14 @@ abstract class NineMangaRepository(
|
|||||||
val doc = loaderContext.httpGet("https://${getDomain()}/category/", PREDEFINED_HEADERS)
|
val doc = loaderContext.httpGet("https://${getDomain()}/category/", PREDEFINED_HEADERS)
|
||||||
.parseHtml()
|
.parseHtml()
|
||||||
val root = doc.body().selectFirst("ul.genreidex")
|
val root = doc.body().selectFirst("ul.genreidex")
|
||||||
return root.select("li").mapToSet { li ->
|
return root?.select("li")?.mapToSet { li ->
|
||||||
val a = li.selectFirst("a")
|
val a = li.selectFirst("a") ?: parseFailed("Link not found")
|
||||||
MangaTag(
|
MangaTag(
|
||||||
title = a.text(),
|
title = a.text(),
|
||||||
key = a.attr("href").substringBetweenLast("/", "."),
|
key = a.attr("href").substringBetweenLast("/", "."),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
} ?: parseFailed("Root not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
class English(loaderContext: MangaLoaderContext) : NineMangaRepository(
|
class English(loaderContext: MangaLoaderContext) : NineMangaRepository(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.math.BigInteger
|
|||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.contracts.contract
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
fun String.longHashCode(): Long {
|
fun String.longHashCode(): Long {
|
||||||
|
|||||||
Reference in New Issue
Block a user