diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt index b512b35f..dacf377b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaChapter.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.parsers.model import org.koitharu.kotatsu.parsers.util.formatSimple +import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty public data class MangaChapter( /** @@ -8,9 +9,10 @@ public data class MangaChapter( */ @JvmField public val id: Long, /** - * User-readable name of chapter + * User-readable name of chapter if provided by parser or null instead + * Do not pass manga title or chapter number here */ - @JvmField public val name: String, + @JvmField public val title: String?, /** * Chapter number starting from 1, 0 if unknown */ @@ -40,6 +42,15 @@ public data class MangaChapter( @JvmField public val source: MangaSource, ) { + @Deprecated("Use title instead", ReplaceWith("title")) + val name: String + get() = title.ifNullOrEmpty { + buildString { + if (volume > 0) append("Vol ").append(volume).append(' ') + if (number > 0) append("Chapter ").append(number) else append("Unnamed") + } + } + public fun numberString(): String? = if (number > 0f) { number.formatSimple() } else { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt index f8d14623..6f3f8d35 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt @@ -365,7 +365,7 @@ internal class BatoToParser(context: MangaLoaderContext) : LegacyPagedMangaParse val href = a.attrAsRelativeUrl("href") return MangaChapter( id = generateUid(href), - name = a.text(), + title = a.textOrNull(), number = index + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt index b7687010..d4e74446 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt @@ -226,13 +226,7 @@ internal class ComickFunParser(context: MangaLoaderContext) : } MangaChapter( id = generateUid(jo.getLong("id")), - name = buildString { - if (vol > 0) { - append("Vol ").append(vol).append(' ') - } - append("Chap ").append(chap.formatSimple()) - jo.getStringOrNull("title")?.let { append(": ").append(it) } - }, + title = jo.getStringOrNull("title"), number = chap, volume = vol, url = jo.getString("hid"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt index dbea6816..fd58032c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ExHentaiParser.kt @@ -235,7 +235,7 @@ internal class ExHentaiParser( val url = "${manga.url}?p=${i - 1}" chapters += MangaChapter( id = generateUid(url), - name = "${manga.title} #$i", + title = null, number = i.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt index 480da7d3..ab5af543 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/HitomiLaParser.kt @@ -583,7 +583,7 @@ internal class HitomiLaParser(context: MangaLoaderContext) : LegacyMangaParser(c MangaChapter( id = generateUid(manga.url), url = manga.url, - name = json.getString("title"), + title = json.getStringOrNull("title"), scanlator = json.getString("type").toTitleCase(), number = 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt index 04f32d8b..f9899bfb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ImHentai.kt @@ -175,7 +175,7 @@ internal class ImHentai(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = null, number = 1f, volume = 0, url = manga.url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt index 5e9e0f9e..22de2c80 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/LineWebtoonsParser.kt @@ -102,7 +102,7 @@ internal abstract class LineWebtoonsParser( return episodes.mapChapters { i, jo -> MangaChapter( id = generateUid("$titleNo-$i"), - name = jo.getString("episodeTitle"), + title = jo.getStringOrNull("episodeTitle"), number = jo.getInt("episodeSeq").toFloat(), volume = 0, url = "$titleNo-${jo.get("episodeNo")}", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt index f607ca79..3f5756f1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaDexParser.kt @@ -484,10 +484,7 @@ internal class MangaDexParser(context: MangaLoaderContext) : AbstractMangaParser } val chapter = MangaChapter( id = generateUid(id), - name = attrs.getStringOrNull("title") ?: buildString { - if (volume > 0) append("Vol. ").append(volume).append(' ') - append("Chapter ").append(number.formatSimple()) - }, + title = attrs.getStringOrNull("title"), number = number, volume = volume, url = id, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt index b64a7a3d..032978bb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaFireParser.kt @@ -280,7 +280,7 @@ internal abstract class MangaFireParser( return chapterElements.mapChapters(reversed = true) { _, it -> MangaChapter( id = generateUid(it.attr("href")), - name = it.attr("title").ifBlank { + title = it.attr("title").ifBlank { "${branch.type.toTitleCase()} ${it.attr("data-number")}" }, number = it.attr("data-number").toFloat(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt index f3f0361e..200f5c65 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt @@ -213,7 +213,7 @@ internal class MangaPark(context: MangaLoaderContext) : val dateText = div.selectFirst("span[q:key=Ee_0]")?.text() MangaChapter( id = generateUid(href), - name = a.text(), + title = a.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt index 80c704f1..de467812 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPlusParser.kt @@ -184,7 +184,7 @@ internal abstract class MangaPlusParser( MangaChapter( id = generateUid(chapterId), url = chapterId, - name = subtitle, + title = subtitle, number = chapter.getString("name") .substringAfter("#") .toFloatOrNull() ?: -1f, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt index 510f0030..b8ae0fa8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaReaderToParser.kt @@ -222,7 +222,7 @@ internal class MangaReaderToParser(context: MangaLoaderContext) : chapters.add( MangaChapter( id = generateUid(a.attrAsRelativeUrl("href")), - name = a.attr("title"), + title = a.attrOrNull("title"), number = li.attr("data-number").toFloat(), volume = 0, url = a.attrAsRelativeUrl("href"), @@ -242,7 +242,7 @@ internal class MangaReaderToParser(context: MangaLoaderContext) : chapters.add( MangaChapter( id = generateUid(url), - name = name, + title = name, number = numRegex.find(name)?.groupValues?.getOrNull(1)?.toFloatOrNull() ?: 0f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt index 97076f16..4d453ee0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineMangaParser.kt @@ -150,7 +150,7 @@ internal abstract class NineMangaParser( val href = a.attrAsRelativeUrl("href").replace("%20", " ") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt index 1f194126..6c74c651 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/NineNineNineHentaiParser.kt @@ -284,7 +284,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = generateUid(id), - name = name, + title = name, number = 1f, volume = 0, url = id, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt index 8df0a8b1..78f60cf6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt @@ -97,7 +97,7 @@ internal abstract class WebtoonsParser( episodes.mapChapters { i, jo -> MangaChapter( id = generateUid("$titleNo-$i"), - name = jo.getString("episodeTitle"), + title = jo.getStringOrNull("episodeTitle"), number = jo.getInt("episodeSeq").toFloat(), volume = 0, url = "$titleNo-${jo.get("episodeNo")}", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt index 8609103b..ae7246a2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/AnimeBootstrapParser.kt @@ -168,7 +168,7 @@ internal abstract class AnimeBootstrapParser( val href = a.attr("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt index 109cd706..17b3b5fa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/animebootstrap/fr/PapScan.kt @@ -124,7 +124,7 @@ internal class PapScan(context: MangaLoaderContext) : val dateText = li.selectFirst("span.date-chapter-title-rtl")?.text() MangaChapter( id = generateUid(href), - name = li.selectFirstOrThrow("span em").text(), + title = li.selectFirstOrThrow("span em").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt index 3537b6c1..63b0209e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/FlixScans.kt @@ -191,7 +191,7 @@ internal class FlixScans(context: MangaLoaderContext) : MangaChapter( id = generateUid(url), url = url, - name = j.getString("slug").replace('-', ' '), + title = j.getString("slug").replace('-', ' '), number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt index 929e9d69..d126fbcd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/MangaStorm.kt @@ -101,7 +101,7 @@ internal class MangaStorm(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt index 8810e03a..3c29ac48 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ar/TeamXNovel.kt @@ -195,7 +195,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : val url = li.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = li.selectFirstOrThrow(".epl-title").text(), + title = li.selectFirstOrThrow(".epl-title").text(), number = url.substringAfterLast('/').toFloatOrNull() ?: 0f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt index 4b90d247..0286f831 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt @@ -159,7 +159,7 @@ internal class AnibelParser(context: MangaLoaderContext) : LegacyMangaParser(con val number = jo.getInt("chapter") MangaChapter( id = generateUid(jo.getString("id")), - name = "Глава $number", + title = null, number = number.toFloat(), volume = 0, url = "${manga.url}/read/$number", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt index cb7c9cd8..af3fe50e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/cupfox/CupFoxParser.kt @@ -142,7 +142,7 @@ internal abstract class CupFoxParser( val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt index 88b7ac05..cf3eec6b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt @@ -172,7 +172,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) : val cleanDate = date.replace(regexDate, "$1") MangaChapter( id = generateUid(url), - name = div.selectFirst("h3")?.text() ?: "Chapter : ${i + 1f}", + title = div.selectFirst("h3")?.textOrNull(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt index 9846202c..611c117f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/BeeToon.kt @@ -119,7 +119,7 @@ internal class BeeToon(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) MangaChapter( id = generateUid(url), - name = a.selectFirstOrThrow(".chap").text(), + title = a.selectFirstOrThrow(".chap").text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt index de54b3d4..d265cefd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/CloneMangaParser.kt @@ -65,7 +65,7 @@ internal class CloneMangaParser(context: MangaLoaderContext) : for (i in 0..numChapters) { val chapter = MangaChapter( id = generateUid("$series&page=$i"), - name = "Chapter ${i + 1}", + title = null, number = i + 1f, volume = 0, url = "$series&page=$i", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt index 26f1cef7..562e9da4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ComicExtra.kt @@ -136,7 +136,7 @@ internal class ComicExtra(context: MangaLoaderContext) : } MangaChapter( id = generateUid(url), - name = name, + title = name, number = elements.size - i.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt index 6b182980..b89bfee6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt @@ -179,7 +179,7 @@ internal class DynastyScans(context: MangaLoaderContext) : val dateText = li.select("small").last()?.text()?.replace("released ", "")?.replace("'", "") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlameComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlameComics.kt index 10292916..937e31c8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlameComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlameComics.kt @@ -183,7 +183,7 @@ internal class FlameComics(context: MangaLoaderContext) : val number = jo.getFloatOrDefault("chapter", 0f) MangaChapter( id = generateUid(longOf(seriesId.toInt(), chapterId.toInt())), - name = jo.getStringOrNull("name") ?: ("Chapter " + number.formatSimple()), + title = jo.getStringOrNull("name"), number = number, volume = 0, url = seriesId.toString() + "?" + jo.getStringOrNull("token").orEmpty(), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt index 07e9b911..2b540df7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt @@ -175,7 +175,7 @@ internal class FlixScansOrg(context: MangaLoaderContext) : MangaChapter( id = generateUid(url), url = url, - name = j.getString("slug").replace('-', ' '), + title = j.getString("slug").replace('-', ' '), number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt index a477e5fe..09a96c13 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaGeko.kt @@ -144,7 +144,7 @@ internal class MangaGeko(context: MangaLoaderContext) : .replace(".", "").replace("Sept", "Sep") MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt index a14f77f5..42ef9c3f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaKawaiiEn.kt @@ -134,7 +134,7 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt index 96473551..ddb055e2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt @@ -176,7 +176,7 @@ internal class MangaTownParser(context: MangaLoaderContext) : dateFormat, li.selectFirst("span.time")?.text(), ), - name = name.ifEmpty { "${manga.title} - ${i + 1}" }, + title = name.nullIfEmpty(), scanlator = null, branch = null, ) @@ -273,7 +273,7 @@ internal class MangaTownParser(context: MangaLoaderContext) : dateFormat, li.selectFirst("span.time")?.text(), ), - name = name.ifEmpty { "${manga.title} - ${i + 1}" }, + title = name.nullIfEmpty(), scanlator = null, branch = null, ) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt index fd05e5ef..e74af971 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Mangaowl.kt @@ -166,7 +166,7 @@ internal class Mangaowl(context: MangaLoaderContext) : val name = t.substringAfter("name:\"").substringBefore('"') MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Com.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Com.kt index 8a585bd3..f03a809b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Com.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Com.kt @@ -178,7 +178,7 @@ internal class Manhwa18Com(context: MangaLoaderContext) : val uploadDate = parseUploadDate(element.selectFirst(".chapter-time")?.text()) MangaChapter( id = generateUid(chapterUrl), - name = element.selectFirst(".chapter-name")?.text().orEmpty(), + title = element.selectFirst(".chapter-name")?.textOrNull(), number = index + 1f, volume = 0, url = chapterUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt index 76b2788e..f634bb71 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Manhwa18Parser.kt @@ -178,7 +178,7 @@ internal class Manhwa18Parser(context: MangaLoaderContext) : val uploadDate = parseUploadDate(element.selectFirst(".chapter-time")?.text()) MangaChapter( id = generateUid(chapterUrl), - name = element.selectFirst(".chapter-name")?.text().orEmpty(), + title = element.selectFirst(".chapter-name")?.textOrNull(), number = index + 1f, volume = 0, url = chapterUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt index 522f9092..9004ac47 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ManhwasMen.kt @@ -129,7 +129,7 @@ internal class ManhwasMen(context: MangaLoaderContext) : val url = li.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = li.selectFirstOrThrow(".flex-grow-1 span").text(), + title = li.selectFirstOrThrow(".flex-grow-1 span").text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MyComicList.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MyComicList.kt index c26f96d7..5a29c591 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MyComicList.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MyComicList.kt @@ -111,7 +111,7 @@ internal class MyComicList(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), - name = name, + title = name, number = name.substringAfter('#').toFloatOrNull() ?: (i + 1f), url = href, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt index 27606d4b..15df7040 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Po2Scans.kt @@ -79,7 +79,7 @@ internal class Po2Scans(context: MangaLoaderContext) : val url = "/" + a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) MangaChapter( id = generateUid(url), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt index f5dd3736..46e45c91 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/Pururin.kt @@ -149,7 +149,7 @@ internal class Pururin(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = manga.url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt index b1d8ec48..a2bfcf90 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/VyManga.kt @@ -152,7 +152,7 @@ internal class VyManga(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = a.selectFirst("span")?.text() ?: "Chapter ${i + 1}", + title = a.selectFirst("span")?.textOrNull(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/WeebCentral.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/WeebCentral.kt index d54737b6..86787065 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/WeebCentral.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/WeebCentral.kt @@ -303,7 +303,7 @@ internal class WeebCentral(context: MangaLoaderContext) : LegacyMangaParser(cont MangaChapter( id = generateUid(chapterId), url = chapterId, - name = name, + title = name, number = Regex("""(? .text-right > a").attrAsRelativeUrl("href") return MangaChapter( id = generateUid(href), - name = "One Shot", + title = "One Shot", number = 1f, volume = 0, url = href, @@ -227,7 +227,7 @@ internal class TuMangaOnlineParser(context: MangaLoaderContext) : LegacyPagedMan val href = element.selectFirstOrThrow("div.row > .text-right > a").attrAsRelativeUrl("href") return MangaChapter( id = generateUid(href), - name = chName, + title = chName, number = number + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt index 94040eac..e3370b2c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt @@ -218,7 +218,7 @@ internal abstract class FmreaderParser( val dateText = a.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = a.selectFirstOrThrow("div.chapter-name").text(), + title = a.selectFirstOrThrow("div.chapter-name").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt index 64ab2913..dcaa6ca3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/Klz9.kt @@ -64,7 +64,7 @@ internal class Klz9(context: MangaLoaderContext) : val dateText = a.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = a.selectFirstOrThrow("a").text(), + title = a.selectFirstOrThrow("a").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/WeLoveManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/WeLoveManga.kt index 1eaaa1e6..18c76209 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/WeLoveManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/ja/WeLoveManga.kt @@ -24,7 +24,7 @@ internal class WeLoveManga(context: MangaLoaderContext) : val dateText = a.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = a.selectFirstOrThrow("a").text(), + title = a.selectFirstOrThrow("a").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt index d7024c86..cb76cce0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/foolslide/FoolSlideParser.kt @@ -143,7 +143,7 @@ internal abstract class FoolSlideParser( val dateText = div.selectFirst(selectDate)?.text()?.substringAfter(", ") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt index 0b004c1f..2cff4fef 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/BentomangaParser.kt @@ -234,11 +234,11 @@ internal class BentomangaParser(context: MangaLoaderContext) : .select(".component-chapter").map { div -> val a = div.selectFirstOrThrow("a:not([style*='display:none'])") val href = a.attrAsRelativeUrl("href") - val title = div.selectFirstOrThrow(".chapter_volume").text() + val title = div.selectFirstOrThrow(".chapter_volume").textOrNull() val name = div.selectFirst(".chapter_title")?.textOrNull() MangaChapter( id = generateUid(href), - name = if (name != null && name != title) "$title: $name" else title, + title = if (name != null && name != title) "$title: $name" else title, number = href.substringAfterLast('/').toFloatOrNull() ?: 0f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt index 70e3ace1..2b132bf9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/FuryoSociety.kt @@ -105,7 +105,7 @@ internal class FuryoSociety(context: MangaLoaderContext) : val dateText = div.selectFirst("div.meta_r")?.text()?.replace("Hier", "1 jour") MangaChapter( id = generateUid(href), - name = div.selectFirst("div.title")?.text() + " : " + div.selectFirst("div.name")?.text(), + title = div.selectFirst("div.title")?.text() + " : " + div.selectFirst("div.name")?.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt index 775f20ff..d47b83e3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LegacyScansParser.kt @@ -185,11 +185,11 @@ internal class LegacyScansParser(context: MangaLoaderContext) : chapters = root.select("div.chapterList a") .mapChapters(reversed = true) { i, a -> val href = a.attrAsRelativeUrl("href") - val name = a.selectFirst("span")?.text() + val name = a.selectFirst("span")?.textOrNull() val dateText = a.selectLast("span")?.text() ?: "0" MangaChapter( id = generateUid(href), - name = name ?: "Chapitre : ${i + 1f}", + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt index 0e319639..cc1d2084 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LireScan.kt @@ -116,7 +116,7 @@ internal class LireScan(context: MangaLoaderContext) : LegacyPagedMangaParser(co val dateText = div.select("p").last()?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i.toFloat(), volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt index 228d0f44..416fe558 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt @@ -179,7 +179,7 @@ internal class LugnicaScans(context: MangaLoaderContext) : ) MangaChapter( id = generateUid(url), - name = "Chapitre : $id", + title = null, number = i.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt index 48711a26..de0ba2a4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaKawaii.kt @@ -135,7 +135,7 @@ internal class MangaKawaii(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt index a719e280..9269b1c7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/MangaMana.kt @@ -295,7 +295,7 @@ internal class MangaMana(context: MangaLoaderContext) : val chapterN = href.substringAfterLast('/').replace("-", ".").replace("[^0-9.]".toRegex(), "").toFloat() MangaChapter( id = generateUid(href), - name = name, + title = name, number = chapterN, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt index e6217027..499fb517 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt @@ -142,7 +142,7 @@ internal class ScansMangasMe(context: MangaLoaderContext) : val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = li.selectFirstOrThrow("span.mobile chapter").text(), + title = li.selectFirstOrThrow("span.mobile chapter").text(), number = li.selectFirstOrThrow("span.mobile chapter").text().substringAfterLast(" ").toFloat(), volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt index 4b35c0ed..1854e668 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScantradUnion.kt @@ -155,7 +155,7 @@ internal class ScantradUnion(context: MangaLoaderContext) : val date = li.select(".name-chapter").first()?.children()?.elementAt(2)?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i.toFloat(), volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt index 8222960e..8aad166f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fuzzydoodle/FuzzyDoodleParser.kt @@ -240,7 +240,7 @@ internal abstract class FuzzyDoodleParser( val chapterN = href.substringAfterLast('/').replace("-", ".").replace("[^0-9.]".toRegex(), "").toFloat() MangaChapter( id = generateUid(href), - name = name, + title = name, number = chapterN, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt index 74edbcbd..8e3e0505 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt @@ -173,7 +173,7 @@ internal abstract class GalleryAdultsParser( chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = urlChapters, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt index d5a35e63..1f503034 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt @@ -133,7 +133,7 @@ internal class HentaiEra(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = urlChapters, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt index 9af0eb07..278df84e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/gattsu/GattsuParser.kt @@ -120,7 +120,7 @@ internal abstract class GattsuParser( chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = urlChapter, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt index 8e4edaa9..1caffa85 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/guya/GuyaParser.kt @@ -95,7 +95,7 @@ internal abstract class GuyaParser( chapters.add( MangaChapter( id = generateUid(url), - name = chapter.getString("title"), + title = chapter.getString("title"), number = i.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt index ffb6472a..f8519aaf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/HeanCms.kt @@ -159,7 +159,7 @@ internal abstract class HeanCms( "/series/${it.getJSONObject("series").getString("series_slug")}/${it.getString("chapter_slug")}" MangaChapter( id = it.getLong("id"), - name = it.getString("chapter_name"), + title = it.getString("chapter_name"), number = i + 1f, volume = 0, url = chapterUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt index 172ca3b6..52e68e7e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/HeanCmsAlt.kt @@ -97,7 +97,7 @@ internal abstract class HeanCmsAlt( val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) MangaChapter( id = generateUid(url), - name = a.selectFirst(selectChapterTitle)?.text() ?: "Chapter : ${i + 1f}", + title = a.selectFirst(selectChapterTitle)?.textOrNull(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/Brakeout.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/Brakeout.kt index 6d8809a3..7850924e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/Brakeout.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancmsalt/es/Brakeout.kt @@ -34,7 +34,7 @@ internal class Brakeout(context: MangaLoaderContext) : val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) MangaChapter( id = generateUid(url), - name = div.selectFirstOrThrow(selectChapterTitle).text(), + title = div.selectFirstOrThrow(selectChapterTitle).text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt index 1304c768..b7cbaccf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/hotcomics/HotComicsParser.kt @@ -150,7 +150,7 @@ internal abstract class HotComicsParser( val chapterNum = li.selectFirst(".num")?.text()?.toFloat() ?: (i + 1f) MangaChapter( id = generateUid(url), - name = "Chapter : $chapterNum", + title = null, number = chapterNum, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt index c30d21a4..754b69bc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/DoujinDesuParser.kt @@ -161,7 +161,7 @@ internal class DoujinDesuParser(context: MangaLoaderContext) : val url = titleTag.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = titleTag.text(), + title = titleTag.text(), number = index + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt index 2174d4b1..6df85b69 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/HentaiCrot.kt @@ -106,7 +106,7 @@ internal class HentaiCrot(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = fullUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt index 464a845f..0512718f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/id/PixHentai.kt @@ -106,7 +106,7 @@ internal class PixHentai(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = fullUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt index c271b7bb..382e42d2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt @@ -6,10 +6,7 @@ import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.core.LegacyPagedMangaParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* -import org.koitharu.kotatsu.parsers.util.json.asTypedList -import org.koitharu.kotatsu.parsers.util.json.getBooleanOrDefault -import org.koitharu.kotatsu.parsers.util.json.getStringOrNull -import org.koitharu.kotatsu.parsers.util.json.mapJSON +import org.koitharu.kotatsu.parsers.util.json.* import java.text.SimpleDateFormat import java.util.* @@ -147,8 +144,8 @@ internal abstract class IkenParser( val chapterUrl = "/series/$slugName/${it.getString("slug")}" MangaChapter( id = it.getLong("id"), - name = "Chapter : ${it.getInt("number")}", - number = it.getInt("number").toFloat(), + title = null, + number = it.getFloatOrDefault("number", 0f), volume = 0, url = chapterUrl, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt index 1a10bb63..7121bab8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ja/NicovideoSeigaParser.kt @@ -137,7 +137,7 @@ internal class NicovideoSeigaParser(context: MangaLoaderContext) : ?.attrAsRelativeUrl("href") ?: li.parseFailed() MangaChapter( id = generateUid(href), - name = li.select("div > div.description > div.title > a").text(), + title = li.select("div > div.description > div.title > a").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt index d9cd8e45..66ac6f0d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/keyoapp/KeyoappParser.kt @@ -193,7 +193,7 @@ internal abstract class KeyoappParser( val dateText = a.selectLast("div.text-xs.w-fit")?.text() ?: "0" MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt index 9263b258..febf15ff 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/likemanga/LikeMangaParser.kt @@ -200,7 +200,7 @@ internal abstract class LikeMangaParser( } MangaChapter( id = generateUid(url), - name = name, + title = name, number = chapNum.toFloatOrNull() ?: 0f, volume = 0, url = url, @@ -229,7 +229,7 @@ internal abstract class LikeMangaParser( MangaChapter( id = generateUid(url), - name = li.selectFirstOrThrow("a").text(), + title = li.selectFirstOrThrow("a").text(), number = chapNum.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/liliana/LilianaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/liliana/LilianaParser.kt index 67ac4b58..648d4144 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/liliana/LilianaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/liliana/LilianaParser.kt @@ -173,7 +173,7 @@ internal abstract class LilianaParser( val href = element.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = element.selectFirst("a")?.text() ?: "Chapter : ${i + 1f}", + title = element.selectFirst("a")?.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt index 61d24f08..c2e04fc0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MadaraParser.kt @@ -600,7 +600,7 @@ internal abstract class MadaraParser( val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, @@ -635,7 +635,7 @@ internal abstract class MadaraParser( MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt index a942af9f..d9825e00 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/Ero18x.kt @@ -30,7 +30,7 @@ internal class Ero18x(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt index 054b7350..a6401e9f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/all/ManhwaRaw.kt @@ -28,7 +28,7 @@ internal class ManhwaRaw(context: MangaLoaderContext) : val name = a.selectFirst("h4")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/LekMangaCom.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/LekMangaCom.kt index a6b65aac..b7358f19 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/LekMangaCom.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/LekMangaCom.kt @@ -30,7 +30,7 @@ internal class LekMangaCom(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/RocksManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/RocksManga.kt index c07c2fdc..00fe70a8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/RocksManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/RocksManga.kt @@ -33,7 +33,7 @@ internal class RocksManga(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt index 1831d94e..330073e2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/AdultWebtoon.kt @@ -140,7 +140,7 @@ internal class AdultWebtoon(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt index a95ca486..a3fe0c8a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiManga.kt @@ -134,7 +134,7 @@ internal class HentaiManga(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt index b3454079..f348c19f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/HentaiWebtoon.kt @@ -134,7 +134,7 @@ internal class HentaiWebtoon(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentaixdickgirl.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentaixdickgirl.kt index 7ff70f2e..7460d291 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentaixdickgirl.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/Hentaixdickgirl.kt @@ -27,7 +27,7 @@ internal class Hentaixdickgirl(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt index c8481a3b..f6c36806 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt @@ -122,7 +122,7 @@ internal class IsekaiScan(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = a.ownText(), + title = a.ownText(), number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt index cbbedfea..e8a4e46a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDass.kt @@ -155,7 +155,7 @@ internal class MangaDass(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDistrict.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDistrict.kt index e3259480..90fdd8e1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDistrict.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDistrict.kt @@ -31,7 +31,7 @@ internal class MangaDistrict(context: MangaLoaderContext) : val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing") MangaChapter( id = generateUid(href), - name = a.ownText(), + title = a.ownText(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt index 3cb6cd8f..cc9b8020 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaDna.kt @@ -148,7 +148,7 @@ internal class MangaDna(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt index f283582c..a2fcba56 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/MangaPure.kt @@ -127,7 +127,7 @@ internal class MangaPure(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = a.ownText(), + title = a.ownText(), number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt index 313a068e..0b479e57 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaHentai.kt @@ -94,7 +94,7 @@ internal class ManhwaHentai(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaTop.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaTop.kt index eda11aa4..c96f1b90 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaTop.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManhwaTop.kt @@ -38,7 +38,7 @@ internal class ManhwaTop(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt index 48e94a04..3e9eb60c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/ManyToon.kt @@ -137,7 +137,7 @@ internal class ManyToon(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MangasNoSekai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MangasNoSekai.kt index 5e049035..6aab7d25 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MangasNoSekai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/MangasNoSekai.kt @@ -62,7 +62,7 @@ internal class MangasNoSekai(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt index a2c9806d..1e486fb1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaEs.kt @@ -28,7 +28,7 @@ internal class ManhwaEs(context: MangaLoaderContext) : val name = li.selectFirstOrThrow(".mini-letters a").text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaLatino.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaLatino.kt index f606bfdc..feb56478 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaLatino.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/ManhwaLatino.kt @@ -74,7 +74,7 @@ internal class ManhwaLatino(context: MangaLoaderContext) : val name = li.selectFirst("a:contains(Capitulo)")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt index 45e486d8..a649f38b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/TmoManga.kt @@ -91,7 +91,7 @@ internal class TmoManga(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt index 4eb6091a..ed306510 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/Hentaizone.kt @@ -34,7 +34,7 @@ internal class Hentaizone(context: MangaLoaderContext) : val name = a.selectFirst("p")?.text() ?: a.ownText() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = link, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/ToonFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/ToonFr.kt index df1f2680..61627189 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/ToonFr.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/fr/ToonFr.kt @@ -31,7 +31,7 @@ internal class ToonFr(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = href, - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Neoxscans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Neoxscans.kt index 66909a11..2eec5f0a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Neoxscans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/Neoxscans.kt @@ -27,7 +27,7 @@ internal class Neoxscans(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt index f9d44222..3dbf29e7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/LaviniaFansub.kt @@ -8,7 +8,6 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrlOrNull -import org.koitharu.kotatsu.parsers.util.domain import org.koitharu.kotatsu.parsers.util.generateUid import org.koitharu.kotatsu.parsers.util.mapChapters import org.koitharu.kotatsu.parsers.util.parseFailed @@ -42,7 +41,7 @@ internal class LaviniaFansub(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt index 4c66f304..4c143d1b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/TitanManga.kt @@ -23,7 +23,7 @@ internal class TitanManga(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/TruyenTranhDamMyy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/TruyenTranhDamMyy.kt index fe6aea30..ae62707c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/TruyenTranhDamMyy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/TruyenTranhDamMyy.kt @@ -25,7 +25,7 @@ internal class TruyenTranhDamMyy(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = link, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt index 7118e7d8..634e5e33 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/MadthemeParser.kt @@ -213,7 +213,7 @@ internal abstract class MadthemeParser( val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = li.selectFirst(".chapter-title")?.text() ?: "Chapters : ${i + 1f}", + title = li.selectFirst(".chapter-title")?.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaJinx.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaJinx.kt index f3e86d06..01b03060 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaJinx.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaJinx.kt @@ -25,7 +25,7 @@ internal class MangaJinx(context: MangaLoaderContext) : val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = li.selectFirstOrThrow(".chapter-title").text(), + title = li.selectFirstOrThrow(".chapter-title").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt index 2ea4ff1d..5481c7d5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/Manga18Parser.kt @@ -184,7 +184,7 @@ internal abstract class Manga18Parser( val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = a.text(), + title = a.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt index 4015a885..cd8056a1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/manga18/zh/Hanman18.kt @@ -27,7 +27,7 @@ internal class Hanman18(context: MangaLoaderContext) : val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt index 644258f1..e8eac915 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/MangaboxParser.kt @@ -248,7 +248,7 @@ internal abstract class MangaboxParser( MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt index e3c61fc5..f510ae50 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangabox/en/Mangakakalot.kt @@ -176,7 +176,7 @@ internal class Mangakakalot(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt index bf82556e..51d92c2f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangadventure/MangAdventureParser.kt @@ -135,7 +135,7 @@ internal abstract class MangAdventureParser( chapters = chapters?.optJSONArray("results")?.asTypedList()?.mapChapters { _, it -> MangaChapter( id = generateUid(it.getLong("id")), - name = it.getString("full_title"), + title = it.getStringOrNull("full_title"), number = it.getFloatOrDefault("number", 0f), volume = it.getIntOrDefault("volume", 0), url = it.getString("url"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt index 93e44f22..b7a0057b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt @@ -181,7 +181,7 @@ internal abstract class MangaReaderParser( val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Manjanoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Manjanoon.kt index f31ad566..9d3bdb1c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Manjanoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Manjanoon.kt @@ -18,7 +18,7 @@ internal class Manjanoon(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt index 48577a6f..18d0eeb2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/Normoyun.kt @@ -81,7 +81,7 @@ internal class Normoyun(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst("a")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst("a")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt index 7af53bf7..035883b6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/VexManga.kt @@ -28,7 +28,7 @@ internal class VexManga(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt index a6f2cbc5..3bb05b9c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Constellarcomic.kt @@ -28,7 +28,7 @@ internal class Constellarcomic(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.textOrNull() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt index b6fcf510..0cc995bd 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/HentaiReader.kt @@ -81,7 +81,7 @@ internal class HentaiReader(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst("h2")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst("h2")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt index 469ffe85..44e2f202 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/LectorHentai.kt @@ -80,7 +80,7 @@ internal class LectorHentai(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst("h2")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst("h2")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt index 94c6c776..d81bba0a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TuManhwas.kt @@ -59,7 +59,7 @@ internal class TuManhwas(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.textOrNull() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = url, number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt index 8c4c9adf..0e634bd3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/RevolutionScantrad.kt @@ -118,7 +118,7 @@ internal class RevolutionScantrad(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = "$urlStart/$url", number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt index b9329303..94168f3d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/XxxRevolutionScantrad.kt @@ -112,7 +112,7 @@ internal class XxxRevolutionScantrad(context: MangaLoaderContext) : val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), - name = element.selectFirst(".chapternum")?.text() ?: "Chapter ${index + 1}", + title = element.selectFirst(".chapternum")?.textOrNull(), url = "$urlStart/$url", number = index + 1f, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt index 2fbd2e0e..5de60d97 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Komikcast.kt @@ -13,229 +13,229 @@ import java.util.* @MangaSourceParser("KOMIKCAST", "KomikCast", "id") internal class Komikcast(context: MangaLoaderContext) : - MangaReaderParser(context, MangaParserSource.KOMIKCAST, "komikcast.bz", pageSize = 60, searchPageSize = 28) { - - override val listUrl = "/daftar-komik" - override val datePattern = "MMM d, yyyy" - override val sourceLocale: Locale = Locale.ENGLISH - override val availableSortOrders: Set = - EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.ALPHABETICAL) - - override val filterCapabilities: MangaListFilterCapabilities - get() = super.filterCapabilities.copy( - isTagsExclusionSupported = false, - ) - - override suspend fun getFilterOptions() = super.getFilterOptions().copy( - availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), - ) - - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { - val url = buildString { - append("https://") - append(domain) - - when { - - !filter.query.isNullOrEmpty() -> { - append("/page/") - append(page.toString()) - append("/?s=") - append(filter.query.urlEncoded()) - } - - else -> { - append(listUrl) - append("/page/") - append(page.toString()) - append("/?type=") - append( - when (order) { - SortOrder.ALPHABETICAL -> "&orderby=titleasc" - SortOrder.ALPHABETICAL_DESC -> "&orderby=titledesc" - SortOrder.POPULARITY -> "&orderby=popular" - SortOrder.UPDATED -> "" // To get the Updated list, you don't need "orderby" in the url. - else -> "" - }, - ) - val tagKey = "genre[]".urlEncoded() - val tagQuery = - if (filter.tags.isEmpty()) "" - else filter.tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } - append(tagQuery) - - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append("&status=") - when (it) { - MangaState.ONGOING -> append("Ongoing") - MangaState.FINISHED -> append("Completed") - else -> append("") - } - } - } - } - } - } - - return parseMangaList(webClient.httpGet(url).parseHtml()) - } - - override suspend fun getDetails(manga: Manga): Manga { - val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val dateFormat = SimpleDateFormat(datePattern, sourceLocale) - val chapters = docs.select("#chapter-wrapper > li").mapChapters(reversed = true) { index, element -> - val url = element.selectFirst("a.chapter-link-item")?.attrAsRelativeUrl("href") ?: return@mapChapters null - MangaChapter( - id = generateUid(url), - name = element.selectFirst("a.chapter-link-item")?.ownText().orEmpty(), - url = url, - number = index + 1f, - volume = 0, - scanlator = null, - uploadDate = parseChapterDate( - dateFormat, - element.selectFirst("div.chapter-link-time")?.text(), - ), - branch = null, - source = source, - ) - } - return parseInfo(docs, manga, chapters) - } - - override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List): Manga { - val tagMap = getOrCreateTagMap() - val tags = docs.select(".komik_info-content-genre > a").mapNotNullToSet { tagMap[it.text()] } - val state = docs.selectFirst(".komik_info-content-meta span:contains(Status)")?.html() - val mangaState = if (state!!.contains("Ongoing")) { - MangaState.ONGOING - } else { - MangaState.FINISHED - } - val author = docs.selectFirst(".komik_info-content-meta span:contains(Author)") - ?.lastElementChild()?.textOrNull() - val nsfw = - docs.selectFirst(".restrictcontainer") != null || docs.selectFirst(".info-right .alr") != null || docs.selectFirst( - ".postbody .alr", - ) != null - - return manga.copy( - description = docs.selectFirst("div.komik_info-description-sinopsis")?.text(), - state = mangaState, - authors = setOfNotNull(author), - contentRating = if (manga.isNsfw || nsfw) { - ContentRating.ADULT - } else { - ContentRating.SAFE - }, - tags = tags, - chapters = chapters, - ) - } - - override fun parseMangaList(docs: Document): List { - return docs.select("div.list-update_item").mapNotNull { - val a = it.selectFirstOrThrow("a.data-tooltip") - val relativeUrl = a.attrAsRelativeUrl("href") - val rating = it.selectFirst(".numscore")?.text()?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN - val name = it.selectFirst("h3.title")?.text().orEmpty() - Manga( - id = generateUid(relativeUrl), - url = relativeUrl, - title = name, - altTitles = emptySet(), - publicUrl = a.attrAsAbsoluteUrl("href"), - rating = rating, - contentRating = if (isNsfwSource) ContentRating.ADULT else null, - coverUrl = it.selectFirst("img.ts-post-image")?.src(), - tags = emptySet(), - state = null, - authors = emptySet(), - source = source, - ) - } - } - - override suspend fun getPages(chapter: MangaChapter): List { - val chapterUrl = chapter.url.toAbsoluteUrl(domain) - val docs = webClient.httpGet(chapterUrl).parseHtml() - val test = docs.select("script:containsData(ts_reader)") - if (test.isNullOrEmpty()) { - return docs.select("div#chapter_body img").map { img -> - val url = img.requireSrc().toRelativeUrl(domain) - MangaPage( - id = generateUid(url), - url = url, - preview = null, - source = source, - ) - } - } else { - val script = docs.selectFirstOrThrow("script:containsData(ts_reader)") - val images = JSONObject(script.data().substringAfter('(').substringBeforeLast(')')).getJSONArray("sources") - .getJSONObject(0).getJSONArray("images") - val pages = ArrayList(images.length()) - for (i in 0 until images.length()) { - pages.add( - MangaPage( - id = generateUid(images.getString(i)), - url = images.getString(i), - preview = null, - source = source, - ), - ) - } - return pages - } - } - - private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { - date ?: return 0 - return when { - date.endsWith(" ago", ignoreCase = true) -> { - parseRelativeDate(date) - } - - else -> dateFormat.tryParse(date) - } - } - - private fun parseRelativeDate(date: String): Long { - val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 - val cal = Calendar.getInstance() - return when { - WordSet( - "day", - "days", - ).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis - - WordSet("hour", "hours").anyWordIn(date) -> cal.apply { - add( - Calendar.HOUR, - -number, - ) - }.timeInMillis - - WordSet( - "mins", - ).anyWordIn(date) -> cal.apply { - add( - Calendar.MINUTE, - -number, - ) - }.timeInMillis - - WordSet("second").anyWordIn(date) -> cal.apply { - add( - Calendar.SECOND, - -number, - ) - }.timeInMillis - - WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis - WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis - else -> 0 - } - } + MangaReaderParser(context, MangaParserSource.KOMIKCAST, "komikcast.bz", pageSize = 60, searchPageSize = 28) { + + override val listUrl = "/daftar-komik" + override val datePattern = "MMM d, yyyy" + override val sourceLocale: Locale = Locale.ENGLISH + override val availableSortOrders: Set = + EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.ALPHABETICAL) + + override val filterCapabilities: MangaListFilterCapabilities + get() = super.filterCapabilities.copy( + isTagsExclusionSupported = false, + ) + + override suspend fun getFilterOptions() = super.getFilterOptions().copy( + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + ) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val url = buildString { + append("https://") + append(domain) + + when { + + !filter.query.isNullOrEmpty() -> { + append("/page/") + append(page.toString()) + append("/?s=") + append(filter.query.urlEncoded()) + } + + else -> { + append(listUrl) + append("/page/") + append(page.toString()) + append("/?type=") + append( + when (order) { + SortOrder.ALPHABETICAL -> "&orderby=titleasc" + SortOrder.ALPHABETICAL_DESC -> "&orderby=titledesc" + SortOrder.POPULARITY -> "&orderby=popular" + SortOrder.UPDATED -> "" // To get the Updated list, you don't need "orderby" in the url. + else -> "" + }, + ) + val tagKey = "genre[]".urlEncoded() + val tagQuery = + if (filter.tags.isEmpty()) "" + else filter.tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } + append(tagQuery) + + if (filter.states.isNotEmpty()) { + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + when (it) { + MangaState.ONGOING -> append("Ongoing") + MangaState.FINISHED -> append("Completed") + else -> append("") + } + } + } + } + } + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + + override suspend fun getDetails(manga: Manga): Manga { + val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + val chapters = docs.select("#chapter-wrapper > li").mapChapters(reversed = true) { index, element -> + val url = element.selectFirst("a.chapter-link-item")?.attrAsRelativeUrl("href") ?: return@mapChapters null + MangaChapter( + id = generateUid(url), + title = element.selectFirst("a.chapter-link-item")?.ownTextOrNull(), + url = url, + number = index + 1f, + volume = 0, + scanlator = null, + uploadDate = parseChapterDate( + dateFormat, + element.selectFirst("div.chapter-link-time")?.text(), + ), + branch = null, + source = source, + ) + } + return parseInfo(docs, manga, chapters) + } + + override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List): Manga { + val tagMap = getOrCreateTagMap() + val tags = docs.select(".komik_info-content-genre > a").mapNotNullToSet { tagMap[it.text()] } + val state = docs.selectFirst(".komik_info-content-meta span:contains(Status)")?.html() + val mangaState = if (state!!.contains("Ongoing")) { + MangaState.ONGOING + } else { + MangaState.FINISHED + } + val author = docs.selectFirst(".komik_info-content-meta span:contains(Author)") + ?.lastElementChild()?.textOrNull() + val nsfw = + docs.selectFirst(".restrictcontainer") != null || docs.selectFirst(".info-right .alr") != null || docs.selectFirst( + ".postbody .alr", + ) != null + + return manga.copy( + description = docs.selectFirst("div.komik_info-description-sinopsis")?.text(), + state = mangaState, + authors = setOfNotNull(author), + contentRating = if (manga.isNsfw || nsfw) { + ContentRating.ADULT + } else { + ContentRating.SAFE + }, + tags = tags, + chapters = chapters, + ) + } + + override fun parseMangaList(docs: Document): List { + return docs.select("div.list-update_item").mapNotNull { + val a = it.selectFirstOrThrow("a.data-tooltip") + val relativeUrl = a.attrAsRelativeUrl("href") + val rating = it.selectFirst(".numscore")?.text()?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN + val name = it.selectFirst("h3.title")?.text().orEmpty() + Manga( + id = generateUid(relativeUrl), + url = relativeUrl, + title = name, + altTitles = emptySet(), + publicUrl = a.attrAsAbsoluteUrl("href"), + rating = rating, + contentRating = if (isNsfwSource) ContentRating.ADULT else null, + coverUrl = it.selectFirst("img.ts-post-image")?.src(), + tags = emptySet(), + state = null, + authors = emptySet(), + source = source, + ) + } + } + + override suspend fun getPages(chapter: MangaChapter): List { + val chapterUrl = chapter.url.toAbsoluteUrl(domain) + val docs = webClient.httpGet(chapterUrl).parseHtml() + val test = docs.select("script:containsData(ts_reader)") + if (test.isNullOrEmpty()) { + return docs.select("div#chapter_body img").map { img -> + val url = img.requireSrc().toRelativeUrl(domain) + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } else { + val script = docs.selectFirstOrThrow("script:containsData(ts_reader)") + val images = JSONObject(script.data().substringAfter('(').substringBeforeLast(')')).getJSONArray("sources") + .getJSONObject(0).getJSONArray("images") + val pages = ArrayList(images.length()) + for (i in 0 until images.length()) { + pages.add( + MangaPage( + id = generateUid(images.getString(i)), + url = images.getString(i), + preview = null, + source = source, + ), + ) + } + return pages + } + } + + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + date ?: return 0 + return when { + date.endsWith(" ago", ignoreCase = true) -> { + parseRelativeDate(date) + } + + else -> dateFormat.tryParse(date) + } + } + + private fun parseRelativeDate(date: String): Long { + val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + return when { + WordSet( + "day", + "days", + ).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + + WordSet("hour", "hours").anyWordIn(date) -> cal.apply { + add( + Calendar.HOUR, + -number, + ) + }.timeInMillis + + WordSet( + "mins", + ).anyWordIn(date) -> cal.apply { + add( + Calendar.MINUTE, + -number, + ) + }.timeInMillis + + WordSet("second").anyWordIn(date) -> cal.apply { + add( + Calendar.SECOND, + -number, + ) + }.timeInMillis + + WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis + else -> 0 + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/PointZeroToons.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/PointZeroToons.kt index 89af0b58..69a48a30 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/PointZeroToons.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/PointZeroToons.kt @@ -7,7 +7,6 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* -import java.util.* @MangaSourceParser("POINTZEROTOONS", "PointZero Toons", "pt") internal class PointZeroToons(context: MangaLoaderContext) : @@ -26,7 +25,7 @@ internal class PointZeroToons(context: MangaLoaderContext) : val numChap = findNumChap(name) MangaChapter( id = generateUid(url), - name = name, + title = name, url = url, number = numChap, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt index ebf85d4f..87b446e9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/SssScanlator.kt @@ -7,7 +7,6 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* -import java.util.* @MangaSourceParser("SSSSCANLATOR", "YomuComics", "pt") internal class SssScanlator(context: MangaLoaderContext) : @@ -26,7 +25,7 @@ internal class SssScanlator(context: MangaLoaderContext) : val numChap = findNumChap(name) MangaChapter( id = generateUid(url), - name = name, + title = name, url = url, number = numChap, volume = 0, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt index ed21b865..79e48e1b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangaworld/MangaWorldParser.kt @@ -198,7 +198,7 @@ internal abstract class MangaWorldParser( val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) MangaChapter( id = generateUid(url), - name = a.selectFirst("span.d-inline-block")?.text() ?: "Chapter : ${i + 1f}", + title = a.selectFirst("span.d-inline-block")?.textOrNull(), number = i + 1f, volume = 0, url = "$url?style=list", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt index db67bb93..544f201d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mmrcms/MmrcmsParser.kt @@ -224,7 +224,7 @@ internal abstract class MmrcmsParser( val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = li.selectFirst("h5")?.text() ?: "Chapter : ${i + 1f}", + title = li.selectFirst("h5")?.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt index 63eb754b..375afd1c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/nepnep/NepnepParser.kt @@ -225,7 +225,7 @@ internal abstract class NepnepParser( val date = j.getStringOrNull("Date") MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt index c80eab1d..8644d991 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/onemanga/OneMangaParser.kt @@ -65,7 +65,7 @@ internal abstract class OneMangaParser( val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt index d5926532..8eb8ed33 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/otakusanctuary/OtakuSanctuaryParser.kt @@ -185,7 +185,7 @@ internal abstract class OtakuSanctuaryParser( val name = a.text() MangaChapter( id = generateUid(url), - name = name, + title = name, number = i.toFloat(), volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt index 6d6f49de..36b50225 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pizzareader/PizzaReaderParser.kt @@ -229,7 +229,7 @@ internal abstract class PizzaReaderParser( val date = j.getStringOrNull("updated_at") MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt index 244761db..31f11f29 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/BrMangas.kt @@ -143,7 +143,7 @@ internal class BrMangas(context: MangaLoaderContext) : LegacyPagedMangaParser(co val name = a.text() MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt index f9e25a6d..96d4225d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerManga.kt @@ -139,7 +139,7 @@ internal class LerManga(context: MangaLoaderContext) : LegacyPagedMangaParser(co val href = a.attrAsAbsoluteUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt index edb2a659..0b05b630 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt @@ -119,7 +119,7 @@ internal class LerMangaOnline(context: MangaLoaderContext) : val dateText = a.selectFirstOrThrow("span").text() MangaChapter( id = generateUid(href), - name = title, + title = title, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt index 3d656b04..9c5751c3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LuratoonScansParser.kt @@ -101,7 +101,7 @@ internal class LuratoonScansParser(context: MangaLoaderContext) : val span = li.selectFirstOrThrow(".numero__capitulo") MangaChapter( id = generateUid(href), - name = span.text(), + title = span.text(), number = 0.0f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt index e1fb15df..d827a6dc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt @@ -112,7 +112,7 @@ internal class MangaOnline(context: MangaLoaderContext) : val dateText = a.selectFirst("span.date")?.text() MangaChapter( id = generateUid(href), - name = title, + title = title, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt index aff4d719..d05ab250 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MuitoHentai.kt @@ -104,7 +104,7 @@ internal class MuitoHentai(context: MangaLoaderContext) : val href = a.attrAsAbsoluteUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt index 37511f56..61515a69 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/OnePieceEx.kt @@ -82,7 +82,7 @@ internal class OnePieceEx(context: MangaLoaderContext) : val href = a.attrAsRelativeUrl("value") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, @@ -102,7 +102,7 @@ internal class OnePieceEx(context: MangaLoaderContext) : val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt index 15392807..0ec00ba5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt @@ -121,7 +121,7 @@ internal class YugenMangas(context: MangaLoaderContext) : val url = "https://api.$domain/api/serie/${manga.url}/chapter/${j.getString("slug")}/images/imgs/get/" MangaChapter( id = generateUid(url), - name = j.getString("name"), + title = j.getString("name"), number = j.getString("name").removePrefix("Capítulo ").toFloat(), volume = 0, url = url, @@ -133,7 +133,7 @@ internal class YugenMangas(context: MangaLoaderContext) : branch = null, source = source, ) - }.sortedBy { it.name }, + }.sortedBy { it.title }, ) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt index 182cb39b..9de1bad4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/AComics.kt @@ -149,7 +149,7 @@ internal class AComics(context: MangaLoaderContext) : chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = manga.url.replace("/about", "/"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt index 6de91ec1..ca650c8c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt @@ -132,13 +132,7 @@ internal class DesuMeParser(context: MangaLoaderContext) : source = manga.source, url = "$baseChapterUrl$chid", uploadDate = jo.getLong("date") * 1000, - name = jo.getStringOrNull("title") ?: buildString { - append("Том ") - append(volume) - append(" Глава ") - append(number) - removeTrailingZero() - }, + title = jo.getStringOrNull("title"), volume = volume, number = number, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt index 6bbca02f..45a2c20f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/MangaWtfParser.kt @@ -208,11 +208,7 @@ internal class MangaWtfParser( val branchId = jo.getString("branchId") MangaChapter( id = generateUid(jo.getString("id")), - name = - jo.getStringOrNull("name") ?: buildString { - if (volume > 0) append("Том ").append(volume).append(' ') - if (number > 0) append("Глава ").append(number) else append("Без имени") - }, + title = jo.getStringOrNull("name"), number = number, volume = volume, url = jo.getString("id"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt index e48c1fb7..5ff1b31f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/NudeMoonParser.kt @@ -141,7 +141,7 @@ internal class NudeMoonParser( source = source, number = 0f, volume = 0, - name = manga.title, + title = manga.title, scanlator = root.getElementsByAttributeValueContaining("href", "/perevod/").firstOrNull() ?.textOrNull(), uploadDate = dateFormat.tryParse( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt index 42ed6fc6..0e6553f8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/RemangaParser.kt @@ -176,17 +176,7 @@ internal class RemangaParser( url = "/api/titles/chapters/$id/", number = jo.getIntOrDefault("index", chapters.size - i).toFloat(), volume = 0, - name = buildString { - append("Том ") - append(jo.optString("tome", "0")) - append(". ") - append("Глава ") - append(jo.optString("chapter", "0")) - if (name.isNotEmpty()) { - append(" - ") - append(name) - } - }, + title = name.nullIfEmpty(), uploadDate = dateFormat.tryParse(jo.getString("upload_date")), scanlator = publishers?.optJSONObject(0)?.getStringOrNull("name"), source = MangaParserSource.REMANGA, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/WaMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/WaMangaParser.kt index c399a361..53da620a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/WaMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/WaMangaParser.kt @@ -92,7 +92,7 @@ internal class WaMangaParser( source = source, number = it.getFloatOrDefault("chapter", 0f), volume = it.getIntOrDefault("volume", 0), - name = it.getStringOrNull("full_title") ?: manga.title, + title = it.getStringOrNull("full_title"), scanlator = it.getJSONArray("teams").getJSONObject(0)?.getStringOrNull("name"), uploadDate = dateFormat.tryParse(it.getStringOrNull("published_on")), branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt index 48fe3a4f..4ad4ad05 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/grouple/GroupleParser.kt @@ -40,511 +40,511 @@ private const val RELATED_TITLE = "Связанные произведения" private const val NO_CHAPTERS = "В этой манге еще нет ни одной главы" internal abstract class GroupleParser( - context: MangaLoaderContext, - source: MangaParserSource, - private val siteId: Int, + context: MangaLoaderContext, + source: MangaParserSource, + private val siteId: Int, ) : LegacyMangaParser(context, source), MangaParserAuthProvider, Interceptor { - @Volatile - private var cachedPagesServer: String? = null - - override val userAgentKey = ConfigKey.UserAgent( - "Mozilla/5.0 (X11; U; UNICOS lcLinux; en-US) Gecko/20140730 (KHTML, like Gecko, Safari/419.3) Arora/0.8.0", - ) - private val splitTranslationsKey = ConfigKey.SplitByTranslations(false) - private val tagsIndex = suspendLazy(initializer = ::fetchTagsMap) - - override fun getRequestHeaders(): Headers = Headers.Builder() - .add("User-Agent", config[userAgentKey]) - .add("Accept-Language", "ru,en-US;q=0.7,en;q=0.3") - .build() - - override val availableSortOrders: Set = EnumSet.of( - SortOrder.UPDATED, - SortOrder.POPULARITY, - SortOrder.NEWEST, - SortOrder.RATING, - SortOrder.ALPHABETICAL, - SortOrder.ADDED, - ) - - override val authUrl: String - get() { - val targetUri = "https://${domain}/".urlEncoded() - return "https://grouple.co/internal/auth/sso?siteId=$siteId&=targetUri=$targetUri" - } - - override val isAuthorized: Boolean - get() = context.cookieJar.getCookies(domain).any { it.name == "gwt" } - - override val filterCapabilities: MangaListFilterCapabilities - get() = MangaListFilterCapabilities( - isMultipleTagsSupported = true, - isTagsExclusionSupported = true, - isSearchSupported = true, - isSearchWithFiltersSupported = true, - isYearRangeSupported = true, - ) - - override suspend fun getFilterOptions() = MangaListFilterOptions( - availableTags = fetchAvailableTags(), - availableStates = EnumSet.of(MangaState.FINISHED, MangaState.ABANDONED, MangaState.UPCOMING), - ) - - override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { - val domain = domain - val root = if (filter.isEmpty()) { - webClient.httpGet( - "https://$domain/list?sortType=${ - getSortKey(order) - }&offset=${offset upBy PAGE_SIZE}", - ).parseHtml().body().let { doc -> (doc.getElementById("mangaBox") ?: doc.getElementById("mangaResults")) } - } else { - advancedSearch(offset, order, filter).parseHtml() - } - val tiles = root.selectFirst("div.tiles.row") - if (tiles == null) { - if (!root.getElementsContainingOwnText(NOTHING_FOUND).isNullOrEmpty()) { - return emptyList() - } - root.parseFailed("No tiles found") - } - return tiles.select("div.tile").mapNotNull(::parseManga) - } - - override suspend fun getDetails(manga: Manga): Manga { - val response = webClient.httpGet(manga.url.toAbsoluteUrl(domain)) - val doc = response.parseHtml() - val root = doc.body().requireElementById("mangaBox").run { - selectFirst("div.leftContent") ?: this - } - val dateFormat = SimpleDateFormat("dd.MM.yy", Locale.US) - val coverImg = root.selectFirst("div.subject-cover")?.selectFirst("img") - val translations = if (config[splitTranslationsKey]) { - root.selectFirst("div.translator-selection") - ?.select(".translator-selection-item") - ?.associate { - it.id().removePrefix("tr-").toLong() to it.selectFirst(".translator-selection-name")?.textOrNull() - } - } else { - null - } - val newSource = getSource(response.request.url) - val chaptersList = root.getElementById("chapters-list") - if (chaptersList == null && root.getElementsContainingOwnText(NO_CHAPTERS).isEmpty()) { - root.parseFailed("No chapters found") - } - val hashRegex = Regex("window.user_hash\\s*=\\s*\'([^\']+)\'") - val userHash = doc.select("script").firstNotNullOfOrNull { it.html().findGroupValue(hashRegex) } - val hasNsfwAlert = root.select(".alert-warning").any { it.ownText().contains(NSFW_ALERT) } - return manga.copy( - source = newSource, - title = doc.metaValue("name") ?: manga.title, - altTitles = root.selectFirst(".all-names-popover")?.select(".name")?.mapNotNullToSet { - it.textOrNull() - } ?: manga.altTitles, - publicUrl = response.request.url.toString(), - description = root.selectFirst("div.manga-description")?.html(), - largeCoverUrl = coverImg?.attrAsAbsoluteUrlOrNull("data-full"), - coverUrl = manga.coverUrl - ?: coverImg?.attrAsAbsoluteUrlOrNull("data-thumb")?.replace("_p.", "."), - tags = root.selectFirstOrThrow("div.subject-meta") - .getElementsByAttributeValueContaining("href", "/list/genre/").mapTo(manga.tags.toMutableSet()) { a -> - MangaTag( - title = a.text().toTitleCase(), - key = a.attr("href").removeSuffix('/').substringAfterLast('/'), - source = source, - ) - }, - authors = root.select(".elem_author,.elem_illustrator,.elem_screenwriter") - .select("a.person-link") - .mapNotNullToSet { it.textOrNull() } + manga.authors, - contentRating = (if (hasNsfwAlert) ContentRating.SUGGESTIVE else ContentRating.SAFE) - .coerceAtLeast(manga.contentRating ?: ContentRating.SAFE), - chapters = chaptersList?.select("a.chapter-link") - ?.flatMapChapters(reversed = true) { a -> - val tr = a.selectFirstParent("tr") ?: return@flatMapChapters emptyList() - val href = a.attrAsRelativeUrl("href") - val number = tr.attr("data-num").toFloatOrNull()?.div(10f) ?: 0f - val volume = tr.attr("data-vol").toIntOrNull() ?: 0 - if (translations.isNullOrEmpty() || a.attr("data-translations").isEmpty()) { - var translators = "" - val translatorElement = a.attr("title") - if (!translatorElement.isNullOrBlank()) { - translators = translatorElement.replace("(Переводчик),", "&").removeSuffix(" (Переводчик)") - } - listOf( - MangaChapter( - id = generateUid(href), - name = a.text().removePrefix(manga.title).trim(), - number = number, - volume = volume, - url = href.withQueryParam("d", userHash), - uploadDate = dateFormat.tryParse(tr.selectFirst("td.date")?.text()), - scanlator = translators, - source = newSource, - branch = null, - ), - ) - } else { - val translationData = JSONArray(a.attr("data-translations")) - translationData.mapJSON { jo -> - val personId = jo.getLong("personId") - val link = href.withQueryParam("tran", personId.toString()) - MangaChapter( - id = generateUid(link), - name = a.text().removePrefix(manga.title).trim(), - number = number, - volume = volume, - url = link.withQueryParam("d", userHash), - uploadDate = dateFormat.tryParse(jo.getStringOrNull("dateCreated")), - scanlator = null, - source = newSource, - branch = translations[personId], - ) - } - } - }.orEmpty(), - ) - } - - override suspend fun getPages(chapter: MangaChapter): List { - if (chapter.source != source && chapter.source is MangaParserSource) { // handle redirects between websites - return context.newParserInstance(chapter.source).getPages(chapter) - } - val url = chapter.url.toAbsoluteUrl(domain).toHttpUrl().newBuilder().setQueryParameter("mtr", "1").build() - val doc = webClient.httpGet(url).parseHtml() - val scripts = doc.select("script") - for (script in scripts) { - val data = script.html() - var pos = data.indexOf("rm_h.readerDoInit(") - if (pos != -1) { - parsePagesV2(data, pos)?.let { return it } - } - pos = data.indexOf("rm_h.readerInit( 0,") - if (pos != -1) { - parsePagesV1(data, pos)?.let { return it } - } - pos = data.indexOf(".readerInit(") - if (pos != -1) { - parsePagesV3(data, pos).let { return it } - } - } - doc.parseFailed("Pages list not found at ${chapter.url}") - } - - override suspend fun getPageUrl(page: MangaPage): String { - val parts = page.url.split('|') - if (parts.size < 2) { - return page.url - } - val path = parts.last() - // fast path - cachedPagesServer?.let { host -> - val url = concatUrl("https://$host/", path) - if (tryHead(url)) { - return url - } else { - cachedPagesServer = null - } - } - // slow path - val candidates = HashSet((parts.size - 1) * 2) - for (i in 0 until parts.size - 1) { - val server = parts[i].trim().ifEmpty { "https://$domain/" } - candidates.add(concatUrl(server, path)) - candidates.add(concatUrl(server, path.substringBeforeLast('?'))) - } - return try { - channelFlow { - for (url in candidates) { - launch { - if (tryHead(url)) { - send(url) - } - } - } - }.first().also { - cachedPagesServer = it.toHttpUrlOrNull()?.host - } - } catch (e: NoSuchElementException) { - candidates.randomOrNull() ?: throw ParseException("No page url candidates", page.url, e) - } - } - - private suspend fun fetchAvailableTags(): Set { - val doc = webClient.httpGet("https://${domain}/list/genres/sort_name").parseHtml() - val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")?.selectFirst("table.table") - ?: doc.parseFailed("Cannot find root") - return root.select("a.element-link").mapToSet { a -> - MangaTag( - title = a.text().toTitleCase(), - key = a.attr("href").substringAfterLast('/'), - source = source, - ) - } - } - - override suspend fun getUsername(): String { - val root = webClient.httpGet("https://grouple.co/").parseHtml().body() - val element = root.selectFirst("img.user-avatar") ?: throw AuthRequiredException(source) - val res = element.parent()?.text() - return if (res.isNullOrEmpty()) { - root.parseFailed("Cannot find username") - } else res - } - - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - if (!request.header(HEADER_ACCEPT).isNullOrEmpty()) { - return chain.proceed(request).checkIfAuthRequired() - } - val ext = request.url.pathSegments.lastOrNull()?.substringAfterLast('.', "")?.lowercase(Locale.ROOT) - return if (ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "webp") { - chain.proceed( - request.newBuilder().header(HEADER_ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8").build(), - ) - } else { - chain.proceed(request).checkIfAuthRequired() - } - } - - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - keys.add(splitTranslationsKey) - } - - override suspend fun getRelatedManga(seed: Manga): List { - val doc = webClient.httpGet(seed.url.toAbsoluteUrl(domain)).parseHtml() - val root = doc.body().requireElementById("mangaBox").select("h4").first { it.ownText() == RELATED_TITLE } - .nextElementSibling() ?: doc.parseFailed("Cannot find root") - return root.select("div.tile").mapNotNull(::parseManga) - } - - protected open fun getSource(url: HttpUrl): MangaSource = when (url.host) { - in SeiMangaParser.domains -> MangaParserSource.SEIMANGA - in MintMangaParser.domains -> MangaParserSource.MINTMANGA - in ReadmangaParser.domains -> MangaParserSource.READMANGA_RU - in SelfMangaParser.domains -> MangaParserSource.SELFMANGA - in UsagiParser.domains -> MangaParserSource.USAGI - else -> source - } - - private fun getSortKey(sortOrder: SortOrder) = when (sortOrder) { - SortOrder.ALPHABETICAL -> "name" - SortOrder.POPULARITY -> "rate" - SortOrder.UPDATED -> "updated" - SortOrder.ADDED, - SortOrder.NEWEST, - -> "created" - - SortOrder.RATING -> "votes" - else -> "rate" - } - - private suspend fun advancedSearch(offset: Int, order: SortOrder, filter: MangaListFilter): Response { - val tagsMap = tagsIndex.get() - val url = urlBuilder() - .addPathSegment("search") - .addPathSegment("advancedResults") - url.addQueryParameter("q", filter.query) - url.addQueryParameter("offset", offset.toString()) - filter.tags.forEach { tag -> - val tagId = requireNotNull(tagsMap[tag.title.lowercase()]) { "Tag ${tag.title} not found" } - url.addQueryParameter(tagId, "in") - } - filter.tagsExclude.forEach { tag -> - val tagId = requireNotNull(tagsMap[tag.title.lowercase()]) { "Tag ${tag.title} not found" } - url.addQueryParameter(tagId, "ex") - } - url.addQueryParameter( - "years", - buildString { - append(filter.yearFrom.ifZero { YEAR_MIN }) - append(',') - append(filter.yearTo.ifZero { YEAR_MAX }) - }, - ) - url.addQueryParameter( - "sortType", - when (order) { - SortOrder.RATING -> "USER_RATING" - SortOrder.ALPHABETICAL -> "NAME" - SortOrder.ADDED -> "YEAR" - SortOrder.POPULARITY -> "POPULARITY" - SortOrder.NEWEST -> "DATE_CREATE" - SortOrder.UPDATED -> "DATE_UPDATE" - else -> "RATING" - }, - ) - filter.states.forEach { state -> - when (state) { - MangaState.FINISHED -> "s_completed" - MangaState.ABANDONED -> "s_abandoned_popular" - MangaState.UPCOMING -> "s_wait_upload" - else -> null - }?.let { - url.addQueryParameter(it, "in") - } - } - - return webClient.httpGet(url.build()) - } - - private suspend fun tryHead(url: String): Boolean = runCatchingCancellable { - webClient.httpHead(url).use { response -> - response.isSuccessful && !response.isPumpkin() && response.headersContentLength() >= MIN_IMAGE_SIZE - } - }.getOrDefault(false) - - private fun Response.isPumpkin(): Boolean = request.url.host == "upload.wikimedia.org" - - private fun parseManga(node: Element): Manga? { - val imgDiv = node.selectFirst("div.img") ?: return null - val descDiv = node.selectFirst("div.desc") ?: return null - if (descDiv.selectFirst("i.fa-user") != null || descDiv.selectFirst("i.fa-external-link") != null) { - return null // skip author - } - val href = imgDiv.selectFirst("a")?.attrAsAbsoluteUrlOrNull("href") ?: return null - val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text() ?: return null - val tileInfo = descDiv.selectFirst("div.tile-info") - val relUrl = href.toRelativeUrl(domain) - if (relUrl.contains("://")) { - return null - } - val author = tileInfo?.selectFirst("a.person-link")?.text() - return Manga( - id = generateUid(relUrl), - url = relUrl, - publicUrl = href, - title = title, - altTitles = setOfNotNull(descDiv.selectFirst("h5")?.textOrNull()), - coverUrl = imgDiv.selectFirst("img.lazy")?.attrAsAbsoluteUrlOrNull("data-original")?.replace("_p.", "."), - rating = runCatching { - node.selectFirst(".compact-rate")?.attr("title")?.toFloatOrNull()?.div(5f) - }.getOrNull() ?: RATING_UNKNOWN, - authors = setOfNotNull(author), - contentRating = if (isNsfwSource) ContentRating.ADULT else null, - tags = runCatching { - tileInfo?.select("a.element-link")?.mapToSet { - MangaTag( - title = it.text().toTitleCase(), - key = it.attr("href").substringAfterLast('/'), - source = source, - ) - } - }.getOrNull().orEmpty(), - state = when { - node.selectFirst("div.tags")?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED - - else -> null - }, - source = source, - ) - } - - private fun parsePagesV1(data: String, pos: Int): List? { - val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')') - if (json.isEmpty()) { - return null - } - val ja = JSONArray("[$json]") - val pages = ja.getJSONArray(1) - val servers = ja.getJSONArray(3).mapJSON { it.getString("path") } - val serversStr = servers.joinToString("|") - return (0 until pages.length()).map { i -> - val page = pages.getJSONArray(i) - val primaryServer = page.getString(0) - val url = page.getString(2) - MangaPage( - id = generateUid(url), - url = "$primaryServer|$serversStr|$url", - preview = null, - source = source, - ) - } - } - - private fun parsePagesV2(data: String, pos: Int): List? { - val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')') - if (json.isEmpty()) { - return null - } - val ja = JSONArray("[$json]") - val pages = ja.getJSONArray(0) - val servers = ja.getJSONArray(2).mapJSON { it.getString("path") } - val serversStr = servers.joinToString("|") - return (0 until pages.length()).map { i -> - val page = pages.getJSONArray(i) - val primaryServer = page.getString(0) - val url = page.getString(2) - MangaPage( - id = generateUid(url), - url = "$primaryServer|$serversStr|$url", - preview = null, - source = source, - ) - } - } - - private fun parsePagesV3(data: String, pos: Int): List { - val json = JSONArray(data.substring(pos).substringBetween("(", ")").substringBeforeLast(',')) - return (0 until json.length()).map { i -> - val ja = json.getJSONArray(i) - val server = ja.getString(0).ifEmpty { "https://$domain" } - val url = ja.getString(2) - MangaPage( - id = generateUid(url), - url = concatUrl(server, url), - preview = null, - source = source, - ) - } - } - - private suspend fun fetchTagsMap(): ScatterMap { - val url = "https://$domain/search/advanced" - val properties = - webClient.httpGet(url).parseHtml().body().selectFirst("form.search-form")?.select("div.form-group") - ?.find { it.selectFirst("li.property") != null } - ?.select("li.property") - ?: throw ParseException("Genres filter element not found", url) - val result = MutableScatterMap(properties.size) - properties.forEach { li -> - val name = li.text().lowercase() - val id = li.selectFirstOrThrow("input").id() - result[name] = id - } - return result - } - - private fun String.withQueryParam(name: String, value: String?): String { - if (value == null) return this - return toAbsoluteUrl(domain) - .toHttpUrl() - .newBuilder() - .setQueryParameter(name, value) - .build() - .toString() - .toRelativeUrl(domain) - } - - @Throws(IOException::class) - private fun Response.checkIfAuthRequired(): Response { - val lastPathSegment = request.url.pathSegments.lastOrNull() - if (lastPathSegment == "login") { - closeQuietly() - throw AuthRequiredException(source) - } - if (code == HttpURLConnection.HTTP_NOT_FOUND) { - if (!isAuthorized) { - closeQuietly() - throw AuthRequiredException(source) - } else { - return newBuilder().code(HttpURLConnection.HTTP_OK).build() - } - } - return this - } + @Volatile + private var cachedPagesServer: String? = null + + override val userAgentKey = ConfigKey.UserAgent( + "Mozilla/5.0 (X11; U; UNICOS lcLinux; en-US) Gecko/20140730 (KHTML, like Gecko, Safari/419.3) Arora/0.8.0", + ) + private val splitTranslationsKey = ConfigKey.SplitByTranslations(false) + private val tagsIndex = suspendLazy(initializer = ::fetchTagsMap) + + override fun getRequestHeaders(): Headers = Headers.Builder() + .add("User-Agent", config[userAgentKey]) + .add("Accept-Language", "ru,en-US;q=0.7,en;q=0.3") + .build() + + override val availableSortOrders: Set = EnumSet.of( + SortOrder.UPDATED, + SortOrder.POPULARITY, + SortOrder.NEWEST, + SortOrder.RATING, + SortOrder.ALPHABETICAL, + SortOrder.ADDED, + ) + + override val authUrl: String + get() { + val targetUri = "https://${domain}/".urlEncoded() + return "https://grouple.co/internal/auth/sso?siteId=$siteId&=targetUri=$targetUri" + } + + override val isAuthorized: Boolean + get() = context.cookieJar.getCookies(domain).any { it.name == "gwt" } + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isMultipleTagsSupported = true, + isTagsExclusionSupported = true, + isSearchSupported = true, + isSearchWithFiltersSupported = true, + isYearRangeSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = fetchAvailableTags(), + availableStates = EnumSet.of(MangaState.FINISHED, MangaState.ABANDONED, MangaState.UPCOMING), + ) + + override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List { + val domain = domain + val root = if (filter.isEmpty()) { + webClient.httpGet( + "https://$domain/list?sortType=${ + getSortKey(order) + }&offset=${offset upBy PAGE_SIZE}", + ).parseHtml().body().let { doc -> (doc.getElementById("mangaBox") ?: doc.getElementById("mangaResults")) } + } else { + advancedSearch(offset, order, filter).parseHtml() + } + val tiles = root.selectFirst("div.tiles.row") + if (tiles == null) { + if (!root.getElementsContainingOwnText(NOTHING_FOUND).isNullOrEmpty()) { + return emptyList() + } + root.parseFailed("No tiles found") + } + return tiles.select("div.tile").mapNotNull(::parseManga) + } + + override suspend fun getDetails(manga: Manga): Manga { + val response = webClient.httpGet(manga.url.toAbsoluteUrl(domain)) + val doc = response.parseHtml() + val root = doc.body().requireElementById("mangaBox").run { + selectFirst("div.leftContent") ?: this + } + val dateFormat = SimpleDateFormat("dd.MM.yy", Locale.US) + val coverImg = root.selectFirst("div.subject-cover")?.selectFirst("img") + val translations = if (config[splitTranslationsKey]) { + root.selectFirst("div.translator-selection") + ?.select(".translator-selection-item") + ?.associate { + it.id().removePrefix("tr-").toLong() to it.selectFirst(".translator-selection-name")?.textOrNull() + } + } else { + null + } + val newSource = getSource(response.request.url) + val chaptersList = root.getElementById("chapters-list") + if (chaptersList == null && root.getElementsContainingOwnText(NO_CHAPTERS).isEmpty()) { + root.parseFailed("No chapters found") + } + val hashRegex = Regex("window.user_hash\\s*=\\s*\'([^\']+)\'") + val userHash = doc.select("script").firstNotNullOfOrNull { it.html().findGroupValue(hashRegex) } + val hasNsfwAlert = root.select(".alert-warning").any { it.ownText().contains(NSFW_ALERT) } + return manga.copy( + source = newSource, + title = doc.metaValue("name") ?: manga.title, + altTitles = root.selectFirst(".all-names-popover")?.select(".name")?.mapNotNullToSet { + it.textOrNull() + } ?: manga.altTitles, + publicUrl = response.request.url.toString(), + description = root.selectFirst("div.manga-description")?.html(), + largeCoverUrl = coverImg?.attrAsAbsoluteUrlOrNull("data-full"), + coverUrl = manga.coverUrl + ?: coverImg?.attrAsAbsoluteUrlOrNull("data-thumb")?.replace("_p.", "."), + tags = root.selectFirstOrThrow("div.subject-meta") + .getElementsByAttributeValueContaining("href", "/list/genre/").mapTo(manga.tags.toMutableSet()) { a -> + MangaTag( + title = a.text().toTitleCase(), + key = a.attr("href").removeSuffix('/').substringAfterLast('/'), + source = source, + ) + }, + authors = root.select(".elem_author,.elem_illustrator,.elem_screenwriter") + .select("a.person-link") + .mapNotNullToSet { it.textOrNull() } + manga.authors, + contentRating = (if (hasNsfwAlert) ContentRating.SUGGESTIVE else ContentRating.SAFE) + .coerceAtLeast(manga.contentRating ?: ContentRating.SAFE), + chapters = chaptersList?.select("a.chapter-link") + ?.flatMapChapters(reversed = true) { a -> + val tr = a.selectFirstParent("tr") ?: return@flatMapChapters emptyList() + val href = a.attrAsRelativeUrl("href") + val number = tr.attr("data-num").toFloatOrNull()?.div(10f) ?: 0f + val volume = tr.attr("data-vol").toIntOrNull() ?: 0 + if (translations.isNullOrEmpty() || a.attr("data-translations").isEmpty()) { + var translators = "" + val translatorElement = a.attr("title") + if (!translatorElement.isNullOrBlank()) { + translators = translatorElement.replace("(Переводчик),", "&").removeSuffix(" (Переводчик)") + } + listOf( + MangaChapter( + id = generateUid(href), + title = a.text().removePrefix(manga.title).trim().nullIfEmpty(), + number = number, + volume = volume, + url = href.withQueryParam("d", userHash), + uploadDate = dateFormat.tryParse(tr.selectFirst("td.date")?.text()), + scanlator = translators, + source = newSource, + branch = null, + ), + ) + } else { + val translationData = JSONArray(a.attr("data-translations")) + translationData.mapJSON { jo -> + val personId = jo.getLong("personId") + val link = href.withQueryParam("tran", personId.toString()) + MangaChapter( + id = generateUid(link), + title = a.text().removePrefix(manga.title).trim(), + number = number, + volume = volume, + url = link.withQueryParam("d", userHash), + uploadDate = dateFormat.tryParse(jo.getStringOrNull("dateCreated")), + scanlator = null, + source = newSource, + branch = translations[personId], + ) + } + } + }.orEmpty(), + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + if (chapter.source != source && chapter.source is MangaParserSource) { // handle redirects between websites + return context.newParserInstance(chapter.source).getPages(chapter) + } + val url = chapter.url.toAbsoluteUrl(domain).toHttpUrl().newBuilder().setQueryParameter("mtr", "1").build() + val doc = webClient.httpGet(url).parseHtml() + val scripts = doc.select("script") + for (script in scripts) { + val data = script.html() + var pos = data.indexOf("rm_h.readerDoInit(") + if (pos != -1) { + parsePagesV2(data, pos)?.let { return it } + } + pos = data.indexOf("rm_h.readerInit( 0,") + if (pos != -1) { + parsePagesV1(data, pos)?.let { return it } + } + pos = data.indexOf(".readerInit(") + if (pos != -1) { + parsePagesV3(data, pos).let { return it } + } + } + doc.parseFailed("Pages list not found at ${chapter.url}") + } + + override suspend fun getPageUrl(page: MangaPage): String { + val parts = page.url.split('|') + if (parts.size < 2) { + return page.url + } + val path = parts.last() + // fast path + cachedPagesServer?.let { host -> + val url = concatUrl("https://$host/", path) + if (tryHead(url)) { + return url + } else { + cachedPagesServer = null + } + } + // slow path + val candidates = HashSet((parts.size - 1) * 2) + for (i in 0 until parts.size - 1) { + val server = parts[i].trim().ifEmpty { "https://$domain/" } + candidates.add(concatUrl(server, path)) + candidates.add(concatUrl(server, path.substringBeforeLast('?'))) + } + return try { + channelFlow { + for (url in candidates) { + launch { + if (tryHead(url)) { + send(url) + } + } + } + }.first().also { + cachedPagesServer = it.toHttpUrlOrNull()?.host + } + } catch (e: NoSuchElementException) { + candidates.randomOrNull() ?: throw ParseException("No page url candidates", page.url, e) + } + } + + private suspend fun fetchAvailableTags(): Set { + val doc = webClient.httpGet("https://${domain}/list/genres/sort_name").parseHtml() + val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")?.selectFirst("table.table") + ?: doc.parseFailed("Cannot find root") + return root.select("a.element-link").mapToSet { a -> + MangaTag( + title = a.text().toTitleCase(), + key = a.attr("href").substringAfterLast('/'), + source = source, + ) + } + } + + override suspend fun getUsername(): String { + val root = webClient.httpGet("https://grouple.co/").parseHtml().body() + val element = root.selectFirst("img.user-avatar") ?: throw AuthRequiredException(source) + val res = element.parent()?.text() + return if (res.isNullOrEmpty()) { + root.parseFailed("Cannot find username") + } else res + } + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + if (!request.header(HEADER_ACCEPT).isNullOrEmpty()) { + return chain.proceed(request).checkIfAuthRequired() + } + val ext = request.url.pathSegments.lastOrNull()?.substringAfterLast('.', "")?.lowercase(Locale.ROOT) + return if (ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "webp") { + chain.proceed( + request.newBuilder().header(HEADER_ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8").build(), + ) + } else { + chain.proceed(request).checkIfAuthRequired() + } + } + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + keys.add(splitTranslationsKey) + } + + override suspend fun getRelatedManga(seed: Manga): List { + val doc = webClient.httpGet(seed.url.toAbsoluteUrl(domain)).parseHtml() + val root = doc.body().requireElementById("mangaBox").select("h4").first { it.ownText() == RELATED_TITLE } + .nextElementSibling() ?: doc.parseFailed("Cannot find root") + return root.select("div.tile").mapNotNull(::parseManga) + } + + protected open fun getSource(url: HttpUrl): MangaSource = when (url.host) { + in SeiMangaParser.domains -> MangaParserSource.SEIMANGA + in MintMangaParser.domains -> MangaParserSource.MINTMANGA + in ReadmangaParser.domains -> MangaParserSource.READMANGA_RU + in SelfMangaParser.domains -> MangaParserSource.SELFMANGA + in UsagiParser.domains -> MangaParserSource.USAGI + else -> source + } + + private fun getSortKey(sortOrder: SortOrder) = when (sortOrder) { + SortOrder.ALPHABETICAL -> "name" + SortOrder.POPULARITY -> "rate" + SortOrder.UPDATED -> "updated" + SortOrder.ADDED, + SortOrder.NEWEST, + -> "created" + + SortOrder.RATING -> "votes" + else -> "rate" + } + + private suspend fun advancedSearch(offset: Int, order: SortOrder, filter: MangaListFilter): Response { + val tagsMap = tagsIndex.get() + val url = urlBuilder() + .addPathSegment("search") + .addPathSegment("advancedResults") + url.addQueryParameter("q", filter.query) + url.addQueryParameter("offset", offset.toString()) + filter.tags.forEach { tag -> + val tagId = requireNotNull(tagsMap[tag.title.lowercase()]) { "Tag ${tag.title} not found" } + url.addQueryParameter(tagId, "in") + } + filter.tagsExclude.forEach { tag -> + val tagId = requireNotNull(tagsMap[tag.title.lowercase()]) { "Tag ${tag.title} not found" } + url.addQueryParameter(tagId, "ex") + } + url.addQueryParameter( + "years", + buildString { + append(filter.yearFrom.ifZero { YEAR_MIN }) + append(',') + append(filter.yearTo.ifZero { YEAR_MAX }) + }, + ) + url.addQueryParameter( + "sortType", + when (order) { + SortOrder.RATING -> "USER_RATING" + SortOrder.ALPHABETICAL -> "NAME" + SortOrder.ADDED -> "YEAR" + SortOrder.POPULARITY -> "POPULARITY" + SortOrder.NEWEST -> "DATE_CREATE" + SortOrder.UPDATED -> "DATE_UPDATE" + else -> "RATING" + }, + ) + filter.states.forEach { state -> + when (state) { + MangaState.FINISHED -> "s_completed" + MangaState.ABANDONED -> "s_abandoned_popular" + MangaState.UPCOMING -> "s_wait_upload" + else -> null + }?.let { + url.addQueryParameter(it, "in") + } + } + + return webClient.httpGet(url.build()) + } + + private suspend fun tryHead(url: String): Boolean = runCatchingCancellable { + webClient.httpHead(url).use { response -> + response.isSuccessful && !response.isPumpkin() && response.headersContentLength() >= MIN_IMAGE_SIZE + } + }.getOrDefault(false) + + private fun Response.isPumpkin(): Boolean = request.url.host == "upload.wikimedia.org" + + private fun parseManga(node: Element): Manga? { + val imgDiv = node.selectFirst("div.img") ?: return null + val descDiv = node.selectFirst("div.desc") ?: return null + if (descDiv.selectFirst("i.fa-user") != null || descDiv.selectFirst("i.fa-external-link") != null) { + return null // skip author + } + val href = imgDiv.selectFirst("a")?.attrAsAbsoluteUrlOrNull("href") ?: return null + val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text() ?: return null + val tileInfo = descDiv.selectFirst("div.tile-info") + val relUrl = href.toRelativeUrl(domain) + if (relUrl.contains("://")) { + return null + } + val author = tileInfo?.selectFirst("a.person-link")?.text() + return Manga( + id = generateUid(relUrl), + url = relUrl, + publicUrl = href, + title = title, + altTitles = setOfNotNull(descDiv.selectFirst("h5")?.textOrNull()), + coverUrl = imgDiv.selectFirst("img.lazy")?.attrAsAbsoluteUrlOrNull("data-original")?.replace("_p.", "."), + rating = runCatching { + node.selectFirst(".compact-rate")?.attr("title")?.toFloatOrNull()?.div(5f) + }.getOrNull() ?: RATING_UNKNOWN, + authors = setOfNotNull(author), + contentRating = if (isNsfwSource) ContentRating.ADULT else null, + tags = runCatching { + tileInfo?.select("a.element-link")?.mapToSet { + MangaTag( + title = it.text().toTitleCase(), + key = it.attr("href").substringAfterLast('/'), + source = source, + ) + } + }.getOrNull().orEmpty(), + state = when { + node.selectFirst("div.tags")?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED + + else -> null + }, + source = source, + ) + } + + private fun parsePagesV1(data: String, pos: Int): List? { + val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')') + if (json.isEmpty()) { + return null + } + val ja = JSONArray("[$json]") + val pages = ja.getJSONArray(1) + val servers = ja.getJSONArray(3).mapJSON { it.getString("path") } + val serversStr = servers.joinToString("|") + return (0 until pages.length()).map { i -> + val page = pages.getJSONArray(i) + val primaryServer = page.getString(0) + val url = page.getString(2) + MangaPage( + id = generateUid(url), + url = "$primaryServer|$serversStr|$url", + preview = null, + source = source, + ) + } + } + + private fun parsePagesV2(data: String, pos: Int): List? { + val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')') + if (json.isEmpty()) { + return null + } + val ja = JSONArray("[$json]") + val pages = ja.getJSONArray(0) + val servers = ja.getJSONArray(2).mapJSON { it.getString("path") } + val serversStr = servers.joinToString("|") + return (0 until pages.length()).map { i -> + val page = pages.getJSONArray(i) + val primaryServer = page.getString(0) + val url = page.getString(2) + MangaPage( + id = generateUid(url), + url = "$primaryServer|$serversStr|$url", + preview = null, + source = source, + ) + } + } + + private fun parsePagesV3(data: String, pos: Int): List { + val json = JSONArray(data.substring(pos).substringBetween("(", ")").substringBeforeLast(',')) + return (0 until json.length()).map { i -> + val ja = json.getJSONArray(i) + val server = ja.getString(0).ifEmpty { "https://$domain" } + val url = ja.getString(2) + MangaPage( + id = generateUid(url), + url = concatUrl(server, url), + preview = null, + source = source, + ) + } + } + + private suspend fun fetchTagsMap(): ScatterMap { + val url = "https://$domain/search/advanced" + val properties = + webClient.httpGet(url).parseHtml().body().selectFirst("form.search-form")?.select("div.form-group") + ?.find { it.selectFirst("li.property") != null } + ?.select("li.property") + ?: throw ParseException("Genres filter element not found", url) + val result = MutableScatterMap(properties.size) + properties.forEach { li -> + val name = li.text().lowercase() + val id = li.selectFirstOrThrow("input").id() + result[name] = id + } + return result + } + + private fun String.withQueryParam(name: String, value: String?): String { + if (value == null) return this + return toAbsoluteUrl(domain) + .toHttpUrl() + .newBuilder() + .setQueryParameter(name, value) + .build() + .toString() + .toRelativeUrl(domain) + } + + @Throws(IOException::class) + private fun Response.checkIfAuthRequired(): Response { + val lastPathSegment = request.url.pathSegments.lastOrNull() + if (lastPathSegment == "login") { + closeQuietly() + throw AuthRequiredException(source) + } + if (code == HttpURLConnection.HTTP_NOT_FOUND) { + if (!isAuthorized) { + closeQuietly() + throw AuthRequiredException(source) + } else { + return newBuilder().code(HttpURLConnection.HTTP_OK).build() + } + } + return this + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt index a29c260c..6a83c0f8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/ChanParser.kt @@ -92,7 +92,7 @@ internal abstract class ChanParser( ?: return@mapChapters null MangaChapter( id = generateUid(href), - name = tr.selectFirst("a")?.text().orEmpty(), + title = tr.selectFirst("a")?.textOrNull(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt index cc5c2221..6b4017f0 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/HenChanParser.kt @@ -51,7 +51,7 @@ internal class HenChanParser(context: MangaLoaderContext) : ChanParser(context, number = 0f, volume = 0, uploadDate = 0L, - name = manga.title, + title = null, scanlator = null, branch = null, ), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/YaoiChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/YaoiChanParser.kt index 10cae236..848002a7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/YaoiChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/multichan/YaoiChanParser.kt @@ -33,7 +33,7 @@ internal class YaoiChanParser(context: MangaLoaderContext) : ChanParser(context, val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt index 3238d8f1..500ca0ad 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/rulib/LibSocialParser.kt @@ -263,7 +263,7 @@ internal abstract class LibSocialParser( val team = bjo.getJSONArray("teams").optJSONObject(0)?.getStringOrNull("name") builder += MangaChapter( id = generateUid(id), - name = name, + title = name, number = number, volume = volume, url = "${manga.url}/chapter?number=$numberString&volume=$volume", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt index 319ce521..7eec3ae1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/ScanParser.kt @@ -156,8 +156,8 @@ internal abstract class ScanParser( val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = div.selectFirst("h5")?.html()?.substringBefore("") - .orEmpty(), + title = div.selectFirst("h5")?.html()?.substringBefore("") + ?.nullIfEmpty(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt index e6aeebcf..6a56f672 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/fr/MangaFr.kt @@ -35,7 +35,7 @@ internal class MangaFr(context: MangaLoaderContext) : val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = div.selectFirstOrThrow("h5").html().substringBefore(""), + title = div.selectFirstOrThrow("h5").html().substringBefore(""), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/it/ScanIta.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/it/ScanIta.kt index 7e23374f..a132e1f7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/it/ScanIta.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/scan/it/ScanIta.kt @@ -45,7 +45,7 @@ internal class ScanIta(context: MangaLoaderContext) : val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = div.selectFirstOrThrow("h5").html().substringBefore(""), + title = div.selectFirstOrThrow("h5").html().substringBefore(""), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt index d7393ee6..deef1772 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/sinmh/SinmhParser.kt @@ -174,7 +174,7 @@ internal abstract class SinmhParser( val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt index b087351f..5b4d3534 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/MangaAy.kt @@ -160,7 +160,7 @@ internal class MangaAy(context: MangaLoaderContext) : LegacyPagedMangaParser(con val href = a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt index 86723f1d..f3b1b91c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/SadScans.kt @@ -80,7 +80,7 @@ internal class SadScans(context: MangaLoaderContext) : val url = "/" + a.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt index 43b869f5..35982be1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/tr/TrWebtoon.kt @@ -179,7 +179,7 @@ internal class TrWebtoon(context: MangaLoaderContext) : val url = tr.selectFirstOrThrow("a").attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = tr.selectFirst("a")?.text() ?: "Chapter : ${i + 1f}", + title = tr.selectFirst("a")?.textOrNull(), number = i + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt index e087d24d..b5a9a515 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HentaiUkrParser.kt @@ -73,7 +73,7 @@ internal class HentaiUkrParser(context: MangaLoaderContext) : LegacyMangaParser( chapters = listOf( MangaChapter( id = generateUid(manga.id), - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = manga.url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt index 88d9ac7e..4645c3c4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/HoneyMangaParser.kt @@ -75,17 +75,7 @@ internal class HoneyMangaParser(context: MangaLoaderContext) : val volume = jo.getIntOrDefault("volume", 0) MangaChapter( id = generateUid(jo.getString("id")), - name = buildString { - append("Том ") - append(volume) - append(". ") - append("Розділ ") - append(number) - if (jo.optString("title") != "Title") { - append(" - ") - append(jo.optString("title")) - } - }, + title = jo.getStringOrNull("title"), number = number, volume = volume, url = jo.optString("chapterResourcesId"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt index 4bb085ce..4497bee9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/uk/MangaInUaParser.kt @@ -130,7 +130,7 @@ internal class MangaInUaParser(context: MangaLoaderContext) : LegacyPagedMangaPa if (!isAlternative) i++ MangaChapter( id = generateUid(href), - name = if (isAlternative) { + title = if (isAlternative) { prevChapterName ?: return@mapChapters null } else { prevChapterName = name diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt index 786ab60c..6398983e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenParser.kt @@ -153,7 +153,7 @@ internal class BlogTruyenParser(context: MangaLoaderContext) : val uploadDate = dateFormat.tryParse(element.select("span.publishedDate").text()) MangaChapter( id = generateUid(id), - name = name, + title = name, number = index + 1f, volume = 0, url = relativeUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVN.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVN.kt index 3eed2de1..c6aa80aa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVN.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BlogTruyenVN.kt @@ -205,7 +205,7 @@ internal class BlogTruyenVN(context: MangaLoaderContext) : val uploadDate = dateFormat.tryParse(element.select("span.publishedDate").text()) MangaChapter( id = generateUid(id), - name = name, + title = name, number = index + 1f, volume = 0, url = relativeUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BuonDuaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BuonDuaParser.kt index dc47cd94..6a2ac288 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BuonDuaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/BuonDuaParser.kt @@ -36,7 +36,7 @@ internal class BuonDuaParser(context: MangaLoaderContext) : LegacyMangaParser(co val relUrl = element.attrAsRelativeUrl("href") MangaChapter( id = generateUid(relUrl), - name = "Chapter ${element.text()}", + title = null, number = index + 1f, volume = 0, url = relUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CMangaParser.kt index a5b5b8c3..c0b95732 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CMangaParser.kt @@ -85,7 +85,7 @@ internal class CMangaParser(context: MangaLoaderContext) : val chapterNumber = info.getFloatOrDefault("num", -1f) + 1f MangaChapter( id = generateUid(chapterId), - name = if (info.isLocked()) "Chapter $chapterNumber - locked" else "Chapter $chapterNumber", + title = if (info.isLocked()) "Chapter $chapterNumber - locked" else "Chapter $chapterNumber", number = chapterNumber, volume = 0, url = "/album/$slug/chapter-$mangaId-$chapterId", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt index c55e1af4..8791d34c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/CuuTruyenParser.kt @@ -172,7 +172,7 @@ internal class CuuTruyenParser(context: MangaLoaderContext) : val number = jo.getFloatOrDefault("number", 0f) MangaChapter( id = generateUid(chapterId), - name = jo.getStringOrNull("name") ?: number.formatSimple(), + title = jo.getStringOrNull("name"), number = number, volume = 0, url = "/api/v2/chapters/$chapterId", diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/DuaLeoTruyen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/DuaLeoTruyen.kt index d512cf5c..e6f14591 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/DuaLeoTruyen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/DuaLeoTruyen.kt @@ -113,7 +113,7 @@ internal class DuaLeoTruyen(context: MangaLoaderContext) : val dateText = div.selectFirst(".chap_update")?.text() MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, url = href, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/GocTruyenTranh.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/GocTruyenTranh.kt index c5fead17..ea7ec862 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/GocTruyenTranh.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/GocTruyenTranh.kt @@ -180,7 +180,7 @@ internal class GocTruyenTranh(context: MangaLoaderContext) : val dateText = li.selectFirst("div.w-\\[50\\%\\].truncate.text-center")?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Hentai18VN.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Hentai18VN.kt index 031b1fb7..d1b909dc 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Hentai18VN.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/Hentai18VN.kt @@ -150,7 +150,7 @@ internal class Hentai18VN(context: MangaLoaderContext) : val a = li.selectFirst("a") ?: return@mapChapters null MangaChapter( id = generateUid(a.attr("href")), - name = a.text(), + title = a.text(), number = i + 1f, url = a.attrAsRelativeUrl("href"), uploadDate = parseChapterDate(li.selectFirst(".time")?.text()), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt index 670d3a6a..30f8a2d8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVNParser.kt @@ -246,7 +246,7 @@ internal class HentaiVNParser(context: MangaLoaderContext) : LegacyMangaParser(c val dateStr = element.selectLast("td")?.text() MangaChapter( id = generateUid(titleEl.attrAsRelativeUrl("href")), - name = titleEl.text(), + title = titleEl.text(), number = index + 1f, volume = 0, url = titleEl.attrAsRelativeUrl("href"), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVnBuzz.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVnBuzz.kt index 4490b7cf..94c55889 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVnBuzz.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/HentaiVnBuzz.kt @@ -183,7 +183,7 @@ internal class HentaiVnBuzz(context: MangaLoaderContext) : val name = element.text().removePrefix("- ") MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/KuroNeko.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/KuroNeko.kt index 81bcb551..e554e7b9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/KuroNeko.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/KuroNeko.kt @@ -173,7 +173,7 @@ internal class KuroNeko(context: MangaLoaderContext) : LegacyPagedMangaParser(co MangaChapter( id = generateUid(href), - name = name, + title = name, number = i.toFloat(), volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt index 7fef668b..32e20f06 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt @@ -174,7 +174,7 @@ internal class LxManga(context: MangaLoaderContext) : LegacyPagedMangaParser(con MangaChapter( id = generateUid(href), - name = name, + title = name, number = i.toFloat(), volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/SayHentai.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/SayHentai.kt index b3d5ca33..e8cce5ec 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/SayHentai.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/SayHentai.kt @@ -109,7 +109,7 @@ internal class SayHentai(context: MangaLoaderContext) : val a = element.selectFirst("a") ?: return@mapChapters null MangaChapter( id = generateUid(a.attrAsRelativeUrl("href")), - name = a.text(), + title = a.text(), number = i + 1f, url = a.attrAsRelativeUrl("href"), uploadDate = parseChapterDate(element.selectFirst("span.chapter-release-date")?.text()), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenGG.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenGG.kt index 145cde6a..52f7bee8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenGG.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenGG.kt @@ -164,7 +164,7 @@ internal class TruyenGG(context: MangaLoaderContext) : LegacyPagedMangaParser(co val dateText = div.select("span.cl99").text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenHentaiVN.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenHentaiVN.kt index 5935c2c0..491d5d1e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenHentaiVN.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenHentaiVN.kt @@ -126,7 +126,7 @@ internal class TruyenHentaiVN(context: MangaLoaderContext) : MangaChapter( id = generateUid(url), - name = name, + title = name, number = i + 1f, url = url, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenQQ.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenQQ.kt index 2e2baab7..aea928ed 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenQQ.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenQQ.kt @@ -175,7 +175,7 @@ internal class TruyenQQ(context: MangaLoaderContext) : LegacyPagedMangaParser(co val dateText = div.selectFirst(".time-chap")?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt index 56b5b830..689f8e40 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt @@ -162,7 +162,7 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : val dateText = div.selectFirst(".time-chap")?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/VcomycsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/VcomycsParser.kt index 615bcea6..9e2b983a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/VcomycsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/VcomycsParser.kt @@ -155,7 +155,7 @@ internal class VcomycsParser(context: MangaLoaderContext) : val url = element.attrAsRelativeUrl("href") MangaChapter( id = generateUid(url), - name = element.selectFirst("span")?.text().orEmpty().trim(), + title = element.selectFirst("span")?.textOrNull(), number = index + 1f, volume = 0, url = url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt index 233990ab..fa475bc1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/YurinekoParser.kt @@ -106,7 +106,7 @@ internal class YurinekoParser(context: MangaLoaderContext) : val chapterId = jo.getInt("id") MangaChapter( id = generateUid(chapterId.toLong()), - name = jo.getString("name"), + title = jo.getStringOrNull("name"), number = i + 1f, volume = 0, scanlator = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt index 775daaa4..582e84f8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vmp/VmpParser.kt @@ -122,7 +122,7 @@ internal abstract class VmpParser( chapters = listOf( MangaChapter( id = manga.id, - name = manga.title, + title = manga.title, number = 1f, volume = 0, url = manga.url, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt index a98b2b1c..df7c66f3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/WpComicsParser.kt @@ -250,7 +250,7 @@ internal abstract class WpComicsParser( } MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt index 1e33118c..e27fe808 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/en/XoxoComics.kt @@ -160,7 +160,7 @@ internal class XoxoComics(context: MangaLoaderContext) : val dateText = li.selectFirst("div.col-xs-3")?.text() MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = 0f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/DocTruyen3Q.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/DocTruyen3Q.kt index cb049cd1..0727c311 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/DocTruyen3Q.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/DocTruyen3Q.kt @@ -166,7 +166,7 @@ internal class DocTruyen3Q(context: MangaLoaderContext) : val timeText = timeElement?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = number, url = href, uploadDate = parseChapterDate(timeText), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyen.kt index 098cc690..88e45040 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyen.kt @@ -8,6 +8,7 @@ import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.util.* +import org.koitharu.kotatsu.parsers.util.json.getStringOrNull import java.text.SimpleDateFormat @MangaSourceParser("NETTRUYEN", "NetTruyen", "vi") @@ -59,7 +60,7 @@ internal class NetTruyen(context: MangaLoaderContext) : MangaChapter( id = generateUid(chapterUrl), - name = jo.getString("chapter_name"), + title = jo.getStringOrNull("chapter_name"), number = i + 1f, volume = 0, url = chapterUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenVie.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenVie.kt index a261693d..1f21c601 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenVie.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenVie.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser import org.koitharu.kotatsu.parsers.util.* +import org.koitharu.kotatsu.parsers.util.json.getStringOrNull import java.text.SimpleDateFormat @MangaSourceParser("NETTRUYENVIE", "NetTruyenVie", "vi") @@ -52,7 +53,7 @@ internal class NetTruyenVie(context: MangaLoaderContext) : MangaChapter( id = generateUid(chapterUrl), - name = jo.getString("chapter_name"), + title = jo.getStringOrNull("chapter_name"), number = i + 1f, volume = 0, url = chapterUrl, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NewTruyen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NewTruyen.kt index 10493a08..81ba9de7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NewTruyen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NewTruyen.kt @@ -62,7 +62,7 @@ internal class NewTruyen(context: MangaLoaderContext) : val dateText = li.selectFirst("div.col-xs-4.text-center.small")?.text() // Broken, will fix it later MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenVN.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenVN.kt index 3b6b069c..89bf2e70 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenVN.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenVN.kt @@ -28,7 +28,7 @@ internal class NhatTruyenVN(context: MangaLoaderContext) : val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = a.text(), + title = a.text(), number = chapterNumber, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/TopTruyen.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/TopTruyen.kt index 57939328..22f0539a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/TopTruyen.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/TopTruyen.kt @@ -168,7 +168,7 @@ internal class TopTruyen(context: MangaLoaderContext) : val timeText = timeElement?.text() MangaChapter( id = generateUid(href), - name = name, + title = name, number = number, url = href, uploadDate = parseChapterDate(timeText), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt index 9905f12c..5a93916b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt @@ -295,7 +295,7 @@ internal abstract class ZeistMangaParser( MangaChapter( id = generateUid(href), url = href, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikGes.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikGes.kt index bf88bbc6..d73f9a53 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikGes.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikGes.kt @@ -45,7 +45,7 @@ internal class KomikGes(context: MangaLoaderContext) : MangaChapter( id = generateUid(href), url = href, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt index 2b443d00..305cad02 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt @@ -33,7 +33,7 @@ internal class ToonCubus(context: MangaLoaderContext) : MangaChapter( id = generateUid(url), url = url, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt index 65d39166..c49098e1 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt @@ -36,7 +36,7 @@ internal class AnimeXNovel(context: MangaLoaderContext) : MangaChapter( id = generateUid(url), url = url, - name = name, + title = name, number = i + 1f, volume = 0, branch = null, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt index e8130108..d3cb9036 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zh/Baozimh.kt @@ -16,240 +16,240 @@ import java.util.* @MangaSourceParser("BAOZIMH", "Baozimh", "zh") internal class Baozimh(context: MangaLoaderContext) : - LegacyPagedMangaParser(context, MangaParserSource.BAOZIMH, pageSize = 36) { - - override val configKeyDomain = ConfigKey.Domain("www.baozimh.com") - - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - - override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) - - override val filterCapabilities: MangaListFilterCapabilities - get() = MangaListFilterCapabilities( - isSearchSupported = true, - ) - - override suspend fun getFilterOptions() = MangaListFilterOptions( - availableTags = tagsMap.get().values.toSet(), - availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), - availableContentTypes = EnumSet.of( - ContentType.MANGA, - ContentType.MANHWA, - ContentType.MANHUA, - ContentType.COMICS, - ), - ) - - private val tagsMap = suspendLazy(initializer = ::parseTags) - - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { - when { - !filter.query.isNullOrEmpty() -> { - if (page > 1) return emptyList() - val url = buildString { - append("https://") - append(domain) - append("/search?q=") - append(filter.query.urlEncoded()) - } - return parseMangaListSearch(webClient.httpGet(url).parseHtml()) - } - - else -> { - val url = buildString { - append("https://") - append(domain) - append("/api/bzmhq/amp_comic_list?filter=*®ion=") - - if (filter.types.isNotEmpty()) { - filter.types.oneOrThrowIfMany().let { - append( - when (it) { - ContentType.MANGA -> "jp" - ContentType.MANHWA -> "kr" - ContentType.MANHUA -> "cn" - ContentType.COMICS -> "en" - else -> "all" - }, - ) - } - } else append("all") - - - append("&type=") - if (filter.tags.isNotEmpty()) { - filter.tags.oneOrThrowIfMany()?.let { - append(it.key) - } - } else append("all") - - append("&state=") - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append( - when (it) { - MangaState.ONGOING -> "serial" - MangaState.FINISHED -> "pub" - else -> "all" - }, - ) - } - } else append("all") - - append("&limit=36&page=") - append(page.toString()) - } - - return parseMangaList(webClient.httpGet(url).parseJson().getJSONArray("items")) - } - } - } - - private fun parseMangaList(json: JSONArray): List { - return json.mapJSON { j -> - val href = "https://$domain/comic/" + j.getString("comic_id") - val author = j.getString("author") - Manga( - id = generateUid(href), - url = href, - publicUrl = href, - coverUrl = "https://static-tw${domain.removePrefix("www")}/cover/" + j.getString("topic_img"), - title = j.getString("name"), - altTitles = emptySet(), - rating = RATING_UNKNOWN, - tags = emptySet(), - authors = setOfNotNull(author), - state = null, - source = source, - contentRating = if (isNsfwSource) ContentRating.ADULT else null, - ) - } - } - - private fun parseMangaListSearch(doc: Document): List { - return doc.select("div.comics-card").map { div -> - val href = "https://$domain" + div.selectFirstOrThrow("a").attrAsRelativeUrl("href") - Manga( - id = generateUid(href), - url = href, - publicUrl = href, - coverUrl = div.selectFirst("amp-img")?.src().orEmpty(), - title = div.selectFirst(".comics-card__title h3")?.text().orEmpty(), - altTitles = emptySet(), - rating = RATING_UNKNOWN, - tags = emptySet(), - authors = emptySet(), - state = null, - source = source, - contentRating = if (isNsfwSource) ContentRating.ADULT else null, - ) - } - } - - private suspend fun parseTags(): Map { - val tagElements = webClient.httpGet("https://$domain/classify").parseHtml() - .select("div.nav")[3].select("a.item:not(.active)") - val tagMap = ArrayMap(tagElements.size) - for (el in tagElements) { - val name = el.text() - if (name.isEmpty()) continue - tagMap[name] = MangaTag( - key = el.attr("href").substringAfter("type=").substringBefore("&"), - title = name, - source = source, - ) - } - return tagMap - } - - override suspend fun getDetails(manga: Manga): Manga { - val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val state = doc.selectFirst(".tag-list span.tag")?.text() - val tagMap = tagsMap.get() - val selectTag = doc.select(".tag-list span.tag").drop(1) - val tags = selectTag.mapNotNullToSet { tagMap[it.text()] } - var chaptersReversed = false - val chapters = try { - doc.requireElementById("chapter-items") - .select("div.comics-chapters a") + doc.requireElementById("chapters_other_list") - .select("div.comics-chapters a") - } catch (e: ParseException) { - chaptersReversed = true - // If the above fails it means the manga is new, so we select the chapters using the "comics-chapters__item" query - doc.select(".comics-chapters__item") - } - return manga.copy( - description = doc.selectFirst(".comics-detail__desc")?.text().orEmpty(), - state = when (state) { - "連載中" -> MangaState.ONGOING - "已完結" -> MangaState.FINISHED - else -> null - }, - tags = tags, - chapters = chapters.mapChapters(chaptersReversed) { i, a -> - val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) - MangaChapter( - id = generateUid(url), - name = a.selectFirst("span")?.text() ?: "Chapter ${i + 1f}", - number = i + 1f, - volume = 0, - url = url, - scanlator = null, - uploadDate = 0, - branch = null, - source = source, - ) - }, - ) - } - - override suspend fun getPages(chapter: MangaChapter): List { - val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() - val pagesList = doc.requireElementById("__nuxt") - var chapterLink = doc.select("link[rel=canonical]").attr("href") - var nextChapterLink = doc.select("a#next-chapter").attr("href") - var part = 2 - val idSet = HashSet() - var pages = pagesList.select("button.pure-button").map { btn -> - val urlPage = btn.attr("on").substringAfter(": '").substringBefore("?t=") - val id = generateUid(urlPage) - idSet.add(id) - MangaPage( - id = id, - url = urlPage, - preview = null, - source = source, - ) - } - - var chapterPart = chapterLink.substringAfterLast("/").substringBefore(".html") - var nexChapterPart = nextChapterLink.substringAfterLast("/").substringBefore(".html") - while (nextChapterLink != "" && (nexChapterPart == chapterPart + "_" + part.toString())) { - val doc2 = webClient.httpGet(nextChapterLink).parseHtml() - val pages2 = doc2.requireElementById("__nuxt").select("button.pure-button").mapNotNull { btn -> - val urlPage = btn.attr("on").substringAfter(": '").substringBefore("?t=") - val id = generateUid(urlPage) - if (!idSet.add(id)) { - null - } else { - MangaPage( - id = id, - url = urlPage, - preview = null, - source = source, - ) - } - } - pages = pages + pages2 - part++ - chapterLink = doc2.select("link[rel=canonical]").attr("href") - nextChapterLink = doc2.select("a#next-chapter").attr("href") - chapterPart = chapterLink.substringAfterLast("/").substringBefore(".html").substringBeforeLast("_") - nexChapterPart = nextChapterLink.substringAfterLast("/").substringBefore(".html") - } - return pages - } + LegacyPagedMangaParser(context, MangaParserSource.BAOZIMH, pageSize = 36) { + + override val configKeyDomain = ConfigKey.Domain("www.baozimh.com") + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + + override val availableSortOrders: Set = EnumSet.of(SortOrder.POPULARITY) + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( + isSearchSupported = true, + ) + + override suspend fun getFilterOptions() = MangaListFilterOptions( + availableTags = tagsMap.get().values.toSet(), + availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), + availableContentTypes = EnumSet.of( + ContentType.MANGA, + ContentType.MANHWA, + ContentType.MANHUA, + ContentType.COMICS, + ), + ) + + private val tagsMap = suspendLazy(initializer = ::parseTags) + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + when { + !filter.query.isNullOrEmpty() -> { + if (page > 1) return emptyList() + val url = buildString { + append("https://") + append(domain) + append("/search?q=") + append(filter.query.urlEncoded()) + } + return parseMangaListSearch(webClient.httpGet(url).parseHtml()) + } + + else -> { + val url = buildString { + append("https://") + append(domain) + append("/api/bzmhq/amp_comic_list?filter=*®ion=") + + if (filter.types.isNotEmpty()) { + filter.types.oneOrThrowIfMany().let { + append( + when (it) { + ContentType.MANGA -> "jp" + ContentType.MANHWA -> "kr" + ContentType.MANHUA -> "cn" + ContentType.COMICS -> "en" + else -> "all" + }, + ) + } + } else append("all") + + + append("&type=") + if (filter.tags.isNotEmpty()) { + filter.tags.oneOrThrowIfMany()?.let { + append(it.key) + } + } else append("all") + + append("&state=") + if (filter.states.isNotEmpty()) { + filter.states.oneOrThrowIfMany()?.let { + append( + when (it) { + MangaState.ONGOING -> "serial" + MangaState.FINISHED -> "pub" + else -> "all" + }, + ) + } + } else append("all") + + append("&limit=36&page=") + append(page.toString()) + } + + return parseMangaList(webClient.httpGet(url).parseJson().getJSONArray("items")) + } + } + } + + private fun parseMangaList(json: JSONArray): List { + return json.mapJSON { j -> + val href = "https://$domain/comic/" + j.getString("comic_id") + val author = j.getString("author") + Manga( + id = generateUid(href), + url = href, + publicUrl = href, + coverUrl = "https://static-tw${domain.removePrefix("www")}/cover/" + j.getString("topic_img"), + title = j.getString("name"), + altTitles = emptySet(), + rating = RATING_UNKNOWN, + tags = emptySet(), + authors = setOfNotNull(author), + state = null, + source = source, + contentRating = if (isNsfwSource) ContentRating.ADULT else null, + ) + } + } + + private fun parseMangaListSearch(doc: Document): List { + return doc.select("div.comics-card").map { div -> + val href = "https://$domain" + div.selectFirstOrThrow("a").attrAsRelativeUrl("href") + Manga( + id = generateUid(href), + url = href, + publicUrl = href, + coverUrl = div.selectFirst("amp-img")?.src().orEmpty(), + title = div.selectFirst(".comics-card__title h3")?.text().orEmpty(), + altTitles = emptySet(), + rating = RATING_UNKNOWN, + tags = emptySet(), + authors = emptySet(), + state = null, + source = source, + contentRating = if (isNsfwSource) ContentRating.ADULT else null, + ) + } + } + + private suspend fun parseTags(): Map { + val tagElements = webClient.httpGet("https://$domain/classify").parseHtml() + .select("div.nav")[3].select("a.item:not(.active)") + val tagMap = ArrayMap(tagElements.size) + for (el in tagElements) { + val name = el.text() + if (name.isEmpty()) continue + tagMap[name] = MangaTag( + key = el.attr("href").substringAfter("type=").substringBefore("&"), + title = name, + source = source, + ) + } + return tagMap + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val state = doc.selectFirst(".tag-list span.tag")?.text() + val tagMap = tagsMap.get() + val selectTag = doc.select(".tag-list span.tag").drop(1) + val tags = selectTag.mapNotNullToSet { tagMap[it.text()] } + var chaptersReversed = false + val chapters = try { + doc.requireElementById("chapter-items") + .select("div.comics-chapters a") + doc.requireElementById("chapters_other_list") + .select("div.comics-chapters a") + } catch (e: ParseException) { + chaptersReversed = true + // If the above fails it means the manga is new, so we select the chapters using the "comics-chapters__item" query + doc.select(".comics-chapters__item") + } + return manga.copy( + description = doc.selectFirst(".comics-detail__desc")?.text().orEmpty(), + state = when (state) { + "連載中" -> MangaState.ONGOING + "已完結" -> MangaState.FINISHED + else -> null + }, + tags = tags, + chapters = chapters.mapChapters(chaptersReversed) { i, a -> + val url = a.attrAsRelativeUrl("href").toAbsoluteUrl(domain) + MangaChapter( + id = generateUid(url), + title = a.selectFirst("span")?.textOrNull(), + number = i + 1f, + volume = 0, + url = url, + scanlator = null, + uploadDate = 0, + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() + val pagesList = doc.requireElementById("__nuxt") + var chapterLink = doc.select("link[rel=canonical]").attr("href") + var nextChapterLink = doc.select("a#next-chapter").attr("href") + var part = 2 + val idSet = HashSet() + var pages = pagesList.select("button.pure-button").map { btn -> + val urlPage = btn.attr("on").substringAfter(": '").substringBefore("?t=") + val id = generateUid(urlPage) + idSet.add(id) + MangaPage( + id = id, + url = urlPage, + preview = null, + source = source, + ) + } + + var chapterPart = chapterLink.substringAfterLast("/").substringBefore(".html") + var nexChapterPart = nextChapterLink.substringAfterLast("/").substringBefore(".html") + while (nextChapterLink != "" && (nexChapterPart == chapterPart + "_" + part.toString())) { + val doc2 = webClient.httpGet(nextChapterLink).parseHtml() + val pages2 = doc2.requireElementById("__nuxt").select("button.pure-button").mapNotNull { btn -> + val urlPage = btn.attr("on").substringAfter(": '").substringBefore("?t=") + val id = generateUid(urlPage) + if (!idSet.add(id)) { + null + } else { + MangaPage( + id = id, + url = urlPage, + preview = null, + source = source, + ) + } + } + pages = pages + pages2 + part++ + chapterLink = doc2.select("link[rel=canonical]").attr("href") + nextChapterLink = doc2.select("a#next-chapter").attr("href") + chapterPart = chapterLink.substringAfterLast("/").substringBefore(".html").substringBeforeLast("_") + nexChapterPart = nextChapterLink.substringAfterLast("/").substringBefore(".html") + } + return pages + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt index b52f59ff..6e392f4f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt @@ -257,7 +257,7 @@ internal abstract class ZMangaParser( val dateText = li.selectFirst(selectDate)?.text() MangaChapter( id = generateUid(href), - name = li.selectFirstOrThrow(".flexch-infoz span:not(.date)").text(), + title = li.selectFirstOrThrow(".flexch-infoz span:not(.date)").text(), number = i + 1f, volume = 0, url = href, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/id/MaidId.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/id/MaidId.kt index 856dac94..1b8a625c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/id/MaidId.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/id/MaidId.kt @@ -27,7 +27,7 @@ internal class MaidId(context: MangaLoaderContext) : .substringBefore("