Fix nullable strings usage

Koitharu 1 year ago
parent 5df1445e29
commit 6abcdd8d4b
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -182,7 +182,7 @@ internal class ComickFunParser(context: MangaLoaderContext) :
var alt = "" var alt = ""
comic.getJSONArray("md_titles").mapJSON { alt += it.getString("title") + " - " } comic.getJSONArray("md_titles").mapJSON { alt += it.getString("title") + " - " }
return manga.copy( return manga.copy(
altTitle = alt.ifEmpty { comic.getStringOrNull("title") }, altTitle = alt.ifEmpty { comic.getStringOrNull("title") }?.nullIfEmpty(),
contentRating = if (jo.getBooleanOrDefault("matureContent", false) contentRating = if (jo.getBooleanOrDefault("matureContent", false)
|| comic.getBooleanOrDefault("hentai", false) || comic.getBooleanOrDefault("hentai", false)
) { ) {
@ -199,7 +199,7 @@ internal class ComickFunParser(context: MangaLoaderContext) :
source = source, source = source,
) )
}, },
author = jo.getJSONArray("artists").optJSONObject(0)?.getString("name"), author = jo.getJSONArray("artists").optJSONObject(0)?.getStringOrNull("name"),
chapters = getChapters(comic.getString("hid")), chapters = getChapters(comic.getString("hid")),
) )
} }

