[AsuraComic] Fix pages parsing

Koitharu 1 year ago
parent 27d2814ef9
commit e02b563bf4
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -235,6 +235,6 @@ internal class ImHentai(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
val img = doc.body().requireElementById("gimg")
return img.src() ?: doc.parseFailed("Cannot find image src")
return img.requireSrc()
}
}

@ -120,7 +120,7 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml().requireElementById("content")
return doc.select("div.text-center .img-fluid").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -212,7 +212,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".image_list img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -183,7 +183,7 @@ internal abstract class CupFoxParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPages).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -115,7 +115,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
state = when (a.selectLast("span.status")?.text().orEmpty()) {
state = when (a.selectLast("span.status")?.text()) {
"Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
"Hiatus" -> MangaState.PAUSED
@ -147,7 +147,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
)
}
tagCache = tagMap
return@withLock tagMap
tagMap
}
private val regexDate = """(\d+)(st|nd|rd|th)""".toRegex()
@ -185,8 +185,8 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return doc.select("div > img[alt*=chapter]").map { img ->
val urlPage = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
return doc.selectOrThrow("div.w-full > img.object-cover").map { img ->
val urlPage = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(urlPage),
url = urlPage,

@ -135,7 +135,7 @@ internal class BeeToon(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return doc.select(".chapter-content-inner center img").map { img ->
val urlPage = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val urlPage = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(urlPage),
url = urlPage,

@ -167,7 +167,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".chapter-container img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -158,7 +158,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.requireElementById("chapter-reader").select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -171,7 +171,7 @@ internal class ManhwasMen(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("chapter_imgs")
return doc.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -187,7 +187,7 @@ internal class Pururin(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".gallery-preview img").map { url ->
val img = url.src()?.toRelativeUrl(domain) ?: url.parseFailed("Image src not found")
val img = url.requireSrc().toRelativeUrl(domain)
val urlImage = img.replace("t.", ".")
MangaPage(
id = generateUid(urlImage),

@ -194,7 +194,7 @@ internal class VyManga(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return doc.select("img.d-block").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -85,7 +85,7 @@ internal class TempleScanEsp(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("main.contenedor img.readChapter_image__450v_").map { url ->
val img = url.src()?.toRelativeUrl(domain) ?: url.parseFailed("Image src not found")
val img = url.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(img),
url = img,

@ -239,7 +239,7 @@ internal abstract class FmreaderParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -19,7 +19,7 @@ internal class OlimpoScans(context: MangaLoaderContext) :
val fullUrl = ("/" + chapter.url).toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { img ->
val url = ("/proxy.php?link=" + img.src()).toRelativeUrl(domain)
val url = ("/proxy.php?link=" + img.requireSrc()).toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -85,7 +85,7 @@ internal class Klz9(context: MangaLoaderContext) :
val cid = doc.selectFirstOrThrow("#chapter").attr("value")
val docLoad = webClient.httpGet("https://$domain/app/manga/controllers/cont.listImg.php?cid=$cid").parseHtml()
return docLoad.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -45,7 +45,7 @@ internal class WeLoveManga(context: MangaLoaderContext) :
val cid = doc.selectFirstOrThrow("#chapter").attr("value")
val docLoad = webClient.httpGet("https://$domain/app/manga/controllers/cont.listImg.php?cid=$cid").parseHtml()
return docLoad.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),

@ -120,7 +120,7 @@ internal class FuryoSociety(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.main-img img").map { url ->
val img = url.src()?.toRelativeUrl(domain) ?: url.parseFailed("Image src not found")
val img = url.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(img),
url = img,

@ -206,7 +206,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.readerComics img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -260,7 +260,7 @@ internal abstract class FuzzyDoodleParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPages).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -209,7 +209,7 @@ internal abstract class GalleryAdultsParser(
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.requireElementById(idImg).src() ?: doc.parseFailed("Image src not found")
return doc.requireElementById(idImg).requireSrc()
}
protected fun String.cleanupTitle() = replace(regexBrackets, "")

@ -58,7 +58,7 @@ internal class DoujinDesuUk(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
val root = doc.body()
return root.requireElementById(idImg).selectFirstOrThrow("img").src() ?: root.parseFailed("Image src not found")
return root.requireElementById(idImg).selectFirstOrThrow("img").requireSrc()
}
}

@ -94,7 +94,7 @@ internal class Hentai3(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found")
return doc.selectFirstOrThrow(idImg).requireSrc()
}
private fun buildQuery(tags: Collection<MangaTag>, language: Locale?): String {

@ -50,7 +50,7 @@ internal class HentaiForce(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found")
return doc.selectFirstOrThrow(idImg).requireSrc()
}
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {

@ -107,7 +107,7 @@ internal class NHentaiParser(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
val root = doc.body()
return root.requireElementById(idImg).selectFirstOrThrow("img").src() ?: root.parseFailed("Image src not found")
return root.requireElementById(idImg).selectFirstOrThrow("img").requireSrc()
}
override fun Element.parseTags() = select("a").mapToSet {

@ -152,6 +152,6 @@ internal abstract class GattsuParser(
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.selectFirstOrThrow("div.galeria-foto img").src() ?: doc.parseFailed("Image src not found")
return doc.selectFirstOrThrow("div.galeria-foto img").requireSrc()
}
}

@ -42,7 +42,7 @@ internal class UniversoHentai(context: MangaLoaderContext) :
val images = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("galeria")
.select(".galeria-foto img")
return images.map { img ->
val urlImages = img.src() ?: img.parseFailed("Image src not found")
val urlImages = img.requireSrc()
MangaPage(
id = generateUid(urlImages),
url = urlImages,

@ -178,7 +178,7 @@ internal abstract class HeanCms(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPages).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -119,7 +119,7 @@ internal abstract class HeanCmsAlt(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -167,7 +167,7 @@ internal abstract class HotComicsParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPages).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -120,7 +120,7 @@ internal class HentaiCrot(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".thumbnail img, figure.gallery-item img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -120,7 +120,7 @@ internal class PixHentai(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".thumbnail img, figure.gallery-item img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -164,7 +164,7 @@ internal abstract class IkenParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPages).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -233,7 +233,7 @@ internal abstract class KeyoappParser(
?.also { return it }
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -116,7 +116,7 @@ internal abstract class LikeMangaParser(
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
coverUrl = div.selectFirstOrThrow("img").src()?.toAbsoluteUrl(domain).orEmpty(),
coverUrl = div.selectFirstOrThrow("img").src().orEmpty(),
tags = emptySet(),
state = null,
author = null,
@ -269,7 +269,7 @@ internal abstract class LikeMangaParser(
} else {
return doc.select(".reading-detail img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -684,7 +684,7 @@ internal abstract class MadaraParser(
)
return root.select(selectPage).flatMap { div ->
div.selectOrThrow("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -113,7 +113,7 @@ internal class Manhwa18Cc(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow(selectBodyPage)
return root.select("img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -168,7 +168,7 @@ internal class AdultWebtoon(context: MangaLoaderContext) :
)
return root.select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url.replace("http:", "https:"),

@ -9,16 +9,8 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.CryptoAES
import org.koitharu.kotatsu.parsers.util.domain
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.parseFailed
import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import org.koitharu.kotatsu.parsers.util.src
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.toRelativeUrl
import java.util.Base64
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@MangaSourceParser("DARK_SCANS", "DarkScans", "en")
internal class DarkScans(context: MangaLoaderContext) :
@ -40,7 +32,7 @@ internal class DarkScans(context: MangaLoaderContext) :
)
return root.select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url.replace("http:", "https:"),

@ -174,7 +174,7 @@ internal class MangaDass(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow("div.read-manga").selectFirstOrThrow("div.read-content")
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -167,7 +167,7 @@ internal class MangaDna(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow("div.read-manga").selectFirstOrThrow("div.read-content")
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -19,7 +19,7 @@ internal class Manhuaplus(context: MangaLoaderContext) :
val root = doc.body().selectFirst("div.main-col-inner")?.selectFirst("div.reading-content")
?: throw ParseException("Root not found", fullUrl)
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -20,7 +20,7 @@ internal class Manhwaden(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow(selectBodyPage)
return root.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -27,7 +27,7 @@ internal class DoujinHentaiNet(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -81,7 +81,7 @@ internal class MangasNoSekai(context: MangaLoaderContext) :
?: throw ParseException("No image found, try to log in", fullUrl)
return root.select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -108,7 +108,7 @@ internal class TmoManga(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().requireElementById("images_chapter")
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain).orEmpty()
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -33,7 +33,7 @@ internal class FrScan(context: MangaLoaderContext) :
} else {
return doc.body().selectFirstOrThrow(selectBodyPage).select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -19,7 +19,7 @@ internal class RhPlusManga(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow("div.main-col-inner").selectFirstOrThrow("div.reading-content")
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -27,7 +27,7 @@ internal class HentaiCube(context: MangaLoaderContext) :
val root = doc.body().selectFirst("div.main-col-inner")?.selectFirst("div.reading-content")
?: throw ParseException("Root not found", fullUrl)
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -20,7 +20,7 @@ internal class Quaanhdaocuteo(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow(selectBodyPage)
return root.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -233,7 +233,7 @@ internal abstract class MadthemeParser(
val result = ArrayList<MangaPage>()
// html parisng
doc.select(selectPage).forEach { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
if (known.add(url)) {
result += MangaPage(
id = generateUid(url),

@ -233,7 +233,7 @@ internal abstract class MangaboxParser(
val doc2 = webClient.httpGet(fullUrl2).parseHtml()
return doc2.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
@ -244,7 +244,7 @@ internal abstract class MangaboxParser(
}
} else {
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),

@ -297,7 +297,7 @@ internal abstract class MangaReaderParser(
val test = docs.select(selectTestScript)
if (test.isNullOrEmpty() and !encodedSrc) {
return docs.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -30,7 +30,7 @@ internal class CatharsisFantasy(context: MangaLoaderContext) :
val test = docs.select(selectTestScript)
if (test.isNullOrEmpty() and !encodedSrc) {
return docs.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -20,7 +20,7 @@ internal class LelManga(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow("div.maincontent").requireElementById("readerarea")
return root.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -158,7 +158,7 @@ internal class Komikcast(context: MangaLoaderContext) :
val test = docs.select("script:containsData(ts_reader)")
if (test.isNullOrEmpty()) {
return docs.select("div#chapter_body img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -217,7 +217,7 @@ internal abstract class MangaWorldParser(
val selectMangaPages = "img.img-fluid"
val imgSelector = if (doc.select(selectWebtoonPages).isNotEmpty()) selectWebtoonPages else selectMangaPages
return doc.select(imgSelector).map { img ->
val urlPage = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val urlPage = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(urlPage),
url = urlPage,

@ -242,7 +242,7 @@ internal abstract class MmrcmsParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { url ->
val img = url.src()?.toRelativeUrl(domain) ?: url.parseFailed("Image src not found")
val img = url.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(img),
url = img,

@ -79,7 +79,7 @@ internal abstract class OneMangaParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.elementor-widget-container img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -228,7 +228,7 @@ internal abstract class OtakuSanctuaryParser(
}
} else {
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -140,7 +140,7 @@ internal class LerMangaOnline(context: MangaLoaderContext) :
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".images img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -152,7 +152,7 @@ internal class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(conte
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".content p img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -177,6 +177,6 @@ internal class AComics(context: MangaLoaderContext) :
override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.requireElementById("mainImage").src() ?: doc.parseFailed("Image src not found")
return doc.requireElementById("mainImage").requireSrc()
}
}

@ -177,7 +177,7 @@ internal class MangaAy(context: MangaLoaderContext) : PagedMangaParser(context,
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.mt-2 img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -96,7 +96,7 @@ internal class SadScans(context: MangaLoaderContext) : SinglePageMangaParser(con
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".swiper-slide img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -227,7 +227,7 @@ internal class TrWebtoon(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("images")
return doc.select("img").map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -136,7 +136,7 @@ internal class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val docs = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return docs.select("#image > img").map {
val pageUrl = it.src() ?: it.parseFailed("Image src not found")
val pageUrl = it.requireSrc()
MangaPage(
id = generateUid(pageUrl),
url = pageUrl,

@ -14,11 +14,11 @@ import java.util.*
@MangaSourceParser("TRUYENGG", "TruyenGG", "vi")
internal class TruyenGG(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.TRUYENGG, 42) {
override val configKeyDomain = ConfigKey.Domain("truyengg.com")
override val configKeyDomain = ConfigKey.Domain("truyengg.com")
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
@ -62,6 +62,7 @@ internal class TruyenGG(context: MangaLoaderContext) : PagedMangaParser(context,
append(filter.query.urlEncoded())
}
}
else -> {
buildString {
append("https://")
@ -188,7 +189,7 @@ internal class TruyenGG(context: MangaLoaderContext) : PagedMangaParser(context,
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(".content_detail img").map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,
@ -198,14 +199,14 @@ internal class TruyenGG(context: MangaLoaderContext) : PagedMangaParser(context,
}
}
private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/tim-kiem-nang-cao.html").parseHtml()
return doc.select(".advsearch-form div.genre-item").mapToSet {
MangaTag(
key = it.selectFirstOrThrow("span").attr("data-id"),
title = it.text(),
source = source,
)
}
}
private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/tim-kiem-nang-cao.html").parseHtml()
return doc.select(".advsearch-form div.genre-item").mapToSet {
MangaTag(
key = it.selectFirstOrThrow("span").attr("data-id"),
title = it.text(),
source = source,
)
}
}
}

@ -201,7 +201,7 @@ internal class TruyenQQ(context: MangaLoaderContext) : PagedMangaParser(context,
val root = doc.body().selectFirstOrThrow(".chapter_content")
return root.select("div.page-chapter").map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -144,7 +144,7 @@ internal abstract class VmpParser(
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.wp-content img").map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -268,7 +268,7 @@ internal abstract class WpComicsParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { url ->
val img = url.src()?.toRelativeUrl(domain) ?: url.parseFailed("Image src not found")
val img = url.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(img),
url = img,

@ -341,7 +341,7 @@ internal abstract class ZeistMangaParser(
} else {
doc.select(selectPage).map { img ->
val url = img.src() ?: img.parseFailed("Image src not found")
val url = img.requireSrc()
MangaPage(
id = generateUid(url),
url = url,

@ -275,7 +275,7 @@ internal abstract class ZMangaParser(
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).map { img ->
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
val url = img.requireSrc().toRelativeUrl(domain)
MangaPage(
id = generateUid(url),
url = url,

@ -9,6 +9,7 @@ import org.jsoup.select.QueryParser
import org.jsoup.select.Selector
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.exception.ParseException
import kotlin.contracts.contract
public val Element.host: String?
get() {
@ -24,8 +25,7 @@ public val Element.host: String?
* Return an attribute value or null if it is missing or empty
* @see [Element.attr] which returns empty string instead of null
*/
public fun Element.attrOrNull(attributeKey: String) = attr(attributeKey).takeUnless { it.isBlank() }?.trim()
public fun Element.attrOrNull(attributeKey: String): String? = attr(attributeKey).takeUnless { it.isBlank() }?.trim()
/**
* Return an attribute value or throw an exception if it is missing
@ -48,7 +48,7 @@ public fun Element.attrAsRelativeUrlOrNull(attributeKey: String): String? {
if (attr.isEmpty() || attr.startsWith("data:")) {
return null
}
if (attr.startsWith("/")) {
if (attr.startsWith('/')) {
return attr
}
val host = baseUri().toHttpUrlOrNull()?.host ?: return null
@ -90,7 +90,7 @@ public fun Element.attrAsAbsoluteUrlOrNull(attributeKey: String): String? {
* @see attrAsRelativeUrlOrNull
*/
public fun Element.attrAsAbsoluteUrl(attributeKey: String): String {
return requireNotNull(attrAsAbsoluteUrlOrNull(attributeKey)) {
return parseNotNull(attrAsAbsoluteUrlOrNull(attributeKey)) {
"Cannot get absolute url for $attributeKey: \"${attr(attributeKey)}\""
}
}
@ -107,8 +107,8 @@ public fun Element.styleValueOrNull(property: String): String? {
/**
* Like a `expectFirst` but with detailed error message
*/
public fun Element.selectFirstOrThrow(cssQuery: String): Element {
return Selector.selectFirst(cssQuery, this) ?: throw ParseException("Cannot find \"$cssQuery\"", baseUri())
public fun Element.selectFirstOrThrow(cssQuery: String): Element = parseNotNull(Selector.selectFirst(cssQuery, this)) {
"Cannot find \"$cssQuery\""
}
public fun Element.selectOrThrow(cssQuery: String): Elements {
@ -117,16 +117,14 @@ public fun Element.selectOrThrow(cssQuery: String): Elements {
}
}
public fun Element.requireElementById(id: String): Element {
return getElementById(id) ?: throw ParseException("Cannot find \"#$id\"", baseUri())
public fun Element.requireElementById(id: String): Element = parseNotNull(getElementById(id)) {
"Cannot find \"#$id\""
}
public fun Element.selectLast(cssQuery: String): Element? {
return select(cssQuery).lastOrNull()
}
public fun Element.selectLast(cssQuery: String): Element? = select(cssQuery).lastOrNull()
public fun Element.selectLastOrThrow(cssQuery: String): Element {
return selectLast(cssQuery) ?: throw ParseException("Cannot find \"$cssQuery\"", baseUri())
public fun Element.selectLastOrThrow(cssQuery: String): Element = parseNotNull(selectLast(cssQuery)) {
"Cannot find \"$cssQuery\""
}
public fun Element.textOrNull(): String? = text().takeUnless { it.isEmpty() }
@ -142,8 +140,9 @@ public fun Element.selectFirstParent(query: String): Element? {
}
}
public fun Element.selectFirstParentOrThrow(query: String): Element =
selectFirstParent(query) ?: throw ParseException("Cannot find parent \"$query\"", baseUri())
public fun Element.selectFirstParentOrThrow(query: String): Element = parseNotNull(selectFirstParent(query)) {
"Cannot find parent \"$query\""
}
/**
* Return a first non-empty attribute value of [names] or null if it is missing or empty
@ -183,6 +182,11 @@ public fun Element.src(
return null
}
@InternalParsersApi
public fun Element.requireSrc(): String = parseNotNull(src()) {
"Image src not found"
}
public fun Element.metaValue(itemprop: String): String? = getElementsByAttributeValue("itemprop", itemprop)
.firstNotNullOfOrNull { element ->
element.attrOrNull("content")
@ -200,3 +204,16 @@ public fun String.cssUrl(): String? {
substring(fromIndex + 4, toIndex).trim()
}
}
internal inline fun <T : Any> Element.parseNotNull(value: T?, lazyMessage: () -> String): T {
contract {
returns() implies (value != null)
}
if (value == null) {
val message = lazyMessage()
throw ParseException(message, baseUri())
} else {
return value
}
}

@ -190,7 +190,7 @@ internal class MangaParserTest {
fun favicon(source: MangaParserSource) = runTest(timeout = timeout) {
val parser = context.newParserInstance(source)
val favicons = parser.getFavicons()
val types = setOf("png", "svg", "ico", "gif", "jpg", "jpeg")
val types = setOf("png", "svg", "ico", "gif", "jpg", "jpeg", "webp", "avif")
assert(favicons.isNotEmpty())
favicons.forEach {
assert(it.url.isUrlAbsolute()) { "Favicon url is not absolute: ${it.url}" }

Loading…
Cancel
Save