Update jsoup (#15321)

This commit is contained in:
arkon 2023-02-11 22:22:32 -05:00 committed by GitHub
parent 7964dc4613
commit e1f59a845e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
262 changed files with 1207 additions and 1374 deletions

View File

@ -21,7 +21,7 @@ coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-androi
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" } injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" } rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" } rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
jsoup = { module = "org.jsoup:jsoup", version = "1.13.1" } jsoup = { module = "org.jsoup:jsoup", version = "1.15.1" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" } quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }

View File

@ -29,7 +29,7 @@ class EpikManga : FMReader("Epik Manga", "https://www.epikmanga.com", "tr") {
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/seri-listesi?type=text", headers) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/seri-listesi?type=text", headers)
private fun searchMangaParse(response: Response, query: String): MangasPage { private fun searchMangaParse(response: Response, query: String): MangasPage {
val mangas = response.asJsoup().select("div.char.col-lg-4 a") val mangas = response.asJsoup().select("div.char.col-lg-4 a").toList()
.filter { it.text().contains(query, ignoreCase = true) } .filter { it.text().contains(query, ignoreCase = true) }
.map { .map {
SManga.create().apply { SManga.create().apply {
@ -40,7 +40,7 @@ class EpikManga : FMReader("Epik Manga", "https://www.epikmanga.com", "tr") {
return MangasPage(mangas, false) return MangasPage(mangas, false)
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.col-md-9 div.row").first() val infoElement = document.select("div.col-md-9 div.row").first()!!
return SManga.create().apply { return SManga.create().apply {
status = parseStatus(infoElement.select("h4:contains(Durum:)").firstOrNull()?.ownText()) status = parseStatus(infoElement.select("h4:contains(Durum:)").firstOrNull()?.ownText())

View File

@ -9,7 +9,7 @@ class KSGroupScans : FMReader("KSGroupScans", "https://ksgroupscans.com", "en")
override fun chapterFromElement(element: Element, mangaTitle: String): SChapter { override fun chapterFromElement(element: Element, mangaTitle: String): SChapter {
return SChapter.create().apply { return SChapter.create().apply {
element.select(chapterUrlSelector).first().let { element.select(chapterUrlSelector).first()!!.let {
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
name = element.select(".chapter-name").text() name = element.select(".chapter-name").text()
} }

View File

@ -48,7 +48,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
val infoElement: Element = document.select("div#tab1").first() val infoElement: Element = document.select("div#tab1").first()!!
manga.author = infoElement.select("table + table tr + tr td a").first()?.text() manga.author = infoElement.select("table + table tr + tr td a").first()?.text()
manga.artist = infoElement.select("table + table tr + tr td + td a").first()?.text() manga.artist = infoElement.select("table + table tr + tr td + td a").first()?.text()

View File

@ -10,7 +10,7 @@ import org.jsoup.nodes.Document
class SayTruyen : FMReader("Say Truyen", "https://saytruyenvip.com", "vi") { class SayTruyen : FMReader("Say Truyen", "https://saytruyenvip.com", "vi") {
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val info = document.select("div.row").first() val info = document.select("div.row").first()!!
return SManga.create().apply { return SManga.create().apply {
author = info.select("div.row li:has(b:contains(Tác giả)) small").text() author = info.select("div.row li:has(b:contains(Tác giả)) small").text()
genre = info.select("div.row li:has(b:contains(Thể loại)) small a").joinToString { it.text() } genre = info.select("div.row li:has(b:contains(Thể loại)) small a").joinToString { it.text() }

View File

@ -24,8 +24,8 @@ class UniversoHentai : Gattsu(
override fun latestUpdatesSelector() = "div.meio div.videos div.video a[href^=$baseUrl]:not(:has(span.selo-hd))" override fun latestUpdatesSelector() = "div.meio div.videos div.video a[href^=$baseUrl]:not(:has(span.selo-hd))"
override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("span.video-titulo").text().trim() title = element.selectFirst("span.video-titulo")!!.text().trim()
thumbnail_url = element.selectFirst("img.wp-post-image").attr("src") thumbnail_url = element.selectFirst("img.wp-post-image")!!.attr("src")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))
} }
@ -54,11 +54,11 @@ class UniversoHentai : Gattsu(
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
name = "Capítulo único" name = "Capítulo único"
scanlator = element.select("ul.paginaPostItens li:contains(Tradutor) a").firstOrNull()?.text() scanlator = element.select("ul.paginaPostItens li:contains(Tradutor) a").firstOrNull()?.text()
date_upload = element.ownerDocument().select("meta[property=article:published_time]").firstOrNull() date_upload = element.ownerDocument()!!.select("meta[property=article:published_time]").firstOrNull()
?.attr("content") ?.attr("content")
.orEmpty() .orEmpty()
.toDate() .toDate()
setUrlWithoutDomain(element.selectFirst("a[title=Abrir galeria]").attr("href")) setUrlWithoutDomain(element.selectFirst("a[title=Abrir galeria]")!!.attr("href"))
} }
override fun pageListSelector() = "div.meio div.galeria div.galeria-foto a img" override fun pageListSelector() = "div.meio div.galeria div.galeria-foto a img"

View File

@ -8,7 +8,7 @@ class LynxScans : Genkan("LynxScans", "https://lynxscans.com", "en", "/web/comic
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
val allImages = document.select("div#pages-container + script").first().data() val allImages = document.select("div#pages-container + script").first()!!.data()
.substringAfter("[").substringBefore("];") .substringAfter("[").substringBefore("];")
.replace(Regex("""["\\]"""), "") .replace(Regex("""["\\]"""), "")
.split(",/") .split(",/")

View File

@ -23,9 +23,9 @@ class ComicGardo : GigaViewer(
override fun popularMangaSelector(): String = "ul.series-section-list li.series-section-item > a" override fun popularMangaSelector(): String = "ul.series-section-list li.series-section-item > a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h5.series-title").text() title = element.selectFirst("h5.series-title")!!.text()
thumbnail_url = element.selectFirst("div.thumb img").attr("data-src") thumbnail_url = element.selectFirst("div.thumb img")!!.attr("data-src")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href")!!)
} }
override fun getCollections(): List<Collection> = listOf( override fun getCollections(): List<Collection> = listOf(

View File

@ -27,8 +27,8 @@ class KurageBunch : GigaViewer(
override fun popularMangaSelector(): String = "ul.page-series-list li div.item-box" override fun popularMangaSelector(): String = "ul.page-series-list li div.item-box"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("a.series-data-container h4").text() title = element.selectFirst("a.series-data-container h4")!!.text()
thumbnail_url = element.selectFirst("a.series-thumb img").attr("data-src") thumbnail_url = element.selectFirst("a.series-thumb img")!!.attr("data-src")
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
} }

View File

@ -21,9 +21,9 @@ class MagazinePocket : GigaViewer(
override fun popularMangaSelector(): String = "ul.daily-series li.daily-series-item > a" override fun popularMangaSelector(): String = "ul.daily-series li.daily-series-item > a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h4.daily-series-title").text() title = element.selectFirst("h4.daily-series-title")!!.text()
thumbnail_url = element.selectFirst("div.daily-series-thumb img").attr("data-src") thumbnail_url = element.selectFirst("div.daily-series-thumb img")!!.attr("data-src")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href")!!)
} }
override fun latestUpdatesSelector(): String = "section.daily.$dayOfWeek " + popularMangaSelector() override fun latestUpdatesSelector(): String = "section.daily.$dayOfWeek " + popularMangaSelector()

View File

@ -23,8 +23,8 @@ class TonariNoYoungJump : GigaViewer(
override fun popularMangaSelector(): String = "ul.series-table-list li.subpage-table-list-item > a" override fun popularMangaSelector(): String = "ul.series-table-list li.subpage-table-list-item > a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h4.title").text() title = element.selectFirst("h4.title")!!.text()
thumbnail_url = element.selectFirst("div.subpage-image-wrapper img").attr("data-src") thumbnail_url = element.selectFirst("div.subpage-image-wrapper img")!!.attr("data-src")
.replace("{width}", "528") .replace("{width}", "528")
.replace("{height}", "528") .replace("{height}", "528")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))

View File

@ -37,7 +37,7 @@ class Manga18fx : Madara(
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
loadGenres(document) loadGenres(document)
val block = document.selectFirst(Evaluator.Class("trending-block")) val block = document.selectFirst(Evaluator.Class("trending-block"))!!
val mangas = block.select(Evaluator.Tag("a")).map(::mangaFromElement) val mangas = block.select(Evaluator.Tag("a")).map(::mangaFromElement)
return MangasPage(mangas, false) return MangasPage(mangas, false)
} }
@ -45,7 +45,7 @@ class Manga18fx : Madara(
private fun mangaFromElement(element: Element) = SManga.create().apply { private fun mangaFromElement(element: Element) = SManga.create().apply {
url = element.attr("href") url = element.attr("href")
title = element.attr("title") title = element.attr("title")
thumbnail_url = element.selectFirst(Evaluator.Tag("img")).attr("data-src") thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.attr("data-src")
} }
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page", headers) override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page", headers)
@ -54,9 +54,9 @@ class Manga18fx : Madara(
val document = response.asJsoup() val document = response.asJsoup()
loadGenres(document) loadGenres(document)
val mangas = document.select(Evaluator.Class("bsx-item")).map { val mangas = document.select(Evaluator.Class("bsx-item")).map {
mangaFromElement(it.selectFirst(Evaluator.Tag("a"))) mangaFromElement(it.selectFirst(Evaluator.Tag("a"))!!)
} }
val nextButton = document.selectFirst(Evaluator.Class("next")) val nextButton = document.selectFirst(Evaluator.Class("next"))!!
val hasNextPage = nextButton != null && nextButton.hasClass("disabled").not() val hasNextPage = nextButton != null && nextButton.hasClass("disabled").not()
return MangasPage(mangas, hasNextPage) return MangasPage(mangas, hasNextPage)
} }
@ -91,7 +91,7 @@ class Manga18fx : Madara(
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()
val container = document.selectFirst(Evaluator.Class("row-content-chapter")) val container = document.selectFirst(Evaluator.Class("row-content-chapter"))!!
return container.children().map(::chapterFromElement) return container.children().map(::chapterFromElement)
} }

View File

@ -16,7 +16,7 @@ class MangaLeveling : Madara("Manga Leveling", "https://mangaleveling.com", "en"
chapter.url = urlElement.attr("abs:href").let { chapter.url = urlElement.attr("abs:href").let {
it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "" it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else ""
} }
chapter.name = urlElement.selectFirst("span").text() chapter.name = urlElement.selectFirst("span")!!.text()
} }
// Dates can be part of a "new" graphic or plain text // Dates can be part of a "new" graphic or plain text
// Added "title" alternative // Added "title" alternative

View File

@ -16,7 +16,7 @@ class MMScans : Madara("MMScans", "https://mm-scans.org", "en") {
with(element) { with(element) {
select(popularMangaUrlSelector).first()?.let { select(popularMangaUrlSelector).first()?.let {
manga.setUrlWithoutDomain(it.attr("abs:href")) manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.selectFirst("h3").ownText() manga.title = it.selectFirst("h3")!!.ownText()
} }
select("img").first()?.let { select("img").first()?.let {
@ -35,7 +35,7 @@ class MMScans : Madara("MMScans", "https://mm-scans.org", "en") {
chapter.url = urlElement.attr("abs:href").let { chapter.url = urlElement.attr("abs:href").let {
it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "" it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else ""
} }
chapter.name = urlElement.selectFirst(".chapter-title-date p").text() chapter.name = urlElement.selectFirst(".chapter-title-date p")!!.text()
} }
chapter.date_upload = parseChapterDate(select(chapterDateSelector()).firstOrNull()?.text()) chapter.date_upload = parseChapterDate(select(chapterDateSelector()).firstOrNull()?.text())
} }

View File

@ -62,16 +62,16 @@ class Vomic : MangabzTheme("vomic", ""), ConfigurableSource {
val mangas = buildList { val mangas = buildList {
// ranking sidebar // ranking sidebar
addAll(document.selectFirst(Evaluator.Class("rank-list")).children()) addAll(document.selectFirst(Evaluator.Class("rank-list"))!!.children())
// carousel list // carousel list
addAll(document.selectFirst(Evaluator.Class("carousel-right-list")).children()) addAll(document.selectFirst(Evaluator.Class("carousel-right-list"))!!.children())
// recommend list // recommend list
addAll(document.select(Evaluator.Class("index-manga-item"))) addAll(document.select(Evaluator.Class("index-manga-item"))!!)
}.map { element -> }.map { element ->
SManga.create().apply { SManga.create().apply {
title = element.selectFirst(paragraph).text() title = element.selectFirst(paragraph)!!.text()
url = element.selectFirst(link).attr("href") url = element.selectFirst(link)!!.attr("href")
thumbnail_url = element.selectFirst(image).attr("src") thumbnail_url = element.selectFirst(image)!!.attr("src")
} }
} }

View File

@ -23,11 +23,11 @@ class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Tita
override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-3" override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-3"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val urlElemnt = element.selectFirst("a") val urlElement = element.selectFirst("a")!!
name = listOfNotNull( name = listOfNotNull(
urlElemnt.text(), urlElement.text(),
element.selectFirst("div.text-xs").text().takeUnless { it.isBlank() }, element.selectFirst("div.text-xs")!!.text().takeUnless { it.isBlank() },
).joinToString(" - ") { it.trim() } ).joinToString(" - ") { it.trim() }
url = urlElemnt.attr("abs:href") url = urlElement.attr("abs:href")
} }
} }

View File

@ -58,10 +58,10 @@ class MangaRaw : MangaRawTheme("MangaRaw", ""), ConfigurableSource {
GET("$baseUrl/?s=$query&page=$page", headers) GET("$baseUrl/?s=$query&page=$page", headers)
override fun Document.getSanitizedDetails(): Element = override fun Document.getSanitizedDetails(): Element =
selectFirst(selectors.detailsSelector).apply { selectFirst(selectors.detailsSelector)!!.apply {
val recommendClass = selectors.recommendClass val recommendClass = selectors.recommendClass
children().find { it.hasClass(recommendClass) }?.remove() children().find { it.hasClass(recommendClass) }?.remove()
selectFirst(Evaluator.Class("list-scoll")).remove() selectFirst(Evaluator.Class("list-scoll"))!!.remove()
} }
override fun chapterListSelector() = ".list-scoll a" override fun chapterListSelector() = ".list-scoll a"

View File

@ -28,8 +28,8 @@ class SyoSetu : MangaRawTheme("SyoSetu", "https://syosetu.top") {
GET("$baseUrl/page/$page?s=$query") GET("$baseUrl/page/$page?s=$query")
override fun Document.getSanitizedDetails(): Element = override fun Document.getSanitizedDetails(): Element =
selectFirst(Evaluator.Tag("article")).selectFirst(Evaluator.Class("content-wrap-inner")).apply { selectFirst(Evaluator.Tag("article"))!!.selectFirst(Evaluator.Class("content-wrap-inner"))!!.apply {
selectFirst(Evaluator.Class("chaplist")).remove() selectFirst(Evaluator.Class("chaplist"))!!.remove()
} }
override fun chapterListSelector() = ".chaplist a" override fun chapterListSelector() = ".chaplist a"

View File

@ -26,9 +26,9 @@ class Seemangas : MangaSar(
override fun popularMangaSelector() = "ul.sidebar-popular li.popular-treending" override fun popularMangaSelector() = "ul.sidebar-popular li.popular-treending"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h4.title").text() title = element.selectFirst("h4.title")!!.text()
thumbnail_url = element.selectFirst("div.tumbl img").attr("data-lazy-src") thumbnail_url = element.selectFirst("div.tumbl img")!!.attr("data-lazy-src")
setUrlWithoutDomain(element.selectFirst("a").attr("abs:href")) setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
} }
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
@ -58,15 +58,15 @@ class Seemangas : MangaSar(
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup() val document = response.asJsoup()
val infoElement = document.selectFirst("div.box-single:has(div.mangapage)") val infoElement = document.selectFirst("div.box-single:has(div.mangapage)")!!
return SManga.create().apply { return SManga.create().apply {
title = infoElement.selectFirst("h1.kw-title").text() title = infoElement.selectFirst("h1.kw-title")!!.text()
author = infoElement.selectFirst("div.mdq.author").text().trim() author = infoElement.selectFirst("div.mdq.author")!!.text().trim()
description = infoElement.selectFirst("div.sinopse-page").text() description = infoElement.selectFirst("div.sinopse-page")!!.text()
genre = infoElement.select("div.generos a.widget-btn").joinToString { it.text() } genre = infoElement.select("div.generos a.widget-btn")!!.joinToString { it.text() }
status = infoElement.selectFirst("span.mdq").text().toStatus() status = infoElement.selectFirst("span.mdq")!!.text().toStatus()
thumbnail_url = infoElement.selectFirst("div.thumb img").attr("abs:data-lazy-src") thumbnail_url = infoElement.selectFirst("div.thumb img")!!.attr("abs:data-lazy-src")
} }
} }
override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request {

View File

@ -66,7 +66,7 @@ open class FlameScans(
return super.pageListParse(document) return super.pageListParse(document)
} }
return document.select("#readerarea p:has(img), $composedSelector") return document.select("#readerarea p:has(img), $composedSelector").toList()
.filter { .filter {
it.select("img").all { imgEl -> it.select("img").all { imgEl ->
imgEl.attr("abs:src").isNullOrEmpty().not() imgEl.attr("abs:src").isNullOrEmpty().not()

View File

@ -20,7 +20,7 @@ class Kiryuu : MangaThemesia("Kiryuu", "https://kiryuu.id", "id", dateFormat = S
// manga details // manga details
override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply {
title = document.selectFirst(seriesThumbnailSelector).attr("title") title = document.selectFirst(seriesThumbnailSelector)!!.attr("title")
} }
override val hasProjectPage = true override val hasProjectPage = true

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import okhttp3.Headers import okhttp3.Headers
@ -62,7 +61,7 @@ class KomikCast : MangaThemesia(
override fun searchMangaSelector() = "div.list-update_item" override fun searchMangaSelector() = "div.list-update_item"
override fun searchMangaFromElement(element: Element) = super.searchMangaFromElement(element).apply { override fun searchMangaFromElement(element: Element) = super.searchMangaFromElement(element).apply {
title = element.selectFirst("h3.title").ownText() title = element.selectFirst("h3.title")!!.ownText()
} }
override val seriesDetailsSelector = "div.komik_info:has(.komik_info-content)" override val seriesDetailsSelector = "div.komik_info:has(.komik_info-content)"
@ -78,7 +77,7 @@ class KomikCast : MangaThemesia(
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a") val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href")) setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".lch a, .chapternum").text().ifBlank { urlElements.first().text() } name = element.select(".lch a, .chapternum")!!.text().ifBlank { urlElements.first()!!.text() }
date_upload = parseChapterDate2(element.select(".chapter-link-time").text()) date_upload = parseChapterDate2(element.select(".chapter-link-time").text())
} }
@ -130,7 +129,7 @@ class KomikCast : MangaThemesia(
var imageServer = "cdn" var imageServer = "cdn"
if (!imageList.containsKey(imageServer)) imageServer = imageList.keys.first() if (!imageList.containsKey(imageServer)) imageServer = imageList.keys.first()
val imageElement = imageList[imageServer]!!.jsonArray.joinToString("") val imageElement = imageList[imageServer]!!.jsonArray.joinToString("")
doc = Jsoup.parse(json.decodeFromString(imageElement)) doc = Jsoup.parse(imageElement)
cssQuery = "img.size-full" cssQuery = "img.size-full"
} }

View File

@ -59,7 +59,7 @@ class MangaRawOrg : MangaThemesia("Manga Raw.org", "https://mangaraw.org", "ja")
} }
private fun pageListParse(response: Response, chapterUrl: String): List<Page> { private fun pageListParse(response: Response, chapterUrl: String): List<Page> {
return response.asJsoup().select("span.page-link").first().ownText().substringAfterLast(" ").toInt() return response.asJsoup().select("span.page-link").first()!!.ownText().substringAfterLast(" ").toInt()
.let { lastNum -> IntRange(1, lastNum) } .let { lastNum -> IntRange(1, lastNum) }
.map { num -> Page(num, "$chapterUrl/$num") } .map { num -> Page(num, "$chapterUrl/$num") }
} }

View File

@ -31,7 +31,7 @@ class MangaSwat : MangaThemesia(
override val seriesStatusSelector = "span:contains(الحالة)" override val seriesStatusSelector = "span:contains(الحالة)"
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val scriptContent = document.selectFirst("script:containsData(ts_reader)").data() val scriptContent = document.selectFirst("script:containsData(ts_reader)")!!.data()
val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");") val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");")
val tsReader = json.decodeFromString<TSReader>(jsonString) val tsReader = json.decodeFromString<TSReader>(jsonString)
val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList() val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList()

View File

@ -10,7 +10,7 @@ class MangKomik : MangaThemesia("MangKomik", "https://mangkomik.net", "id") {
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
// Get external JS for image urls // Get external JS for image urls
val scriptEl = document.selectFirst("script[data-minify]") val scriptEl = document.selectFirst("script[data-minify]")!!
val scriptUrl = scriptEl?.attr("src") val scriptUrl = scriptEl?.attr("src")
if (scriptUrl.isNullOrEmpty()) { if (scriptUrl.isNullOrEmpty()) {
return super.pageListParse(document) return super.pageListParse(document)

View File

@ -51,7 +51,7 @@ class ManhwaFreak : MangaThemesia("Manhwa Freak", "https://manhwafreak.com", "en
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a") val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href")) setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".chapter-info p:nth-child(1)").text().ifBlank { urlElements.first().text() } name = element.select(".chapter-info p:nth-child(1)")!!.text().ifBlank { urlElements.first()!!.text() }
date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate() date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate()
} }

View File

@ -31,7 +31,7 @@ class xCaliBRScans : MangaThemesia("xCaliBR Scans", "https://xcalibrscans.com",
.forEach { element -> .forEach { element ->
when { when {
element.tagName() == "p" -> { element.tagName() == "p" -> {
val imgUrl = element.selectFirst("img").imgAttr() val imgUrl = element.selectFirst("img")!!.imgAttr()
imgUrls.add(imgUrl) imgUrls.add(imgUrl)
} }
element.tagName() == "div" && element.hasClass("kage") -> { element.tagName() == "div" && element.hasClass("kage") -> {

View File

@ -58,12 +58,12 @@ class AnimaRegia : MMRCMS("AnimaRegia", "https://animaregia.net", "pt-BR") {
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val document = response.asJsoup() val document = response.asJsoup()
title = document.selectFirst("h1.widget-title").text() title = document.selectFirst("h1.widget-title")!!.text()
thumbnail_url = coverGuess( thumbnail_url = coverGuess(
document.select("div.col-sm-5 img.img-thumbnail").firstOrNull()?.attr("abs:src"), document.select("div.col-sm-5 img.img-thumbnail").firstOrNull()?.attr("abs:src"),
document.location(), document.location(),
) )
description = document.select("div.row div.well p").text().trim() description = document.select("div.row div.well p")!!.text().trim()
for (element in document.select("div.col-sm-5 ul.list-group li.list-group-item")) { for (element in document.select("div.col-sm-5 ul.list-group li.list-group-item")) {
when (element.text().trim().lowercase(BRAZILIAN_LOCALE).substringBefore(":")) { when (element.text().trim().lowercase(BRAZILIAN_LOCALE).substringBefore(":")) {
@ -94,7 +94,7 @@ class AnimaRegia : MMRCMS("AnimaRegia", "https://animaregia.net", "pt-BR") {
.joinToString(" & ") { it.text().trim() } .joinToString(" & ") { it.text().trim() }
date_upload = el.select("div.col-md-4").firstOrNull() date_upload = el.select("div.col-md-4").firstOrNull()
?.text()?.removeSuffix("Download")?.toDate() ?: 0L ?.text()?.removeSuffix("Download")?.toDate() ?: 0L
setUrlWithoutDomain(el.select("h5.chapter-title-rtl a").first().attr("href")) setUrlWithoutDomain(el.select("h5.chapter-title-rtl a").first()!!.attr("href"))
} }
} }
} }

View File

@ -16,8 +16,8 @@ class FallenAngels : MMRCMS("Fallen Angels", "https://manga.fascans.com", "en")
override fun nullableChapterFromElement(element: Element): SChapter? { override fun nullableChapterFromElement(element: Element): SChapter? {
val chapter = SChapter.create() val chapter = SChapter.create()
val titleWrapper = element.select("[class^=chapter-title-rtl]").first() val titleWrapper = element.select("[class^=chapter-title-rtl]").first()!!
val chapterElement = titleWrapper.getElementsByTag("a") val chapterElement = titleWrapper.getElementsByTag("a")!!
val url = chapterElement.attr("href") val url = chapterElement.attr("href")
chapter.url = getUrlWithoutBaseUrl(url) chapter.url = getUrlWithoutBaseUrl(url)
@ -28,7 +28,7 @@ class FallenAngels : MMRCMS("Fallen Angels", "https://manga.fascans.com", "en")
val chapterText = chapterElement.text() val chapterText = chapterElement.text()
val numberRegex = Regex("""[1-9]\d*(\.\d+)*""") val numberRegex = Regex("""[1-9]\d*(\.\d+)*""")
val chapterNumber = numberRegex.find(chapterText)?.value.orEmpty() val chapterNumber = numberRegex.find(chapterText)?.value.orEmpty()
val chapterTitle = titleWrapper.getElementsByTag("em").text() val chapterTitle = titleWrapper.getElementsByTag("em")!!.text()
chapter.name = "Chapter $chapterNumber : $chapterTitle" chapter.name = "Chapter $chapterNumber : $chapterTitle"
// Parse date // Parse date

View File

@ -105,7 +105,7 @@ class HenChan : MultiChan("HenChan", "http://y.hchan.live", "ru"), ConfigurableS
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = super.popularMangaFromElement(element) val manga = super.popularMangaFromElement(element)
manga.thumbnail_url = element.select("img").first().attr("src").getHQThumbnail() manga.thumbnail_url = element.select("img").first()!!.attr("src").getHQThumbnail()
return manga return manga
} }

View File

@ -40,12 +40,12 @@ class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") {
} }
override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply {
val comment = document.selectFirst(".book-title > h2").text() val comment = document.selectFirst(".book-title > h2")!!.text()
if (comment.isNotEmpty()) description = "$comment\n\n$description" if (comment.isNotEmpty()) description = "$comment\n\n$description"
} }
override fun mangaDetailsParseDefaultGenre(document: Document, detailsList: Element): String = override fun mangaDetailsParseDefaultGenre(document: Document, detailsList: Element): String =
document.selectFirst("div.crumb").select("a[href^=/list/]") document.selectFirst("div.crumb")!!.select("a[href^=/list/]")
.map { it.text().removeSuffix("").removeSuffix("漫画") } .map { it.text().removeSuffix("").removeSuffix("漫画") }
.filter { it.isNotEmpty() }.joinToString(", ") .filter { it.isNotEmpty() }.joinToString(", ")
@ -62,7 +62,7 @@ class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") {
override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers) override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val script = document.selectFirst("body > script").html() val script = document.selectFirst("body > script")!!.html()
val unpacked = Unpacker.unpack(script, ":[", "]") val unpacked = Unpacker.unpack(script, ":[", "]")
.ifEmpty { return emptyList() } .ifEmpty { return emptyList() }
.replace("\\", "") .replace("\\", "")
@ -85,7 +85,7 @@ class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") {
document.select(Evaluator.Class("filter")).forEach { row -> document.select(Evaluator.Class("filter")).forEach { row ->
val tags = row.select(linkSelector) val tags = row.select(linkSelector)
if (tags.isEmpty()) return@forEach if (tags.isEmpty()) return@forEach
val name = row.selectFirst(labelSelector).text().removeSuffix("") val name = row.selectFirst(labelSelector)!!.text().removeSuffix("")
if (!filterMap.containsKey(name)) { if (!filterMap.containsKey(name)) {
filterMap[name] = LinkedHashMap(tags.size * 2) filterMap[name] = LinkedHashMap(tags.size * 2)
} }

View File

@ -27,14 +27,14 @@ class TruyenChon : WPComics("TruyenChon", "http://truyenchon.com", "vi", SimpleD
title = it.text().replace("Truyện tranh ", "") title = it.text().replace("Truyện tranh ", "")
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
} }
thumbnail_url = imageOrNull(element.select("div.image:first-of-type img").first()) thumbnail_url = imageOrNull(element.select("div.image:first-of-type img").first()!!)
} }
} }
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
title = element.attr("title").replace("Truyện tranh ", "") title = element.attr("title").replace("Truyện tranh ", "")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))
thumbnail_url = imageOrNull(element.select("img").first()) thumbnail_url = imageOrNull(element.select("img").first()!!)
} }
} }
} }