@ -197,7 +197,7 @@ internal class ExHentaiParser(
return manga.copy( return manga.copy(
title = title?.getElementById("gn")?.text()?.cleanupTitle() ?: manga.title, title = title?.getElementById("gn")?.text()?.cleanupTitle() ?: manga.title,
altTitle = title?.getElementById("gj")?.text()?.cleanupTitle() ?: manga.altTitle, altTitle = (title?.getElementById("gj")?.text()?.cleanupTitle() ?: manga.altTitle)?.nullIfEmpty(),
publicUrl = doc.baseUri().ifEmpty { manga.publicUrl }, publicUrl = doc.baseUri().ifEmpty { manga.publicUrl },
rating = root.getElementById("rating_label")?.text() rating = root.getElementById("rating_label")?.text()
?.substringAfterLast(' ') ?.substringAfterLast(' ')

@ -182,7 +182,7 @@ internal abstract class MangaFireParser(
return manga.copy( return manga.copy(
title = document.selectFirstOrThrow(".info > h1").ownText(), title = document.selectFirstOrThrow(".info > h1").ownText(),
altTitle = document.selectFirst(".info > h6")?.ownText(), altTitle = document.selectFirst(".info > h6")?.ownTextOrNull(),
rating = document.selectFirst("div.rating-box")?.attr("data-score") rating = document.selectFirst("div.rating-box")?.attr("data-score")
?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN, ?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN,
coverUrl = document.selectFirstOrThrow("div.manga-detail div.poster img") coverUrl = document.selectFirstOrThrow("div.manga-detail div.poster img")
@ -212,7 +212,7 @@ internal abstract class MangaFireParser(
} }
}, },
author = document.select("div.meta a[href*=/author/]") author = document.select("div.meta a[href*=/author/]")
.joinToString { it.ownText() }, .joinToString { it.ownText() }.nullIfEmpty(),
description = document.selectFirstOrThrow("#synopsis div.modal-content").html(), description = document.selectFirstOrThrow("#synopsis div.modal-content").html(),
chapters = getChapters(manga.url, document), chapters = getChapters(manga.url, document),
) )

@ -179,9 +179,9 @@ internal class MangaPark(context: MangaLoaderContext) :
val nsfw = tags.any { t -> t.key == "hentai" || t.key == "adult" } val nsfw = tags.any { t -> t.key == "hentai" || t.key == "adult" }
val dateFormat = SimpleDateFormat("dd/MM/yyyy", sourceLocale) val dateFormat = SimpleDateFormat("dd/MM/yyyy", sourceLocale)
manga.copy( manga.copy(
altTitle = doc.selectFirst("div[q:key=tz_2]")?.text().orEmpty(), altTitle = doc.selectFirst("div[q:key=tz_2]")?.textOrNull(),
author = doc.selectFirst("div[q:key=tz_4]")?.text().orEmpty(), author = doc.selectFirst("div[q:key=tz_4]")?.textOrNull(),
description = doc.selectFirst("react-island[q:key=0a_9]")?.html().orEmpty(), description = doc.selectFirst("react-island[q:key=0a_9]")?.html(),
state = when (doc.selectFirst("span[q:key=Yn_5]")?.text()?.lowercase()) { state = when (doc.selectFirst("span[q:key=Yn_5]")?.text()?.lowercase()) {
"ongoing" -> MangaState.ONGOING "ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED "completed" -> MangaState.FINISHED

@ -172,10 +172,10 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
return manga.copy( return manga.copy(
title = document.selectFirst("h2.manga-name")!!.ownText(), title = document.selectFirst("h2.manga-name")!!.ownText(),
altTitle = document.selectFirst("div.manga-name-or")?.ownText(), altTitle = document.selectFirst("div.manga-name-or")?.ownTextOrNull(),
rating = document.selectFirst("div.anisc-info .item:contains(score:) > .name") rating = document.selectFirst("div.anisc-info .item:contains(score:) > .name")
?.text()?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN, ?.text()?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN,
coverUrl = document.selectFirst(".manga-poster > img")!!.attr("src"), coverUrl = document.selectFirst(".manga-poster > img")?.attrAsAbsoluteUrlOrNull("src"),
tags = document.select("div.genres > a[href*=/genre/]").mapNotNullToSet { tags = document.select("div.genres > a[href*=/genre/]").mapNotNullToSet {
val tag = it.ownText() val tag = it.ownText()
if (tag == "Hentai") { if (tag == "Hentai") {
@ -202,8 +202,8 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
} }
}, },
author = document.select("div.anisc-info a[href*=/author/]") author = document.select("div.anisc-info a[href*=/author/]")
.joinToString { it.ownText().replace(", ", " ") }, .joinToString { it.ownText().replace(", ", " ") }.nullIfEmpty(),
description = document.select("div.description").text(), description = document.select("div.description").html(),
chapters = parseChapters(document), chapters = parseChapters(document),
source = source, source = source,
) )

@ -269,7 +269,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) :
altTitle = name, altTitle = name,
coverUrl = cover.first, coverUrl = cover.first,
largeCoverUrl = cover.second, largeCoverUrl = cover.second,
author = tags?.filter { it.type == "artist" }?.joinToString { it.name.toCamelCase() }, author = tags?.filter { it.type == "artist" }?.joinToString { it.name.toCamelCase() }?.nullIfEmpty(),
contentRating = ContentRating.ADULT, contentRating = ContentRating.ADULT,
tags = tags?.mapToSet { tags = tags?.mapToSet {
MangaTag( MangaTag(

@ -88,8 +88,6 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val root = doc.selectFirstOrThrow(".card-body .col-lg-9") val root = doc.selectFirstOrThrow(".card-body .col-lg-9")
return manga.copy( return manga.copy(
altTitle = null,
state = null,
tags = root.select(".flex-wrap a").mapToSet { a -> tags = root.select(".flex-wrap a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
@ -97,8 +95,7 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex
source = source, source = source,
) )
}, },
author = null, description = root.selectFirstOrThrow(".card-text").html(),
description = root.selectFirstOrThrow(".card-text").text(),
chapters = doc.select(".card-body a.btn-fixed-width").mapChapters(reversed = true) { i, a -> chapters = doc.select(".card-body a.btn-fixed-width").mapChapters(reversed = true) { i, a ->
val url = a.attrAsRelativeUrl("href") val url = a.attrAsRelativeUrl("href")
MangaChapter( MangaChapter(

@ -148,7 +148,6 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
} }
} }
return manga.copy( return manga.copy(
altTitle = null,
state = when (doc.selectFirstOrThrow(".full-list-info:contains(الحالة:) a").text()) { state = when (doc.selectFirstOrThrow(".full-list-info:contains(الحالة:) a").text()) {
"مستمرة" -> MangaState.ONGOING "مستمرة" -> MangaState.ONGOING
"مكتمل" -> MangaState.FINISHED "مكتمل" -> MangaState.FINISHED
@ -162,8 +161,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
source = source, source = source,
) )
}, },
author = null, description = doc.selectFirstOrThrow(".review-content").html(),
description = doc.selectFirstOrThrow(".review-content").text(),
chapters = run { chapters = run {
if (maxPageChapter == 1) { if (maxPageChapter == 1) {
parseChapters(doc) parseChapters(doc)

@ -124,8 +124,7 @@ internal abstract class CupFoxParser(
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
altTitle = doc.selectFirst(selectMangaDetailsAltTitle)?.text()?.substringAfter(""), altTitle = doc.selectFirst(selectMangaDetailsAltTitle)?.text()?.substringAfter("")?.nullIfEmpty(),
state = null,
tags = doc.select(selectMangaDetailsTags).mapToSet { a -> tags = doc.select(selectMangaDetailsTags).mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'), key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
@ -133,9 +132,8 @@ internal abstract class CupFoxParser(
source = source, source = source,
) )
}, },
author = doc.selectFirst(selectMangaDetailsAuthor)?.text()?.substringAfter(""), author = doc.selectFirst(selectMangaDetailsAuthor)?.text()?.substringAfter("")?.nullIfEmpty(),
description = doc.selectFirst(selectMangaDescription) description = doc.selectFirst(selectMangaDescription)?.html(),
?.html(),
chapters = doc.select(selectMangaChapters) chapters = doc.select(selectMangaChapters)
.mapChapters { i, li -> .mapChapters { i, li ->
val a = li.selectFirstOrThrow("a") val a = li.selectFirstOrThrow("a")

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.en package org.koitharu.kotatsu.parsers.site.en
import org.koitharu.kotatsu.parsers.ErrorMessages
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser import org.koitharu.kotatsu.parsers.PagedMangaParser
@ -96,7 +95,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
return doc.select("ul.lf-list li a").mapToSet { a -> return doc.select("ul.lf-list li a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
title = a.text(), title = a.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
} }
@ -105,7 +104,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
altTitle = doc.selectFirstOrThrow("div.anime-top h1.title").text(), altTitle = doc.selectFirstOrThrow("div.anime-top h1.title").textOrNull(),
state = when (doc.selectFirstOrThrow("ul.anime-genres li.status a").text()) { state = when (doc.selectFirstOrThrow("ul.anime-genres li.status a").text()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED "Completed" -> MangaState.FINISHED
@ -114,14 +113,14 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
tags = doc.select("ul.anime-genres li a").mapToSet { a -> tags = doc.select("ul.anime-genres li a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
title = a.text(), title = a.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
}, },
author = doc.selectFirst("table.full-table tr:contains(Author:) td:nth-child(2)")?.text(), author = doc.selectFirst("table.full-table tr:contains(Author:) td:nth-child(2)")?.textOrNull(),
description = doc.selectFirstOrThrow("div.detail-desc-content p").text(), description = doc.selectFirstOrThrow("div.detail-desc-content p").html(),
chapters = doc.select("ul.basic-list li").let { elements -> chapters = doc.select("ul.basic-list li").let { elements ->
elements.mapChapters() { i, li -> elements.mapChapters { i, li ->
val a = li.selectFirstOrThrow("a.ch-name") val a = li.selectFirstOrThrow("a.ch-name")
val url = a.attrAsRelativeUrl("href") val url = a.attrAsRelativeUrl("href")
val name = a.text() val name = a.text()

@ -144,7 +144,7 @@ internal class DynastyScans(context: MangaLoaderContext) :
private fun Element.parseTags() = select("a").mapToSet { private fun Element.parseTags() = select("a").mapToSet {
MangaTag( MangaTag(
key = it.attr("href").removeSuffix('/').substringAfterLast('/'), key = it.attr("href").removeSuffix('/').substringAfterLast('/'),
title = it.text(), title = it.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
} }
@ -157,7 +157,6 @@ internal class DynastyScans(context: MangaLoaderContext) :
.find { it.ownText() == "This manga has been licensed" } .find { it.ownText() == "This manga has been licensed" }
?.nextElementSibling()?.html() ?.nextElementSibling()?.html()
return manga.copy( return manga.copy(
altTitle = null,
state = when (root.select("h2.tag-title small").last()?.text()) { state = when (root.select("h2.tag-title small").last()?.text()) {
"— Ongoing" -> MangaState.ONGOING "— Ongoing" -> MangaState.ONGOING
"— Completed", "— Completed and Licensed" -> MangaState.FINISHED "— Completed", "— Completed and Licensed" -> MangaState.FINISHED
@ -165,10 +164,8 @@ internal class DynastyScans(context: MangaLoaderContext) :
"— On Hiatus" -> MangaState.PAUSED "— On Hiatus" -> MangaState.PAUSED
else -> null else -> null
}, },
coverUrl = root.selectFirst("img.thumbnail")?.src() coverUrl = root.selectFirst("img.thumbnail")?.src(),
.orEmpty(), // It is needed if the manga was found via the search.
tags = root.selectFirstOrThrow("div.tag-tags").parseTags(), tags = root.selectFirstOrThrow("div.tag-tags").parseTags(),
author = null,
description = licensedText, description = licensedText,
chapters = chapters, chapters = chapters,
) )

@ -109,7 +109,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val chaptersDeferred = async { loadChapters(manga.url) } val chaptersDeferred = async { loadChapters(manga.url) }
manga.copy( manga.copy(
altTitle = doc.selectFirstOrThrow(".alternative-title").text(), altTitle = doc.selectFirstOrThrow(".alternative-title").textOrNull(),
state = when (doc.selectFirstOrThrow(".header-stats span:contains(Status) strong").text()) { state = when (doc.selectFirstOrThrow(".header-stats span:contains(Status) strong").text()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED "Completed" -> MangaState.FINISHED
@ -122,7 +122,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
source = source, source = source,
) )
}, },
author = doc.selectFirstOrThrow(".author").text(), author = doc.selectFirstOrThrow(".author").textOrNull(),
description = doc.selectFirstOrThrow(".description").html(), description = doc.selectFirstOrThrow(".description").html(),
chapters = chaptersDeferred.await(), chapters = chaptersDeferred.await(),
) )

@ -100,9 +100,9 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) :
val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href") val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href")
val chaptersDeferred = async { loadChapters(firstChapter) } val chaptersDeferred = async { loadChapters(firstChapter) }
manga.copy( manga.copy(
description = doc.selectFirst("dd.text-justify.text-break")?.text().orEmpty(), description = doc.selectFirst("dd.text-justify.text-break")?.html(),
altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }, altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }.nullIfEmpty(),
author = doc.select("a[href*=author]").text(), author = doc.select("a[href*=author]").textOrNull(),
state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) { state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"" -> MangaState.FINISHED "" -> MangaState.FINISHED

@ -147,6 +147,7 @@ internal class Manhwa18Com(context: MangaLoaderContext) :
val author = cardInfoElement?.selectFirst(".info-name:contains(Author)")?.parent() val author = cardInfoElement?.selectFirst(".info-name:contains(Author)")?.parent()
?.select("a") ?.select("a")
?.joinToString(", ") { it.text() } ?.joinToString(", ") { it.text() }
?.nullIfEmpty()
val availableTags = tagsMap.get() val availableTags = tagsMap.get()
val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre)")?.parent() val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre)")?.parent()
?.select("a") ?.select("a")
@ -163,7 +164,8 @@ internal class Manhwa18Com(context: MangaLoaderContext) :
} }
return manga.copy( return manga.copy(
altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownText()?.removePrefix(": "), altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownTextOrNull()
?.removePrefix(": "),
author = author, author = author,
description = docs.selectFirst(".series-summary .summary-content")?.html(), description = docs.selectFirst(".series-summary .summary-content")?.html(),
tags = tags.orEmpty(), tags = tags.orEmpty(),

@ -147,6 +147,7 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
val author = cardInfoElement?.selectFirst(".info-name:contains(Author)")?.parent() val author = cardInfoElement?.selectFirst(".info-name:contains(Author)")?.parent()
?.select("a") ?.select("a")
?.joinToString(", ") { it.text() } ?.joinToString(", ") { it.text() }
?.nullIfEmpty()
val availableTags = tagsMap.get() val availableTags = tagsMap.get()
val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre)")?.parent() val tags = cardInfoElement?.selectFirst(".info-name:contains(Genre)")?.parent()
?.select("a") ?.select("a")
@ -163,7 +164,8 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
} }
return manga.copy( return manga.copy(
altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownText()?.removePrefix(": "), altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownTextOrNull()
?.removePrefix(": "),
author = author, author = author,
description = docs.selectFirst(".series-summary .summary-content")?.html(), description = docs.selectFirst(".series-summary .summary-content")?.html(),
tags = tags.orEmpty(), tags = tags.orEmpty(),

@ -63,15 +63,14 @@ internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(con
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH)
return manga.copy( return manga.copy(
altTitle = null,
state = when (doc.select(".status span").last()?.text()) { state = when (doc.select(".status span").last()?.text()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"Done" -> MangaState.FINISHED "Done" -> MangaState.FINISHED
else -> null else -> null
}, },
tags = emptySet(), tags = emptySet(),
author = doc.select(".author span").last()?.text(), author = doc.selectLast(".author span")?.textOrNull(),
description = doc.selectFirstOrThrow(".summary").text(), description = doc.selectFirstOrThrow(".summary").html(),
chapters = doc.select(".chap-section .chap") chapters = doc.select(".chap-section .chap")
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->
val a = div.selectFirstOrThrow("a") val a = div.selectFirstOrThrow("a")

@ -189,8 +189,8 @@ internal abstract class FmreaderParser(
else -> null else -> null
} }
} }
val alt = doc.body().selectFirst(selectAlt)?.text()?.replace("Other names", "") val alt = doc.body().selectFirst(selectAlt)?.text()?.replace("Other names", "")?.nullIfEmpty()
val auth = doc.body().selectFirst(selectAut)?.text() val auth = doc.body().selectFirst(selectAut)?.textOrNull()
manga.copy( manga.copy(
tags = doc.body().select(selectTag).mapToSet { a -> tags = doc.body().select(selectTag).mapToSet { a ->
MangaTag( MangaTag(

@ -125,10 +125,8 @@ internal abstract class FoolSlideParser(
} }
manga.copy( manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl, coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc.orEmpty(), description = desc?.nullIfEmpty(),
altTitle = null, author = author?.nullIfEmpty(),
author = author.orEmpty(),
state = null,
chapters = chapters, chapters = chapters,
) )
} }

@ -35,10 +35,8 @@ internal class Seinagi(context: MangaLoaderContext) :
} }
manga.copy( manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl, coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc, description = desc?.nullIfEmpty(),
altTitle = null, author = author?.nullIfEmpty(),
author = author,
state = null,
chapters = chapters, chapters = chapters,
) )
} }

@ -34,10 +34,8 @@ internal class Pzykosis666hFansub(context: MangaLoaderContext) :
} }
manga.copy( manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl, coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc.orEmpty(), description = desc?.nullIfEmpty(),
altTitle = null, author = author?.nullIfEmpty(),
author = author.orEmpty(),
state = null,
chapters = chapters, chapters = chapters,
) )
} }

@ -34,10 +34,8 @@ internal class SeinagiAdulto(context: MangaLoaderContext) :
} }
manga.copy( manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search coverUrl = doc.selectFirst(".thumbnail img")?.src().orEmpty(),// for manga result on search
description = desc.orEmpty(), description = desc?.nullIfEmpty(),
altTitle = null, author = author?.nullIfEmpty(),
author = author.orEmpty(),
state = null,
chapters = chapters, chapters = chapters,
) )
} }

