From 6a3421df8a267eeeea52b96b2485168661df18fd Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 14 Jul 2021 07:01:11 +0300 Subject: [PATCH] Adjust nullability in parsers --- .../core/parser/RemoteMangaRepository.kt | 5 ++ .../core/parser/site/AnibelRepository.kt | 52 +++++++++++-------- .../core/parser/site/ChanRepository.kt | 12 ++--- .../core/parser/site/DesuMeRepository.kt | 8 +-- .../core/parser/site/GroupleRepository.kt | 8 +-- .../core/parser/site/HenChanRepository.kt | 2 +- .../core/parser/site/MangaLibRepository.kt | 23 ++++---- .../core/parser/site/MangaTownRepository.kt | 30 ++++++----- .../core/parser/site/MangareadRepository.kt | 25 +++++---- .../core/parser/site/NineMangaRepository.kt | 23 ++++---- .../koitharu/kotatsu/utils/ext/StringExt.kt | 1 + 11 files changed, 105 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index ce91c0ddc..87e7812c8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt index 35c5e6187..82c924435 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt @@ -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 { 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 { 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 diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt index e5828c154..242686815 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt @@ -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 { 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 ) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt index c09530342..4d62b7af5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt @@ -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 { 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() ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index b680b5cf0..268ad03b0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -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 { 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 ) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt index e405aed9c..4033584d0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt @@ -36,7 +36,7 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load description = root.getElementById("description")?.html()?.substringBeforeLast(" 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 { 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 diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt index 4fb4085dc..44fd5937e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt @@ -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 { 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() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt index 96430b755..ed390f61a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt @@ -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 { 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, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt index aca8baca3..cbbd59ee1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt @@ -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(""), 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( diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt index cc6a7d295..fbf8326ae 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt @@ -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 {