View File

@ -13,7 +13,7 @@ class DatGarScanlation : ZeistManga("DatGarScanlation", "https://datgarscanlatio
var script = doc.selectFirst(scriptSelector) var script = doc.selectFirst(scriptSelector)
if (script == null) { if (script == null) {
script = doc.selectFirst(altScriptSelector) script = doc.selectFirst(altScriptSelector)!!
chapterRegex = altChapterFeedRegex chapterRegex = altChapterFeedRegex
} }

View File

@ -11,7 +11,7 @@ class MangaAiLand : ZeistManga("Manga Ai Land", "https://manga-ai-land.blogspot.
override val imgSelectorAttr = "href" override val imgSelectorAttr = "href"
override fun getChaptersUrl(doc: Document): String { override fun getChaptersUrl(doc: Document): String {
val script = doc.selectFirst(scriptSelector).attr("src") val script = doc.selectFirst(scriptSelector)!!.attr("src")
val feed = chapterFeedRegex val feed = chapterFeedRegex
.find(script) .find(script)
?.groupValues?.get(1) ?.groupValues?.get(1)

View File

@ -49,7 +49,7 @@ class SekteKomik : ZManga("Sekte Komik", "https://sektekomik.com", "id") {
override fun latestUpdatesFromElement(element: Element): SManga { override fun latestUpdatesFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
setUrlWithoutDomain(element.select("div.flexbox4-content a").attr("href")) setUrlWithoutDomain(element.select("div.flexbox4-content a").attr("href"))
title = element.select("div.flexbox4-side .title").first().text() title = element.select("div.flexbox4-side .title").first()!!.text()
thumbnail_url = element.select("img").attr("abs:src") thumbnail_url = element.select("img").attr("abs:src")
} }
} }
@ -79,7 +79,7 @@ class SekteKomik : ZManga("Sekte Komik", "https://sektekomik.com", "id") {
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
setUrlWithoutDomain(element.select("div.flexbox2-content a").attr("href")) setUrlWithoutDomain(element.select("div.flexbox2-content a").attr("href"))
title = element.select("div.flexbox2-title > span").first().text() title = element.select("div.flexbox2-title > span").first()!!.text()
thumbnail_url = element.select("img").attr("abs:src") thumbnail_url = element.select("img").attr("abs:src")
} }
} }

View File