@ -148,7 +148,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
val root = webClient.httpGet(mangaUrl).parseHtml() val root = webClient.httpGet(mangaUrl).parseHtml()
.requireElementById("container_manga_show") .requireElementById("container_manga_show")
return manga.copy( return manga.copy(
altTitle = root.selectFirst(".component-manga-title_alt")?.text(), altTitle = root.selectFirst(".component-manga-title_alt")?.textOrNull(),
description = root.selectFirst(".datas_synopsis")?.html().assertNotNull("description") description = root.selectFirst(".datas_synopsis")?.html().assertNotNull("description")
?: manga.description, ?: manga.description,
state = when (root.selectFirst(".datas_more-status-data")?.textOrNull().assertNotNull("status")) { state = when (root.selectFirst(".datas_more-status-data")?.textOrNull().assertNotNull("status")) {

@ -171,7 +171,6 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH) val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH)
return manga.copy( return manga.copy(
altTitle = null,
tags = root.select("div.serieGenre span").mapToSet { span -> tags = root.select("div.serieGenre span").mapToSet { span ->
MangaTag( MangaTag(
key = span.text(), key = span.text(),
@ -179,8 +178,8 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
source = source, source = source,
) )
}, },
coverUrl = root.selectFirst("div.serieImg img")?.attr("src").orEmpty(), coverUrl = root.selectFirst("div.serieImg img")?.attr("src"),
author = root.select("div.serieAdd p:contains(Auteur:) strong").text(), author = root.select("div.serieAdd p:contains(Auteur:) strong").textOrNull(),
description = root.selectFirst("div.serieDescription div")?.html(), description = root.selectFirst("div.serieDescription div")?.html(),
chapters = root.select("div.chapterList a") chapters = root.select("div.chapterList a")
.mapChapters(reversed = true) { i, a -> .mapChapters(reversed = true) { i, a ->

@ -87,7 +87,7 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context,
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE) val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
return manga.copy( return manga.copy(
altTitle = root.select("ul.pmovie__list li:contains(Nom Alternatif:)").text() altTitle = root.select("ul.pmovie__list li:contains(Nom Alternatif:)").text()
.replace("Nom Alternatif:", ""), .replace("Nom Alternatif:", "").nullIfEmpty(),
state = when (root.select("ul.pmovie__list li:contains(Status:)").text()) { state = when (root.select("ul.pmovie__list li:contains(Status:)").text()) {
"Status: OnGoing", "Status: En cours" -> MangaState.ONGOING "Status: OnGoing", "Status: En cours" -> MangaState.ONGOING
"Status: Fini" -> MangaState.FINISHED "Status: Fini" -> MangaState.FINISHED
@ -97,11 +97,13 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context,
.replace("Genre:", "").split(" / ").mapToSet { tag -> .replace("Genre:", "").split(" / ").mapToSet { tag ->
MangaTag( MangaTag(
key = tag.lowercase(), key = tag.lowercase(),
title = tag, title = tag.toTitleCase(sourceLocale),
source = source, source = source,
) )
}, },
author = root.select("ul.pmovie__list li:contains(Artist(s):)").text().replace("Artist(s):", ""), author = root.select("ul.pmovie__list li:contains(Artist(s):)").text()
.replace("Artist(s):", "")
.nullIfEmpty(),
description = root.selectFirst("div.pmovie__text")?.html(), description = root.selectFirst("div.pmovie__text")?.html(),
chapters = root.select("ul li div.chapter") chapters = root.select("ul li div.chapter")
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->

@ -10,6 +10,7 @@ import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.network.UserAgents import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.getFloatOrDefault import org.koitharu.kotatsu.parsers.util.json.getFloatOrDefault
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.json.mapJSON
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -161,15 +162,14 @@ internal class LugnicaScans(context: MangaLoaderContext) :
val slug = manga.url.substringAfterLast("/") val slug = manga.url.substringAfterLast("/")
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE) val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
return manga.copy( return manga.copy(
altTitle = null,
state = when (jsonManga.getString("status")) { state = when (jsonManga.getString("status")) {
"0" -> MangaState.ONGOING "0" -> MangaState.ONGOING
"1" -> MangaState.FINISHED "1" -> MangaState.FINISHED
"3" -> MangaState.ABANDONED "3" -> MangaState.ABANDONED
else -> null else -> null
}, },
author = jsonManga.getString("author"), author = jsonManga.getStringOrNull("author"),
description = jsonManga.getString("description"), description = jsonManga.getStringOrNull("description"),
chapters = chapters.mapChapters { i, it -> chapters = chapters.mapChapters { i, it ->
val id = it.substringAfter("\"chapter\":").substringBefore(",") val id = it.substringAfter("\"chapter\":").substringBefore(",")
val url = "https://$domain/api/get/chapter/$slug/$id" val url = "https://$domain/api/get/chapter/$slug/$id"

@ -99,8 +99,8 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte
val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href") val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href")
val chaptersDeferred = async { loadChapters(firstChapter) } val chaptersDeferred = async { loadChapters(firstChapter) }
manga.copy( manga.copy(
description = doc.selectFirst("dd.text-justify.text-break")?.text().orEmpty(), description = doc.selectFirst("dd.text-justify.text-break")?.html(),
altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }, altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }.nullIfEmpty(),
author = doc.select("a[href*=author]").text(), author = doc.select("a[href*=author]").text(),
state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) { state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) {
"En Cours" -> MangaState.ONGOING "En Cours" -> MangaState.ONGOING

@ -113,7 +113,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
val chaptersDeferred = getChapters(doc) val chaptersDeferred = getChapters(doc)
val desc = doc.selectFirstOrThrow("div.desc").html().nullIfEmpty() val desc = doc.selectFirstOrThrow("div.desc").html().nullIfEmpty()
val alt = doc.body().select("div.infox span.alter").text().nullIfEmpty() val alt = doc.body().select("div.infox span.alter").textOrNull()
val aut = doc.select("div.spe span")[2].text().replace("Auteur:", "").nullIfEmpty() val aut = doc.select("div.spe span")[2].text().replace("Auteur:", "").nullIfEmpty()
manga.copy( manga.copy(
tags = doc.select("div.spe span:contains(Genres) a").mapToSet { a -> tags = doc.select("div.spe span:contains(Genres) a").mapToSet { a ->

@ -124,7 +124,7 @@ internal class ScantradUnion(context: MangaLoaderContext) :
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE) val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
return manga.copy( return manga.copy(
altTitle = root.select(".divider2:contains(Noms associés :)").firstOrNull()?.text(), altTitle = root.select(".divider2:contains(Noms associés :)").firstOrNull()?.textOrNull(),
state = when (root.select(".label.label-primary")[2].text()) { state = when (root.select(".label.label-primary")[2].text()) {
"En cours" -> MangaState.ONGOING "En cours" -> MangaState.ONGOING
"Terminé", "Abondonné", "One Shot" -> MangaState.FINISHED "Terminé", "Abondonné", "One Shot" -> MangaState.FINISHED
@ -137,7 +137,7 @@ internal class ScantradUnion(context: MangaLoaderContext) :
source = source, source = source,
) )
}, },
author = root.select("div.project-details a[href*=auteur]").text(), author = root.select("div.project-details a[href*=auteur]").textOrNull(),
description = root.selectFirst("p.sContent")?.html(), description = root.selectFirst("p.sContent")?.html(),
chapters = root.select("div.chapter-list li") chapters = root.select("div.chapter-list li")
.mapChapters(reversed = true) { i, li -> .mapChapters(reversed = true) { i, li ->

@ -183,7 +183,7 @@ internal abstract class FuzzyDoodleParser(
} }
manga.copy( manga.copy(
altTitle = doc.selectLast(selectAltTitle)?.text(), altTitle = doc.selectLast(selectAltTitle)?.textOrNull(),
state = when (doc.selectFirst(selectState)?.text()?.lowercase().orEmpty()) { state = when (doc.selectFirst(selectState)?.text()?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING in ongoing -> MangaState.ONGOING
in finished -> MangaState.FINISHED in finished -> MangaState.FINISHED
@ -191,8 +191,8 @@ internal abstract class FuzzyDoodleParser(
in paused -> MangaState.PAUSED in paused -> MangaState.PAUSED
else -> null else -> null
}, },
author = doc.selectFirst(selectAuthor)?.text().orEmpty(), author = doc.selectFirst(selectAuthor)?.textOrNull(),
description = doc.select(selectDescription).text(), description = doc.select(selectDescription).html(),
tags = doc.select(selectTagManga).mapToSet { tags = doc.select(selectTagManga).mapToSet {
val key = it.attr("href").substringAfterLast('=') val key = it.attr("href").substringAfterLast('=')
MangaTag( MangaTag(

@ -89,7 +89,7 @@ internal abstract class HeanCmsAlt(
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale) val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
return manga.copy( return manga.copy(
altTitle = doc.selectFirst(selectAlt)?.text().orEmpty(), altTitle = doc.selectFirst(selectAlt)?.textOrNull(),
description = doc.selectFirst(selectDesc)?.html(), description = doc.selectFirst(selectDesc)?.html(),
chapters = doc.select(selectChapter) chapters = doc.select(selectChapter)
.mapChapters(reversed = true) { i, a -> .mapChapters(reversed = true) { i, a ->

@ -25,7 +25,7 @@ internal class Brakeout(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale) val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
return manga.copy( return manga.copy(
altTitle = doc.selectFirst(selectAlt)?.text().orEmpty(), altTitle = doc.selectFirst(selectAlt)?.textOrNull(),
description = doc.selectFirstOrThrow(selectDesc).html(), description = doc.selectFirstOrThrow(selectDesc).html(),
chapters = doc.select(selectChapter) chapters = doc.select(selectChapter)
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->

@ -96,9 +96,9 @@ internal class HentaiCrot(context: MangaLoaderContext) :
val fullUrl = manga.url.toAbsoluteUrl(domain) val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
return manga.copy( return manga.copy(
description = doc.selectFirst("div.entry-content p")?.text().orEmpty(), description = doc.selectFirst("div.entry-content p")?.html(),
altTitle = doc.selectFirst("div.entry-content ul li:contains(Alternative Name(s) :) em")?.text().orEmpty(), altTitle = doc.selectFirst("div.entry-content ul li:contains(Alternative Name(s) :) em")?.textOrNull(),
author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.text().orEmpty(), author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.textOrNull(),
state = null, state = null,
chapters = listOf( chapters = listOf(
MangaChapter( MangaChapter(

@ -96,9 +96,9 @@ internal class PixHentai(context: MangaLoaderContext) :
val fullUrl = manga.url.toAbsoluteUrl(domain) val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
return manga.copy( return manga.copy(
description = doc.selectFirst("div.entry-content p")?.text().orEmpty(), description = doc.selectFirst("div.entry-content p")?.html(),
altTitle = doc.selectFirst("div.entry-content ul li:contains(Alternative Name(s) :) em")?.text().orEmpty(), altTitle = doc.selectFirst("div.entry-content ul li:contains(Alternative Name(s) :) em")?.textOrNull(),
author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.text().orEmpty(), author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.textOrNull(),
state = null, state = null,
chapters = listOf( chapters = listOf(
MangaChapter( MangaChapter(

@ -150,8 +150,7 @@ internal abstract class LikeMangaParser(
} }
} }
return manga.copy( return manga.copy(
altTitle = doc.selectFirstOrThrow(".list-info li.othername h2").text(), altTitle = doc.selectFirst(".list-info li.othername h2")?.textOrNull(),
state = null,
tags = doc.select("li.kind a").mapToSet { a -> tags = doc.select("li.kind a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'), key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
@ -159,8 +158,8 @@ internal abstract class LikeMangaParser(
source = source, source = source,
) )
}, },
author = doc.select("li.author p").last()?.text(), author = doc.selectLast("li.author p")?.textOrNull(),
description = doc.requireElementById("summary_shortened").text(), description = doc.requireElementById("summary_shortened").html(),
chapters = run { chapters = run {
if (maxPageChapter == 1) { if (maxPageChapter == 1) {
parseChapters(doc) parseChapters(doc)

@ -28,11 +28,11 @@ internal class MangasNoSekai(context: MangaLoaderContext) :
source = source, source = source,
) )
}, },
author = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Autor)) p a")?.text() author = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Autor)) p a")
.orEmpty(), ?.textOrNull(),
description = body.selectFirst("#section-sinopsis p")?.text().orEmpty(), description = body.selectFirst("#section-sinopsis p")?.text().orEmpty(),
altTitle = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Otros nombres)) p")?.text() altTitle = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Otros nombres)) p")
.orEmpty(), ?.textOrNull(),
state = body.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Estado)) p") state = body.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Estado)) p")
?.let { ?.let {
when (it.text()) { when (it.text()) {

@ -186,8 +186,8 @@ internal abstract class MadthemeParser(
source = source, source = source,
) )
}, },
description = desc.orEmpty(), description = desc?.nullIfEmpty(),
altTitle = alt.orEmpty(), altTitle = alt.nullIfEmpty(),
state = state, state = state,
chapters = chaptersDeferred.await(), chapters = chaptersDeferred.await(),
contentRating = if (nsfw || manga.isNsfw) { contentRating = if (nsfw || manga.isNsfw) {

@ -154,8 +154,8 @@ internal abstract class Manga18Parser(
else -> null else -> null
} }
} }
val alt = body.selectFirst(selectAlt)?.text().takeIf { it != "Updating" || it.isNotEmpty() } val alt = body.selectFirst(selectAlt)?.textOrNull().takeUnless { it == "Updating" }
val author = body.selectFirst(selectAuthor)?.text().takeIf { it != "Updating" } val author = body.selectFirst(selectAuthor)?.textOrNull()?.takeUnless { it == "Updating" }
manga.copy( manga.copy(
tags = doc.body().select(selectTag).mapToSet { a -> tags = doc.body().select(selectTag).mapToSet { a ->
MangaTag( MangaTag(
@ -164,7 +164,7 @@ internal abstract class Manga18Parser(
source = source, source = source,
) )
}, },
description = desc.orEmpty(), description = desc?.nullIfEmpty(),
altTitle = alt, altTitle = alt,
author = author, author = author,
state = state, state = state,

@ -107,7 +107,7 @@ internal abstract class MangAdventureParser(
.addEncodedQueryParameter("date_format", "timestamp").get() .addEncodedQueryParameter("date_format", "timestamp").get()
return manga.copy( return manga.copy(
description = details.getStringOrNull("description"), description = details.getStringOrNull("description"),
altTitle = details.getJSONArray("aliases").joinToString(), altTitle = details.getJSONArray("aliases").joinToString().nullIfEmpty(),
author = buildString { author = buildString {
val authors = details.getJSONArray("authors") val authors = details.getJSONArray("authors")
val artists = details.getJSONArray("artists") val artists = details.getJSONArray("artists")

@ -155,13 +155,13 @@ internal abstract class MangaWorldParser(
tags = tags, tags = tags,
author = div.selectFirst(".author a")?.text(), author = div.selectFirst(".author a")?.text(),
state = state =
when (div.selectFirst(".status a")?.text()?.lowercase()) { when (div.selectFirst(".status a")?.text()?.lowercase()) {
"in corso" -> MangaState.ONGOING "in corso" -> MangaState.ONGOING
"finito" -> MangaState.FINISHED "finito" -> MangaState.FINISHED
"droppato" -> MangaState.ABANDONED "droppato" -> MangaState.ABANDONED
"in pausa" -> MangaState.PAUSED "in pausa" -> MangaState.PAUSED
else -> null else -> null
}, },
source = source, source = source,
isNsfw = isNsfwSource, isNsfw = isNsfwSource,
) )
@ -184,30 +184,31 @@ internal abstract class MangaWorldParser(
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
altTitle = altTitle =
doc.selectFirst(".meta-data .font-weight-bold:contains(Titoli alternativi:)") doc.selectFirst(".meta-data .font-weight-bold:contains(Titoli alternativi:)")
?.parent() ?.parent()
?.ownText() ?.ownText()
?.substringAfter(": ") ?.substringAfter(": ")
?.trim(), ?.trim()
?.nullIfEmpty(),
description = doc.getElementById("noidungm")?.text().orEmpty(), description = doc.getElementById("noidungm")?.text().orEmpty(),
chapters = chapters =
doc.select(".chapters-wrapper .chapter a").mapChapters(reversed = true) { i, a -> doc.select(".chapters-wrapper .chapter a").mapChapters(reversed = true) { i, a ->
val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain)
MangaChapter( MangaChapter(
id = generateUid(url), id = generateUid(url),
name = a.selectFirst("span.d-inline-block")?.text() ?: "Chapter : ${i + 1f}", name = a.selectFirst("span.d-inline-block")?.text() ?: "Chapter : ${i + 1f}",
number = i + 1f, number = i + 1f,
volume = 0, volume = 0,
url = "$url?style=list", url = "$url?style=list",
scanlator = null, scanlator = null,
uploadDate = uploadDate =
SimpleDateFormat("dd MMMM yyyy", Locale.ITALIAN).tryParse( SimpleDateFormat("dd MMMM yyyy", Locale.ITALIAN).tryParse(
a.selectFirst(".chap-date")?.text(), a.selectFirst(".chap-date")?.text(),
), ),
branch = null, branch = null,
source = source, source = source,
) )
}, },
) )
} }

@ -193,14 +193,14 @@ internal abstract class MmrcmsParser(
else -> null else -> null
} }
} }
val alt = doc.body().selectFirst(selectAlt)?.nextElementSibling()?.text() val alt = doc.body().selectFirst(selectAlt)?.nextElementSibling()?.textOrNull()
val auth = doc.body().selectFirst(selectAut)?.nextElementSibling()?.text() val auth = doc.body().selectFirst(selectAut)?.nextElementSibling()?.textOrNull()
val tags = doc.body().selectFirst(selectTag)?.nextElementSibling()?.select("a") ?: emptySet() val tags = doc.body().selectFirst(selectTag)?.nextElementSibling()?.select("a") ?: emptySet()
manga.copy( manga.copy(
tags = tags.mapToSet { a -> tags = tags.mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'), key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text().toTitleCase(), title = a.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
}, },

@ -57,14 +57,14 @@ internal class Onma(context: MangaLoaderContext) :
else -> null else -> null
} }
} }
val alt = doc.body().selectFirst(selectAlt)?.text() val alt = doc.body().selectFirst(selectAlt)?.textOrNull()
val auth = doc.body().selectFirst(selectAut)?.text() val auth = doc.body().selectFirst(selectAut)?.textOrNull()
val tags = doc.body().selectFirst(selectTag)?.select("a") ?: emptySet() val tags = doc.body().selectFirst(selectTag)?.select("a") ?: emptySet()
manga.copy( manga.copy(
tags = tags.mapToSet { a -> tags = tags.mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'), key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text().toTitleCase(), title = a.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
}, },

@ -184,7 +184,6 @@ internal abstract class NepnepParser(
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:SS", sourceLocale) val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:SS", sourceLocale)
return manga.copy( return manga.copy(
altTitle = null,
state = when (doc.selectFirstOrThrow(".list-group-item:contains(Status:) a").text()) { state = when (doc.selectFirstOrThrow(".list-group-item:contains(Status:) a").text()) {
"Ongoing (Scan)", "Ongoing (Publish)", "Ongoing (Scan)", "Ongoing (Publish)",
-> MangaState.ONGOING -> MangaState.ONGOING
@ -204,12 +203,12 @@ internal abstract class NepnepParser(
tags = doc.select(".list-group-item:contains(Genre(s):) a").mapToSet { a -> tags = doc.select(".list-group-item:contains(Genre(s):) a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").substringAfterLast('='), key = a.attr("href").substringAfterLast('='),
title = a.text(), title = a.text().toTitleCase(sourceLocale),
source = source, source = source,
) )
}, },
author = doc.select(".list-group-item:contains(Author(s):) a").text(), author = doc.select(".list-group-item:contains(Author(s):) a").textOrNull(),
description = doc.selectFirstOrThrow(".top-5.Content").text(), description = doc.selectFirstOrThrow(".top-5.Content").textOrNull(),
chapters = chapter.mapJSONIndexed { i, j -> chapters = chapter.mapJSONIndexed { i, j ->
val indexChapter = j.getString("Chapter")!! val indexChapter = j.getString("Chapter")!!

@ -159,8 +159,8 @@ internal abstract class OtakuSanctuaryParser(
} }
} }
val alt = doc.body().selectFirst(selectAlt)?.text()?.replace("Other names", "") val alt = doc.body().selectFirst(selectAlt)?.textOrNull()?.replace("Other names", "")?.nullIfEmpty()
val auth = doc.body().selectFirst(selectAut)?.text() val auth = doc.body().selectFirst(selectAut)?.textOrNull()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale) val dateFormat = SimpleDateFormat(datePattern, sourceLocale)

@ -122,8 +122,6 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
altTitle = null,
state = null,
tags = doc.select("div.serie-infos li:contains(Categorias:) a").mapToSet { a -> tags = doc.select("div.serie-infos li:contains(Categorias:) a").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'), key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
@ -132,7 +130,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
) )
}, },
author = doc.select("div.serie-infos li:contains(Autor:)").text().replace("Autor:", "").nullIfEmpty(), author = doc.select("div.serie-infos li:contains(Autor:)").text().replace("Autor:", "").nullIfEmpty(),
description = doc.select(".serie-texto p").text(), description = doc.select(".serie-texto p").html(),
contentRating = if (doc.select("div.serie-infos li:contains(Categorias:)").text().contains("Hentai")) { contentRating = if (doc.select("div.serie-infos li:contains(Categorias:)").text().contains("Hentai")) {
ContentRating.ADULT ContentRating.ADULT
} else { } else {

@ -7,6 +7,7 @@ import org.koitharu.kotatsu.parsers.SinglePageMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.json.mapJSON
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -105,9 +106,9 @@ internal class YugenMangas(context: MangaLoaderContext) :
return manga.copy( return manga.copy(
description = detailManga.getString("synopsis"), description = detailManga.getString("synopsis"),
coverUrl = detailManga.getString("cover"), coverUrl = detailManga.getString("cover"),
altTitle = detailManga.getString("alternative_names"), altTitle = detailManga.getStringOrNull("alternative_names"),
author = detailManga.getString("author"), author = detailManga.getStringOrNull("author"),
state = detailManga.getString("status")?.let { state = detailManga.getStringOrNull("status")?.let {
when (it) { when (it) {
"ongoing" -> MangaState.ONGOING "ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED "completed" -> MangaState.FINISHED

@ -145,7 +145,7 @@ internal abstract class LibSocialParser(
} }
manga.copy( manga.copy(
title = json.getStringOrNull("rus_name") ?: manga.title, title = json.getStringOrNull("rus_name") ?: manga.title,
altTitle = json.getString("name"), altTitle = json.getStringOrNull("name"),
tags = tagsSetOf(tags, genres), tags = tagsSetOf(tags, genres),
author = json.getJSONArray("authors").optJSONObject(0)?.getStringOrNull("name"), author = json.getJSONArray("authors").optJSONObject(0)?.getStringOrNull("name"),
description = json.getString("summary").nl2br(), description = json.getString("summary").nl2br(),

@ -146,9 +146,9 @@ internal abstract class ScanParser(
?: RATING_UNKNOWN, ?: RATING_UNKNOWN,
tags = tags, tags = tags,
author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div, .card-series-about .mb-3:contains(Autore) a") author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div, .card-series-about .mb-3:contains(Autore) a")
?.text(), ?.textOrNull(),
altTitle = doc.selectFirst(".card div.col-12.mb-4 h2, .card-series-about .h6")?.text().orEmpty(), altTitle = doc.selectFirst(".card div.col-12.mb-4 h2, .card-series-about .h6")?.textOrNull(),
description = doc.selectFirst(".card div.col-12.mb-4 p, .card-series-desc .mb-4 p")?.html().orEmpty(), description = doc.selectFirst(".card div.col-12.mb-4 p, .card-series-desc .mb-4 p")?.html(),
chapters = doc.select(".chapters-list .col-chapter, .card-list-chapter .col-chapter") chapters = doc.select(".chapters-list .col-chapter, .card-list-chapter .col-chapter")
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")

@ -25,9 +25,9 @@ internal class MangaFr(context: MangaLoaderContext) :
?: RATING_UNKNOWN, ?: RATING_UNKNOWN,
tags = emptySet(), tags = emptySet(),
author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div, .card-series-about .mb-3:contains(Autore) a") author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div, .card-series-about .mb-3:contains(Autore) a")
?.text(), ?.textOrNull(),
altTitle = doc.selectFirst(".card div.col-12.mb-4 h2, .card-series-about .h6")?.text().orEmpty(), altTitle = doc.selectFirst(".card div.col-12.mb-4 h2, .card-series-about .h6")?.textOrNull(),
description = doc.selectFirst(".card div.col-12.mb-4 p, .card-series-desc .mb-4 p")?.html().orEmpty(), description = doc.selectFirst(".card div.col-12.mb-4 p, .card-series-desc .mb-4 p")?.html(),
chapters = doc.select(".chapters-list .col-chapter, .card-list-chapter .col-chapter") chapters = doc.select(".chapters-list .col-chapter, .card-list-chapter .col-chapter")
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")

@ -27,9 +27,9 @@ internal class ScanIta(context: MangaLoaderContext) :
rating = doc.selectFirst(".card-series-detail .rate-value span")?.ownText()?.toFloatOrNull()?.div(5f) rating = doc.selectFirst(".card-series-detail .rate-value span")?.ownText()?.toFloatOrNull()?.div(5f)
?: RATING_UNKNOWN, ?: RATING_UNKNOWN,
tags = tags, tags = tags,
author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div")?.text(), author = doc.selectFirst(".card-series-detail .col-6:contains(Autore) div")?.textOrNull(),
altTitle = doc.selectFirst(".card div.col-12.mb-4 h2")?.text().orEmpty(), altTitle = doc.selectFirst(".card div.col-12.mb-4 h2")?.textOrNull(),
description = doc.selectFirst(".card div.col-12.mb-4 p")?.html().orEmpty(), description = doc.selectFirst(".card div.col-12.mb-4 p")?.html(),
chapters = chaptersDeferred.await(), chapters = chaptersDeferred.await(),
) )
} }

@ -64,15 +64,14 @@ internal class SadScans(context: MangaLoaderContext) : SinglePageMangaParser(con
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH)
return manga.copy( return manga.copy(
altTitle = null,
state = when (doc.select(".status span").last()?.text()) { state = when (doc.select(".status span").last()?.text()) {
"Devam ediyor" -> MangaState.ONGOING "Devam ediyor" -> MangaState.ONGOING
"Tamamlandı" -> MangaState.FINISHED "Tamamlandı" -> MangaState.FINISHED
else -> null else -> null
}, },
tags = emptySet(), tags = emptySet(),
author = doc.select(".author span").last()?.text(), author = doc.selectLast(".author span")?.textOrNull(),
description = doc.selectFirstOrThrow(".summary").text(), description = doc.selectFirstOrThrow(".summary").html(),
chapters = doc.select(".chap-section .chap") chapters = doc.select(".chap-section .chap")
.mapChapters(reversed = true) { i, div -> .mapChapters(reversed = true) { i, div ->
val a = div.selectFirstOrThrow("a") val a = div.selectFirstOrThrow("a")

@ -112,8 +112,9 @@ internal class HentaiVNParser(context: MangaLoaderContext) : MangaParser(context
val stateDoc = stateDocDeferred.await() val stateDoc = stateDocDeferred.await()
manga.copy( manga.copy(
altTitle = infoEl.selectFirst("span.info:contains(Tên Khác:)")?.parent()?.select("span:not(.info) > a") altTitle = infoEl.selectFirst("span.info:contains(Tên Khác:)")?.parent()?.select("span:not(.info) > a")
?.joinToString { it.text() }, ?.joinToString { it.text() }
author = infoEl.select("p:contains(Tác giả:) a").text(), ?.nullIfEmpty(),
author = infoEl.select("p:contains(Tác giả:) a").textOrNull(),
description = infoEl.select("p:contains(Nội dung:) + p").html(), description = infoEl.select("p:contains(Nội dung:) + p").html(),
tags = tags, tags = tags,
state = stateDoc.select("p:contains(Tình Trạng:) a").firstOrNull()?.text()?.let { state = stateDoc.select("p:contains(Tình Trạng:) a").firstOrNull()?.text()?.let {

@ -87,8 +87,8 @@ internal class SayHentai(context: MangaLoaderContext) : PagedMangaParser(context
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy( return manga.copy(
altTitle = doc.selectFirst("h2.other-name")?.text(), altTitle = doc.selectFirst("h2.other-name")?.textOrNull(),
author = doc.selectFirst("div.summary-heading:contains(Tác giả) + div.summary-content")?.text(), author = doc.selectFirst("div.summary-heading:contains(Tác giả) + div.summary-content")?.textOrNull(),
tags = doc.select("div.genres-content a[rel=tag]").mapToSet { a -> tags = doc.select("div.genres-content a[rel=tag]").mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),

@ -4,8 +4,8 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -149,7 +149,7 @@ internal class TruyenGG(context: MangaLoaderContext) : PagedMangaParser(context,
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH)
return manga.copy( return manga.copy(
altTitle = doc.selectFirst("h2.other-name")?.text(), altTitle = doc.selectFirst("h2.other-name")?.textOrNull(),
author = doc.select("p:contains(Tác Giả) + p").joinToString { it.text() }.nullIfEmpty(), author = doc.select("p:contains(Tác Giả) + p").joinToString { it.text() }.nullIfEmpty(),
tags = doc.select("a.clblue").mapToSet { tags = doc.select("a.clblue").mapToSet {
MangaTag( MangaTag(

@ -159,7 +159,7 @@ internal class TruyenQQ(context: MangaLoaderContext) : PagedMangaParser(context,
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH)
return manga.copy( return manga.copy(
altTitle = doc.selectFirst("h2.other-name")?.text(), altTitle = doc.selectFirst("h2.other-name")?.textOrNull(),
tags = doc.select("ul.list01 li").mapToSet { tags = doc.select("ul.list01 li").mapToSet {
val key = it.attr("href").substringAfterLast("-").substringBeforeLast(".") val key = it.attr("href").substringAfterLast("-").substringBeforeLast(".")
MangaTag( MangaTag(

@ -119,10 +119,6 @@ internal abstract class VmpParser(
source = source, source = source,
) )
}, },
description = null,
altTitle = null,
author = null,
state = null,
chapters = listOf( chapters = listOf(
MangaChapter( MangaChapter(
id = manga.id, id = manga.id,

@ -216,9 +216,9 @@ internal abstract class WpComicsParser(
val tagsElement = doc.select("li.kind p.col-xs-8 a") val tagsElement = doc.select("li.kind p.col-xs-8 a")
val mangaTags = tagsElement.mapNotNullToSet { tagMap[it.text()] } val mangaTags = tagsElement.mapNotNullToSet { tagMap[it.text()] }
manga.copy( manga.copy(
description = doc.selectFirst(selectDesc)?.html().orEmpty(), description = doc.selectFirst(selectDesc)?.html(),
altTitle = doc.selectFirst("h2.other-name")?.text().orEmpty(), altTitle = doc.selectFirst("h2.other-name")?.textOrNull(),
author = doc.body().select(selectAut).text(), author = doc.body().select(selectAut).textOrNull(),
state = doc.selectFirst(selectState)?.let { state = doc.selectFirst(selectState)?.let {
when (it.text()) { when (it.text()) {
in ongoing -> MangaState.ONGOING in ongoing -> MangaState.ONGOING

@ -128,7 +128,7 @@ internal class XoxoComics(context: MangaLoaderContext) :
else -> null else -> null
} }
} }
val aut = doc.body().select(selectAut).text() val aut = doc.body().select(selectAut).textOrNull()
manga.copy( manga.copy(
tags = doc.body().select(selectTag).mapToSet { a -> tags = doc.body().select(selectTag).mapToSet { a ->
MangaTag( MangaTag(
@ -138,7 +138,6 @@ internal class XoxoComics(context: MangaLoaderContext) :
) )
}, },
description = desc, description = desc,
altTitle = null,
author = aut, author = aut,
state = state, state = state,
chapters = chaptersDeferred.await(), chapters = chaptersDeferred.await(),

@ -1,29 +1,23 @@
package org.koitharu.kotatsu.parsers.site.wpcomics.vi package org.koitharu.kotatsu.parsers.site.wpcomics.vi
import androidx.collection.ArrayMap
import androidx.collection.ArraySet
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("NETTRUYEN", "NetTruyen", "vi") @MangaSourceParser("NETTRUYEN", "NetTruyen", "vi")
internal class NetTruyen(context: MangaLoaderContext) : internal class NetTruyen(context: MangaLoaderContext) :
WpComicsParser(context, MangaParserSource.NETTRUYEN, "nettruyenrr.com", 44) { WpComicsParser(context, MangaParserSource.NETTRUYEN, "nettruyenrr.com", 44) {
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("nettruyenrr.com", "nettruyenww.com", "nettruyenx.com") override val configKeyDomain: ConfigKey.Domain =
ConfigKey.Domain("nettruyenrr.com", "nettruyenww.com", "nettruyenx.com")
override suspend fun getDetails(manga: Manga): Manga = coroutineScope { override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val fullUrl = manga.url.toAbsoluteUrl(domain) val fullUrl = manga.url.toAbsoluteUrl(domain)
@ -33,9 +27,9 @@ internal class NetTruyen(context: MangaLoaderContext) :
val tagsElement = doc.select("li.kind p.col-xs-8 a") val tagsElement = doc.select("li.kind p.col-xs-8 a")
val mangaTags = tagsElement.mapNotNullToSet { tagMap[it.text()] } val mangaTags = tagsElement.mapNotNullToSet { tagMap[it.text()] }
manga.copy( manga.copy(
description = doc.selectFirst(selectDesc)?.html().orEmpty(), description = doc.selectFirst(selectDesc)?.html(),
altTitle = doc.selectFirst("h2.other-name")?.text().orEmpty(), altTitle = doc.selectFirst("h2.other-name")?.textOrNull(),
author = doc.body().select(selectAut).text(), author = doc.body().select(selectAut).textOrNull(),
state = doc.selectFirst(selectState)?.let { state = doc.selectFirst(selectState)?.let {
when (it.text()) { when (it.text()) {
in ongoing -> MangaState.ONGOING in ongoing -> MangaState.ONGOING

@ -129,6 +129,8 @@ public fun Element.selectLastOrThrow(cssQuery: String): Element = parseNotNull(s
public fun Element.textOrNull(): String? = text().nullIfEmpty() public fun Element.textOrNull(): String? = text().nullIfEmpty()
public fun Elements.textOrNull(): String? = text().nullIfEmpty()
public fun Element.ownTextOrNull(): String? = ownText().nullIfEmpty() public fun Element.ownTextOrNull(): String? = ownText().nullIfEmpty()
public fun Element.selectFirstParent(query: String): Element? { public fun Element.selectFirstParent(query: String): Element? {

Loading…
Cancel
Save