Remove deprecated Manga constructor and introduce authors field

palaks-1 1 year ago committed by Koitharu
parent 86e7c21e4d
commit f6145bc412

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.parsers.model
import androidx.collection.ArrayMap
import org.koitharu.kotatsu.parsers.util.findById
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
public data class Manga constructor(
@ -49,9 +50,9 @@ public data class Manga constructor(
*/
@JvmField public val state: MangaState?,
/**
* Author of the manga, may be null
* Authors of the manga
*/
@JvmField public val author: String?,
@JvmField public val authors: Set<String>,
/**
* Large cover url (absolute), null if is no large cover
* @see coverUrl
@ -70,41 +71,12 @@ public data class Manga constructor(
*/
@JvmField public val source: MangaSource,
) {
@Deprecated("Prefer constructor with contentRating instead of isNsfw")
public constructor(
id: Long,
title: String,
altTitle: String?,
url: String,
publicUrl: String,
rating: Float,
isNsfw: Boolean,
coverUrl: String?,
tags: Set<MangaTag>,
state: MangaState?,
author: String?,
largeCoverUrl: String? = null,
description: String? = null,
chapters: List<MangaChapter>? = null,
source: MangaSource,
) : this(
id = id,
title = title,
altTitle = altTitle?.nullIfEmpty(),
url = url,
publicUrl = publicUrl,
rating = rating,
contentRating = if (isNsfw) ContentRating.ADULT else null,
coverUrl = coverUrl?.nullIfEmpty(),
tags = tags,
state = state,
author = author?.nullIfEmpty(),
largeCoverUrl = largeCoverUrl?.nullIfEmpty(),
description = description?.nullIfEmpty(),
chapters = chapters,
source = source,
)
/**
* Author of the manga, may be null
*/
@Deprecated("Please use authors")
public val author: String?
get() = authors.firstOrNull()
/**
* Return if manga has a specified rating

@ -214,6 +214,7 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser(
val attrs = details.selectFirst(".attr-main")?.select(".attr-item")?.associate {
it.child(0).text() to it.child(1)
}.orEmpty()
val author = attrs["Authors:"]?.textOrNull()
return manga.copy(
title = root.selectFirst("h3.item-title")?.text() ?: manga.title,
contentRating = if (root.selectFirst("alert")?.getElementsContainingOwnText("NSFW").isNullOrEmpty()) {
@ -233,7 +234,7 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser(
"Hiatus" -> MangaState.PAUSED
else -> manga.state
},
author = attrs["Authors:"]?.textOrNull() ?: manga.author,
authors = author?.let { setOf(it) } ?: manga.authors,
chapters = root.selectFirst(".episode-list")
?.selectFirst(".main")
?.children()
@ -337,13 +338,13 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser(
url = href,
publicUrl = a.absUrl("href"),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirst("img[src]")?.absUrl("src"),
largeCoverUrl = null,
description = null,
tags = div.selectFirst(".item-genre")?.parseTags().orEmpty(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -168,7 +168,7 @@ internal class ComickFunParser(context: MangaLoaderContext) :
4 -> MangaState.PAUSED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}
@ -182,6 +182,7 @@ internal class ComickFunParser(context: MangaLoaderContext) :
val alt = comic.getJSONArray("md_titles").asTypedList<JSONObject>().joinToString("\n") {
it.getStringOrNull("title").orEmpty()
}
val author = jo.getJSONArray("artists").optJSONObject(0)?.getStringOrNull("name")
return manga.copy(
altTitle = alt.nullIfEmpty(),
contentRating = if (jo.getBooleanOrDefault("matureContent", false)
@ -200,7 +201,7 @@ internal class ComickFunParser(context: MangaLoaderContext) :
source = source,
)
},
author = jo.getJSONArray("artists").optJSONObject(0)?.getStringOrNull("name"),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = getChapters(comic.getString("hid")),
)
}

@ -168,6 +168,8 @@ internal class ExHentaiParser(
val href = a.attrAsRelativeUrl("href")
val tagsDiv = gLink.nextElementSibling() ?: gLink.parseFailed("tags div not found")
val rawTitle = gLink.text()
val author = tagsDiv.getElementsContainingOwnText("artist:").first()
?.nextElementSibling()?.textOrNull()
Manga(
id = generateUid(href),
title = rawTitle.cleanupTitle(),
@ -182,8 +184,7 @@ internal class ExHentaiParser(
rawTitle.contains("(ongoing)", ignoreCase = true) -> MangaState.ONGOING
else -> null
},
author = tagsDiv.getElementsContainingOwnText("artist:").first()
?.nextElementSibling()?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
source = source,
)
}

@ -524,9 +524,9 @@ internal class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context
doc.selectFirstOrThrow("h1 > a")
.attrAsRelativeUrl("href")
.toAbsoluteUrl(domain),
author = null,
authors = emptySet(),
tags = emptySet(),
isNsfw = true,
contentRating = ContentRating.ADULT,
rating = RATING_UNKNOWN,
altTitle = null,
state = null,
@ -542,6 +542,10 @@ internal class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context
.parseRaw()
.substringAfter("var galleryinfo = ")
.let(::JSONObject)
val author =
json.optJSONArray("artists")
?.mapJSON { it.getString("artist").toCamelCase() }
?.joinToString()
return manga.copy(
title = json.getString("title"),
@ -554,10 +558,7 @@ internal class HitomiLaParser(context: MangaLoaderContext) : MangaParser(context
"https://${getDomain("${subDomain}a")}/webp/$commonId$imageId/$hash.webp"
},
author =
json.optJSONArray("artists")
?.mapJSON { it.getString("artist").toCamelCase() }
?.joinToString(),
authors = author?.let { setOf(it) } ?: emptySet(),
publicUrl = json.getString("galleryurl").toAbsoluteUrl(domain),
tags =
buildSet {

@ -125,10 +125,10 @@ internal class ImHentai(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -161,6 +161,7 @@ internal class ImHentai(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val author = doc.selectFirst("li:contains(Artists) a.tag")?.ownTextOrNull()
manga.copy(
tags = doc.body().select("li:contains(Tags) a.tag").mapNotNullToSet {
val href = it.attr("href").substringAfterLast("tag/").substringBeforeLast('/')
@ -170,7 +171,7 @@ internal class ImHentai(context: MangaLoaderContext) :
source = source,
)
},
author = doc.selectFirst("li:contains(Artists) a.tag")?.ownTextOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = listOf(
MangaChapter(
id = manga.id,
@ -202,10 +203,10 @@ internal class ImHentai(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = false,
contentRating = null,
)
}
}

@ -121,6 +121,8 @@ internal abstract class LineWebtoonsParser(
makeRequest("/lineWebtoon/webtoon/challengeTitleInfo.json?v=2&titleNo=${titleNo}")
.getJSONObject("titleInfo")
.let { jo ->
val isNsfwSource = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource)
val author = jo.getStringOrNull("writingAuthorName")
Manga(
id = generateUid(titleNo),
title = jo.getString("title"),
@ -128,11 +130,11 @@ internal abstract class LineWebtoonsParser(
url = "$titleNo",
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=${titleNo}",
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
tags = setOf(parseTag(jo.getJSONObject("genreInfo"))),
author = jo.getStringOrNull("writingAuthorName"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = jo.getString("synopsis"),
// I don't think the API provides this info
state = null,
@ -150,6 +152,7 @@ internal abstract class LineWebtoonsParser(
.getJSONArray("titleList")
.mapJSON { jo ->
val titleNo = jo.getLong("titleNo")
val author = jo.getStringOrNull("writingAuthorName")
Manga(
id = generateUid(titleNo),
@ -158,11 +161,11 @@ internal abstract class LineWebtoonsParser(
url = titleNo.toString(),
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo",
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
largeCoverUrl = null,
tags = emptySet(),
author = jo.getStringOrNull("writingAuthorName"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = null,
state = null,
source = source,
@ -194,6 +197,8 @@ internal abstract class LineWebtoonsParser(
.getJSONArray("titles")
.mapJSON { jo ->
val titleNo = jo.getLong("titleNo")
val isNsfwSource = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource)
val author = jo.getStringOrNull("writingAuthorName")
Manga(
id = generateUid(titleNo),
@ -202,11 +207,11 @@ internal abstract class LineWebtoonsParser(
url = titleNo.toString(),
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo",
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
tags = setOfNotNull(genres[jo.getString("representGenre")]),
author = jo.getStringOrNull("writingAuthorName"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = jo.getString("synopsis"),
// I don't think the API provides this info
state = null,

@ -287,6 +287,9 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context
?.let {
"https://uploads.$domain/covers/$id/$it"
}
val author = (relations["author"] ?: relations["artist"])
?.getJSONObject("attributes")
?.getStringOrNull("name")
return Manga(
id = generateUid(id),
title = requireNotNull(attrs.getJSONObject("title").selectByLocale()) {
@ -322,9 +325,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : MangaParser(context
"cancelled" -> MangaState.ABANDONED
else -> null
},
author = (relations["author"] ?: relations["artist"])
?.getJSONObject("attributes")
?.getStringOrNull("name"),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = chapters,
source = source,
)

@ -165,7 +165,7 @@ internal abstract class MangaFireParser(
source = source,
altTitle = null,
largeCoverUrl = null,
author = null,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,
@ -179,6 +179,8 @@ internal abstract class MangaFireParser(
val availableTags = tags.get()
var isAdult = false
var isSuggestive = false
val author = document.select("div.meta a[href*=/author/]")
.joinToString { it.ownText() }.nullIfEmpty()
return manga.copy(
title = document.selectFirstOrThrow(".info > h1").ownText(),
@ -211,8 +213,7 @@ internal abstract class MangaFireParser(
else -> null
}
},
author = document.select("div.meta a[href*=/author/]")
.joinToString { it.ownText() }.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = document.selectFirstOrThrow("#synopsis div.modal-content").html(),
chapters = getChapters(manga.url, document),
)
@ -332,7 +333,7 @@ internal abstract class MangaFireParser(
source = source,
altTitle = null,
largeCoverUrl = null,
author = null,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,
@ -355,7 +356,7 @@ internal abstract class MangaFireParser(
source = source,
altTitle = null,
largeCoverUrl = null,
author = null,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,

@ -160,10 +160,10 @@ internal class MangaPark(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst("span.text-yellow-500")?.text()?.toFloatOrNull()?.div(10F) ?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -193,9 +193,10 @@ internal class MangaPark(context: MangaLoaderContext) :
val tags = selectTag.mapNotNullToSet { tagMap[it.text()] }
val nsfw = tags.any { t -> t.key == "hentai" || t.key == "adult" }
val dateFormat = SimpleDateFormat("dd/MM/yyyy", sourceLocale)
val author = doc.selectFirst("div[q:key=tz_4]")?.textOrNull()
manga.copy(
altTitle = doc.selectFirst("div[q:key=tz_2]")?.textOrNull(),
author = doc.selectFirst("div[q:key=tz_4]")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.selectFirst("react-island[q:key=0a_9]")?.html(),
state = when (doc.selectFirst("span[q:key=Yn_5]")?.text()?.lowercase()) {
"ongoing" -> MangaState.ONGOING

@ -122,8 +122,8 @@ internal abstract class MangaPlusParser(
title = name,
coverUrl = it.getString("portraitImageUrl"),
altTitle = null,
author = author,
isNsfw = false,
authors = setOf(author),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,
source = source,
@ -143,13 +143,14 @@ internal abstract class MangaPlusParser(
}
val hiatus = json.getStringOrNull("nonAppearanceInfo")?.contains("on a hiatus") == true
val author = title.getString("author")
.split("/").joinToString(transform = String::trim)
return manga.copy(
title = title.getString("name"),
publicUrl = "/titles/${title.getInt("titleId")}".toAbsoluteUrl(domain),
coverUrl = title.getString("portraitImageUrl"),
author = title.getString("author")
.split("/").joinToString(transform = String::trim),
authors = setOf(author),
description = buildString {
json.getString("overview").let(::append)
json.getStringOrNull("viewingPeriodDescription")

@ -132,8 +132,8 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
coverUrl = thumb.attr("src"),
source = source,
altTitle = null,
author = null,
isNsfw = false,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,
tags = emptySet(),
@ -155,7 +155,7 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
coverUrl = thumb.attrAsAbsoluteUrlOrNull("src"),
source = source,
altTitle = null,
author = null,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
state = null,
@ -169,6 +169,8 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
val availableTags = tags.get()
var isAdult = false
var isSuggestive = false
val author = document.select("div.anisc-info a[href*=/author/]")
.joinToString { it.ownText().replace(", ", " ") }.nullIfEmpty()
return manga.copy(
title = document.selectFirst("h2.manga-name")!!.ownText(),
@ -201,8 +203,7 @@ internal class MangaReaderToParser(context: MangaLoaderContext) :
else -> null
}
},
author = document.select("div.anisc-info a[href*=/author/]")
.joinToString { it.ownText().replace(", ", " ") }.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = document.select("div.description").html(),
chapters = parseChapters(document),
source = source,

@ -116,8 +116,8 @@ internal abstract class NineMangaParser(
altTitle = null,
coverUrl = node.selectFirst("img")?.src(),
rating = RATING_UNKNOWN,
author = null,
isNsfw = false,
authors = emptySet(),
contentRating = null,
tags = emptySet(),
state = null,
source = source,
@ -135,11 +135,12 @@ internal abstract class NineMangaParser(
val tagMap = getOrCreateTagMap()
val selectTag = infoRoot.getElementsByAttributeValue("itemprop", "genre").first()?.select("a")
val tags = selectTag?.mapNotNullToSet { tagMap[it.text()] }
val author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.textOrNull()
return manga.copy(
title = root.selectFirst("h1[itemprop=name]")?.textOrNull()?.removeSuffix("Manga")?.trimEnd()
?: manga.title,
tags = tags.orEmpty(),
author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = parseStatus(infoRoot.select("li a.red").text()),
description = infoRoot.getElementsByAttributeValue("itemprop", "description").first()?.html()
?.substringAfter("</b>"),

@ -206,7 +206,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) :
cover == null -> null
else -> "https://${cdnHost.get()}/$cover"
},
author = null,
authors = emptySet(),
contentRating = ContentRating.ADULT,
url = id,
publicUrl = "/hchapter/$id".toAbsoluteUrl(domain),
@ -264,12 +264,13 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) :
type = it.getStringOrNull("tagType"),
)
}
val author = tags?.filter { it.type == "artist" }?.joinToString { it.name.toCamelCase() }?.nullIfEmpty()
return manga.copy(
title = name.replace(shortenTitleRegex, "").trim(),
altTitle = name,
coverUrl = cover.first,
largeCoverUrl = cover.second,
author = tags?.filter { it.type == "artist" }?.joinToString { it.name.toCamelCase() }?.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
contentRating = ContentRating.ADULT,
tags = tags?.mapToSet {
MangaTag(

@ -123,6 +123,8 @@ internal abstract class WebtoonsParser(
val chapters = chaptersDeferred.await()
makeRequest("/lineWebtoon/webtoon/titleInfo.json?titleNo=${titleNo}&anyServiceStatus=false").getJSONObject("titleInfo")
.let { jo ->
val isNsfwSource = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource)
val author = jo.getStringOrNull("writingAuthorName")
MangaWebtoon(
Manga(
id = generateUid(titleNo),
@ -131,11 +133,11 @@ internal abstract class WebtoonsParser(
url = "$titleNo",
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=${titleNo}",
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
tags = setOf(parseTag(jo.getJSONObject("genreInfo"))),
author = jo.getStringOrNull("writingAuthorName"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = jo.getString("synopsis"),
// I don't think the API provides this info,
state = null,
@ -158,6 +160,8 @@ internal abstract class WebtoonsParser(
makeRequest("/lineWebtoon/webtoon/titleList.json?").getJSONObject("titleList").getJSONArray("titles")
.mapJSON { jo ->
val titleNo = jo.getLong("titleNo")
val isNsfwSource = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource)
val author = jo.getStringOrNull("writingAuthorName")
MangaWebtoon(
Manga(
id = generateUid(titleNo),
@ -166,8 +170,8 @@ internal abstract class WebtoonsParser(
title = jo.getString("title"),
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
altTitle = null,
author = jo.getStringOrNull("writingAuthorName"),
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
authors = author?.let { setOf(it) } ?: emptySet(),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
tags = setOfNotNull(allGenreCache.get()[jo.getString("representGenre")]),
description = jo.getString("synopsis"),
@ -196,6 +200,7 @@ internal abstract class WebtoonsParser(
makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch")
.getJSONArray("titleList").mapJSON { jo ->
val titleNo = jo.getLong("titleNo")
val author = jo.getStringOrNull("writingAuthorName")
MangaWebtoon(
Manga(
id = generateUid(titleNo),
@ -204,11 +209,11 @@ internal abstract class WebtoonsParser(
url = titleNo.toString(),
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
largeCoverUrl = null,
tags = emptySet(),
author = jo.getStringOrNull("writingAuthorName"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = null,
state = null,
source = source,

@ -110,10 +110,10 @@ internal abstract class AnimeBootstrapParser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -72,10 +72,10 @@ internal class PapScan(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -112,7 +112,7 @@ internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = cover,
tags = emptySet(),
state = when (j.getString("status")) {
@ -122,7 +122,7 @@ internal class FlixScans(context: MangaLoaderContext) : PagedMangaParser(context
"droped" -> MangaState.ABANDONED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}

@ -74,11 +74,11 @@ internal class MangaStorm(context: MangaLoaderContext) : PagedMangaParser(contex
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -108,7 +108,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").src()?.replace("thumbnail_", ""),
tags = emptySet(),
state = when (div.selectFirst(".status")?.text()) {
@ -117,7 +117,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
"متوقف" -> MangaState.ABANDONED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}

@ -90,8 +90,8 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context,
coverUrl = jo.getString("poster").removePrefix("/cdn")
.toAbsoluteUrl(getDomain("cdn")) + "?width=200&height=280",
altTitle = title.optJSONArray("alt")?.optString(0)?.nullIfEmpty(),
author = null,
isNsfw = false,
authors = emptySet(),
contentRating = null,
rating = jo.getDouble("rating").toFloat() / 10f,
url = href,
publicUrl = "https://${domain}/$href",
@ -237,8 +237,8 @@ internal class AnibelParser(context: MangaLoaderContext) : MangaParser(context,
coverUrl = jo.getString("poster").removePrefix("/cdn")
.toAbsoluteUrl(getDomain("cdn")) + "?width=200&height=280",
altTitle = title.getString("en").nullIfEmpty(),
author = null,
isNsfw = false,
authors = emptySet(),
contentRating = null,
rating = RATING_UNKNOWN,
url = href,
publicUrl = "https://${domain}/$href",

@ -99,11 +99,11 @@ internal abstract class CupFoxParser(
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = li.selectFirst(selectMangasCover)?.src(),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -122,6 +122,7 @@ internal abstract class CupFoxParser(
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val author = doc.selectFirst(selectMangaDetailsAuthor)?.text()?.substringAfter("")?.nullIfEmpty()
return manga.copy(
altTitle = doc.selectFirst(selectMangaDetailsAltTitle)?.text()?.substringAfter("")?.nullIfEmpty(),
tags = doc.select(selectMangaDetailsTags).mapToSet { a ->
@ -131,7 +132,7 @@ internal abstract class CupFoxParser(
source = source,
)
},
author = doc.selectFirst(selectMangaDetailsAuthor)?.text()?.substringAfter("")?.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.selectFirst(selectMangaDescription)?.html(),
chapters = doc.select(selectMangaChapters)
.mapChapters { i, li ->
@ -164,11 +165,11 @@ internal abstract class CupFoxParser(
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = li.selectFirst(selectMangasCover)?.src(),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -116,7 +116,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
altTitle = null,
rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = when (a.selectLast("span.status")?.text()) {
"Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
@ -126,7 +126,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -159,10 +159,11 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
val tagMap = getOrCreateTagMap()
val selectTag = doc.select("div[class^=space] > div.flex > button.text-white")
val tags = selectTag.mapNotNullToSet { tagMap[it.text()] }
val author = doc.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Author)) > h3:eq(1)")?.text().orEmpty()
return manga.copy(
description = doc.selectFirst("span.font-medium.text-sm")?.text().orEmpty(),
tags = tags,
author = doc.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Author)) > h3:eq(1)")?.text().orEmpty(),
authors = setOf(author),
chapters = doc.select("div.scrollbar-thumb-themecolor > div.group").mapChapters(reversed = true) { i, div ->
val a = div.selectLastOrThrow("a")
val urlRelative = "/series/" + a.attrAsRelativeUrl("href")

@ -78,14 +78,14 @@ internal class BeeToon(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst(".counter")?.text()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = when (div.selectLastOrThrow(".status span").text()) {
"Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -103,6 +103,7 @@ internal class BeeToon(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val author = doc.selectFirst(".info .author a")?.text()
return manga.copy(
description = doc.getElementById("desc")?.text().orEmpty(),
rating = doc.selectFirst(".counter")?.text()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
@ -113,7 +114,7 @@ internal class BeeToon(context: MangaLoaderContext) :
source = source,
)
},
author = doc.selectFirst(".info .author a")?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = doc.select(".items-chapters a").mapChapters(reversed = true) { i, a ->
val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain)
MangaChapter(

@ -39,10 +39,10 @@ internal class CloneMangaParser(context: MangaLoaderContext) :
title = item.selectFirst("h3")?.text() ?: return@mapNotNull null,
coverUrl = "https://$domain/$cover",
altTitle = null,
author = "Dan Kim",
authors = setOf("Dan Kim"),
rating = RATING_UNKNOWN,
url = href,
isNsfw = false,
contentRating = null,
tags = emptySet(),
state = null,
publicUrl = href.toAbsoluteUrl(domain),

@ -74,7 +74,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").attrAsAbsoluteUrl("src"),
tags = div.select("div.egb-details a").mapToSet { a ->
MangaTag(
@ -84,7 +84,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
)
},
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -103,6 +103,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val author = doc.selectFirst("table.full-table tr:contains(Author:) td:nth-child(2)")?.textOrNull()
return manga.copy(
altTitle = doc.selectFirstOrThrow("div.anime-top h1.title").textOrNull(),
state = when (doc.selectFirstOrThrow("ul.anime-genres li.status a").text()) {
@ -117,7 +118,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
source = source,
)
},
author = doc.selectFirst("table.full-table tr:contains(Author:) td:nth-child(2)")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.selectFirstOrThrow("div.detail-desc-content p").html(),
chapters = doc.select("ul.basic-list li").let { elements ->
elements.mapChapters { i, li ->

@ -90,11 +90,11 @@ internal class DynastyScans(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").attrAsAbsoluteUrl("src"),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -111,7 +111,7 @@ internal class DynastyScans(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = null,
tags = div.select("span.tags a").mapToSet { a ->
MangaTag(
@ -121,7 +121,7 @@ internal class DynastyScans(context: MangaLoaderContext) :
)
},
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -128,6 +128,7 @@ internal class FlameComics(context: MangaLoaderContext) :
private fun parseManga(jo: JSONObject): Manga {
val seriesId = jo.getLong("series_id")
val cover = jo.getStringOrNull("cover")
val author = jo.getStringOrNull("author")
return Manga(
id = generateUid(seriesId),
title = jo.getString("title"),
@ -137,7 +138,7 @@ internal class FlameComics(context: MangaLoaderContext) :
url = seriesId.toString(),
publicUrl = "https://${domain}/series/$seriesId",
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = if (cover != null) {
imageUrl(seriesId, cover, 256)
} else {
@ -153,7 +154,7 @@ internal class FlameComics(context: MangaLoaderContext) :
"Ongoing" -> MangaState.ONGOING
else -> null
},
author = jo.getStringOrNull("author"),
authors = author?.let { setOf(it) } ?: emptySet(),
largeCoverUrl = if (cover != null) {
imageUrl(seriesId, cover, 640)
} else {

@ -91,7 +91,7 @@ internal class FlixScansOrg(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = cover,
tags = emptySet(),
state = when (j.getString("status")) {
@ -101,7 +101,7 @@ internal class FlixScansOrg(context: MangaLoaderContext) :
"droped" -> MangaState.ABANDONED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}

@ -77,6 +77,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
val doc = webClient.httpGet(url).parseHtml()
return doc.select("li.novel-item").map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val author = div.selectFirstOrThrow("h6").text().removePrefix("Author(S): ").nullIfEmpty()
Manga(
id = generateUid(href),
title = div.selectFirstOrThrow("h4").text(),
@ -84,11 +85,11 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").src(),
tags = emptySet(),
state = null,
author = div.selectFirstOrThrow("h6").text().removePrefix("Author(S): ").nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
source = source,
)
}
@ -108,6 +109,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val chaptersDeferred = async { loadChapters(manga.url) }
val author = doc.selectFirstOrThrow(".author").textOrNull()
manga.copy(
altTitle = doc.selectFirstOrThrow(".alternative-title").textOrNull(),
state = when (doc.selectFirstOrThrow(".header-stats span:contains(Status) strong").text()) {
@ -122,7 +124,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
source = source,
)
},
author = doc.selectFirstOrThrow(".author").textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.selectFirstOrThrow(".description").html(),
chapters = chaptersDeferred.await(),
)

@ -86,10 +86,10 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -99,10 +99,11 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) :
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href")
val chaptersDeferred = async { loadChapters(firstChapter) }
val author = doc.select("a[href*=author]").textOrNull()
manga.copy(
description = doc.selectFirst("dd.text-justify.text-break")?.html(),
altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }.nullIfEmpty(),
author = doc.select("a[href*=author]").textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) {
"Ongoing" -> MangaState.ONGOING
"" -> MangaState.FINISHED

@ -110,6 +110,9 @@ internal class MangaTownParser(context: MangaLoaderContext) :
val views = li.select("p.view")
val status = views.firstNotNullOfOrNull { it.ownText().takeIf { x -> x.startsWith("Status:") } }
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
val author = views.firstNotNullOfOrNull { it.text().takeIf { x -> x.startsWith("Author:") } }
?.substringAfter(':')
?.trim()
Manga(
id = generateUid(href),
title = a.attr("title"),
@ -118,9 +121,7 @@ internal class MangaTownParser(context: MangaLoaderContext) :
altTitle = null,
rating = li.selectFirst("p.score")?.selectFirst("b")
?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
author = views.firstNotNullOfOrNull { it.text().takeIf { x -> x.startsWith("Author:") } }
?.substringAfter(':')
?.trim(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (status) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
@ -134,7 +135,7 @@ internal class MangaTownParser(context: MangaLoaderContext) :
)
}.orEmpty(),
url = href,
isNsfw = false,
contentRating = null,
publicUrl = href.toAbsoluteUrl(a.host ?: domain),
)
}

@ -109,10 +109,10 @@ internal class Mangaowl(context: MangaLoaderContext) :
altTitle = null,
rating = div.select("span").last()?.text()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = false,
contentRating = null,
)
}
}

@ -129,11 +129,11 @@ internal class Manhwa18Com(context: MangaLoaderContext) :
url = absUrl.toRelativeUrl(domain),
publicUrl = absUrl,
rating = RATING_UNKNOWN,
isNsfw = true,
contentRating = ContentRating.ADULT,
coverUrl = it.selectFirst("div.img-in-ratio")?.attrAsAbsoluteUrl("data-bg"),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
largeCoverUrl = null,
description = null,
source = MangaParserSource.MANHWA18,
@ -166,7 +166,7 @@ internal class Manhwa18Com(context: MangaLoaderContext) :
return manga.copy(
altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownTextOrNull()
?.removePrefix(": "),
author = author,
authors = author?.let { setOf(it) } ?: emptySet(),
description = docs.selectFirst(".series-summary .summary-content")?.html(),
tags = tags.orEmpty(),
state = state,

@ -129,11 +129,11 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
url = absUrl.toRelativeUrl(domain),
publicUrl = absUrl,
rating = RATING_UNKNOWN,
isNsfw = true,
contentRating = ContentRating.ADULT,
coverUrl = it.selectFirst("div.img-in-ratio")?.attrAsAbsoluteUrl("data-bg"),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
largeCoverUrl = null,
description = null,
source = MangaParserSource.MANHWA18,
@ -166,7 +166,7 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
return manga.copy(
altTitle = cardInfoElement?.selectFirst("b:contains(Other names)")?.parent()?.ownTextOrNull()
?.removePrefix(": "),
author = author,
authors = author?.let { setOf(it) } ?: emptySet(),
description = docs.selectFirst(".series-summary .summary-content")?.html(),
tags = tags.orEmpty(),
state = state,

@ -88,10 +88,10 @@ internal class ManhwasMen(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -74,10 +74,10 @@ internal class MyComicList(context: MangaLoaderContext) : PagedMangaParser(conte
publicUrl = href.toAbsoluteUrl(domain),
title = div.selectFirst("h3 a")?.text().orEmpty(),
altTitle = null,
author = null,
authors = emptySet(),
tags = emptySet(),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = img?.attrAsAbsoluteUrlOrNull("data-src"),
state = null,
source = source,
@ -87,6 +87,7 @@ internal class MyComicList(context: MangaLoaderContext) : PagedMangaParser(conte
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val author = doc.selectFirst("td:contains(Author:) + td")?.textOrNull()
return manga.copy(
tags = doc.select("td:contains(Genres:) + td a").mapToSet { a ->
MangaTag(
@ -95,7 +96,7 @@ internal class MyComicList(context: MangaLoaderContext) : PagedMangaParser(conte
source = source,
)
},
author = doc.selectFirst("td:contains(Author:) + td")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (doc.selectFirst("td:contains(Status:) + td a")?.text()?.lowercase()) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED

@ -49,11 +49,11 @@ internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(con
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -62,6 +62,7 @@ internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(con
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd MMM, yy", Locale.ENGLISH)
val author = doc.selectLast(".author span")?.textOrNull()
return manga.copy(
state = when (doc.select(".status span").last()?.text()) {
"Ongoing" -> MangaState.ONGOING
@ -69,7 +70,7 @@ internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(con
else -> null
},
tags = emptySet(),
author = doc.selectLast(".author span")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.selectFirstOrThrow(".summary").html(),
chapters = doc.select(".chap-section .chap")
.mapChapters(reversed = true) { i, div ->

@ -99,10 +99,10 @@ internal class Pururin(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -133,6 +133,7 @@ internal class Pururin(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val author = doc.selectFirst("a[itemprop=author]")?.text()
manga.copy(
description = doc.selectFirst("p.mb-2")?.text().orEmpty(),
rating = doc.selectFirst("td span.rating")?.attr("content")?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
@ -144,7 +145,7 @@ internal class Pururin(context: MangaLoaderContext) :
source = source,
)
},
author = doc.selectFirst("a[itemprop=author]")?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = listOf(
MangaChapter(
id = manga.id,
@ -175,10 +176,10 @@ internal class Pururin(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = false,
contentRating = null,
)
}
}

@ -110,10 +110,10 @@ internal class VyManga(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -179,6 +179,7 @@ internal class WeebCentral(context: MangaLoaderContext) : MangaParser(context, M
.attrAsAbsoluteUrl("href")
.toHttpUrl()
.pathSegments[1]
val author = document.select("div:contains(author) a").eachText().joinToString().nullIfEmpty()
Manga(
id = generateUid(mangaId),
url = mangaId,
@ -210,7 +211,7 @@ internal class WeebCentral(context: MangaLoaderContext) : MangaParser(context, M
"Hiatus" -> PAUSED
else -> null
},
author = document.select("div:contains(author) a").eachText().joinToString().nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
largeCoverUrl = null,
chapters = null,
source = source,
@ -226,6 +227,8 @@ internal class WeebCentral(context: MangaLoaderContext) : MangaParser(context, M
val sectionLeft = document.select("section[x-data] > section")[0]
val sectionRight = document.select("section[x-data] > section")[1]
val author = sectionLeft.select("ul > li:has(strong:contains(Author)) > span > a")
.eachText().joinToString()
manga.copy(
title = sectionRight.selectFirstOrThrow("h1").text(),
@ -253,8 +256,7 @@ internal class WeebCentral(context: MangaLoaderContext) : MangaParser(context, M
"Hiatus" -> PAUSED
else -> null
},
author = sectionLeft.select("ul > li:has(strong:contains(Author)) > span > a")
.eachText().joinToString(),
authors = setOf(author),
description = Element("div").also { desc ->
sectionRight.selectFirst("li:has(strong:contains(Description)) > p")?.let {
desc.appendChild(it)

@ -47,10 +47,10 @@ internal class TempleScanEsp(context: MangaLoaderContext) :
altTitle = it.getStringOrNull("alternativeName"),
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -151,6 +151,7 @@ internal class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaPars
val doc = webClient.httpGet(url, getRequestHeaders()).parseHtml()
val items = doc.body().select("div.element")
return items.mapNotNull { item ->
val isNsfwSource = item.select("i").hasClass("fas fa-heartbeat fa-2x")
val href =
item.selectFirst("a")?.attrAsRelativeUrlOrNull("href")?.substringAfter(' ') ?: return@mapNotNull null
Manga(
@ -158,10 +159,10 @@ internal class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaPars
title = item.selectFirst("h4.text-truncate")?.text() ?: return@mapNotNull null,
coverUrl = item.select("style").toString().substringAfter("('").substringBeforeLast("')"),
altTitle = null,
author = null,
authors = emptySet(),
rating = item.selectFirst("span.score")?.text()?.toFloatOrNull()?.div(10F) ?: RATING_UNKNOWN,
url = href,
isNsfw = item.select("i").hasClass("fas fa-heartbeat fa-2x"),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
tags = emptySet(),
state = null,
publicUrl = href.toAbsoluteUrl(doc.host ?: domain),
@ -174,6 +175,7 @@ internal class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaPars
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val contents = doc.body().selectFirstOrThrow("section.element-header-content")
val author = contents.selectFirst("h5.card-title")?.attr("title")?.substringAfter(", ")
return manga.copy(
description = contents.selectFirst("p.element-description")?.html(),
tags = contents.select("h6 a").mapToSet { a ->
@ -185,7 +187,7 @@ internal class TuMangaOnlineParser(context: MangaLoaderContext) : PagedMangaPars
},
largeCoverUrl = contents.selectFirst(".book-thumbnail")?.attrAsAbsoluteUrlOrNull("src"),
state = parseStatus(contents.select("span.book-status").text().orEmpty()),
author = contents.selectFirst("h5.card-title")?.attr("title")?.substringAfter(", "),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = if (doc.select("div.chapters").isEmpty()) {
doc.select(oneShotChapterListSelector).mapChapters(reversed = true) { _, item ->
oneShotChapterFromElement(item)

@ -147,10 +147,10 @@ internal abstract class FmreaderParser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -190,7 +190,7 @@ internal abstract class FmreaderParser(
}
}
val alt = doc.body().selectFirst(selectAlt)?.text()?.replace("Other names", "")?.nullIfEmpty()
val auth = doc.body().selectFirst(selectAut)?.textOrNull()
val author = doc.body().selectFirst(selectAut)?.textOrNull()
manga.copy(
tags = doc.body().select(selectTag).mapToSet { a ->
MangaTag(
@ -201,7 +201,7 @@ internal abstract class FmreaderParser(
},
description = desc,
altTitle = alt,
author = auth,
authors = author?.let { setOf(it) } ?: emptySet(),
state = state,
chapters = chaptersDeferred.await(),
)

@ -35,10 +35,10 @@ internal class Klz9(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -93,10 +93,10 @@ internal abstract class FoolSlideParser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
@ -126,7 +126,7 @@ internal abstract class FoolSlideParser(
manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc?.nullIfEmpty(),
author = author?.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = chapters,
)
}

@ -36,7 +36,7 @@ internal class Seinagi(context: MangaLoaderContext) :
manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc?.nullIfEmpty(),
author = author?.nullIfEmpty(),
authors = author?.nullIfEmpty()?.let { setOf(it) } ?: emptySet(),
chapters = chapters,
)
}

@ -35,7 +35,7 @@ internal class Pzykosis666hFansub(context: MangaLoaderContext) :
manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src() ?: manga.coverUrl,
description = desc?.nullIfEmpty(),
author = author?.nullIfEmpty(),
authors = author?.nullIfEmpty()?.let { setOf(it) } ?: emptySet(),
chapters = chapters,
)
}

@ -35,7 +35,7 @@ internal class SeinagiAdulto(context: MangaLoaderContext) :
manga.copy(
coverUrl = doc.selectFirst(".thumbnail img")?.src(),// for manga result on search
description = desc?.nullIfEmpty(),
author = author?.nullIfEmpty(),
authors = author?.nullIfEmpty()?.let { setOf(it) } ?: emptySet(),
chapters = chapters,
)
}

@ -111,6 +111,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
return root.select(".manga[data-manga]").map { div ->
val header = div.selectFirstOrThrow(".manga_header")
val href = header.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val isNsfwSource = div.selectFirst(".badge-adult_content") != null
Manga(
id = generateUid(href),
title = div.selectFirst("h1")?.text().orEmpty(),
@ -123,7 +124,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
?.toFloatOrNull()
?.div(10f)
?: RATING_UNKNOWN,
isNsfw = div.selectFirst(".badge-adult_content") != null,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = div.selectFirst("img")?.src().assertNotNull("src"),
tags = div.selectFirst(".component-manga-categories")
.assertNotNull("tags")
@ -136,7 +137,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
)
}.orEmpty(),
state = null,
author = null,
authors = emptySet(),
description = div.selectFirst(".manga_synopsis")?.html().assertNotNull("description"),
source = source,
)
@ -147,6 +148,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
val mangaUrl = manga.url.toAbsoluteUrl(domain)
val root = webClient.httpGet(mangaUrl).parseHtml()
.requireElementById("container_manga_show")
val author = root.selectFirst(".datas_more-authors-people")?.textOrNull()
return manga.copy(
altTitle = root.selectFirst(".component-manga-title_alt")?.textOrNull(),
description = root.selectFirst(".datas_synopsis")?.html().assertNotNull("description")
@ -158,7 +160,7 @@ internal class BentomangaParser(context: MangaLoaderContext) :
"En pause" -> MangaState.PAUSED
else -> null
},
author = root.selectFirst(".datas_more-authors-people")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = run {
val input = root.selectFirst("input[name=\"limit\"]") ?: return@run parseChapters(root)
val max = input.attr("max").toInt()

@ -74,10 +74,10 @@ internal class FuryoSociety(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = false,
contentRating = null,
)
}
}

@ -136,11 +136,11 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
url = urlManga,
publicUrl = urlManga,
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = "https://api.$domain/" + j.getString("cover"),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -157,11 +157,11 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
url = urlManga,
publicUrl = urlManga,
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = null,
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -170,6 +170,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga {
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH)
val author = root.select("div.serieAdd p:contains(Auteur:) strong").textOrNull()
return manga.copy(
tags = root.select("div.serieGenre span").mapToSet { span ->
MangaTag(
@ -179,7 +180,7 @@ internal class LegacyScansParser(context: MangaLoaderContext) :
)
},
coverUrl = root.selectFirst("div.serieImg img")?.attrAsAbsoluteUrlOrNull("src"),
author = root.select("div.serieAdd p:contains(Auteur:) strong").textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = root.selectFirst("div.serieDescription div")?.html(),
chapters = root.select("div.chapterList a")
.mapChapters(reversed = true) { i, a ->

@ -72,11 +72,11 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context,
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = div.selectFirstOrThrow(".item__rating").ownText().toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").attrAsAbsoluteUrl("src"),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -85,6 +85,9 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context,
override suspend fun getDetails(manga: Manga): Manga {
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
val author = root.select("ul.pmovie__list li:contains(Artist(s):)").text()
.replace("Artist(s):", "")
.nullIfEmpty()
return manga.copy(
altTitle = root.select("ul.pmovie__list li:contains(Nom Alternatif:)").text()
.replace("Nom Alternatif:", "").nullIfEmpty(),
@ -101,9 +104,7 @@ internal class LireScan(context: MangaLoaderContext) : PagedMangaParser(context,
source = source,
)
},
author = root.select("ul.pmovie__list li:contains(Artist(s):)").text()
.replace("Artist(s):", "")
.nullIfEmpty(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = root.selectFirst("div.pmovie__text")?.html(),
chapters = root.select("ul li div.chapter")
.mapChapters(reversed = true) { i, div ->

@ -111,11 +111,11 @@ internal class LugnicaScans(context: MangaLoaderContext) :
url = urlManga.toRelativeUrl(domain),
publicUrl = urlManga.toAbsoluteUrl(domain),
rating = j.getFloatOrDefault("manga_rate", RATING_UNKNOWN).div(5f),
isNsfw = false,
contentRating = null,
coverUrl = img,
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -132,7 +132,7 @@ internal class LugnicaScans(context: MangaLoaderContext) :
url = urlManga.toRelativeUrl(domain),
publicUrl = urlManga.toAbsoluteUrl(domain),
rating = j.getString("rate").toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = img,
tags = setOf(),
state = when (j.getString("status")) {
@ -141,7 +141,7 @@ internal class LugnicaScans(context: MangaLoaderContext) :
"3" -> MangaState.ABANDONED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}
@ -161,6 +161,7 @@ internal class LugnicaScans(context: MangaLoaderContext) :
}
val slug = manga.url.substringAfterLast("/")
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
val author = jsonManga.getStringOrNull("author")
return manga.copy(
state = when (jsonManga.getString("status")) {
"0" -> MangaState.ONGOING
@ -168,7 +169,7 @@ internal class LugnicaScans(context: MangaLoaderContext) :
"3" -> MangaState.ABANDONED
else -> null
},
author = jsonManga.getStringOrNull("author"),
authors = author?.let { setOf(it) } ?: emptySet(),
description = jsonManga.getStringOrNull("description"),
chapters = chapters.mapChapters { i, it ->
val id = it.substringAfter("\"chapter\":").substringBefore(",")

@ -85,10 +85,10 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -98,10 +98,11 @@ internal class MangaKawaii(context: MangaLoaderContext) : PagedMangaParser(conte
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val firstChapter = doc.selectFirst("tr[class*='volume-'] a")?.attr("href")
val chaptersDeferred = async { loadChapters(firstChapter) }
val author = doc.select("a[href*=author]").text()
manga.copy(
description = doc.selectFirst("dd.text-justify.text-break")?.html(),
altTitle = doc.select("span[itemprop*=alternativeHeadline]").joinToString { ", " }.nullIfEmpty(),
author = doc.select("a[href*=author]").text(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (doc.selectFirst("span.badge.bg-success.text-uppercase")?.text()) {
"En Cours" -> MangaState.ONGOING
"Terminé" -> MangaState.FINISHED

@ -68,17 +68,18 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
val slug = jo.getString("slug") ?: throw ParseException("Missing Slug", url)
val url = "https://$domain/m/$slug"
val img = "https://$domainCdn/uploads/manga/$slug/cover/cover_thumb.jpg"
val isNsfwSource = when (jo.getIntOrDefault("caution", 0)) {
0 -> false
2 -> true
else -> false
}
Manga(
id = generateUid(url),
title = jo.getString("name").orEmpty(),
coverUrl = img,
altTitle = jo.getString("otherNames").orEmpty(),
author = null,
isNsfw = when (jo.getIntOrDefault("caution", 0)) {
0 -> false
2 -> true
else -> false
},
authors = emptySet(),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
rating = RATING_UNKNOWN,
url = url,
description = jo.getString("summary_old").orEmpty(),
@ -107,8 +108,8 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
val doc = webClient.httpGet("https://$domain/?page=$page").parseHtml()
return doc.select("div.row div.col_home").map { div ->
val href = div.selectFirstOrThrow("h4 a").attrAsRelativeUrl("href")
val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true
val img = if (isNsfw) {
val isNsfwSource = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true
val img = if (isNsfwSource) {
div.selectFirst("img")?.attr("data-adult")
} else {
div.selectFirst("img")?.attr("data-src")?.replace(" ", "")
@ -120,12 +121,12 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfw,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = img,
description = null,
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -170,8 +171,8 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
return doc.select("div.p-2 div.col").map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val isNsfw = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true
val img = if (isNsfw) {
val isNsfwSource = div.selectFirst("img[data-adult]")?.attr("data-adult")?.isNotEmpty() == true
val img = if (isNsfwSource) {
div.selectFirst("img")?.attr("data-adult")
} else {
div.selectFirst("img")?.attr("data-src")?.replace(" ", "")
@ -183,7 +184,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = div.getElementById("avgrating")?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
isNsfw = isNsfw,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = img,
description = div.selectFirst(".mangalist_item_description")?.text().orEmpty(),
tags = div.select("div.mb-1 a").mapToSet {
@ -195,7 +196,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
)
},
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -228,6 +229,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
val doc = webClient.httpGet(mangaUrl).parseHtml()
val maxPageChapterSelect = doc.select("ul.pagination a.page-link")
var maxPageChapter = 1
val author = doc.selectFirst("div.show_details span[itemprop=author]")?.text().orEmpty()
if (!maxPageChapterSelect.isNullOrEmpty()) {
maxPageChapterSelect.map {
val i = it.attr("href").substringAfterLast("=").toInt()
@ -243,7 +245,7 @@ internal class MangaMana(context: MangaLoaderContext) : PagedMangaParser(context
"Abandonné" -> MangaState.ABANDONED
else -> null
},
author = doc.selectFirst("div.show_details span[itemprop=author]")?.text().orEmpty(),
authors = setOf(author),
description = doc.selectFirst("dd[itemprop=description]")?.text(),
rating = doc.getElementById("avgrating")?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
tags = doc.select("ul.list-unstyled li a.category").mapToSet {

@ -87,10 +87,10 @@ internal class ScansMangasMe(context: MangaLoaderContext) :
rating = div.selectFirstOrThrow("div.rating i").ownText().toFloatOrNull()?.div(10f)
?: RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -114,7 +114,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) :
val chaptersDeferred = getChapters(doc)
val desc = doc.selectFirstOrThrow("div.desc").html().nullIfEmpty()
val alt = doc.body().select("div.infox span.alter").textOrNull()
val aut = doc.select("div.spe span")[2].text().replace("Auteur:", "").nullIfEmpty()
val author = doc.select("div.spe span")[2].text().replace("Auteur:", "").nullIfEmpty()
manga.copy(
tags = doc.select("div.spe span:contains(Genres) a").mapToSet { a ->
MangaTag(
@ -125,7 +125,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) :
},
description = desc,
altTitle = alt,
author = aut,
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (doc.selectFirstOrThrow("div.spe span:contains(Statut:)").textOrNull()
?.substringAfterLast(':')) {
" En cours" -> MangaState.ONGOING

@ -88,11 +88,11 @@ internal class ScantradUnion(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = article.selectFirst("img.attachment-thumbnail")?.attrAsAbsoluteUrl("src"),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -108,11 +108,11 @@ internal class ScantradUnion(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = article.selectFirst("img")?.attrAsAbsoluteUrl("src"),
tags = setOf(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -122,6 +122,7 @@ internal class ScantradUnion(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga {
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("main")
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.FRANCE)
val author = root.select("div.project-details a[href*=auteur]").textOrNull()
return manga.copy(
altTitle = root.select(".divider2:contains(Noms associés :)").firstOrNull()?.textOrNull(),
@ -137,7 +138,7 @@ internal class ScantradUnion(context: MangaLoaderContext) :
source = source,
)
},
author = root.select("div.project-details a[href*=auteur]").textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = root.selectFirst("p.sContent")?.html(),
chapters = root.select("div.chapter-list li")
.mapChapters(reversed = true) { i, li ->

@ -152,10 +152,10 @@ internal abstract class FuzzyDoodleParser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -181,6 +181,7 @@ internal abstract class FuzzyDoodleParser(
}
}
}
val author = doc.selectFirst(selectAuthor)?.textOrNull()
manga.copy(
altTitle = doc.selectLast(selectAltTitle)?.textOrNull(),
@ -191,7 +192,7 @@ internal abstract class FuzzyDoodleParser(
in paused -> MangaState.PAUSED
else -> null
},
author = doc.selectFirst(selectAuthor)?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.select(selectDescription).html(),
tags = doc.select(selectTagManga).mapToSet {
val key = it.attr("href").substringAfterLast('=')

@ -113,11 +113,11 @@ internal abstract class GalleryAdultsParser(
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = div.selectFirst(selectGalleryImg)?.src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -165,10 +165,11 @@ internal abstract class GalleryAdultsParser(
val branch = doc.select(selectLanguageChapter).joinToString(separator = " / ") {
it.html().substringBefore("<")
}
val author = doc.selectFirst(selectAuthor)?.html()?.substringBefore("<span")
return manga.copy(
tags = tag.orEmpty(),
title = doc.selectFirst(selectTitle)?.textOrNull()?.cleanupTitle() ?: manga.title,
author = doc.selectFirst(selectAuthor)?.html()?.substringBefore("<span"),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = listOf(
MangaChapter(
id = manga.id,

@ -43,11 +43,11 @@ internal class DoujinDesuUk(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = div.selectLastOrThrow(selectGalleryImg).src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -126,9 +126,10 @@ internal class HentaiEra(context: MangaLoaderContext) :
val branch = doc.select(selectLanguageChapter).joinToString(separator = " / ") {
it.text()
}
val author = doc.selectFirst(selectAuthor)?.text()
return manga.copy(
tags = tag.orEmpty(),
author = doc.selectFirst(selectAuthor)?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = listOf(
MangaChapter(
id = manga.id,

@ -94,11 +94,11 @@ internal class NHentaiParser(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = div.selectFirstOrThrow(selectGalleryImg).src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}

@ -88,7 +88,7 @@ internal class NHentaiToParser(context: MangaLoaderContext) :
coverUrl = div.selectFirstOrThrow(selectGalleryImg).src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
largeCoverUrl = null,
description = null,
chapters = null,

@ -101,7 +101,7 @@ internal class NHentaiXxxParser(context: MangaLoaderContext) :
coverUrl = div.selectFirstOrThrow(selectGalleryImg).src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
largeCoverUrl = null,
description = null,
chapters = null,

@ -84,8 +84,8 @@ internal abstract class GattsuParser(
tags = emptySet(),
description = null,
state = null,
author = null,
isNsfw = isNsfwSource,
authors = emptySet(),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
source = source,
)
}
@ -111,12 +111,12 @@ internal abstract class GattsuParser(
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val urlChapter = doc.selectFirstOrThrow("ul.post-fotos li a, ul.paginaPostBotoes a").attrAsAbsoluteUrl("href")
val author = doc.selectFirst(".post-itens li:contains(Autor) a, .paginaPostInfo li:contains(Artista) a")?.text()
return manga.copy(
description = doc.selectFirst("div.post-texto")?.html(),
tags = doc.selectFirst(".post-itens li:contains(Tags), .paginaPostInfo li:contains(Categorias)")
?.parseTags().orEmpty(),
author = doc.selectFirst(".post-itens li:contains(Autor) a, .paginaPostInfo li:contains(Artista) a")
?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
chapters = listOf(
MangaChapter(
id = manga.id,

@ -3,10 +3,7 @@ package org.koitharu.kotatsu.parsers.site.gattsu.pt
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.gattsu.GattsuParser
import org.koitharu.kotatsu.parsers.util.*
@ -35,8 +32,8 @@ internal class MundoHentaiOficial(context: MangaLoaderContext) :
tags = emptySet(),
description = null,
state = null,
author = null,
isNsfw = isNsfwSource,
authors = emptySet(),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
source = source,
)
}

@ -60,6 +60,7 @@ internal abstract class GuyaParser(
private fun addManga(j: JSONObject, name: String): Manga {
val url = "https://$domain/read/manga/" + j.getString("slug")
val apiUrl = "https://$domain/api/series/" + j.getString("slug")
val author = j.getString("author")
return Manga(
id = generateUid(apiUrl),
url = apiUrl,
@ -71,8 +72,8 @@ internal abstract class GuyaParser(
tags = emptySet(),
description = j.getString("description"),
state = null,
author = j.getString("author"),
isNsfw = isNsfwSource,
authors = author?.let { setOf(it) } ?: emptySet(),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
source = source,
)
}

@ -129,7 +129,7 @@ internal abstract class HeanCms(
publicUrl = publicUrl.toAbsoluteUrl(domain),
description = it.getString("description"),
rating = it.getFloatOrDefault("rating", RATING_UNKNOWN) / 5f,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
coverUrl = cover,
tags = emptySet(),
state = when (it.getString("status")) {
@ -139,7 +139,7 @@ internal abstract class HeanCms(
"Hiatus" -> MangaState.PAUSED
else -> null
},
author = null,
authors = emptySet(),
source = source,
)
}

@ -70,10 +70,10 @@ internal abstract class HeanCmsAlt(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -100,6 +100,8 @@ internal abstract class HotComicsParser(
}
val tags = li.select(".etc span").mapNotNullToSet { tagMap[it.text()] }
val isNsfwSource = a.selectFirst(".ico-18plus") != null
val author = li.selectFirst(".writer")?.text().orEmpty()
Manga(
id = generateUid(url),
@ -111,14 +113,14 @@ internal abstract class HotComicsParser(
rating = RATING_UNKNOWN,
description = li.selectFirst("p[itemprop*=description]")?.text().orEmpty(),
tags = tags,
author = li.selectFirst(".writer")?.text().orEmpty(),
authors = setOf(author),
state = if (doc.selectFirst(".ico_fin") != null) {
MangaState.FINISHED
} else {
MangaState.ONGOING
},
source = source,
isNsfw = a.selectFirst(".ico-18plus") != null,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -120,11 +120,11 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = true,
contentRating = ContentRating.ADULT,
coverUrl = it.selectFirst(".thumbnail > img")?.src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
largeCoverUrl = null,
description = null,
source = source,
@ -141,8 +141,9 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
"Publishing" -> MangaState.ONGOING
else -> null
}
val author = metadataEl?.selectFirst("tr:contains(Author)")?.selectLast("td")?.text()
return manga.copy(
author = metadataEl?.selectFirst("tr:contains(Author)")?.selectLast("td")?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = docs.selectFirst(".wrapper > .metadata > .pb-2")?.selectFirst("p")?.html(),
state = state,
rating = metadataEl?.selectFirst(".rating-prc")?.ownText()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,

@ -71,10 +71,10 @@ internal class HentaiCrot(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -95,10 +95,11 @@ internal class HentaiCrot(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.textOrNull()
return manga.copy(
description = doc.selectFirst("div.entry-content p")?.html(),
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")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = null,
chapters = listOf(
MangaChapter(

@ -71,10 +71,10 @@ internal class PixHentai(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -95,10 +95,11 @@ internal class PixHentai(context: MangaLoaderContext) :
override suspend fun getDetails(manga: Manga): Manga {
val fullUrl = manga.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val author = doc.selectFirst("div.entry-content ul li:contains(Artists :) em")?.textOrNull()
return manga.copy(
description = doc.selectFirst("div.entry-content p")?.html(),
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")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = null,
chapters = listOf(
MangaChapter(

@ -101,6 +101,8 @@ internal abstract class IkenParser(
protected open fun parseMangaList(json: JSONObject): List<Manga> {
return json.getJSONArray("posts").mapJSON {
val url = "/series/${it.getString("slug")}"
val isNsfwSource = it.getBooleanOrDefault("hot", false)
val author = it.getString("author")
Manga(
id = it.getLong("id"),
url = url,
@ -111,7 +113,7 @@ internal abstract class IkenParser(
description = it.getString("postContent"),
rating = RATING_UNKNOWN,
tags = emptySet(),
author = it.getString("author"),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (it.getString("seriesStatus")) {
"ONGOING" -> MangaState.ONGOING
"COMPLETED" -> MangaState.FINISHED
@ -120,7 +122,7 @@ internal abstract class IkenParser(
else -> null
},
source = source,
isNsfw = it.getBooleanOrDefault("hot", false),
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -84,15 +84,16 @@ internal class NicovideoSeigaParser(context: MangaLoaderContext) :
val href =
item.selectFirst(".comic_icon > div > a")?.attrAsRelativeUrlOrNull("href") ?: return@mapNotNull null
val statusText = item.selectFirst(".mg_description_header > .mg_icon > .content_status > span")?.text()
val author = item.selectFirst(".mg_description_header > .mg_author > a")?.text()
Manga(
id = generateUid(href),
title = item.selectFirst(".mg_body > .title > a")?.text() ?: return@mapNotNull null,
coverUrl = item.selectFirst(".comic_icon > div > a > img")?.attrAsAbsoluteUrl("src"),
altTitle = null,
author = item.selectFirst(".mg_description_header > .mg_author > a")?.text(),
authors = author?.let { setOf(it) } ?: emptySet(),
rating = RATING_UNKNOWN,
url = href,
isNsfw = false,
contentRating = null,
tags = item.getElementsByAttributeValueContaining("href", "?category=").mapToSet { a ->
MangaTag(
key = a.attr("href").substringAfterLast('='),
@ -193,11 +194,11 @@ internal class NicovideoSeigaParser(context: MangaLoaderContext) :
title = item.selectFirst(".search_result__item__info > .search_result__item__info--title > a")
?.textOrNull() ?: return@mapNotNull null,
altTitle = null,
author = null,
authors = emptySet(),
tags = emptySet(),
rating = RATING_UNKNOWN,
state = null,
isNsfw = false,
contentRating = null,
source = source,
coverUrl = item.selectFirst(".search_result__item__thumbnail > a > img")
?.attrAsAbsoluteUrl("data-original"),

@ -138,10 +138,10 @@ internal abstract class KeyoappParser(
source = source,
)
},
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}

@ -115,11 +115,11 @@ internal abstract class LikeMangaParser(
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = false,
contentRating = null,
coverUrl = div.selectFirstOrThrow("img").src(),
tags = emptySet(),
state = null,
author = null,
authors = emptySet(),
source = source,
)
}
@ -149,6 +149,7 @@ internal abstract class LikeMangaParser(
}
}
}
val author = doc.selectLast("li.author p")?.textOrNull()
return manga.copy(
altTitle = doc.selectFirst(".list-info li.othername h2")?.textOrNull(),
tags = doc.select("li.kind a").mapToSet { a ->
@ -158,7 +159,7 @@ internal abstract class LikeMangaParser(
source = source,
)
},
author = doc.selectLast("li.author p")?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = doc.requireElementById("summary_shortened").html(),
chapters = run {
if (maxPageChapter == 1) {

@ -119,10 +119,10 @@ internal abstract class LilianaParser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
@ -148,6 +148,9 @@ internal abstract class LilianaParser(
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val author = doc.selectFirst("div.y6x11p i.fas.fa-user + span.dt")?.textOrNull()?.takeUnless {
it.equals("updating", true)
}
return manga.copy(
description = doc.selectFirst("div#syn-target")?.html(),
largeCoverUrl = doc.selectFirst(".a1 > figure img")?.src(),
@ -158,9 +161,7 @@ internal abstract class LilianaParser(
source = source,
)
},
author = doc.selectFirst("div.y6x11p i.fas.fa-user + span.dt")?.textOrNull()?.takeUnless {
it.equals("updating", true)
},
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (doc.selectFirst("div.y6x11p i.fas.fa-rss + span.dt")?.text()?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
in finished -> MangaState.FINISHED

@ -460,6 +460,7 @@ internal abstract class MadaraParser(
}.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -476,7 +477,7 @@ internal abstract class MadaraParser(
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (
summary?.selectFirst(".mg_status")
?.selectFirst(".summary-content")
@ -491,7 +492,7 @@ internal abstract class MadaraParser(
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -660,12 +661,12 @@ internal abstract class MadaraParser(
publicUrl = href.toAbsoluteUrl(a.host ?: domain),
altTitle = null,
title = div.selectFirstOrThrow(".widget-title").text(),
author = null,
authors = emptySet(),
coverUrl = div.selectFirst("img")?.src(),
tags = emptySet(),
rating = RATING_UNKNOWN,
state = null,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
source = source,
)
}

@ -74,10 +74,10 @@ internal class Manga18Fx(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst("div.item-rate span")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -80,10 +80,10 @@ internal class Manhwa18Cc(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst(".item-rate span")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -3,10 +3,7 @@ package org.koitharu.kotatsu.parsers.site.madara.en
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
@ -20,6 +17,7 @@ internal class FireScans(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("h3 a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -36,7 +34,7 @@ internal class FireScans(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (
summary?.selectFirst(".mg_status")
?.selectFirst(".summary-content")
@ -50,7 +48,7 @@ internal class FireScans(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -102,6 +102,7 @@ internal class Hentai4Free(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -118,7 +119,7 @@ internal class Hentai4Free(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -126,7 +127,7 @@ internal class Hentai4Free(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -77,6 +77,7 @@ internal class IsekaiScan(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -92,7 +93,7 @@ internal class IsekaiScan(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase()) {
"ongoing" -> MangaState.ONGOING
@ -100,7 +101,7 @@ internal class IsekaiScan(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -99,6 +99,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -115,7 +116,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -123,7 +124,7 @@ internal class IsekaiScanEuParser(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -75,6 +75,7 @@ internal class MangaDass(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -90,7 +91,7 @@ internal class MangaDass(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -98,7 +99,7 @@ internal class MangaDass(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -68,6 +68,7 @@ internal class MangaDna(context: MangaLoaderContext) :
return doc.select("div.home-item").map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".hcontent")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -83,7 +84,7 @@ internal class MangaDna(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -91,7 +92,7 @@ internal class MangaDna(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -82,6 +82,7 @@ internal class MangaPure(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -97,7 +98,7 @@ internal class MangaPure(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase()) {
"ongoing" -> MangaState.ONGOING
@ -105,7 +106,7 @@ internal class MangaPure(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -71,6 +71,7 @@ internal class Manhwaz(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -86,7 +87,7 @@ internal class Manhwaz(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -94,7 +95,7 @@ internal class Manhwaz(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -3,10 +3,7 @@ package org.koitharu.kotatsu.parsers.site.madara.en
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
@ -21,6 +18,7 @@ internal class ShibaManga(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -37,7 +35,7 @@ internal class ShibaManga(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (
summary?.selectFirst(".mg_status")
?.selectFirst(".summary-content")
@ -52,7 +50,7 @@ internal class ShibaManga(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -2,10 +2,7 @@ package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@ -67,10 +64,10 @@ internal class DragonTranslationParser(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -20,6 +20,7 @@ internal class MangasNoSekai(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
val body = doc.body()
val chaptersDeferred = async { loadChapters(manga.url, doc) }
val author = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Autor)) p a")?.textOrNull()
manga.copy(
tags = doc.body().select("#section-sinopsis a[href*=genre]").mapToSet { a ->
MangaTag(
@ -28,8 +29,7 @@ internal class MangasNoSekai(context: MangaLoaderContext) :
source = source,
)
},
author = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Autor)) p a")
?.textOrNull(),
authors = author?.let { setOf(it) } ?: emptySet(),
description = body.selectFirst("#section-sinopsis p")?.text().orEmpty(),
altTitle = doc.selectFirst("section#section-sinopsis div.d-flex:has(div:contains(Otros nombres)) p")
?.textOrNull(),

@ -75,10 +75,10 @@ internal class TmoManga(context: MangaLoaderContext) :
altTitle = null,
rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -66,6 +66,7 @@ internal class ManhwaHub(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -82,7 +83,7 @@ internal class ManhwaHub(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -90,7 +91,7 @@ internal class ManhwaHub(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -4,10 +4,7 @@ import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@ -25,6 +22,7 @@ internal class MangaFenxi(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -41,7 +39,7 @@ internal class MangaFenxi(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (
summary?.selectFirst(".mg_status")
?.selectFirst(".summary-content")
@ -56,7 +54,7 @@ internal class MangaFenxi(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -72,6 +72,7 @@ internal class Saytruyenhay(context: MangaLoaderContext) :
}.map { div ->
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga(
id = generateUid(href),
url = href,
@ -87,7 +88,7 @@ internal class Saytruyenhay(context: MangaLoaderContext) :
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
authors = author?.let { setOf(it) } ?: emptySet(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()
?.lowercase().orEmpty()) {
in ongoing -> MangaState.ONGOING
@ -95,7 +96,7 @@ internal class Saytruyenhay(context: MangaLoaderContext) :
else -> null
},
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -130,10 +130,10 @@ internal abstract class MadthemeParser(
source = source,
)
},
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

@ -112,10 +112,10 @@ internal abstract class Manga18Parser(
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}
@ -166,7 +166,7 @@ internal abstract class Manga18Parser(
},
description = desc?.nullIfEmpty(),
altTitle = alt,
author = author,
authors = author?.let { setOf(it) } ?: emptySet(),
state = state,
chapters = chaptersDeferred.await(),
)

@ -3,10 +3,7 @@ package org.koitharu.kotatsu.parsers.site.manga18.en
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.manga18.Manga18Parser
import org.koitharu.kotatsu.parsers.util.*
@ -28,10 +25,10 @@ internal class Hentai3zCc(context: MangaLoaderContext) :
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
authors = emptySet(),
state = null,
source = source,
isNsfw = isNsfwSource,
contentRating = if (isNsfwSource) ContentRating.ADULT else null,
)
}
}

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

Loading…
Cancel
Save