@ -142,9 +142,9 @@ open class A3Manga(
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.select("a").attr("href")) setUrlWithoutDomain(element.select("a").attr("href"))
name = element.select("a .hidden-sm").text() name = element.select("a .hidden-sm").text()
date_upload = kotlin.runCatching { date_upload = runCatching {
dateFormat.parse(element.select("td").last().text())?.time dateFormat.parse(element.select("td").last()!!.text())?.time
}.getOrNull() ?: 0L }.getOrNull() ?: 0
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {

View File

@ -29,9 +29,9 @@ abstract class BakaManga(
".li_truyen" ".li_truyen"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a").absUrl("href")) setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.selectFirst(".name").text() title = element.selectFirst(".name")!!.text()
thumbnail_url = element.selectFirst("img").absUrl("src") thumbnail_url = element.selectFirst("img")!!.absUrl("src")
} }
override fun popularMangaNextPageSelector(): String? = override fun popularMangaNextPageSelector(): String? =
@ -47,7 +47,7 @@ abstract class BakaManga(
val filterList = if (filters.isEmpty()) getFilterList() else filters val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
val url = "$baseUrl/category/${genreFilter.toUriPart()}/page/$page" val url = "$baseUrl/category/${genreFilter.toUriPart()}/page/$page"
GET(url.toString(), headers) GET(url, headers)
} }
} }
@ -75,8 +75,8 @@ abstract class BakaManga(
// Details // Details
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
val info = document.selectFirst(".box_info") val info = document.selectFirst(".box_info")!!
title = info.selectFirst("h1").text() title = info.selectFirst("h1")!!.text()
artist = info.select(".info-item:contains(Artist:) > a").joinToString { it.text() } artist = info.select(".info-item:contains(Artist:) > a").joinToString { it.text() }
val descElements = info.select(".story-detail-info:matchText") val descElements = info.select(".story-detail-info:matchText")
@ -99,12 +99,12 @@ abstract class BakaManga(
} }
genre = info.select(".post-categories > li > a").joinToString { it.text() } genre = info.select(".post-categories > li > a").joinToString { it.text() }
status = info.selectFirst(".info-item:contains(Status:)").text() status = info.selectFirst(".info-item:contains(Status:)")!!.text()
.removePrefix("Status:") .removePrefix("Status:")
.trim() .trim()
.toStatus() .toStatus()
thumbnail_url = info.selectFirst(".box_info img").absUrl("src") thumbnail_url = info.selectFirst(".box_info img")!!.absUrl("src")
} }
// Chapters // Chapters
@ -115,14 +115,14 @@ abstract class BakaManga(
".list-chapters > .list-chapters > .box_list > .chapter-item" ".list-chapters > .list-chapters > .box_list > .chapter-item"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
setUrlWithoutDomain(element.selectFirst("a").absUrl("href")) setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
name = element.selectFirst(".chap_name").text() name = element.selectFirst(".chap_name")!!.text()
chapter_number = name chapter_number = name
.substringAfter(' ') .substringAfter(' ')
.substringBefore(' ') .substringBefore(' ')
.toFloatOrNull() ?: -1f .toFloatOrNull() ?: -1f
date_upload = parseRelativeDate(element.selectFirst(".chap_update").text()) date_upload = parseRelativeDate(element.selectFirst(".chap_update")!!.text())
} }
private fun parseRelativeDate(date: String): Long { private fun parseRelativeDate(date: String): Long {

View File

@ -28,16 +28,16 @@ open class ComicGamma(
override fun popularMangaNextPageSelector(): String? = null override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaSelector() = ".tab_panel.active .manga_item" override fun popularMangaSelector() = ".tab_panel.active .manga_item"
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
url = element.selectFirst(Evaluator.Tag("a")).attr("href") url = element.selectFirst(Evaluator.Tag("a"))!!.attr("href")
title = element.selectFirst(Evaluator.Class("manga_title")).text() title = element.selectFirst(Evaluator.Class("manga_title"))!!.text()
author = element.selectFirst(Evaluator.Class("manga_author")).text() author = element.selectFirst(Evaluator.Class("manga_author"))!!.text()
val genreList = element.select(Evaluator.Tag("li")).map { it.text() } val genreList = element.select(Evaluator.Tag("li")).map { it.text() }
genre = genreList.joinToString() genre = genreList.joinToString()
status = when { status = when {
genreList.contains("完結") && !genreList.contains("リピート配信") -> SManga.COMPLETED genreList.contains("完結") && !genreList.contains("リピート配信") -> SManga.COMPLETED
else -> SManga.ONGOING else -> SManga.ONGOING
} }
thumbnail_url = element.selectFirst(Evaluator.Tag("img")).absUrl("src") thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.absUrl("src")
} }
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.") override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.")
@ -60,7 +60,7 @@ open class ComicGamma(
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val titleElement = document.selectFirst(Evaluator.Class("manga__title")) val titleElement = document.selectFirst(Evaluator.Class("manga__title"))!!
val titleName = titleElement.child(0).text() val titleName = titleElement.child(0).text()
val desc = document.selectFirst(".detail__item > p:not(:empty)")?.run { val desc = document.selectFirst(".detail__item > p:not(:empty)")?.run {
select(Evaluator.Tag("br")).prepend("\\n") select(Evaluator.Tag("br")).prepend("\\n")
@ -81,7 +81,7 @@ open class ComicGamma(
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
url = element.attr("href").toOldChapterUrl() url = element.attr("href").toOldChapterUrl()
val number = url.removeSuffix("/").substringAfterLast('/').replace('_', '.') val number = url.removeSuffix("/").substringAfterLast('/').replace('_', '.')
val list = element.selectFirst(Evaluator.Class("read__contents")).children() val list = element.selectFirst(Evaluator.Class("read__contents"))!!.children()
name = "[$number] ${list[0].text()}" name = "[$number] ${list[0].text()}"
if (list.size >= 3) { if (list.size >= 3) {
date_upload = dateFormat.parseJST(list[2].text())?.time ?: 0L date_upload = dateFormat.parseJST(list[2].text())?.time ?: 0L
@ -98,7 +98,7 @@ open class ComicGamma(
time += 12 * 3600 * 1000 // updates at 12 noon time += 12 * 3600 * 1000 // updates at 12 noon
} }
internal fun getJSTFormat(datePattern: String) = private fun getJSTFormat(datePattern: String) =
SimpleDateFormat(datePattern, Locale.JAPANESE).apply { SimpleDateFormat(datePattern, Locale.JAPANESE).apply {
timeZone = TimeZone.getTimeZone("GMT+09:00") timeZone = TimeZone.getTimeZone("GMT+09:00")
} }

View File

@ -92,7 +92,7 @@ open class EroMuse(override val name: String, override val baseUrl: String) : Ht
protected fun parseManga(document: Document): MangasPage { protected fun parseManga(document: Document): MangasPage {
fun internalParse(internalDocument: Document): List<SManga> { fun internalParse(internalDocument: Document): List<SManga> {
val authorDocument = if (stackItem.pageType == VARIOUS_AUTHORS) { val authorDocument = if (stackItem.pageType == VARIOUS_AUTHORS) {
internalDocument.select(albumSelector)?.let { internalDocument.select(albumSelector).let {
elements -> elements ->
elements.reversed().map { pageStack.addLast(StackItem(it.attr("abs:href"), AUTHOR)) } elements.reversed().map { pageStack.addLast(StackItem(it.attr("abs:href"), AUTHOR)) }
} }
@ -107,7 +107,7 @@ open class EroMuse(override val name: String, override val baseUrl: String) : Ht
if (stackItem.pageType in listOf(VARIOUS_AUTHORS, SEARCH_RESULTS_OR_BASE)) document.addNextPageToStack() if (stackItem.pageType in listOf(VARIOUS_AUTHORS, SEARCH_RESULTS_OR_BASE)) document.addNextPageToStack()
val mangas = when (stackItem.pageType) { val mangas = when (stackItem.pageType) {
VARIOUS_AUTHORS -> { VARIOUS_AUTHORS -> {
document.select(albumSelector)?.let { document.select(albumSelector).let {
elements -> elements ->
elements.reversed().map { pageStack.addLast(StackItem(it.attr("abs:href"), AUTHOR)) } elements.reversed().map { pageStack.addLast(StackItem(it.attr("abs:href"), AUTHOR)) }
} }

View File

@ -109,7 +109,7 @@ abstract class FMReader(
val mangas = document.select(popularMangaSelector()).map { popularMangaFromElement(it) } val mangas = document.select(popularMangaSelector()).map { popularMangaFromElement(it) }
// check if there's a next page // check if there's a next page
val hasNextPage = (document.select(popularMangaNextPageSelector())?.first()?.text() ?: "").let { val hasNextPage = (document.select(popularMangaNextPageSelector()).first()?.text() ?: "").let {
if (it.contains(Regex("""\w*\s\d*\s\w*\s\d*"""))) { if (it.contains(Regex("""\w*\s\d*\s\w*\s\d*"""))) {
it.split(" ").let { pageOf -> pageOf[1] != pageOf[3] } // current page not last page it.split(" ").let { pageOf -> pageOf[1] != pageOf[3] } // current page not last page
} else { } else {
@ -134,7 +134,7 @@ abstract class FMReader(
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
element.select("$headerSelector").let { element.select(headerSelector).let {
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
title = it.text() title = it.text()
} }
@ -158,7 +158,7 @@ abstract class FMReader(
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.row").first() val infoElement = document.select("div.row").first()!!
return SManga.create().apply { return SManga.create().apply {
author = infoElement.select("li a.btn-info").eachText().filter { author = infoElement.select("li a.btn-info").eachText().filter {
@ -227,7 +227,7 @@ abstract class FMReader(
open fun chapterFromElement(element: Element, mangaTitle: String = ""): SChapter { open fun chapterFromElement(element: Element, mangaTitle: String = ""): SChapter {
return SChapter.create().apply { return SChapter.create().apply {
if (chapterUrlSelector != "") { if (chapterUrlSelector != "") {
element.select(chapterUrlSelector).first().let { element.select(chapterUrlSelector).first()!!.let {
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
name = it.text().substringAfter("$mangaTitle ") name = it.text().substringAfter("$mangaTitle ")
} }

View File

@ -65,7 +65,7 @@ abstract class FoolSlide(
} }
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
element.select("a[title]").first().let { element.select("a[title]").first()!!.let {
setUrlWithoutDomain(it.attr("href")) setUrlWithoutDomain(it.attr("href"))
title = it.text() title = it.text()
} }
@ -75,7 +75,7 @@ abstract class FoolSlide(
} }
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply { override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
element.select("a[title]").first().let { element.select("a[title]").first()!!.let {
setUrlWithoutDomain(it.attr("href")) setUrlWithoutDomain(it.attr("href"))
title = it.text() title = it.text()
} }
@ -95,7 +95,7 @@ abstract class FoolSlide(
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
element.select("a[title]").first().let { element.select("a[title]").first()!!.let {
manga.setUrlWithoutDomain(it.attr("href")) manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.text() manga.title = it.text()
} }
@ -111,7 +111,7 @@ abstract class FoolSlide(
// if there's no image on the details page, get the first page of the first chapter // if there's no image on the details page, get the first page of the first chapter
protected fun getDetailsThumbnail(document: Document, urlSelector: String = chapterUrlSelector): String? { protected fun getDetailsThumbnail(document: Document, urlSelector: String = chapterUrlSelector): String? {
return document.select("div.thumbnail img, table.thumb img").firstOrNull()?.attr("abs:src") return document.select("div.thumbnail img, table.thumb img").firstOrNull()?.attr("abs:src")
?: document.select(chapterListSelector()).last().select(urlSelector).attr("abs:href") ?: document.select(chapterListSelector()).last()!!.select(urlSelector).attr("abs:href")
.let { url -> client.newCall(allowAdult(GET(url))).execute() } .let { url -> client.newCall(allowAdult(GET(url))).execute() }
.let { response -> pageListParse(response).first().imageUrl } .let { response -> pageListParse(response).first().imageUrl }
} }
@ -142,13 +142,11 @@ abstract class FoolSlide(
protected open val chapterUrlSelector = "a[title]" protected open val chapterUrlSelector = "a[title]"
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElement = element.select(chapterUrlSelector).first() val urlElement = element.select(chapterUrlSelector).first()!!
val dateElement = element.select(chapterDateSelector).first() val dateElement = element.select(chapterDateSelector).first()!!
setUrlWithoutDomain(urlElement.attr("href")) setUrlWithoutDomain(urlElement.attr("href"))
name = urlElement.text() name = urlElement.text()
date_upload = dateElement.text()?.let { date_upload = parseChapterDate(dateElement.text().substringAfter(", ")) ?: 0
parseChapterDate(it.substringAfter(", "))
} ?: 0
} }
protected open fun parseChapterDate(date: String): Long? { protected open fun parseChapterDate(date: String): Long? {

View File

@ -105,11 +105,11 @@ abstract class Gattsu(
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
name = "Capítulo único" name = "Capítulo único"
scanlator = element.select("ul.post-itens li:contains(Tradutor) a").firstOrNull()?.text() scanlator = element.select("ul.post-itens li:contains(Tradutor) a").firstOrNull()?.text()
date_upload = element.ownerDocument().select("meta[property=article:published_time]").firstOrNull() date_upload = element.ownerDocument()!!.select("meta[property=article:published_time]").firstOrNull()
?.attr("content") ?.attr("content")
.orEmpty() .orEmpty()
.toDate() .toDate()
setUrlWithoutDomain(element.ownerDocument().location()) setUrlWithoutDomain(element.ownerDocument()!!.location())
} }
protected open fun pageListSelector(): String = protected open fun pageListSelector(): String =

View File

@ -63,11 +63,11 @@ open class Genkan(
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
element.select("a.list-title").first().let { element.select("a.list-title").first()!!.let {
manga.setUrlWithoutDomain(it.attr("href")) manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.text() manga.title = it.text()
} }
manga.thumbnail_url = styleToUrl(element.select("a.media-content").first()) manga.thumbnail_url = styleToUrl(element.select("a.media-content").first()!!)
return manga return manga
} }
@ -98,9 +98,9 @@ open class Genkan(
protected var countryOfOriginSelector = ".card.mt-2 .list-item:contains(Country of Origin) .no-wrap" protected var countryOfOriginSelector = ".card.mt-2 .list-item:contains(Country of Origin) .no-wrap"
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.select("div#content h5").first().text() title = document.select("div#content h5").first()!!.text()
description = document.select("div.col-lg-9").text().substringAfter("Description ").substringBefore(" Volume") description = document.select("div.col-lg-9").text().substringAfter("Description ").substringBefore(" Volume")
thumbnail_url = styleToUrl(document.select("div.media a").first()) thumbnail_url = styleToUrl(document.select("div.media a").first()!!)
genre = listOfNotNull( genre = listOfNotNull(
document.selectFirst(countryOfOriginSelector)?.let { countryOfOriginToSeriesType(it.text()) }, document.selectFirst(countryOfOriginSelector)?.let { countryOfOriginToSeriesType(it.text()) },
).joinToString() ).joinToString()
@ -119,7 +119,7 @@ open class Genkan(
} else { } else {
"Ch. $chapNum: ${urlElement.text()}" "Ch. $chapNum: ${urlElement.text()}"
} }
date_upload = parseChapterDate(element.select("a.item-company").first().text()) ?: 0 date_upload = parseChapterDate(element.select("a.item-company").first()!!.text()) ?: 0
} }
} }
@ -166,7 +166,7 @@ open class Genkan(
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
val allImages = document.select("div#pages-container + script").first().data() val allImages = document.select("div#pages-container + script").first()!!.data()
.substringAfter("[").substringBefore("];") .substringAfter("[").substringBefore("];")
.replace(Regex("""["\\]"""), "") .replace(Regex("""["\\]"""), "")
.split(",") .split(",")

View File

@ -49,7 +49,7 @@ open class GenkanOriginal(
// search the given document for matches // search the given document for matches
private fun getMatchesFrom(document: Document): MutableList<SManga> { private fun getMatchesFrom(document: Document): MutableList<SManga> {
val searchMatches = mutableListOf<SManga>() val searchMatches = mutableListOf<SManga>()
document.select(searchMangaSelector()) document.select(searchMangaSelector()).toList()
.filter { it.text().contains(searchQuery, ignoreCase = true) } .filter { it.text().contains(searchQuery, ignoreCase = true) }
.map { searchMatches.add(searchMangaFromElement(it)) } .map { searchMatches.add(searchMangaFromElement(it)) }

View File

@ -67,8 +67,8 @@ abstract class GigaViewer(
override fun popularMangaSelector(): String = "ul.series-list li a" override fun popularMangaSelector(): String = "ul.series-list li a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h2.series-list-title").text() title = element.selectFirst("h2.series-list-title")!!.text()
thumbnail_url = element.selectFirst("div.series-list-thumb img") thumbnail_url = element.selectFirst("div.series-list-thumb img")!!
.attr("data-src") .attr("data-src")
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))
} }
@ -114,9 +114,9 @@ abstract class GigaViewer(
override fun searchMangaSelector() = "ul.search-series-list li, ul.series-list li" override fun searchMangaSelector() = "ul.search-series-list li, ul.series-list li"
override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("div.title-box p.series-title").text() title = element.selectFirst("div.title-box p.series-title")!!.text()
thumbnail_url = element.selectFirst("div.thmb-container a img").attr("src") thumbnail_url = element.selectFirst("div.thmb-container a img")!!.attr("src")
setUrlWithoutDomain(element.selectFirst("div.thmb-container a").attr("href")) setUrlWithoutDomain(element.selectFirst("div.thmb-container a")!!.attr("href"))
} }
override fun searchMangaNextPageSelector(): String? = null override fun searchMangaNextPageSelector(): String? = null
@ -126,11 +126,11 @@ abstract class GigaViewer(
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
val infoElement = document.selectFirst(mangaDetailsInfoSelector())!! val infoElement = document.selectFirst(mangaDetailsInfoSelector())!!
title = infoElement.selectFirst("h1.series-header-title").text() title = infoElement.selectFirst("h1.series-header-title")!!.text()
author = infoElement.selectFirst("h2.series-header-author").text() author = infoElement.selectFirst("h2.series-header-author")!!.text()
artist = author artist = author
description = infoElement.selectFirst("p.series-header-description").text() description = infoElement.selectFirst("p.series-header-description")!!.text()
thumbnail_url = infoElement.selectFirst("div.series-header-image-wrapper img") thumbnail_url = infoElement.selectFirst("div.series-header-image-wrapper img")!!
.attr("data-src") .attr("data-src")
} }
@ -183,10 +183,10 @@ abstract class GigaViewer(
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val info = element.selectFirst("a.series-episode-list-container") ?: element val info = element.selectFirst("a.series-episode-list-container") ?: element
val mangaUrl = element.ownerDocument().location() val mangaUrl = element.ownerDocument()!!.location()
return SChapter.create().apply { return SChapter.create().apply {
name = info.selectFirst("h4.series-episode-list-title").text() name = info.selectFirst("h4.series-episode-list-title")!!.text()
if (chapterListMode == CHAPTER_LIST_PAID && element.selectFirst("span.series-episode-list-is-free") == null) { if (chapterListMode == CHAPTER_LIST_PAID && element.selectFirst("span.series-episode-list-is-free") == null) {
name = YEN_BANKNOTE + name name = YEN_BANKNOTE + name
} else if (chapterListMode == CHAPTER_LIST_LOCKED && element.hasClass("private")) { } else if (chapterListMode == CHAPTER_LIST_LOCKED && element.hasClass("private")) {
@ -201,7 +201,7 @@ abstract class GigaViewer(
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val episode = document.selectFirst("script#episode-json") val episode = document.selectFirst("script#episode-json")!!
.attr("data-value") .attr("data-value")
.let { .let {
try { try {

View File

@ -74,7 +74,7 @@ abstract class GroupLe(
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")?.replace("_p.", ".") manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")?.replace("_p.", ".")
element.select("h3 > a").first().let { element.select("h3 > a").first()!!.let {
manga.setUrlWithoutDomain(it.attr("href")) manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.attr("title") manga.title = it.attr("title")
} }
@ -103,11 +103,9 @@ abstract class GroupLe(
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select(".expandable").first() val infoElement = document.select(".expandable").first()!!
val rawCategory = infoElement.select("span.elem_category").text() val rawCategory = infoElement.select("span.elem_category").text()
val category = if (rawCategory.isNotEmpty()) { val category = rawCategory.ifEmpty {
rawCategory
} else {
"манга" "манга"
} }
@ -187,8 +185,8 @@ abstract class GroupLe(
override fun chapterListSelector() = "div.chapters-link > table > tbody > tr:has(td > a):has(td.date:not(.text-info))" override fun chapterListSelector() = "div.chapters-link > table > tbody > tr:has(td > a):has(td.date:not(.text-info))"
private fun chapterFromElement(element: Element, manga: SManga): SChapter { private fun chapterFromElement(element: Element, manga: SManga): SChapter {
val urlElement = element.select("a.chapter-link").first() val urlElement = element.select("a.chapter-link").first()!!
val chapterInf = element.select("td.item-title").first() val chapterInf = element.select("td.item-title").first()!!
val urlText = urlElement.text() val urlText = urlElement.text()
val chapter = SChapter.create() val chapter = SChapter.create()

View File

@ -55,10 +55,10 @@ open class Kemono(
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
val cardList = document.selectFirst(Evaluator.Class("card-list")) val cardList = document.selectFirst(Evaluator.Class("card-list"))!!
val creators = cardList.select(Evaluator.Tag("article")).map { val creators = cardList.select(Evaluator.Tag("article")).map {
val children = it.children() val children = it.children()
val avatar = children[0].selectFirst(Evaluator.Tag("img")).attr("src") val avatar = children[0].selectFirst(Evaluator.Tag("img"))!!.attr("src")
val link = children[1].child(0) val link = children[1].child(0)
val service = children[2].ownText() val service = children[2].ownText()
SManga.create().apply { SManga.create().apply {
@ -100,13 +100,13 @@ open class Kemono(
val baseUrl = baseUrl val baseUrl = baseUrl
return if (page == 1) { return if (page == 1) {
val document = client.newCall(GET(baseUrl + path, headers)).execute().asJsoup() val document = client.newCall(GET(baseUrl + path, headers)).execute().asJsoup()
val cardList = document.selectFirst(Evaluator.Class("card-list__items")) val cardList = document.selectFirst(Evaluator.Class("card-list__items"))!!
val creators = cardList.children().map { val creators = cardList.children().map {
SManga.create().apply { SManga.create().apply {
url = it.attr("href") url = it.attr("href")
title = it.selectFirst(Evaluator.Class("user-card__name")).ownText() title = it.selectFirst(Evaluator.Class("user-card__name"))!!.ownText()
author = it.selectFirst(Evaluator.Class("user-card__service")).ownText() author = it.selectFirst(Evaluator.Class("user-card__service"))!!.ownText()
thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img")).attr("src") thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img"))!!.attr("src")
description = PROMPT description = PROMPT
initialized = true initialized = true
} }
@ -227,7 +227,7 @@ open class Kemono(
private const val POST_PAGES_MAX = 50 private const val POST_PAGES_MAX = 50
private fun Element.hasNextPage(): Boolean { private fun Element.hasNextPage(): Boolean {
val pagination = selectFirst(Evaluator.Class("paginator")) val pagination = selectFirst(Evaluator.Class("paginator"))!!
return pagination.selectFirst("a[title=Next page]") != null return pagination.selectFirst("a[title=Next page]") != null
} }

View File

@ -207,7 +207,7 @@ abstract class LibGroup(
val manga = SManga.create() val manga = SManga.create()
val body = document.select("div.media-info-list").first() val body = document.select("div.media-info-list").first()!!
val rawCategory = document.select(".media-short-info a.media-short-info__item").text() val rawCategory = document.select(".media-short-info a.media-short-info__item").text()
val category = when { val category = when {
rawCategory == "Комикс западный" -> "Комикс" rawCategory == "Комикс западный" -> "Комикс"
@ -217,8 +217,8 @@ abstract class LibGroup(
val rawAgeStop = document.select(".media-short-info .media-short-info__item[data-caution]").text() val rawAgeStop = document.select(".media-short-info .media-short-info__item[data-caution]").text()
val ratingValue = document.select(".media-rating__value").last().text().toFloat() * 2 val ratingValue = document.select(".media-rating__value").last()!!.text().toFloat() * 2
val ratingVotes = document.select(".media-rating__votes").last().text() val ratingVotes = document.select(".media-rating__votes").last()!!.text()
val ratingStar = when { val ratingStar = when {
ratingValue > 9.5 -> "★★★★★" ratingValue > 9.5 -> "★★★★★"
ratingValue > 8.5 -> "★★★★✬" ratingValue > 8.5 -> "★★★★✬"
@ -242,19 +242,19 @@ abstract class LibGroup(
manga.author = body.select("div.media-info-list__title:contains(Автор) + div a").joinToString { it.text() } manga.author = body.select("div.media-info-list__title:contains(Автор) + div a").joinToString { it.text() }
manga.artist = body.select("div.media-info-list__title:contains(Художник) + div a").joinToString { it.text() } manga.artist = body.select("div.media-info-list__title:contains(Художник) + div a").joinToString { it.text() }
val StatusTranslate = body.select("div.media-info-list__title:contains(Статус перевода) + div").text().lowercase(Locale.ROOT) val statusTranslate = body.select("div.media-info-list__title:contains(Статус перевода) + div").text().lowercase(Locale.ROOT)
val StatusTitle = body.select("div.media-info-list__title:contains(Статус тайтла) + div").text().lowercase(Locale.ROOT) val statusTitle = body.select("div.media-info-list__title:contains(Статус тайтла) + div").text().lowercase(Locale.ROOT)
manga.status = if (document.html().contains("paper empty section") manga.status = if (document.html().contains("paper empty section")
) { ) {
SManga.LICENSED SManga.LICENSED
} else { } else {
when { when {
StatusTranslate.contains("завершен") && StatusTitle.contains("приостановлен") || StatusTranslate.contains("заморожен") || StatusTranslate.contains("заброшен") -> SManga.ON_HIATUS statusTranslate.contains("завершен") && statusTitle.contains("приостановлен") || statusTranslate.contains("заморожен") || statusTranslate.contains("заброшен") -> SManga.ON_HIATUS
StatusTranslate.contains("завершен") && StatusTitle.contains("выпуск прекращён") -> SManga.CANCELLED statusTranslate.contains("завершен") && statusTitle.contains("выпуск прекращён") -> SManga.CANCELLED
StatusTranslate.contains("продолжается") -> SManga.ONGOING statusTranslate.contains("продолжается") -> SManga.ONGOING
StatusTranslate.contains("завершен") -> SManga.COMPLETED statusTranslate.contains("завершен") -> SManga.COMPLETED
else -> when (StatusTitle) { else -> when (statusTitle) {
"онгоинг" -> SManga.ONGOING "онгоинг" -> SManga.ONGOING
"анонс" -> SManga.ONGOING "анонс" -> SManga.ONGOING
"завершён" -> SManga.COMPLETED "завершён" -> SManga.COMPLETED
@ -445,7 +445,7 @@ abstract class LibGroup(
val chapInfo = document val chapInfo = document
.select("script:containsData(window.__info)") .select("script:containsData(window.__info)")
.first() .first()!!
.html() .html()
.split("window.__info = ") .split("window.__info = ")
.last() .last()
@ -468,7 +468,7 @@ abstract class LibGroup(
// Get pages // Get pages
val pagesArr = document val pagesArr = document
.select("script:containsData(window.__pg)") .select("script:containsData(window.__pg)")
.first() .first()!!
.html() .html()
.trim() .trim()
.removePrefix("window.__pg = ") .removePrefix("window.__pg = ")

View File

@ -1156,8 +1156,8 @@ abstract class Madara(
.orEmpty() .orEmpty()
.map { li -> .map { li ->
Genre( Genre(
li.selectFirst("label").text(), li.selectFirst("label")!!.text(),
li.selectFirst("input[type=checkbox]").`val`(), li.selectFirst("input[type=checkbox]")!!.`val`(),
) )
} }
} }
@ -1196,7 +1196,7 @@ abstract class Madara(
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
val customUa = newValue as String val customUa = newValue as String
preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply() preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply()
if (customUa.isNullOrBlank()) { if (customUa.isBlank()) {
Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show() Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show()
} else { } else {
userAgent = null userAgent = null

View File

@ -176,7 +176,7 @@ abstract class MadTheme(
} }
// Try to show message/error from site // Try to show message/error from site
response.body?.let { body -> response.body.let { body ->
json.decodeFromString<JsonObject>(body.string())["message"] json.decodeFromString<JsonObject>(body.string())["message"]
?.jsonPrimitive ?.jsonPrimitive
?.content ?.content
@ -200,7 +200,7 @@ abstract class MadTheme(
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
// Not using setUrlWithoutDomain() to support external chapters // Not using setUrlWithoutDomain() to support external chapters
url = element.selectFirst("a") url = element.selectFirst("a")!!
.absUrl("href") .absUrl("href")
.removePrefix(baseUrl) .removePrefix(baseUrl)
@ -211,7 +211,7 @@ abstract class MadTheme(
// Pages // Pages
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val html = document.html()!! val html = document.html()
if (!html.contains("var mainServer = \"")) { if (!html.contains("var mainServer = \"")) {
// No fancy CDN, all images are available directly in <img> tags // No fancy CDN, all images are available directly in <img> tags

View File

@ -60,11 +60,11 @@ abstract class MangaBox(
protected fun mangaFromElement(element: Element, urlSelector: String = "h3 a"): SManga { protected fun mangaFromElement(element: Element, urlSelector: String = "h3 a"): SManga {
return SManga.create().apply { return SManga.create().apply {
element.select(urlSelector).first().let { element.select(urlSelector).first()!!.let {
url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain
title = it.text() title = it.text()
} }
thumbnail_url = element.select("img").first().attr("abs:src") thumbnail_url = element.select("img").first()!!.attr("abs:src")
} }
} }
@ -145,14 +145,14 @@ abstract class MangaBox(
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply { return SManga.create().apply {
document.select(mangaDetailsMainSelector).firstOrNull()?.let { infoElement -> document.select(mangaDetailsMainSelector).firstOrNull()?.let { infoElement ->
title = infoElement.select("h1, h2").first().text() title = infoElement.select("h1, h2").first()!!.text()
author = infoElement.select("li:contains(author) a, td:containsOwn(author) + td a").eachText().joinToString() author = infoElement.select("li:contains(author) a, td:containsOwn(author) + td a").eachText().joinToString()
status = parseStatus(infoElement.select("li:contains(status), td:containsOwn(status) + td").text()) status = parseStatus(infoElement.select("li:contains(status), td:containsOwn(status) + td").text())
genre = infoElement.select("div.manga-info-top li:contains(genres)").firstOrNull() genre = infoElement.select("div.manga-info-top li:contains(genres)").firstOrNull()
?.select("a")?.joinToString { it.text() } // kakalot ?.select("a")?.joinToString { it.text() } // kakalot
?: infoElement.select("td:containsOwn(genres) + td a").joinToString { it.text() } // nelo ?: infoElement.select("td:containsOwn(genres) + td a").joinToString { it.text() } // nelo
} ?: checkForRedirectMessage(document) } ?: checkForRedirectMessage(document)
description = document.select(descriptionSelector)?.firstOrNull()?.ownText() description = document.select(descriptionSelector).firstOrNull()?.ownText()
?.replace("""^$title summary:\s""".toRegex(), "") ?.replace("""^$title summary:\s""".toRegex(), "")
?.replace("""<\s*br\s*/?>""".toRegex(), "\n") ?.replace("""<\s*br\s*/?>""".toRegex(), "\n")
?.replace("<[^>]*>".toRegex(), "") ?.replace("<[^>]*>".toRegex(), "")
@ -201,7 +201,7 @@ abstract class MangaBox(
private fun Element.selectDateFromElement(): Element { private fun Element.selectDateFromElement(): Element {
val defaultChapterDateSelector = "span" val defaultChapterDateSelector = "span"
return this.select(defaultChapterDateSelector).lastOrNull() ?: this.select(alternateChapterDateSelector).last() return this.select(defaultChapterDateSelector).lastOrNull() ?: this.select(alternateChapterDateSelector).last()!!
} }
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {

View File

@ -46,27 +46,27 @@ abstract class MangabzTheme(
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup().also(::parseFilters) val document = response.asJsoup().also(::parseFilters)
val mangas = document.selectFirst(Evaluator.Class("mh-list")).children().map { element -> val mangas = document.selectFirst(Evaluator.Class("mh-list"))!!.children().map { element ->
SManga.create().apply { SManga.create().apply {
title = element.selectFirst(Evaluator.Tag("h2")).text() title = element.selectFirst(Evaluator.Tag("h2"))!!.text()
url = element.selectFirst(Evaluator.Tag("a")).attr("href") url = element.selectFirst(Evaluator.Tag("a"))!!.attr("href")
thumbnail_url = element.selectFirst(Evaluator.Tag("img")).attr("src") thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.attr("src")
} }
} }
val hasNextPage = document.run { val hasNextPage = document.run {
val pagination = selectFirst(Evaluator.Class("page-pagination")) val pagination = selectFirst(Evaluator.Class("page-pagination"))
pagination != null && pagination.select(Evaluator.Tag("a")).last().text() == ">" pagination != null && pagination.select(Evaluator.Tag("a")).last()!!.text() == ">"
} }
return MangasPage(mangas, hasNextPage) return MangasPage(mangas, hasNextPage)
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup() val document = response.asJsoup()
val details = document.selectFirst(Evaluator.Class("detail-info-tip")).children()!! val details = document.selectFirst(Evaluator.Class("detail-info-tip"))!!.children()
return SManga.create().apply { return SManga.create().apply {
url = document.location().removePrefix(baseUrl) url = document.location().removePrefix(baseUrl)
title = document.selectFirst(Evaluator.Class("detail-info-title")).ownText() title = document.selectFirst(Evaluator.Class("detail-info-title"))!!.ownText()
thumbnail_url = document.selectFirst(Evaluator.Class("detail-info-cover")).attr("src") thumbnail_url = document.selectFirst(Evaluator.Class("detail-info-cover"))!!.attr("src")
status = when (details[1].child(0).ownText()) { status = when (details[1].child(0).ownText()) {
"连载中" -> SManga.ONGOING "连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED "已完结" -> SManga.COMPLETED
@ -76,7 +76,7 @@ abstract class MangabzTheme(
} }
author = details[0].children().joinToString { it.ownText() } author = details[0].children().joinToString { it.ownText() }
genre = details[2].children().joinToString { it.ownText() } genre = details[2].children().joinToString { it.ownText() }
description = parseDescription(document.selectFirst(Evaluator.Class("detail-info-content")), title, details) description = parseDescription(document.selectFirst(Evaluator.Class("detail-info-content"))!!, title, details)
initialized = true initialized = true
} }
} }
@ -103,7 +103,7 @@ abstract class MangabzTheme(
} }
if (list.isEmpty()) return emptyList() if (list.isEmpty()) return emptyList()
val listTitle = document.selectFirst(Evaluator.Class("detail-list-form-title")).ownText() val listTitle = document.selectFirst(Evaluator.Class("detail-list-form-title"))!!.ownText()
try { try {
list[0].date_upload = parseDate(listTitle) list[0].date_upload = parseDate(listTitle)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -113,7 +113,7 @@ abstract class MangabzTheme(
} }
protected open fun getChapterElements(document: Document): Elements = protected open fun getChapterElements(document: Document): Elements =
document.selectFirst(Evaluator.Id("chapterlistload")).children() document.selectFirst(Evaluator.Id("chapterlistload"))!!.children()
protected open val needPageCount = true protected open val needPageCount = true

View File

@ -171,14 +171,14 @@ abstract class MangaHub(
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.title = document.select(".breadcrumb .active span").text() manga.title = document.select(".breadcrumb .active span").text()
manga.author = document.select("div:has(h1) span:contains(Author) + span")?.first()?.text() manga.author = document.select("div:has(h1) span:contains(Author) + span").first()?.text()
manga.artist = document.select("div:has(h1) span:contains(Artist) + span")?.first()?.text() manga.artist = document.select("div:has(h1) span:contains(Artist) + span").first()?.text()
manga.genre = document.select(".row p a")?.joinToString { it.text() } manga.genre = document.select(".row p a").joinToString { it.text() }
manga.description = document.select(".tab-content p")?.first()?.text() manga.description = document.select(".tab-content p").first()?.text()
manga.thumbnail_url = document.select("img.img-responsive")?.first() manga.thumbnail_url = document.select("img.img-responsive").first()
?.attr("src") ?.attr("src")
document.select("div:has(h1) span:contains(Status) + span")?.first()?.text()?.also { statusText -> document.select("div:has(h1) span:contains(Status) + span").first()?.text()?.also { statusText ->
when { when {
statusText.contains("ongoing", true) -> manga.status = SManga.ONGOING statusText.contains("ongoing", true) -> manga.status = SManga.ONGOING
statusText.contains("completed", true) -> manga.status = SManga.COMPLETED statusText.contains("completed", true) -> manga.status = SManga.COMPLETED

View File

@ -78,7 +78,7 @@ abstract class MangaMainac(
override fun chapterListSelector() = "table.chap_tab tr" override fun chapterListSelector() = "table.chap_tab tr"
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a").first() val urlElement = element.select("a").first()!!
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href")) chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = element.select("a").text() chapter.name = element.select("a").text()

View File

@ -22,8 +22,8 @@ abstract class MangaRawTheme(
protected abstract fun String.sanitizeTitle(): String protected abstract fun String.sanitizeTitle(): String
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst(Evaluator.Tag("a")).attr("href")) setUrlWithoutDomain(element.selectFirst(Evaluator.Tag("a"))!!.attr("href"))
title = element.selectFirst(Evaluator.Tag("h3")).text().sanitizeTitle() title = element.selectFirst(Evaluator.Tag("h3"))!!.text().sanitizeTitle()
thumbnail_url = element.selectFirst(Evaluator.Tag("img"))?.absUrl("data-src") thumbnail_url = element.selectFirst(Evaluator.Tag("img"))?.absUrl("data-src")
} }
@ -69,7 +69,7 @@ abstract class MangaRawTheme(
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val imgSelector = Evaluator.Tag("img") val imgSelector = Evaluator.Tag("img")
return document.select(pageSelector()).mapIndexed { index, element -> return document.select(pageSelector()).mapIndexed { index, element ->
Page(index, imageUrl = element.selectFirst(imgSelector).attr("data-src")) Page(index, imageUrl = element.selectFirst(imgSelector)!!.attr("data-src"))
} }
} }

View File

@ -226,7 +226,7 @@ abstract class MangaThemesia(
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a") val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href")) setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".lch a, .chapternum").text().ifBlank { urlElements.first().text() } name = element.select(".lch a, .chapternum").text().ifBlank { urlElements.first()!!.text() }
date_upload = element.selectFirst(".chapterdate")?.text().parseChapterDate() date_upload = element.selectFirst(".chapterdate")?.text().parseChapterDate()
} }
@ -452,8 +452,8 @@ abstract class MangaThemesia(
private fun parseGenres(document: Document): List<Genre>? { private fun parseGenres(document: Document): List<Genre>? {
return document.selectFirst("ul.genrez")?.select("li")?.map { li -> return document.selectFirst("ul.genrez")?.select("li")?.map { li ->
Genre( Genre(
li.selectFirst("label").text(), li.selectFirst("label")!!.text(),
li.selectFirst("input[type=checkbox]").attr("value"), li.selectFirst("input[type=checkbox]")!!.attr("value"),
) )
} }
} }
@ -464,7 +464,7 @@ abstract class MangaThemesia(
else -> attr("abs:src") else -> attr("abs:src")
} }
protected open fun Elements.imgAttr(): String = this.first().imgAttr() protected open fun Elements.imgAttr(): String = this.first()!!.imgAttr()
// Unused // Unused
override fun popularMangaSelector(): String = throw UnsupportedOperationException("Not used") override fun popularMangaSelector(): String = throw UnsupportedOperationException("Not used")

View File

@ -51,7 +51,7 @@ abstract class MangaWorld(
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = element.select("a.thumb img").attr("src") manga.thumbnail_url = element.select("a.thumb img").attr("src")
element.select("a").first().let { element.select("a").first()!!.let {
manga.setUrlWithoutDomain(it.attr("href").removeSuffix("/")) manga.setUrlWithoutDomain(it.attr("href").removeSuffix("/"))
manga.title = it.attr("title") manga.title = it.attr("title")
} }

View File

@ -28,7 +28,7 @@ open class MCCMSNsfw(
GET(baseUrl + chapter.url, headers) GET(baseUrl + chapter.url, headers)
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val container = response.asJsoup().selectFirst(Evaluator.Class("comic-list")) val container = response.asJsoup().selectFirst(Evaluator.Class("comic-list"))!!
return container.select(Evaluator.Tag("img")).mapIndexed { index, img -> return container.select(Evaluator.Tag("img")).mapIndexed { index, img ->
Page(index, imageUrl = img.attr("src")) Page(index, imageUrl = img.attr("src"))
} }

View File

@ -24,14 +24,14 @@ open class MCCMSWeb(
fun parseListing(document: Document): MangasPage { fun parseListing(document: Document): MangasPage {
val mangas = document.select(Evaluator.Class("common-comic-item")).map { val mangas = document.select(Evaluator.Class("common-comic-item")).map {
SManga.create().apply { SManga.create().apply {
val titleElement = it.selectFirst(Evaluator.Class("comic__title")).child(0) val titleElement = it.selectFirst(Evaluator.Class("comic__title"))!!.child(0)
url = titleElement.attr("href") url = titleElement.attr("href")
title = titleElement.ownText() title = titleElement.ownText()
thumbnail_url = it.selectFirst(Evaluator.Tag("img")).attr("data-original") thumbnail_url = it.selectFirst(Evaluator.Tag("img"))!!.attr("data-original")
}.cleanup() }.cleanup()
} }
val hasNextPage = run { // default pagination val hasNextPage = run { // default pagination
val buttons = document.selectFirst(Evaluator.Id("Pagination")).select(Evaluator.Tag("a")) val buttons = document.selectFirst(Evaluator.Id("Pagination"))!!.select(Evaluator.Tag("a"))
val count = buttons.size val count = buttons.size
// Next page != Last page // Next page != Last page
buttons[count - 1].attr("href") != buttons[count - 2].attr("href") buttons[count - 1].attr("href") != buttons[count - 2].attr("href")
@ -85,12 +85,12 @@ open class MCCMSWeb(
if (manga.url == "/index.php/search") return Observable.just(manga) if (manga.url == "/index.php/search") return Observable.just(manga)
return client.newCall(GET(baseUrl + manga.url, pcHeaders)).asObservableSuccess().map { response -> return client.newCall(GET(baseUrl + manga.url, pcHeaders)).asObservableSuccess().map { response ->
SManga.create().apply { SManga.create().apply {
val document = response.asJsoup().selectFirst(Evaluator.Class("de-info__box")) val document = response.asJsoup().selectFirst(Evaluator.Class("de-info__box"))!!
title = document.selectFirst(Evaluator.Class("comic-title")).ownText() title = document.selectFirst(Evaluator.Class("comic-title"))!!.ownText()
thumbnail_url = document.selectFirst(Evaluator.Tag("img")).attr("src") thumbnail_url = document.selectFirst(Evaluator.Tag("img"))!!.attr("src")
author = document.selectFirst(Evaluator.Class("name")).text() author = document.selectFirst(Evaluator.Class("name"))!!.text()
genre = document.selectFirst(Evaluator.Class("comic-status")).select(Evaluator.Tag("a")).joinToString { it.ownText() } genre = document.selectFirst(Evaluator.Class("comic-status"))!!.select(Evaluator.Tag("a")).joinToString { it.ownText() }
description = document.selectFirst(Evaluator.Class("intro-total")).text() description = document.selectFirst(Evaluator.Class("intro-total"))!!.text()
}.cleanup() }.cleanup()
} }
} }
@ -98,7 +98,7 @@ open class MCCMSWeb(
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
if (manga.url == "/index.php/search") return Observable.just(emptyList()) if (manga.url == "/index.php/search") return Observable.just(emptyList())
return client.newCall(GET(baseUrl + manga.url, pcHeaders)).asObservableSuccess().map { response -> return client.newCall(GET(baseUrl + manga.url, pcHeaders)).asObservableSuccess().map { response ->
response.asJsoup().selectFirst(Evaluator.Class("chapter__list-box")).children().map { response.asJsoup().selectFirst(Evaluator.Class("chapter__list-box"))!!.children().map {
val link = it.child(0) val link = it.child(0)
SChapter.create().apply { SChapter.create().apply {
url = link.attr("href") url = link.attr("href")

View File

@ -35,10 +35,10 @@ abstract class MDB(
override fun popularMangaRequest(page: Int) = GET(listUrl("page-$page"), headers) override fun popularMangaRequest(page: Int) = GET(listUrl("page-$page"), headers)
override fun popularMangaSelector() = "div.comic-main-section > div.comic-book-unit" override fun popularMangaSelector() = "div.comic-main-section > div.comic-book-unit"
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
val link = element.selectFirst("h2 > a") val link = element.selectFirst("h2 > a")!!
setUrlWithoutDomain(link.attr("href")) setUrlWithoutDomain(link.attr("href"))
title = link.text() title = link.text()
thumbnail_url = element.selectFirst(Evaluator.Tag("img")).absUrl("src") thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.absUrl("src")
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
@ -71,22 +71,22 @@ abstract class MDB(
protected open fun transformDescription(description: String) = description protected open fun transformDescription(description: String) = description
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.selectFirst(Evaluator.Tag("h1")).text().let { transformTitle(it) } title = transformTitle(document.selectFirst(Evaluator.Tag("h1"))!!.text())
author = document.selectFirst(authorSelector).text() author = document.selectFirst(authorSelector)!!.text()
description = document.selectFirst("p.comic_story").text().let { transformDescription(it) } description = transformDescription(document.selectFirst("p.comic_story")!!.text())
genre = parseGenre(document).joinToString(", ") genre = parseGenre(document).joinToString(", ")
status = when (document.selectFirst("a.comic-pub-state").text()) { status = when (document.selectFirst("a.comic-pub-state")!!.text()) {
"连载中" -> SManga.ONGOING "连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED "已完结" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
thumbnail_url = document.selectFirst("td.comic-cover > img").absUrl("src") thumbnail_url = document.selectFirst("td.comic-cover > img")!!.absUrl("src")
} }
protected open fun parseGenre(document: Document): List<String> { protected open fun parseGenre(document: Document): List<String> {
val list = mutableListOf<String>() val list = mutableListOf<String>()
list.add(document.selectFirst("th:contains(地区) + td").text()) list.add(document.selectFirst("th:contains(地区) + td")!!.text())
list.add(document.selectFirst("th:contains(面向读者) + td").text().removeSuffix("漫画")) list.add(document.selectFirst("th:contains(面向读者) + td")!!.text().removeSuffix("漫画"))
val tags = document.select("ul.tags > li > a") val tags = document.select("ul.tags > li > a")
for (i in 1 until tags.size) { // skip status for (i in 1 until tags.size) { // skip status
list.add(tags[i].text()) list.add(tags[i].text())
@ -101,12 +101,12 @@ abstract class MDB(
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val imgData = document.selectFirst("body > script:containsData(img_data)").data() val imgData = document.selectFirst("body > script:containsData(img_data)")!!.data()
.substringAfter("img_data = ").run { .substringAfter("img_data = ").run {
val endIndex = indexOf(this[0], startIndex = 1) // find end quote val endIndex = indexOf(this[0], startIndex = 1) // find end quote
substring(1, endIndex) substring(1, endIndex)
} }
val readerConfig = document.selectFirst(Evaluator.Class("vg-r-data")) val readerConfig = document.selectFirst(Evaluator.Class("vg-r-data"))!!
return parseImages(imgData, readerConfig).mapIndexed { i, it -> return parseImages(imgData, readerConfig).mapIndexed { i, it ->
Page(i, imageUrl = it) Page(i, imageUrl = it)
} }
@ -143,7 +143,7 @@ abstract class MDB(
values.add(link.text()) values.add(link.text())
params.add(link.attr("href").let(::extractParams).let(::parseParam)) params.add(link.attr("href").let(::extractParams).let(::parseParam))
} }
val name = children[0].selectFirst(Evaluator.Tag("span")).text() val name = children[0].selectFirst(Evaluator.Tag("span"))!!.text()
list.add(Category(name, values.toTypedArray(), params)) list.add(Category(name, values.toTypedArray(), params))
} else if (filterContainer.hasClass("form-row")) { // Dropdown filter } else if (filterContainer.hasClass("form-row")) { // Dropdown filter
for (select in filterContainer.select(Evaluator.Tag("select"))) { for (select in filterContainer.select(Evaluator.Tag("select"))) {

View File

@ -134,7 +134,7 @@ abstract class MMRCMS(
return client.newCall(GET("$baseUrl/changeMangaList?type=text", headers)) return client.newCall(GET("$baseUrl/changeMangaList?type=text", headers))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .map { response ->
val mangas = response.asJsoup().select("ul.manga-list a") val mangas = response.asJsoup().select("ul.manga-list a").toList()
.filter { it.text().contains(query, ignoreCase = true) } .filter { it.text().contains(query, ignoreCase = true) }
.map { .map {
SManga.create().apply { SManga.create().apply {
@ -198,7 +198,7 @@ abstract class MMRCMS(
private fun latestUpdatesSelector() = "div.mangalist div.manga-item" private fun latestUpdatesSelector() = "div.mangalist div.manga-item"
private fun latestUpdatesNextPageSelector() = "a[rel=next]" private fun latestUpdatesNextPageSelector() = "a[rel=next]"
protected open fun latestUpdatesFromElement(element: Element, urlSelector: String): SManga? { protected open fun latestUpdatesFromElement(element: Element, urlSelector: String): SManga? {
return element.select(urlSelector).first().let { titleElement -> return element.select(urlSelector).first()!!.let { titleElement ->
if (titleElement.text() in latestTitles) { if (titleElement.text() in latestTitles) {
null null
} else { } else {
@ -305,12 +305,12 @@ abstract class MMRCMS(
for (element in document.select(".row .dl-horizontal dt")) { for (element in document.select(".row .dl-horizontal dt")) {
when (element.text().trim().lowercase().removeSuffix(":")) { when (element.text().trim().lowercase().removeSuffix(":")) {
in detailAuthor -> author = element.nextElementSibling().text() in detailAuthor -> author = element.nextElementSibling()!!.text()
in detailArtist -> artist = element.nextElementSibling().text() in detailArtist -> artist = element.nextElementSibling()!!.text()
in detailGenre -> genre = element.nextElementSibling().select("a").joinToString { in detailGenre -> genre = element.nextElementSibling()!!.select("a").joinToString {
it.text().trim() it.text().trim()
} }
in detailStatus -> status = when (element.nextElementSibling().text().trim().lowercase()) { in detailStatus -> status = when (element.nextElementSibling()!!.text().trim().lowercase()) {
in detailStatusComplete -> SManga.COMPLETED in detailStatusComplete -> SManga.COMPLETED
in detailStatusOngoing -> SManga.ONGOING in detailStatusOngoing -> SManga.ONGOING
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
@ -367,7 +367,7 @@ abstract class MMRCMS(
val chapter = SChapter.create() val chapter = SChapter.create()
try { try {
val titleWrapper = element.select("[class^=chapter-title-rtl]").first() val titleWrapper = element.select("[class^=chapter-title-rtl]").first()!!
// Some websites add characters after "..-rtl" thus the need of checking classes that starts with that // Some websites add characters after "..-rtl" thus the need of checking classes that starts with that
val url = titleWrapper.getElementsByTag("a") val url = titleWrapper.getElementsByTag("a")
.first { it.attr("href").contains(urlRegex) } .first { it.attr("href").contains(urlRegex) }

View File

@ -41,9 +41,9 @@ abstract class MultiChan(
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = element.select("img").first().attr("src") manga.thumbnail_url = element.select("img").first()!!.attr("src")
manga.title = element.attr("title") manga.title = element.attr("title")
element.select("h2 > a").first().let { element.select("h2 > a").first()!!.let {
manga.setUrlWithoutDomain(it.attr("href")) manga.setUrlWithoutDomain(it.attr("href"))
} }
return manga return manga
@ -72,10 +72,10 @@ abstract class MultiChan(
val nextSearchPage = document.select(searchMangaNextPageSelector()) val nextSearchPage = document.select(searchMangaNextPageSelector())
if (nextSearchPage.isNotEmpty()) { if (nextSearchPage.isNotEmpty()) {
val query = document.select("input#searchinput").first().attr("value") val query = document.select("input#searchinput").first()!!.attr("value")
val pageNum = nextSearchPage.let { selector -> val pageNum = nextSearchPage.let { selector ->
val onClick = selector.attr("onclick") val onClick = selector.attr("onclick")
onClick?.split("""\\d+""") onClick.split("""\\d+""")
} }
nextSearchPage.attr("href", "$baseUrl/?do=search&subaction=search&story=$query&search_start=$pageNum") nextSearchPage.attr("href", "$baseUrl/?do=search&subaction=search&story=$query&search_start=$pageNum")
hasNextPage = true hasNextPage = true
@ -91,7 +91,7 @@ abstract class MultiChan(
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("#info_wrap tr,#info_wrap > div") val infoElement = document.select("#info_wrap tr,#info_wrap > div")
val descElement = document.select("div#description").first() val descElement = document.select("div#description").first()!!
val rawCategory = infoElement.select(":contains(Тип) a").text().lowercase() val rawCategory = infoElement.select(":contains(Тип) a").text().lowercase()
val manga = SManga.create() val manga = SManga.create()
manga.title = document.select("title").text().substringBefore(" »") manga.title = document.select("title").text().substringBefore(" »")
@ -99,7 +99,7 @@ abstract class MultiChan(
manga.genre = rawCategory + ", " + document.select(".sidetags ul a:last-child").joinToString { it.text() } manga.genre = rawCategory + ", " + document.select(".sidetags ul a:last-child").joinToString { it.text() }
manga.status = parseStatus(infoElement.select(":contains(Загружено)").text()) manga.status = parseStatus(infoElement.select(":contains(Загружено)").text())
manga.description = descElement.textNodes().first().text().trim() manga.description = descElement.textNodes().first().text().trim()
manga.thumbnail_url = document.select("img#cover").first().attr("src") manga.thumbnail_url = document.select("img#cover").first()!!.attr("src")
return manga return manga
} }
@ -112,13 +112,13 @@ abstract class MultiChan(
override fun chapterListSelector() = "table.table_cha tr:gt(1)" override fun chapterListSelector() = "table.table_cha tr:gt(1)"
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a").first() val urlElement = element.select("a").first()!!
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href")) chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text() chapter.name = urlElement.text()
chapter.chapter_number = "(глава\\s|часть\\s)([0-9]+\\.?[0-9]*)".toRegex(RegexOption.IGNORE_CASE).find(chapter.name)?.groupValues?.get(2)?.toFloat() ?: -1F chapter.chapter_number = "(глава\\s|часть\\s)([0-9]+\\.?[0-9]*)".toRegex(RegexOption.IGNORE_CASE).find(chapter.name)?.groupValues?.get(2)?.toFloat() ?: -1F
chapter.date_upload = simpleDateFormat.parse(element.select("div.date").first().text())?.time ?: 0L chapter.date_upload = simpleDateFormat.parse(element.select("div.date").first()!!.text())?.time ?: 0L
return chapter return chapter
} }

View File

@ -86,9 +86,9 @@ abstract class MyMangaCMS(
"div.pagination_wrap a.paging_item:last-of-type:not(.disabled)" "div.pagination_wrap a.paging_item:last-of-type:not(.disabled)"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
setUrlWithoutDomain(element.select("a").first().attr("abs:href")) setUrlWithoutDomain(element.select("a").first()!!.attr("abs:href"))
title = element.select("div.thumb_attr.series-title a[title]").first().text() title = element.select("div.thumb_attr.series-title a[title]").first()!!.text()
thumbnail_url = element.select("div[data-bg]").first().attr("data-bg") thumbnail_url = element.select("div[data-bg]").first()!!.attr("data-bg")
} }
//endregion //endregion
@ -179,10 +179,10 @@ abstract class MyMangaCMS(
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
setUrlWithoutDomain( setUrlWithoutDomain(
document.select(".series-name-group a") document.select(".series-name-group a")
.first() .first()!!
.attr("abs:href"), .attr("abs:href"),
) )
title = document.select(".series-name").first().text().trim() title = document.select(".series-name").first()!!.text().trim()
var alternativeNames: String? = null var alternativeNames: String? = null
document.select(".info-item").forEach { document.select(".info-item").forEach {
@ -194,7 +194,7 @@ abstract class MyMangaCMS(
"Tác giả:" -> author = value.joinToString(", ") { auth -> "Tác giả:" -> author = value.joinToString(", ") { auth ->
auth.text().trim() auth.text().trim()
} }
"Tình trạng:" -> status = when (value.first().text().lowercase().trim()) { "Tình trạng:" -> status = when (value.first()!!.text().lowercase().trim()) {
"đang tiến hành" -> SManga.ONGOING "đang tiến hành" -> SManga.ONGOING
"tạm ngưng" -> SManga.ON_HIATUS "tạm ngưng" -> SManga.ON_HIATUS
"đã hoàn thành" -> SManga.COMPLETED "đã hoàn thành" -> SManga.COMPLETED
@ -226,7 +226,7 @@ abstract class MyMangaCMS(
thumbnail_url = document thumbnail_url = document
.select("div.content.img-in-ratio") .select("div.content.img-in-ratio")
.first() .first()!!
.attr("style") .attr("style")
.let { backgroundImageRegex.find(it)?.groups?.get(1)?.value } .let { backgroundImageRegex.find(it)?.groups?.get(1)?.value }
} }
@ -243,9 +243,9 @@ abstract class MyMangaCMS(
private fun chapterFromElement(element: Element, scanlator: String?): SChapter = private fun chapterFromElement(element: Element, scanlator: String?): SChapter =
SChapter.create().apply { SChapter.create().apply {
setUrlWithoutDomain(element.attr("abs:href")) setUrlWithoutDomain(element.attr("abs:href"))
name = element.select("div.chapter-name").first().text() name = element.select("div.chapter-name").first()!!.text()
date_upload = dateUpdatedParser( date_upload = dateUpdatedParser(
element.select("div.chapter-time").first().text(), element.select("div.chapter-time").first()!!.text(),
) )
val match = floatingNumberRegex.find(name) val match = floatingNumberRegex.find(name)
@ -262,11 +262,11 @@ abstract class MyMangaCMS(
val document = response.asJsoup() val document = response.asJsoup()
val originalScanlator = document.select("div.fantrans-value a") val originalScanlator = document.select("div.fantrans-value a")
val scanlator: String? = if (originalScanlator.isEmpty() || val scanlator: String? = if (originalScanlator.isEmpty() ||
originalScanlator.first().text().trim().lowercase() == "đang cập nhật" originalScanlator.first()!!.text().trim().lowercase() == "đang cập nhật"
) { ) {
null null
} else { } else {
originalScanlator.first().text().trim() originalScanlator.first()!!.text().trim()
} }
return document.select(chapterListSelector()).map { chapterFromElement(it, scanlator) } return document.select(chapterListSelector()).map { chapterFromElement(it, scanlator) }

View File

@ -77,7 +77,7 @@ abstract class NepNep(
// don't use ";" for substringBefore() ! // don't use ";" for substringBefore() !
private fun directoryFromDocument(document: Document): JsonArray { private fun directoryFromDocument(document: Document): JsonArray {
val str = document.select("script:containsData(MainFunction)").first().data() val str = document.select("script:containsData(MainFunction)").first()!!.data()
.substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim() .substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim()
.replace(";", " ") .replace(";", " ")
return json.parseToJsonElement(str).jsonArray return json.parseToJsonElement(str).jsonArray
@ -85,7 +85,7 @@ abstract class NepNep(
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
thumbnailUrl = document.select(".SearchResult > .SearchResultCover img").first().attr("ng-src") thumbnailUrl = document.select(".SearchResult > .SearchResultCover img").first()!!.attr("ng-src")
directory = directoryFromDocument(document).sortedByDescending { it.getString("v") } directory = directoryFromDocument(document).sortedByDescending { it.getString("v") }
return parseDirectory(1) return parseDirectory(1)
} }
@ -111,7 +111,7 @@ abstract class NepNep(
private fun getThumbnailUrl(id: String): String { private fun getThumbnailUrl(id: String): String {
if (thumbnailUrl.isNullOrEmpty()) { if (thumbnailUrl.isNullOrEmpty()) {
val response = client.newCall(popularMangaRequest(1)).execute() val response = client.newCall(popularMangaRequest(1)).execute()
thumbnailUrl = response.asJsoup().select(".SearchResult > .SearchResultCover img").first().attr("ng-src") thumbnailUrl = response.asJsoup().select(".SearchResult > .SearchResultCover img").first()!!.attr("ng-src")
} }
return thumbnailUrl!!.replace("{{Result.i}}", id) return thumbnailUrl!!.replace("{{Result.i}}", id)
@ -308,7 +308,7 @@ abstract class NepNep(
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val vmChapters = response.asJsoup().select("script:containsData(MainFunction)").first().data() val vmChapters = response.asJsoup().select("script:containsData(MainFunction)").first()!!.data()
.substringAfter("vm.Chapters = ").substringBefore(";") .substringAfter("vm.Chapters = ").substringBefore(";")
return json.parseToJsonElement(vmChapters).jsonArray.map { json -> return json.parseToJsonElement(vmChapters).jsonArray.map { json ->
val indexChapter = json.getString("Chapter")!! val indexChapter = json.getString("Chapter")!!
@ -330,7 +330,7 @@ abstract class NepNep(
val document = response.asJsoup() val document = response.asJsoup()
val script = document.selectFirst("script:containsData(MainFunction)")?.data() val script = document.selectFirst("script:containsData(MainFunction)")?.data()
?: client.newCall(GET(document.location().removeSuffix(".html"), headers)) ?: client.newCall(GET(document.location().removeSuffix(".html"), headers))
.execute().asJsoup().selectFirst("script:containsData(MainFunction)").data() .execute().asJsoup().selectFirst("script:containsData(MainFunction)")!!.data()
val curChapter = json.parseToJsonElement(script!!.substringAfter("vm.CurChapter = ").substringBefore(";")).jsonObject val curChapter = json.parseToJsonElement(script!!.substringAfter("vm.CurChapter = ").substringBefore(";")).jsonObject
val pageTotal = curChapter.getString("Page")!!.toInt() val pageTotal = curChapter.getString("Page")!!.toInt()

View File

@ -56,7 +56,7 @@ open class OtakuSanctuary(
val page = emptyList<SManga>().toMutableList() val page = emptyList<SManga>().toMutableList()
for (element in elements) { for (element in elements) {
val url = element.select("div.mdl-card__title a").first().attr("abs:href") val url = element.select("div.mdl-card__title a").first()!!.attr("abs:href")
// ignore external chapters // ignore external chapters
if (url.toHttpUrl().host != baseUrl.toHttpUrl().host) { if (url.toHttpUrl().host != baseUrl.toHttpUrl().host) {
continue continue
@ -80,7 +80,7 @@ open class OtakuSanctuary(
setUrlWithoutDomain(url) setUrlWithoutDomain(url)
title = element.select("div.mdl-card__supporting-text a[target=_blank]").text() title = element.select("div.mdl-card__supporting-text a[target=_blank]").text()
.replaceFirstChar { it.titlecase() } .replaceFirstChar { it.titlecase() }
thumbnail_url = element.select("div.container-3-4.background-contain img").first().attr("abs:src") thumbnail_url = element.select("div.container-3-4.background-contain img").first()!!.attr("abs:src")
} }
} }
return page return page
@ -118,7 +118,7 @@ open class OtakuSanctuary(
title = document.select("h1.title.text-lg-left.text-overflow-2-line") title = document.select("h1.title.text-lg-left.text-overflow-2-line")
.text() .text()
.replaceFirstChar { it.titlecase() } .replaceFirstChar { it.titlecase() }
author = document.select("tr:contains(Tác Giả) a.capitalize").first().text() author = document.select("tr:contains(Tác Giả) a.capitalize").first()!!.text()
.replaceFirstChar { it.titlecase() } .replaceFirstChar { it.titlecase() }
description = document.select("div.summary p").joinToString("\n") { description = document.select("div.summary p").joinToString("\n") {
it.run { it.run {
@ -129,7 +129,7 @@ open class OtakuSanctuary(
genre = document.select("div.genres a").joinToString { it.text() } genre = document.select("div.genres a").joinToString { it.text() }
thumbnail_url = document.select("div.container-3-4.background-contain img").attr("abs:src") thumbnail_url = document.select("div.container-3-4.background-contain img").attr("abs:src")
val statusString = document.select("tr:contains(Tình Trạng) td").first().text().trim() val statusString = document.select("tr:contains(Tình Trạng) td").first()!!.text().trim()
status = when (statusString) { status = when (statusString) {
"Ongoing" -> SManga.ONGOING "Ongoing" -> SManga.ONGOING
"Done" -> SManga.COMPLETED "Done" -> SManga.COMPLETED
@ -155,7 +155,7 @@ open class OtakuSanctuary(
else -> 0L else -> 0L
} }
} else { } else {
return kotlin.runCatching { dateFormat.parse(date)?.time }.getOrNull() ?: 0L return runCatching { dateFormat.parse(date)?.time }.getOrNull() ?: 0L
} }
} }

View File

@ -44,10 +44,10 @@ abstract class SinMH(
protected open val comicItemSelector = "#contList > li, li.list-comic" protected open val comicItemSelector = "#contList > li, li.list-comic"
protected open val comicItemTitleSelector = "p > a, h3 > a" protected open val comicItemTitleSelector = "p > a, h3 > a"
protected open fun mangaFromElement(element: Element) = SManga.create().apply { protected open fun mangaFromElement(element: Element) = SManga.create().apply {
val titleElement = element.selectFirst(comicItemTitleSelector) val titleElement = element.selectFirst(comicItemTitleSelector)!!
title = titleElement.text() title = titleElement.text()
setUrlWithoutDomain(titleElement.attr("href")) setUrlWithoutDomain(titleElement.attr("href"))
val image = element.selectFirst(Evaluator.Tag("img")) val image = element.selectFirst(Evaluator.Tag("img"))!!
thumbnail_url = image.attr("src").ifEmpty { image.attr("data-src") } thumbnail_url = image.attr("src").ifEmpty { image.attr("data-src") }
} }
@ -102,24 +102,24 @@ abstract class SinMH(
// Details // Details
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.selectFirst(".book-title > h1").text() title = document.selectFirst(".book-title > h1")!!.text()
val detailsList = document.selectFirst(Evaluator.Class("detail-list")) val detailsList = document.selectFirst(Evaluator.Class("detail-list"))!!
author = detailsList.select("strong:contains(作者) ~ *").text() author = detailsList.select("strong:contains(作者) ~ *").text()
description = document.selectFirst(Evaluator.Id("intro-all")).text().trim() description = document.selectFirst(Evaluator.Id("intro-all"))!!.text().trim()
.removePrefix("漫画简介:").trim() .removePrefix("漫画简介:").trim()
.removePrefix("漫画简介:").trim() // some sources have double prefix .removePrefix("漫画简介:").trim() // some sources have double prefix
genre = mangaDetailsParseDefaultGenre(document, detailsList) genre = mangaDetailsParseDefaultGenre(document, detailsList)
status = when (detailsList.selectFirst("strong:contains(状态) + *").text()) { status = when (detailsList.selectFirst("strong:contains(状态) + *")!!.text()) {
"连载中" -> SManga.ONGOING "连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED "已完结" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
thumbnail_url = document.selectFirst("div.book-cover img").attr("src") thumbnail_url = document.selectFirst("div.book-cover img")!!.attr("src")
} }
protected open fun mangaDetailsParseDefaultGenre(document: Document, detailsList: Element): String { protected open fun mangaDetailsParseDefaultGenre(document: Document, detailsList: Element): String {
val category = detailsList.selectFirst("strong:contains(类型) + a") val category = detailsList.selectFirst("strong:contains(类型) + a")!!
val breadcrumbs = document.selectFirst("div.breadcrumb-bar").select("a[href^=/list/]") val breadcrumbs = document.selectFirst("div.breadcrumb-bar")!!.select("a[href^=/list/]")
return buildString { return buildString {
append(category.text()) append(category.text())
breadcrumbs.map(Element::text).filter(String::isNotEmpty).joinTo(this, prefix = ", ") breadcrumbs.map(Element::text).filter(String::isNotEmpty).joinTo(this, prefix = ", ")
@ -127,23 +127,23 @@ abstract class SinMH(
} }
protected fun mangaDetailsParseDMZJStyle(document: Document, hasBreadcrumb: Boolean) = SManga.create().apply { protected fun mangaDetailsParseDMZJStyle(document: Document, hasBreadcrumb: Boolean) = SManga.create().apply {
val detailsDiv = document.selectFirst("div.comic_deCon") val detailsDiv = document.selectFirst("div.comic_deCon")!!
title = detailsDiv.selectFirst(Evaluator.Tag("h1")).text() title = detailsDiv.selectFirst(Evaluator.Tag("h1"))!!.text()
val details = detailsDiv.select("> ul > li") val details = detailsDiv.select("> ul > li")
val linkSelector = Evaluator.Tag("a") val linkSelector = Evaluator.Tag("a")
author = details[0].selectFirst(linkSelector).text() author = details[0].selectFirst(linkSelector)!!.text()
status = when (details[1].selectFirst(linkSelector).text()) { status = when (details[1].selectFirst(linkSelector)!!.text()) {
"连载中" -> SManga.ONGOING "连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED "已完结" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
genre = mutableListOf<Element>().apply { genre = mutableListOf<Element>().apply {
add(details[2].selectFirst(linkSelector)) // 类别 add(details[2].selectFirst(linkSelector)!!) // 类别
addAll(details[3].select(linkSelector)) // 类型 addAll(details[3].select(linkSelector)) // 类型
if (hasBreadcrumb) addAll(document.selectFirst("div.mianbao").select("a[href^=/list/]")) if (hasBreadcrumb) addAll(document.selectFirst("div.mianbao")!!.select("a[href^=/list/]"))
}.mapTo(mutableSetOf()) { it.text() }.joinToString(", ") }.mapTo(mutableSetOf()) { it.text() }.joinToString(", ")
description = detailsDiv.selectFirst("> p.comic_deCon_d").text() description = detailsDiv.selectFirst("> p.comic_deCon_d")!!.text()
thumbnail_url = document.selectFirst("div.comic_i_img > img").attr("src") thumbnail_url = document.selectFirst("div.comic_i_img > img")!!.attr("src")
} }
// Chapters // Chapters
@ -167,7 +167,7 @@ abstract class SinMH(
section.select(itemSelector).map { chapterFromElement(it) }.sortedDescending() section.select(itemSelector).map { chapterFromElement(it) }.sortedDescending()
} }
if (isNewDateLogic && list.isNotEmpty()) { if (isNewDateLogic && list.isNotEmpty()) {
val date = document.selectFirst(dateSelector).textNodes().last().text() val date = document.selectFirst(dateSelector)!!.textNodes().last().text()
list[0].date_upload = DATE_FORMAT.parse(date)?.time ?: 0L list[0].date_upload = DATE_FORMAT.parse(date)?.time ?: 0L
} }
return list return list
@ -194,7 +194,7 @@ abstract class SinMH(
// baseUrl/js/common.js/getChapterImage() // baseUrl/js/common.js/getChapterImage()
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val script = document.selectFirst("body > script").html().let(::ProgressiveParser) val script = document.selectFirst("body > script")!!.html().let(::ProgressiveParser)
val images = script.substringBetween("chapterImages = ", ";") val images = script.substringBetween("chapterImages = ", ";")
if (images.length <= 2) return emptyList() // [] or "" if (images.length <= 2) return emptyList() // [] or ""
val path = script.substringBetween("chapterPath = \"", "\";") val path = script.substringBetween("chapterPath = \"", "\";")
@ -237,8 +237,8 @@ abstract class SinMH(
if (categories.isNotEmpty()) return if (categories.isNotEmpty()) return
val labelSelector = Evaluator.Tag("label") val labelSelector = Evaluator.Tag("label")
val linkSelector = Evaluator.Tag("a") val linkSelector = Evaluator.Tag("a")
categories = document.selectFirst(Evaluator.Class("filter-nav")).children().map { element -> categories = document.selectFirst(Evaluator.Class("filter-nav"))!!.children().map { element ->
val name = element.selectFirst(labelSelector).text() val name = element.selectFirst(labelSelector)!!.text()
val tags = element.select(linkSelector) val tags = element.select(linkSelector)
val values = tags.map { it.text() }.toTypedArray() val values = tags.map { it.text() }.toTypedArray()
val uriParts = tags.map { it.attr("href").removePrefix("/list/").removeSuffix("/") }.toTypedArray() val uriParts = tags.map { it.attr("href").removePrefix("/list/").removeSuffix("/") }.toTypedArray()

View File

@ -196,7 +196,7 @@ open class Webtoons(
open fun parseDetailsThumbnail(document: Document): String? { open fun parseDetailsThumbnail(document: Document): String? {
val picElement = document.select("#content > div.cont_box > div.detail_body") val picElement = document.select("#content > div.cont_box > div.detail_body")
val discoverPic = document.select("#content > div.cont_box > div.detail_header > span.thmb") val discoverPic = document.select("#content > div.cont_box > div.detail_header > span.thmb")
return discoverPic.select("img").not("[alt='Representative image']").first()?.attr("src") ?: picElement.attr("style")?.substringAfter("url(")?.substringBeforeLast(")") return discoverPic.select("img").not("[alt='Representative image']").first()?.attr("src") ?: picElement.attr("style").substringAfter("url(")?.substringBeforeLast(")")
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
@ -204,7 +204,7 @@ open class Webtoons(
val infoElement = document.select("#_asideDetail") val infoElement = document.select("#_asideDetail")
val manga = SManga.create() val manga = SManga.create()
manga.title = document.selectFirst("h1.subj, h3.subj").text() manga.title = document.selectFirst("h1.subj, h3.subj")!!.text()
manga.author = detailElement.select(".author:nth-of-type(1)").first()?.ownText() manga.author = detailElement.select(".author:nth-of-type(1)").first()?.ownText()
manga.artist = detailElement.select(".author:nth-of-type(2)").first()?.ownText() ?: manga.author manga.artist = detailElement.select(".author:nth-of-type(2)").first()?.ownText() ?: manga.author
manga.genre = detailElement.select(".genre").joinToString(", ") { it.text() } manga.genre = detailElement.select(".genre").joinToString(", ") { it.text() }
@ -220,7 +220,7 @@ open class Webtoons(
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
override fun imageUrlParse(document: Document): String = document.select("img").first().attr("src") override fun imageUrlParse(document: Document): String = document.select("img").first()!!.attr("src")
// Filters // Filters
@ -260,7 +260,7 @@ open class Webtoons(
if (element.select(".ico_bgm").isNotEmpty()) { if (element.select(".ico_bgm").isNotEmpty()) {
chapter.name += "" chapter.name += ""
} }
chapter.date_upload = element.select("a > div.row > div.col > div.sub_info > span.date").text()?.let { chapterParseDate(it) } ?: 0 chapter.date_upload = element.select("a > div.row > div.col > div.sub_info > span.date").text().let { chapterParseDate(it) } ?: 0
return chapter return chapter
} }

View File

@ -51,7 +51,7 @@ abstract class WPComics(
title = it.text() title = it.text()
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
} }
thumbnail_url = imageOrNull(element.select("div.image:first-of-type img").first()) thumbnail_url = imageOrNull(element.select("div.image:first-of-type img").first()!!)
} }
} }
@ -106,7 +106,7 @@ abstract class WPComics(
title = it.text() title = it.text()
setUrlWithoutDomain(it.attr("abs:href")) setUrlWithoutDomain(it.attr("abs:href"))
} }
thumbnail_url = imageOrNull(element.select("div.image a img").first()) thumbnail_url = imageOrNull(element.select("div.image a img").first()!!)
} }
} }
@ -121,7 +121,7 @@ abstract class WPComics(
status = info.select("li.status p.col-xs-8").text().toStatus() status = info.select("li.status p.col-xs-8").text().toStatus()
genre = info.select("li.kind p.col-xs-8 a").joinToString { it.text() } genre = info.select("li.kind p.col-xs-8 a").joinToString { it.text() }
description = info.select("div.detail-content p").text() description = info.select("div.detail-content p").text()
thumbnail_url = imageOrNull(info.select("div.col-image img").first()) thumbnail_url = imageOrNull(info.select("div.col-image img").first()!!)
} }
} }
} }

View File

@ -53,7 +53,7 @@ abstract class Zbulu(
setUrlWithoutDomain(it.attr("href").addTrailingSlash()) setUrlWithoutDomain(it.attr("href").addTrailingSlash())
title = it.text() title = it.text()
} }
thumbnail_url = element.select("img").first().attr("abs:src") thumbnail_url = element.select("img").first()!!.attr("abs:src")
} }
} }
@ -100,10 +100,10 @@ abstract class Zbulu(
// Manga summary page // Manga summary page
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.single-comic").first() val infoElement = document.select("div.single-comic").first()!!
return SManga.create().apply { return SManga.create().apply {
title = infoElement.select("h1").first().text() title = infoElement.select("h1").first()!!.text()
author = infoElement.select("div.author a").text() author = infoElement.select("div.author a").text()
status = parseStatus(infoElement.select("div.update span[style]").text()) status = parseStatus(infoElement.select("div.update span[style]").text())
genre = infoElement.select("div.genre a").joinToString { it.text() } genre = infoElement.select("div.genre a").joinToString { it.text() }
@ -141,7 +141,7 @@ abstract class Zbulu(
return SChapter.create().apply { return SChapter.create().apply {
setUrlWithoutDomain(element.select("a").attr("href")) setUrlWithoutDomain(element.select("a").attr("href"))
name = element.select("h2").text() name = element.select("h2").text()
date_upload = element.select("div.chapter-date")?.text().toDate() date_upload = element.select("div.chapter-date").text().toDate()
} }
} }

View File

@ -32,7 +32,7 @@ abstract class ZeistManga(
open val imgSelectorAttr = "src" open val imgSelectorAttr = "src"
open fun getChaptersUrl(doc: Document): String { open fun getChaptersUrl(doc: Document): String {
val script = doc.selectFirst(scriptSelector) val script = doc.selectFirst(scriptSelector)!!
val feed = chapterFeedRegex val feed = chapterFeedRegex
.find(script.html()) .find(script.html())
?.groupValues?.get(1) ?.groupValues?.get(1)
@ -109,17 +109,17 @@ abstract class ZeistManga(
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val profileManga = document.selectFirst(".grid.gtc-235fr") val profileManga = document.selectFirst(".grid.gtc-235fr")!!
return SManga.create().apply { return SManga.create().apply {
title = profileManga.selectFirst("h1.mt-0.mb-6.fs-20").text() title = profileManga.selectFirst("h1.mt-0.mb-6.fs-20")!!.text()
thumbnail_url = profileManga.selectFirst("img").attr("src") thumbnail_url = profileManga.selectFirst("img")!!.attr("src")
description = profileManga.select("#synopsis").text() description = profileManga.select("#synopsis").text()
status = SManga.UNKNOWN status = SManga.UNKNOWN
} }
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val images = document.selectFirst("div.check-box") val images = document.selectFirst("div.check-box")!!
return images.select(imgSelector).mapIndexed { i, img -> return images.select(imgSelector).mapIndexed { i, img ->
Page(i, "", img.attr(imgSelectorAttr)) Page(i, "", img.attr(imgSelectorAttr))
} }
@ -153,8 +153,8 @@ abstract class ZeistManga(
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
setUrlWithoutDomain(element.select(".block").attr("href")) setUrlWithoutDomain(element.select(".block").attr("href"))
title = element.selectFirst(".clamp.toe.oh.block").text().trim() title = element.selectFirst(".clamp.toe.oh.block")!!.text().trim()
thumbnail_url = element.selectFirst("img").attr("src") thumbnail_url = element.selectFirst("img")!!.attr("src")
} }
} }

View File

@ -53,7 +53,7 @@ data class ZeistMangaEntryDto(
private fun getThumbnail(html: ZeistMangaEntryContentDto): String { private fun getThumbnail(html: ZeistMangaEntryContentDto): String {
val document = Jsoup.parse(html.t) val document = Jsoup.parse(html.t)
return document.selectFirst("img").attr("src") return document.selectFirst("img")!!.attr("src")
} }
} }

View File

@ -38,7 +38,7 @@ abstract class ZManga(
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
setUrlWithoutDomain(element.select("div.flexbox2-content a").attr("href")) setUrlWithoutDomain(element.select("div.flexbox2-content a").attr("href"))
title = element.select("div.flexbox2-title > span").first().text() title = element.select("div.flexbox2-title > span").first()!!.text()
thumbnail_url = element.select("img").attr("abs:src") thumbnail_url = element.select("img").attr("abs:src")
} }
} }
@ -162,7 +162,7 @@ abstract class ZManga(
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
return SChapter.create().apply { return SChapter.create().apply {
setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))
name = element.select("span").first().ownText() name = element.select("span").first()!!.ownText()
date_upload = parseDate(element.select("span.date").text()) date_upload = parseDate(element.select("span.date").text())
} }
} }

View File

@ -361,9 +361,9 @@ open class BatoTo(
return Jsoup.parse(response.body.string(), response.request.url.toString(), Parser.xmlParser()) return Jsoup.parse(response.body.string(), response.request.url.toString(), Parser.xmlParser())
.select("channel > item").map { item -> .select("channel > item").map { item ->
SChapter.create().apply { SChapter.create().apply {
url = item.selectFirst("guid").text() url = item.selectFirst("guid")!!.text()
name = item.selectFirst("title").text().substringAfter(title).trim() name = item.selectFirst("title")!!.text().substringAfter(title).trim()
date_upload = SimpleDateFormat("E, dd MMM yyyy H:m:s Z", Locale.US).parse(item.selectFirst("pubDate").text())?.time ?: 0L date_upload = SimpleDateFormat("E, dd MMM yyyy H:m:s Z", Locale.US).parse(item.selectFirst("pubDate")!!.text())?.time ?: 0L
} }
} }
} }

View File

@ -65,7 +65,7 @@ class BuonDua() : ParsedHttpSource() {
manga.title = document.select(".article-header").text() manga.title = document.select(".article-header").text()
manga.description = document.select(".article-info > strong").text().trim() manga.description = document.select(".article-info > strong").text().trim()
val genres = mutableListOf<String>() val genres = mutableListOf<String>()
document.select(".article-tags").first().select(".tags > .tag").forEach { document.select(".article-tags").first()!!.select(".tags > .tag").forEach {
genres.add(it.text().substringAfter("#")) genres.add(it.text().substringAfter("#"))
} }
manga.genre = genres.joinToString(", ") manga.genre = genres.joinToString(", ")
@ -74,7 +74,7 @@ class BuonDua() : ParsedHttpSource() {
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(element.select(".is-current").first().attr("abs:href")) chapter.setUrlWithoutDomain(element.select(".is-current").first()!!.attr("abs:href"))
chapter.chapter_number = 0F chapter.chapter_number = 0F
chapter.name = element.select(".article-header").text() chapter.name = element.select(".article-header").text()
chapter.date_upload = SimpleDateFormat("H:m DD-MM-yyyy", Locale.US).parse(element.select(".article-info > small").text())?.time ?: 0L chapter.date_upload = SimpleDateFormat("H:m DD-MM-yyyy", Locale.US).parse(element.select(".article-info > small").text())?.time ?: 0L
@ -86,7 +86,7 @@ class BuonDua() : ParsedHttpSource() {
// Pages // Pages
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val numpages = document.selectFirst(".pagination-list").select(".pagination-link") val numpages = document.selectFirst(".pagination-list")!!.select(".pagination-link")
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
numpages.forEachIndexed { index, page -> numpages.forEachIndexed { index, page ->
@ -94,7 +94,7 @@ class BuonDua() : ParsedHttpSource() {
0 -> document 0 -> document
else -> client.newCall(GET(page.attr("abs:href"))).execute().asJsoup() else -> client.newCall(GET(page.attr("abs:href"))).execute().asJsoup()
} }
doc.select(".article-fulltext img").forEach { it -> doc.select(".article-fulltext img").forEach {
val itUrl = it.attr("abs:src") val itUrl = it.attr("abs:src")
pages.add(Page(pages.size, "", itUrl)) pages.add(Page(pages.size, "", itUrl))
} }

View File

@ -81,7 +81,7 @@ abstract class EHentai(
} }
} }
// Get image // Get image
it.parent().select(".glthumb img")?.first().apply { it.parent()?.select(".glthumb img")?.first().apply {
thumbnail_url = this?.attr("data-src")?.nullIfBlank() thumbnail_url = this?.attr("data-src")?.nullIfBlank()
?: this?.attr("src") ?: this?.attr("src")
} }

View File

@ -30,7 +30,7 @@ class EveriaClub() : ParsedHttpSource() {
// Latest // Latest
override fun latestUpdatesFromElement(element: Element): SManga { override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = element.selectFirst("img").imgSrc manga.thumbnail_url = element.selectFirst("img")!!.imgSrc
manga.title = element.select(".entry-title").text() manga.title = element.select(".entry-title").text()
manga.setUrlWithoutDomain(element.select(".entry-title > a").attr("abs:href")) manga.setUrlWithoutDomain(element.select(".entry-title > a").attr("abs:href"))
return manga return manga
@ -46,7 +46,7 @@ class EveriaClub() : ParsedHttpSource() {
// Popular // Popular
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = element.selectFirst("img").imgSrc manga.thumbnail_url = element.selectFirst("img")!!.imgSrc
manga.title = element.select("h3").text() manga.title = element.select("h3").text()
manga.setUrlWithoutDomain(element.select("h3 > a").attr("abs:href")) manga.setUrlWithoutDomain(element.select("h3 > a").attr("abs:href"))
return manga return manga

View File

@ -46,10 +46,10 @@ class Hennojin(override val lang: String, suffix: String) : ParsedHttpSource() {
override fun popularMangaFromElement(element: Element) = override fun popularMangaFromElement(element: Element) =
SManga.create().apply { SManga.create().apply {
element.selectFirst(".title_link > a").let { element.selectFirst(".title_link > a").let {
title = it.text() title = it!!.text()
setUrlWithoutDomain(it.attr("href")) setUrlWithoutDomain(it.attr("href"))
} }
thumbnail_url = element.selectFirst("img").attr("src") thumbnail_url = element.selectFirst("img")!!.attr("src")
} }
override fun searchMangaSelector() = popularMangaSelector() override fun searchMangaSelector() = popularMangaSelector()

View File

@ -125,8 +125,7 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
url.addQueryParameter(pair.second, toBinary(filter.state == index)) url.addQueryParameter(pair.second, toBinary(filter.state == index))
} }
} }
else -> { else -> {}
}
} }
} }
@ -141,9 +140,9 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
return this.joinToString { return this.joinToString {
listOf( listOf(
it.ownText(), it.ownText(),
it.select(".split_tag")?.text() it.select(".split_tag").text()
?.trim() .trim()
?.removePrefix("| "), .removePrefix("| "),
) )
.filter { s -> !s.isNullOrBlank() } .filter { s -> !s.isNullOrBlank() }
.joinToString(splitTagSeparator) .joinToString(splitTagSeparator)
@ -151,16 +150,16 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
} }
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
title = document.selectFirst("div.right_details > h1").text() title = document.selectFirst("div.right_details > h1")!!.text()
thumbnail_url = document.selectFirst("div.left_cover img")?.let { thumbnail_url = document.selectFirst("div.left_cover img")?.let {
it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src") it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
} }
val mangaInfoElement = document.select(".galleries_info") val mangaInfoElement = document.select(".galleries_info")
val infoMap = mangaInfoElement.select("li:not(.pages)").map { val infoMap = mangaInfoElement.select("li:not(.pages)").associate {
it.select("span.tags_text").text().removeSuffix(":") to it.select(".tag") it.select("span.tags_text").text().removeSuffix(":") to it.select(".tag")
}.toMap() }
artist = infoMap["Artists"]?.csvText(" | ") artist = infoMap["Artists"]?.csvText(" | ")
@ -206,11 +205,11 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
private val json: Json by injectLazy() private val json: Json by injectLazy()
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val image_dir = document.select("#image_dir").`val`() val imageDir = document.select("#image_dir").`val`()
val gallery_id = document.select("#gallery_id").`val`() val galleryId = document.select("#gallery_id").`val`()
val u_id = document.select("#u_id").`val`().toInt() val uId = document.select("#u_id").`val`().toInt()
val random_server = when (u_id) { val randomServer = when (uId) {
in 1..274825 -> "m1.imhentai.xxx" in 1..274825 -> "m1.imhentai.xxx"
in 274826..403818 -> "m2.imhentai.xxx" in 274826..403818 -> "m2.imhentai.xxx"
in 403819..527143 -> "m3.imhentai.xxx" in 403819..527143 -> "m3.imhentai.xxx"
@ -221,20 +220,20 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
} }
val images = json.parseToJsonElement( val images = json.parseToJsonElement(
document.selectFirst("script:containsData(var g_th)").data() document.selectFirst("script:containsData(var g_th)")!!.data()
.substringAfter("$.parseJSON('").substringBefore("');").trim(), .substringAfter("$.parseJSON('").substringBefore("');").trim(),
).jsonObject ).jsonObject
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
for (image in images) { for (image in images) {
val iext = image.value.toString().replace("\"", "").split(",")[0] val iext = image.value.toString().replace("\"", "").split(",")[0]
val iext_pr = when (iext) { val iextPr = when (iext) {
"p" -> "png" "p" -> "png"
"b" -> "bmp" "b" -> "bmp"
"g" -> "gif" "g" -> "gif"
else -> "jpg" else -> "jpg"
} }
pages.add(Page(image.key.toInt() - 1, "", "https://$random_server/$image_dir/$gallery_id/${image.key}.$iext_pr")) pages.add(Page(image.key.toInt() - 1, "", "https://$randomServer/$imageDir/$galleryId/${image.key}.$iextPr"))
} }
return pages return pages
} }

View File

@ -77,7 +77,7 @@ class Junmeitu : ParsedHttpSource() {
// Details // Details
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.title = document.selectFirst(".news-title,.title").text() manga.title = document.selectFirst(".news-title,.title")!!.text()
manga.description = document.select(".news-info,.picture-details").text() + "\n" + document.select(".introduce").text() manga.description = document.select(".news-info,.picture-details").text() + "\n" + document.select(".introduce").text()
manga.genre = document.select(".relation_tags > a").joinToString { it.text() } manga.genre = document.select(".relation_tags > a").joinToString { it.text() }
manga.status = SManga.COMPLETED manga.status = SManga.COMPLETED
@ -86,7 +86,7 @@ class Junmeitu : ParsedHttpSource() {
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(element.select(".position a:last-child").first().attr("abs:href")) chapter.setUrlWithoutDomain(element.select(".position a:last-child").first()!!.attr("abs:href"))
chapter.chapter_number = -2f chapter.chapter_number = -2f
chapter.name = "Gallery" chapter.name = "Gallery"
return chapter return chapter
@ -104,7 +104,7 @@ class Junmeitu : ParsedHttpSource() {
val index = lastIndexOf('.') // .html val index = lastIndexOf('.') // .html
baseUrl + "/ajax_" + substring(baseUrl.length + 1, index) + '-' baseUrl + "/ajax_" + substring(baseUrl.length + 1, index) + '-'
} }
val postfix = document.selectFirst("body > script").data().run { val postfix = document.selectFirst("body > script")!!.data().run {
val script = substringAfterLast("pc_cid = ") val script = substringAfterLast("pc_cid = ")
val categoryId = script.substringBefore(';') val categoryId = script.substringBefore(';')
val contentId = script.substringAfter("pc_id = ").substringBeforeLast(';') val contentId = script.substringAfter("pc_id = ").substringBeforeLast(';')

View File

@ -1164,16 +1164,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.execute().use { response -> .execute().use { response ->
genresListMeta = try { genresListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for genres filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "[Filter] Error decoding JSON for genres filter -> ${response.body}", e) Log.e(LOG_TAG, "[Filter] Error decoding JSON for genres filter -> ${response.body}", e)
emptyList() emptyList()
@ -1183,16 +1174,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Metadata/tags", headersBuilder().build())) client.newCall(GET("$apiUrl/Metadata/tags", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
tagsListMeta = try { tagsListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for tagsList filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "[Filter] Error decoding JSON for tagsList filter", e) Log.e(LOG_TAG, "[Filter] Error decoding JSON for tagsList filter", e)
emptyList() emptyList()
@ -1202,16 +1184,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Metadata/age-ratings", headersBuilder().build())) client.newCall(GET("$apiUrl/Metadata/age-ratings", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
ageRatingsListMeta = try { ageRatingsListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for age-ratings filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1225,16 +1198,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Collection", headersBuilder().build())) client.newCall(GET("$apiUrl/Collection", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
collectionsListMeta = try { collectionsListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for collectionsListMeta filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1248,16 +1212,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Metadata/languages", headersBuilder().build())) client.newCall(GET("$apiUrl/Metadata/languages", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
languagesListMeta = try { languagesListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for languagesListMeta filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1271,16 +1226,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Library", headersBuilder().build())) client.newCall(GET("$apiUrl/Library", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
libraryListMeta = try { libraryListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"[Filter] Error decoding JSON for libraries filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1294,16 +1240,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Metadata/people", headersBuilder().build())) client.newCall(GET("$apiUrl/Metadata/people", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
peopleListMeta = try { peopleListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"error while decoding JSON for peopleListMeta filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1316,16 +1253,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
client.newCall(GET("$apiUrl/Metadata/publication-status", headersBuilder().build())) client.newCall(GET("$apiUrl/Metadata/publication-status", headersBuilder().build()))
.execute().use { response -> .execute().use { response ->
pubStatusListMeta = try { pubStatusListMeta = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(
LOG_TAG,
"error while decoding JSON for publicationStatusListMeta filter: response body is null. Response code: ${response.code}",
)
emptyList()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
LOG_TAG, LOG_TAG,
@ -1345,9 +1273,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.subscribe( .subscribe(
{}, {},
{ tr -> { tr ->
/** // Avoid polluting logs with traces of exception
* Avoid polluting logs with traces of exception
* **/
if (tr is EmptyRequestBody || tr is LoginErrorException) { if (tr is EmptyRequestBody || tr is LoginErrorException) {
Log.e(LOG_TAG, "error while doing initial calls\n${tr.cause}") Log.e(LOG_TAG, "error while doing initial calls\n${tr.cause}")
return@subscribe return@subscribe

View File

@ -581,13 +581,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
try { try {
client.newCall(GET("$baseUrl/api/v1/tags", headers)).execute().use { response -> client.newCall(GET("$baseUrl/api/v1/tags", headers)).execute().use { response ->
tags = try { tags = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(LOG_TAG, "error while decoding JSON for tags filter: response body is null. Response code: ${response.code}")
emptySet()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "error while decoding JSON for tags filter", e) Log.e(LOG_TAG, "error while decoding JSON for tags filter", e)
emptySet() emptySet()
@ -600,13 +594,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
try { try {
client.newCall(GET("$baseUrl/api/v1/publishers", headers)).execute().use { response -> client.newCall(GET("$baseUrl/api/v1/publishers", headers)).execute().use { response ->
publishers = try { publishers = try {
val responseBody = response.body response.body.use { json.decodeFromString(it.string()) }
if (responseBody != null) {
responseBody.use { json.decodeFromString(it.string()) }
} else {
Log.e(LOG_TAG, "error while decoding JSON for publishers filter: response body is null. Response code: ${response.code}")
emptySet()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "error while decoding JSON for publishers filter", e) Log.e(LOG_TAG, "error while decoding JSON for publishers filter", e)
emptySet() emptySet()
@ -619,14 +607,9 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
try { try {
client.newCall(GET("$baseUrl/api/v1/authors", headers)).execute().use { response -> client.newCall(GET("$baseUrl/api/v1/authors", headers)).execute().use { response ->
authors = try { authors = try {
val responseBody = response.body response.body
if (responseBody != null) { .use { json.decodeFromString<List<AuthorDto>>(it.string()) }
val list: List<AuthorDto> = responseBody.use { json.decodeFromString(it.string()) } .groupBy { it.role }
list.groupBy { it.role }
} else {
Log.e(LOG_TAG, "error while decoding JSON for authors filter: response body is null. Response code: ${response.code}")
emptyMap()
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "error while decoding JSON for authors filter", e) Log.e(LOG_TAG, "error while decoding JSON for authors filter", e)
emptyMap() emptyMap()

View File

@ -50,7 +50,7 @@ class LittleGarden : ParsedHttpSource() {
override fun latestUpdatesSelector() = ".d-sm-block.col-sm-6.col-lg-6.col-xl-3.col-12" override fun latestUpdatesSelector() = ".d-sm-block.col-sm-6.col-lg-6.col-xl-3.col-12"
override fun latestUpdatesNextPageSelector(): String? = null override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
title = element.selectFirst("h3").text().trim() title = element.selectFirst("h3")!!.text().trim()
setUrlWithoutDomain(element.select("a").attr("href").substringBeforeLast("/")) setUrlWithoutDomain(element.select("a").attr("href").substringBeforeLast("/"))
thumbnail_url = element.select(".img.image-item").attr("style").substringAfter("(").substringBefore(")") thumbnail_url = element.select(".img.image-item").attr("style").substringAfter("(").substringBefore(")")
} }
@ -172,9 +172,9 @@ class LittleGarden : ParsedHttpSource() {
// Pages // Pages
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
val chapNb = document.selectFirst("div.chapter-number").text().trim().toInt() val chapNb = document.selectFirst("div.chapter-number")!!.text().trim().toInt()
val engChaps: IntArray = intArrayOf(970, 987, 992) val engChaps: IntArray = intArrayOf(970, 987, 992)
if (document.selectFirst("div.manga-name").text().trim() == "One Piece" && (engChaps.contains(chapNb) || chapNb > 1004)) { // Permits to get French pages rather than English pages for some chapters if (document.selectFirst("div.manga-name")!!.text().trim() == "One Piece" && (engChaps.contains(chapNb) || chapNb > 1004)) { // Permits to get French pages rather than English pages for some chapters
oricolPageRegex.findAll(document.select("script:containsData(pages)").toString()).asIterable().mapIndexed { i, it -> oricolPageRegex.findAll(document.select("script:containsData(pages)").toString()).asIterable().mapIndexed { i, it ->
if (it.groups["colored"]?.value?.contains("\"") == true) { // Their JS dict has " " around the link only when available. Also uses colored pages rather than B&W as it's the main strength of this site if (it.groups["colored"]?.value?.contains("\"") == true) { // Their JS dict has " " around the link only when available. Also uses colored pages rather than B&W as it's the main strength of this site
pages.add(Page(i, "", cdnUrl + it.groups["colored"]?.value?.replace("\"", "") + ".webp")) pages.add(Page(i, "", cdnUrl + it.groups["colored"]?.value?.replace("\"", "") + ".webp"))

View File

@ -106,21 +106,21 @@ class MangaPlusCreators(override val lang: String) : HttpSource() {
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val result = response.asJsoup() val result = response.asJsoup()
val bookBox = result.selectFirst(".book-box") val bookBox = result.selectFirst(".book-box")!!
return SManga.create().apply { return SManga.create().apply {
title = bookBox.selectFirst("div.title").text() title = bookBox.selectFirst("div.title")!!.text()
author = bookBox.selectFirst("div.mod-btn-profile div.name").text() author = bookBox.selectFirst("div.mod-btn-profile div.name")!!.text()
description = bookBox.select("div.summary p") description = bookBox.select("div.summary p")
.joinToString("\n\n") { it.text() } .joinToString("\n\n") { it.text() }
status = when (bookBox.selectFirst("div.book-submit-type").text()) { status = when (bookBox.selectFirst("div.book-submit-type")!!.text()) {
"Series" -> SManga.ONGOING "Series" -> SManga.ONGOING
"One-shot" -> SManga.COMPLETED "One-shot" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
genre = bookBox.select("div.genre-area div.tag-genre") genre = bookBox.select("div.genre-area div.tag-genre")
.joinToString { it.text() } .joinToString { it.text() }
thumbnail_url = bookBox.selectFirst("div.cover img").attr("data-src") thumbnail_url = bookBox.selectFirst("div.cover img")!!.attr("data-src")
} }
} }

View File

@ -116,7 +116,7 @@ open class MangaReader(
override fun searchMangaFromElement(element: Element) = override fun searchMangaFromElement(element: Element) =
SManga.create().apply { SManga.create().apply {
url = element.attr("href") url = element.attr("href")
element.selectFirst(Evaluator.Tag("img")).let { element.selectFirst(Evaluator.Tag("img"))!!.let {
title = it.attr("alt") title = it.attr("alt")
thumbnail_url = it.attr("src") thumbnail_url = it.attr("src")
} }
@ -146,23 +146,23 @@ open class MangaReader(
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
url = document.location().removePrefix(baseUrl) url = document.location().removePrefix(baseUrl)
val root = document.selectFirst(Evaluator.Id("ani_detail")) val root = document.selectFirst(Evaluator.Id("ani_detail"))!!
val mangaTitle = root.selectFirst(Evaluator.Tag("h2")).ownText() val mangaTitle = root.selectFirst(Evaluator.Tag("h2"))!!.ownText()
title = if (url.endsWith(VOLUME_URL_SUFFIX)) VOLUME_TITLE_PREFIX + mangaTitle else mangaTitle title = if (url.endsWith(VOLUME_URL_SUFFIX)) VOLUME_TITLE_PREFIX + mangaTitle else mangaTitle
description = root.run { description = root.run {
val description = selectFirst(Evaluator.Class("description")).ownText() val description = selectFirst(Evaluator.Class("description"))!!.ownText()
when (val altTitle = selectFirst(Evaluator.Class("manga-name-or")).ownText()) { when (val altTitle = selectFirst(Evaluator.Class("manga-name-or"))!!.ownText()) {
"", mangaTitle -> description "", mangaTitle -> description
else -> "$description\n\nAlternative Title: $altTitle" else -> "$description\n\nAlternative Title: $altTitle"
} }
} }
thumbnail_url = root.selectFirst(Evaluator.Tag("img")).attr("src") thumbnail_url = root.selectFirst(Evaluator.Tag("img"))!!.attr("src")
genre = root.selectFirst(Evaluator.Class("genres")).children().joinToString { it.ownText() } genre = root.selectFirst(Evaluator.Class("genres"))!!.children().joinToString { it.ownText() }
for (item in root.selectFirst(Evaluator.Class("anisc-info")).children()) { for (item in root.selectFirst(Evaluator.Class("anisc-info"))!!.children()) {
if (item.hasClass("item").not()) continue if (item.hasClass("item").not()) continue
when (item.selectFirst(Evaluator.Class("item-head")).ownText()) { when (item.selectFirst(Evaluator.Class("item-head"))!!.ownText()) {
"Authors:" -> item.parseAuthorsTo(this) "Authors:" -> item.parseAuthorsTo(this)
"Status:" -> status = when (item.selectFirst(Evaluator.Class("name")).ownText()) { "Status:" -> status = when (item.selectFirst(Evaluator.Class("name"))!!.ownText()) {
"Finished" -> SManga.COMPLETED "Finished" -> SManga.COMPLETED
"Publishing" -> SManga.ONGOING "Publishing" -> SManga.ONGOING
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
@ -198,7 +198,7 @@ open class MangaReader(
SChapter.create().apply { SChapter.create().apply {
val number = element.attr("data-number") val number = element.attr("data-number")
chapter_number = number.toFloatOrNull() ?: -1f chapter_number = number.toFloatOrNull() ?: -1f
element.selectFirst(Evaluator.Tag("a")).let { element.selectFirst(Evaluator.Tag("a"))!!.let {
url = it.attr("href") url = it.attr("href")
name = run { name = run {
val name = it.attr("title") val name = it.attr("title")
@ -211,7 +211,7 @@ open class MangaReader(
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val ajaxUrl = document.selectFirst(Evaluator.Id("wrapper")).run { val ajaxUrl = document.selectFirst(Evaluator.Id("wrapper"))!!.run {
val readingBy = attr("data-reading-by") val readingBy = attr("data-reading-by")
val readingId = attr("data-reading-id") val readingId = attr("data-reading-id")
"$baseUrl/ajax/image/list/$readingBy/$readingId?quality=${preferences.quality}" "$baseUrl/ajax/image/list/$readingBy/$readingId?quality=${preferences.quality}"

View File

@ -60,7 +60,7 @@ open class MangaToon(
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.select("div.content-title").text().trim() title = element.select("div.content-title").text().trim()
thumbnail_url = element.select("img").attr("abs:src").toNormalPosterUrl() thumbnail_url = element.select("img").attr("abs:src").toNormalPosterUrl()
url = element.selectFirst("a").attr("href") url = element.selectFirst("a")!!.attr("href")
} }
override fun popularMangaNextPageSelector() = "span.next" override fun popularMangaNextPageSelector() = "span.next"
@ -88,7 +88,7 @@ open class MangaToon(
override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.select("div.recommend-comics-title").text().trim() title = element.select("div.recommend-comics-title").text().trim()
thumbnail_url = element.select("img").attr("abs:src").toNormalPosterUrl() thumbnail_url = element.select("img").attr("abs:src").toNormalPosterUrl()
url = element.selectFirst("a").attr("href") url = element.selectFirst("a")!!.attr("href")
} }
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()

View File

@ -29,13 +29,13 @@ class MeituaTop : HttpSource() {
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
val mangas = document.selectFirst(Evaluator.Class("thumbnail-group")).children().map { val mangas = document.selectFirst(Evaluator.Class("thumbnail-group"))!!.children().map {
SManga.create().apply { SManga.create().apply {
url = it.selectFirst(Evaluator.Tag("a")).attr("href") url = it.selectFirst(Evaluator.Tag("a"))!!.attr("href")
val image = it.selectFirst(Evaluator.Tag("img")) val image = it.selectFirst(Evaluator.Tag("img"))!!
title = image.attr("alt") title = image.attr("alt")
thumbnail_url = image.attr("src") thumbnail_url = image.attr("src")
val info = it.selectFirst(Evaluator.Tag("p")).ownText().split(" - ") val info = it.selectFirst(Evaluator.Tag("p"))!!.ownText().split(" - ")
genre = info[0] genre = info[0]
description = info[1] description = info[1]
status = SManga.COMPLETED status = SManga.COMPLETED
@ -86,8 +86,8 @@ class MeituaTop : HttpSource() {
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup() val document = response.asJsoup()
val images = document.selectFirst(Evaluator.Class("ttnr")).select(Evaluator.Tag("img")) val images = document.selectFirst(Evaluator.Class("ttnr"))!!.select(Evaluator.Tag("img"))
.map { it.attr("src")!! }.distinct() .map { it.attr("src") }.distinct()
return images.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } return images.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) }
} }

View File

@ -56,7 +56,7 @@ open class MyReadingManga(override val lang: String, private val siteLang: Strin
override fun latestUpdatesNextPageSelector() = "li.pagination-next" override fun latestUpdatesNextPageSelector() = "li.pagination-next"
override fun latestUpdatesSelector() = "article" override fun latestUpdatesSelector() = "article"
override fun latestUpdatesFromElement(element: Element) = buildManga(element.select("a[rel]").first(), element.select("a.entry-image-link img").first()) override fun latestUpdatesFromElement(element: Element) = buildManga(element.select("a[rel]").first()!!, element.select("a.entry-image-link img").first())
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
cacheAssistant() cacheAssistant()
return super.latestUpdatesParse(response) return super.latestUpdatesParse(response)
@ -94,7 +94,7 @@ open class MyReadingManga(override val lang: String, private val siteLang: Strin
val totalResults = Regex("""(\d+)""").find(document.select("div.res_info").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0 val totalResults = Regex("""(\d+)""").find(document.select("div.res_info").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0
return MangasPage(mangas, mangaParsedSoFar < totalResults) return MangasPage(mangas, mangaParsedSoFar < totalResults)
} }
override fun searchMangaFromElement(element: Element) = buildManga(element.select("a").first(), element.select("img")?.first()) override fun searchMangaFromElement(element: Element) = buildManga(element.select("a").first()!!, element.select("img").first())
// Build Manga From Element // Build Manga From Element
private fun buildManga(titleElement: Element, thumbnailElement: Element?): SManga { private fun buildManga(titleElement: Element, thumbnailElement: Element?): SManga {
@ -150,9 +150,9 @@ open class MyReadingManga(override val lang: String, private val siteLang: Strin
// too troublesome to achieve 100% accuracy assigning scanlator group during chapterListParse // too troublesome to achieve 100% accuracy assigning scanlator group during chapterListParse
val scanlatedBy = document.select(".entry-terms:has(a[href*=group])").firstOrNull() val scanlatedBy = document.select(".entry-terms:has(a[href*=group])").firstOrNull()
?.select("a[href*=group]")?.joinToString(prefix = "Scanlated by: ") { it.text() } ?.select("a[href*=group]")?.joinToString(prefix = "Scanlated by: ") { it.text() }
val extendedDescription = document.select(".entry-content p:not(p:containsOwn(|)):not(.chapter-class + p)")?.joinToString("\n") { it.text() } val extendedDescription = document.select(".entry-content p:not(p:containsOwn(|)):not(.chapter-class + p)").joinToString("\n") { it.text() }
description = listOfNotNull(basicDescription, scanlatedBy, extendedDescription).joinToString("\n") description = listOfNotNull(basicDescription, scanlatedBy, extendedDescription).joinToString("\n")
status = when (document.select("a[href*=status]")?.first()?.text()) { status = when (document.select("a[href*=status]").first()?.text()) {
"Ongoing" -> SManga.ONGOING "Ongoing" -> SManga.ONGOING
"Completed" -> SManga.COMPLETED "Completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
@ -162,7 +162,7 @@ open class MyReadingManga(override val lang: String, private val siteLang: Strin
thumbnail_url = getThumbnail( thumbnail_url = getThumbnail(
getImage( getImage(
client.newCall(GET("$baseUrl/search/?search=${document.location()}", headers)) client.newCall(GET("$baseUrl/search/?search=${document.location()}", headers))
.execute().asJsoup().select("div.wdm_results div.p_content img").first(), .execute().asJsoup().select("div.wdm_results div.p_content img").first()!!,
), ),
) )
} }
@ -181,16 +181,16 @@ open class MyReadingManga(override val lang: String, private val siteLang: Strin
val date = parseDate(document.select(".entry-time").text()) val date = parseDate(document.select(".entry-time").text())
val mangaUrl = document.baseUri() val mangaUrl = document.baseUri()
val chfirstname = document.select(".chapter-class a[href*=$mangaUrl]")?.first()?.text()?.ifEmpty { "Ch. 1" }?.replaceFirstChar { it.titlecase() } val chfirstname = document.select(".chapter-class a[href*=$mangaUrl]").first()?.text()?.ifEmpty { "Ch. 1" }?.replaceFirstChar { it.titlecase() }
?: "Ch. 1" ?: "Ch. 1"
// create first chapter since its on main manga page // create first chapter since its on main manga page
chapters.add(createChapter("1", document.baseUri(), date, chfirstname)) chapters.add(createChapter("1", document.baseUri(), date, chfirstname))
// see if there are multiple chapters or not // see if there are multiple chapters or not
document.select(chapterListSelector())?.let { it -> document.select(chapterListSelector()).let { it ->
it.forEach { it.forEach {
if (!it.text().contains("Next »", true)) { if (!it.text().contains("Next »", true)) {
val pageNumber = it.text() val pageNumber = it.text()
val chname = document.select(".chapter-class a[href$=/$pageNumber/]")?.text()?.ifEmpty { "Ch. $pageNumber" }?.replaceFirstChar { it.titlecase() } val chname = document.select(".chapter-class a[href$=/$pageNumber/]").text().ifEmpty { "Ch. $pageNumber" }?.replaceFirstChar { it.titlecase() }
?: "Ch. $pageNumber" ?: "Ch. $pageNumber"
chapters.add(createChapter(it.text(), document.baseUri(), date, chname)) chapters.add(createChapter(it.text(), document.baseUri(), date, chname))
} }

View File

@ -33,7 +33,7 @@ data class Title(
get() = "$title_slug|$title_id" get() = "$title_slug|$title_id"
val description: String? val description: String?
get() = Jsoup.parse(story)?.text() get() = Jsoup.parse(story).text()
val thumbnail: String val thumbnail: String
get() = CDN_URL + v_cover_img get() = CDN_URL + v_cover_img

View File

@ -51,7 +51,7 @@ object NHUtils {
} }
fun getNumPages(document: Document): String { fun getNumPages(document: Document): String {
return document.select("#tags > div:nth-child(8) > span > a .name").first().cleanTag() return document.select("#tags > div:nth-child(8) > span > a .name").first()!!.cleanTag()
} }
fun getTime(document: Document): Long { fun getTime(document: Document): Long {

View File

@ -98,7 +98,7 @@ open class NHentai(
title = element.select("a > div").text().replace("\"", "").let { title = element.select("a > div").text().replace("\"", "").let {
if (displayFullTitle) it.trim() else it.shortenTitle() if (displayFullTitle) it.trim() else it.shortenTitle()
} }
thumbnail_url = element.select(".cover img").first().let { img -> thumbnail_url = element.select(".cover img").first()!!.let { img ->
if (img.hasAttr("data-src")) img.attr("abs:data-src") else img.attr("abs:src") if (img.hasAttr("data-src")) img.attr("abs:data-src") else img.attr("abs:src")
} }
} }
@ -247,11 +247,11 @@ open class NHentai(
override fun chapterListSelector() = throw UnsupportedOperationException("Not used") override fun chapterListSelector() = throw UnsupportedOperationException("Not used")
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val script = document.select("script:containsData(media_server)").first().data() val script = document.select("script:containsData(media_server)").first()!!.data()
val media_server = Regex("""media_server\s*:\s*(\d+)""").find(script)?.groupValues!!.get(1) val mediaServer = Regex("""media_server\s*:\s*(\d+)""").find(script)?.groupValues!![1]
return document.select("div.thumbs a > img").mapIndexed { i, img -> return document.select("div.thumbs a > img").mapIndexed { i, img ->
Page(i, "", img.attr("abs:data-src").replace("t.nh", "i.nh").replace("t\\d+.nh".toRegex(), "i$media_server.nh").replace("t.", ".")) Page(i, "", img.attr("abs:data-src").replace("t.nh", "i.nh").replace("t\\d+.nh".toRegex(), "i$mediaServer.nh").replace("t.", "."))
} }
} }

View File

@ -59,7 +59,7 @@ open class NineManga(
title = it.select("li > span:not([class])").text().removeSuffix(" Manga") title = it.select("li > span:not([class])").text().removeSuffix(" Manga")
genre = it.select("li[itemprop=genre] a").joinToString { e -> e.text() } genre = it.select("li[itemprop=genre] a").joinToString { e -> e.text() }
author = it.select("li a[itemprop=author]").text() author = it.select("li a[itemprop=author]").text()
status = parseStatus(it.select("li a.red").first().text()) status = parseStatus(it.select("li a.red").first()!!.text())
description = it.select("p[itemprop=description]").text() description = it.select("p[itemprop=description]").text()
thumbnail_url = it.select("img[itemprop=image]").attr("abs:src") thumbnail_url = it.select("img[itemprop=image]").attr("abs:src")
@ -114,12 +114,12 @@ open class NineManga(
} }
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply { override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
document.select("select#page").first().select("option").forEach { document.select("select#page").first()!!.select("option").forEach {
add(Page(size, baseUrl + it.attr("value"))) add(Page(size, baseUrl + it.attr("value")))
} }
} }
override fun imageUrlParse(document: Document) = document.select("div.pic_box img.manga_pic").first().attr("src").orEmpty() override fun imageUrlParse(document: Document) = document.select("div.pic_box img.manga_pic").first()!!.attr("src").orEmpty()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search/".toHttpUrlOrNull()!!.newBuilder() val url = "$baseUrl/search/".toHttpUrlOrNull()!!.newBuilder()

View File

@ -68,8 +68,8 @@ abstract class NoiseManga(override val lang: String) : ParsedHttpSource() {
override fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers) override fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val mainContent = document.select("div.main-content-page").first() val mainContent = document.select("div.main-content-page").first()!!
val entryContent = mainContent.select("div.entry-content").first() val entryContent = mainContent.select("div.entry-content").first()!!
val descriptionSelector = if (lang == "en") "h4 + h4, h4 + div h4" else "h1 + h4" val descriptionSelector = if (lang == "en") "h4 + h4, h4 + div h4" else "h1 + h4"
val mangaSlug = document.location().replace(baseUrl, "") val mangaSlug = document.location().replace(baseUrl, "")

View File

@ -145,15 +145,15 @@ class PepperCarrot : HttpSource(), ConfigurableSource {
return translatedChapters.map { (number, it) -> return translatedChapters.map { (number, it) ->
SChapter.create().apply { SChapter.create().apply {
url = it.selectFirst(Evaluator.Tag("a")).attr("href").removePrefix(BASE_URL) url = it.selectFirst(Evaluator.Tag("a"))!!.attr("href").removePrefix(BASE_URL)
name = it.selectFirst(Evaluator.Tag("img")).attr("title").run { name = it.selectFirst(Evaluator.Tag("img"))!!.attr("title").run {
val index = lastIndexOf('') val index = lastIndexOf('')
when { when {
index >= 0 -> substring(0, index).trimEnd() index >= 0 -> substring(0, index).trimEnd()
else -> substringBeforeLast('(').trimEnd() else -> substringBeforeLast('(').trimEnd()
} }
} }
date_upload = it.selectFirst(Evaluator.Tag("figcaption")).ownText().let { date_upload = it.selectFirst(Evaluator.Tag("figcaption"))!!.ownText().let {
val date = dateRegex.find(it)!!.value val date = dateRegex.find(it)!!.value
dateFormat.parse(date)!!.time dateFormat.parse(date)!!.time
} }

View File

@ -99,8 +99,8 @@ private fun fetchTitles(client: OkHttpClient, headers: Headers): Map<String, Str
val url = "https://framagit.org/search?project_id=76196&search=core/mod-header.php:4" val url = "https://framagit.org/search?project_id=76196&search=core/mod-header.php:4"
val document = client.newCall(GET(url, headers)).execute().asJsoup() val document = client.newCall(GET(url, headers)).execute().asJsoup()
val result = hashMapOf<String, String>() val result = hashMapOf<String, String>()
for (file in document.selectFirst(Evaluator.Class("search-results")).children()) { for (file in document.selectFirst(Evaluator.Class("search-results"))!!.children()) {
val filename = file.selectFirst(Evaluator.Tag("strong")).ownText() val filename = file.selectFirst(Evaluator.Tag("strong"))!!.ownText()
if (!filename.endsWith(".po") || !filename.startsWith("po/")) continue if (!filename.endsWith(".po") || !filename.startsWith("po/")) continue
val lang = filename.substring(3, filename.length - 3) val lang = filename.substring(3, filename.length - 3)

View File

@ -45,14 +45,14 @@ class Photos18 : HttpSource(), ConfigurableSource {
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
parseCategories(document) parseCategories(document)
val mangas = document.selectFirst(Evaluator.Id("videos")).children().map { val mangas = document.selectFirst(Evaluator.Id("videos"))!!.children().map {
val cardBody = it.selectFirst(Evaluator.Class("card-body")) val cardBody = it.selectFirst(Evaluator.Class("card-body"))!!
val link = cardBody.selectFirst(Evaluator.Tag("a")) val link = cardBody.selectFirst(Evaluator.Tag("a"))!!
SManga.create().apply { SManga.create().apply {
url = link.attr("href").stripLang() url = link.attr("href").stripLang()
title = link.ownText() title = link.ownText()
thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img")).attr("data-src") thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img"))!!.attr("data-src")
genre = cardBody.selectFirst(Evaluator.Tag("label")).ownText() genre = cardBody.selectFirst(Evaluator.Tag("label"))!!.ownText()
status = SManga.COMPLETED status = SManga.COMPLETED
initialized = true initialized = true
} }
@ -98,7 +98,7 @@ class Photos18 : HttpSource(), ConfigurableSource {
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup() val document = response.asJsoup()
val images = document.selectFirst(Evaluator.Id("content")).select(Evaluator.Tag("img")) val images = document.selectFirst(Evaluator.Id("content"))!!.select(Evaluator.Tag("img"))
return images.mapIndexed { index, image -> return images.mapIndexed { index, image ->
Page(index, imageUrl = image.attr("data-src")) Page(index, imageUrl = image.attr("data-src"))
} }
@ -145,12 +145,12 @@ class Photos18 : HttpSource(), ConfigurableSource {
private fun parseCategories(document: Document) { private fun parseCategories(document: Document) {
if (categories.isNotEmpty()) return if (categories.isNotEmpty()) return
val items = document.selectFirst(Evaluator.Id("w3")).children() val items = document.selectFirst(Evaluator.Id("w3"))!!.children()
categories = buildList(items.size + 1) { categories = buildList(items.size + 1) {
add(Pair("All", "")) add(Pair("All", ""))
items.mapTo(this) { items.mapTo(this) {
val value = it.text().substringBefore(" (") val value = it.text().substringBefore(" (")
val queryValue = it.selectFirst(Evaluator.Tag("a")).attr("href").substringAfterLast('/') val queryValue = it.selectFirst(Evaluator.Tag("a"))!!.attr("href").substringAfterLast('/')
Pair(value, queryValue) Pair(value, queryValue)
} }
} }

View File

@ -97,7 +97,7 @@ abstract class SandraAndWoo(
private fun pageImageSelector() = "#comic img" private fun pageImageSelector() = "#comic img"
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val img = document.selectFirst(pageImageSelector()) val img = document.selectFirst(pageImageSelector())!!
val path = img.attr("src") val path = img.attr("src")
return listOf(Page(0, "", "${baseUrl}$path")) return listOf(Page(0, "", "${baseUrl}$path"))

View File

@ -58,7 +58,7 @@ class Tappytoon(override val lang: String) : HttpSource() {
private val apiHeaders by lazy { private val apiHeaders by lazy {
val res = client.newCall(GET(baseUrl, headers)).execute() val res = client.newCall(GET(baseUrl, headers)).execute()
val data = res.asJsoup().getElementById("__NEXT_DATA__") val data = res.asJsoup().getElementById("__NEXT_DATA__")!!
val obj = json.parseToJsonElement(data.data()) val obj = json.parseToJsonElement(data.data())
.jsonObject["props"]!!.jsonObject["initialState"]!! .jsonObject["props"]!!.jsonObject["initialState"]!!
.jsonObject["axios"]!!.jsonObject["headers"]!!.jsonObject .jsonObject["axios"]!!.jsonObject["headers"]!!.jsonObject

View File

@ -50,7 +50,7 @@ abstract class ToomicsGlobal(
override fun popularMangaSelector(): String = "li > div.visual" override fun popularMangaSelector(): String = "li > div.visual"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.select("h4[class$=title]").first().ownText() title = element.select("h4[class$=title]").first()!!.ownText()
// sometimes href contains "/ab/on" at the end and redirects to a chapter instead of manga // sometimes href contains "/ab/on" at the end and redirects to a chapter instead of manga
setUrlWithoutDomain(element.select("a").attr("href").removeSuffix("/ab/on")) setUrlWithoutDomain(element.select("a").attr("href").removeSuffix("/ab/on"))
thumbnail_url = element.select("img").attr("src") thumbnail_url = element.select("img").attr("src")
@ -125,9 +125,9 @@ abstract class ToomicsGlobal(
val num = element.select("div.cell-num").text() val num = element.select("div.cell-num").text()
val numText = if (num.isNotEmpty()) "$num - " else "" val numText = if (num.isNotEmpty()) "$num - " else ""
name = numText + element.select("div.cell-title strong")?.first()?.ownText() name = numText + element.select("div.cell-title strong").first()?.ownText()
chapter_number = num.toFloatOrNull() ?: -1f chapter_number = num.toFloatOrNull() ?: -1f
date_upload = parseChapterDate(element.select("div.cell-time time").text()!!) date_upload = parseChapterDate(element.select("div.cell-time time").text())
scanlator = "Toomics" scanlator = "Toomics"
url = element.select("a").attr("onclick") url = element.select("a").attr("onclick")
.substringAfter("href='") .substringAfter("href='")

View File

@ -65,11 +65,11 @@ class Xinmeitulu : ParsedHttpSource() {
// Details // Details
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
setUrlWithoutDomain(document.selectFirst("link[rel=canonical]").attr("abs:href")) setUrlWithoutDomain(document.selectFirst("link[rel=canonical]")!!.attr("abs:href"))
title = document.select(".container > h1").text() title = document.select(".container > h1").text()
description = document.select(".container > *:not(div)").text() description = document.select(".container > *:not(div)").text()
status = SManga.COMPLETED status = SManga.COMPLETED
thumbnail_url = document.selectFirst("figure img").attr("abs:data-original") thumbnail_url = document.selectFirst("figure img")!!.attr("abs:data-original")
} }
// Chapters // Chapters
@ -77,7 +77,7 @@ class Xinmeitulu : ParsedHttpSource() {
override fun chapterListSelector() = "html" override fun chapterListSelector() = "html"
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.selectFirst("link[rel=canonical]").attr("abs:href")) setUrlWithoutDomain(element.selectFirst("link[rel=canonical]")!!.attr("abs:href"))
name = element.select(".container > h1").text() name = element.select(".container > h1").text()
} }

View File

@ -31,7 +31,7 @@ class XkcdFR : Xkcd("https://xkcd.lapin.org", "fr") {
} }
override fun pageListParse(response: Response) = override fun pageListParse(response: Response) =
response.asJsoup().selectFirst(imageSelector).let { response.asJsoup().selectFirst(imageSelector)!!.let {
// no interactive comics here // no interactive comics here
val img = it.child(2).child(0).child(0) val img = it.child(2).child(0).child(0)

Some files were not shown because too many files have changed in this diff Show More