From 7588617316b502b019cab0d1c062146f6ed9650b Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 27 Jul 2022 15:55:34 +0300 Subject: [PATCH] New source: PrismaScans --- .../parsers/site/madara/Madara6Parser.kt | 68 ++++++++++++ .../site/madara/MangasOriginesParser.kt | 101 ++++++------------ .../parsers/site/madara/PrismaScansParser.kt | 57 ++++++++++ 3 files changed, 155 insertions(+), 71 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Madara6Parser.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/PrismaScansParser.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Madara6Parser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Madara6Parser.kt new file mode 100644 index 00000000..6cef4bf7 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/Madara6Parser.kt @@ -0,0 +1,68 @@ +package org.koitharu.kotatsu.parsers.site.madara + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import org.jsoup.nodes.Element +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +internal abstract class Madara6Parser( + context: MangaLoaderContext, + source: MangaSource, + domain: String, +) : MadaraParser(context, source, domain) { + + override suspend fun getDetails(manga: Manga): Manga { + return coroutineScope { + val chapters = async { loadChapters(manga.url) } + val body = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml().body() + parseDetails(manga, body, chapters.await()) + } + } + + protected fun Element.tableValue(): Element { + for (p in parents()) { + val children = p.children() + if (children.size == 2) { + return children[1] + } + } + parseFailed("Cannot find tableValue for node ${text()}") + } + + protected abstract fun String.asMangaState(): MangaState? + + protected fun Element.asMangaTag() = MangaTag( + title = ownText(), + key = attr("href").removeSuffix('/').substringAfterLast('/') + .replace('-', '+'), + source = source, + ) + + protected open suspend fun loadChapters(mangaUrl: String): List { + val url = mangaUrl.toAbsoluteUrl(getDomain()).removeSuffix('/') + "/ajax/chapters/" + val dateFormat = SimpleDateFormat("dd MMMM yyyy", sourceLocale ?: Locale.ROOT) + val doc = context.httpPost(url, emptyMap()).parseHtml() + return doc.select("li.wp-manga-chapter").asReversed().mapChapters { i, li -> + val a = li.selectFirstOrThrow("a") + val href = a.attrAsRelativeUrl("href") + MangaChapter( + id = generateUid(href), + url = href, + name = a.text(), + number = i + 1, + branch = null, + uploadDate = dateFormat.tryParse( + li.selectFirst(".chapter-release-date")?.text()?.trim(), + ), + scanlator = null, + source = source, + ) + } + } + + protected abstract fun parseDetails(manga: Manga, body: Element, chapters: List): Manga +} \ No newline at end of file diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangasOriginesParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangasOriginesParser.kt index ec6f0751..8b9b567d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangasOriginesParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/MangasOriginesParser.kt @@ -1,18 +1,20 @@ package org.koitharu.kotatsu.parsers.site.madara -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import org.jsoup.nodes.Element import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser -import org.koitharu.kotatsu.parsers.model.* -import org.koitharu.kotatsu.parsers.util.* -import java.text.SimpleDateFormat +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaState +import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull +import org.koitharu.kotatsu.parsers.util.mapToSet +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow import java.util.* @MangaSourceParser("MANGAS_ORIGINES", "Mangas Origines", "fr") internal class MangasOriginesParser(context: MangaLoaderContext) : - MadaraParser(context, MangaSource.MANGAS_ORIGINES, "mangas-origines.fr") { + Madara6Parser(context, MangaSource.MANGAS_ORIGINES, "mangas-origines.fr") { override val tagPrefix = "catalogues-genre/" @@ -20,44 +22,30 @@ internal class MangasOriginesParser(context: MangaLoaderContext) : return "https://${getDomain()}/wp-content/uploads/2020/11/Mangas-150x150.png" } - override suspend fun getDetails(manga: Manga): Manga { - return coroutineScope { - val chapters = async { loadChapters(manga.url) } - val body = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml().body() - val root = body.selectFirstOrThrow(".site-content") - val postContent = root.selectFirstOrThrow(".post-content") - val tags = postContent.getElementsContainingOwnText("Genre") - .firstOrNull()?.tableValue() - ?.getElementsByAttributeValueContaining("href", tagPrefix) - ?.mapToSet { a -> a.asMangaTag() } ?: manga.tags - manga.copy( - largeCoverUrl = root.selectFirst("picture") - ?.selectFirst("img[data-src]") - ?.attrAsAbsoluteUrlOrNull("data-src"), - description = (root.selectFirst(".detail-content") - ?: root.selectFirstOrThrow(".manga-excerpt")).html(), - author = postContent.getElementsContainingOwnText("Auteur") - .firstOrNull()?.tableValue()?.text()?.trim(), - state = postContent.getElementsContainingOwnText("STATUS") - .firstOrNull()?.tableValue()?.text()?.asMangaState(), - tags = tags, - isNsfw = body.hasClass("adult-content"), - chapters = chapters.await(), - ) - } + override fun parseDetails(manga: Manga, body: Element, chapters: List): Manga { + val root = body.selectFirstOrThrow(".site-content") + val postContent = root.selectFirstOrThrow(".post-content") + val tags = postContent.getElementsContainingOwnText("Genre") + .firstOrNull()?.tableValue() + ?.getElementsByAttributeValueContaining("href", tagPrefix) + ?.mapToSet { a -> a.asMangaTag() } ?: manga.tags + return manga.copy( + largeCoverUrl = root.selectFirst("picture") + ?.selectFirst("img[data-src]") + ?.attrAsAbsoluteUrlOrNull("data-src"), + description = (root.selectFirst(".detail-content") + ?: root.selectFirstOrThrow(".manga-excerpt")).html(), + author = postContent.getElementsContainingOwnText("Auteur") + .firstOrNull()?.tableValue()?.text()?.trim(), + state = postContent.getElementsContainingOwnText("STATUS") + .firstOrNull()?.tableValue()?.text()?.asMangaState(), + tags = tags, + isNsfw = body.hasClass("adult-content"), + chapters = chapters, + ) } - private fun Element.tableValue(): Element { - for (p in parents()) { - val children = p.children() - if (children.size == 2) { - return children[1] - } - } - parseFailed("Cannot find tableValue for node ${text()}") - } - - private fun String.asMangaState() = when (trim().lowercase(Locale.FRANCE)) { + override fun String.asMangaState() = when (trim().lowercase(Locale.FRANCE)) { "en cours" -> MangaState.ONGOING "abandonné", "terminé", @@ -65,33 +53,4 @@ internal class MangasOriginesParser(context: MangaLoaderContext) : else -> null } - - private fun Element.asMangaTag() = MangaTag( - title = ownText(), - key = attr("href").removeSuffix('/').substringAfterLast('/') - .replace('-', '+'), - source = source, - ) - - private suspend fun loadChapters(mangaUrl: String): List { - val url = mangaUrl.toAbsoluteUrl(getDomain()).removeSuffix('/') + "/ajax/chapters/" - val dateFormat = SimpleDateFormat("dd MMMM yyyy", Locale.FRANCE) - val doc = context.httpPost(url, emptyMap()).parseHtml() - return doc.select("li.wp-manga-chapter").asReversed().mapChapters { i, li -> - val a = li.selectFirstOrThrow("a") - val href = a.attrAsRelativeUrl("href") - MangaChapter( - id = generateUid(href), - url = href, - name = a.text(), - number = i + 1, - branch = null, - uploadDate = dateFormat.tryParse( - li.selectFirst(".chapter-release-date")?.text()?.trim(), - ), - scanlator = null, - source = source, - ) - } - } } \ No newline at end of file diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/PrismaScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/PrismaScansParser.kt new file mode 100644 index 00000000..da7dff3a --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/PrismaScansParser.kt @@ -0,0 +1,57 @@ +package org.koitharu.kotatsu.parsers.site.madara + +import org.jsoup.nodes.Element +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaState +import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull +import org.koitharu.kotatsu.parsers.util.mapToSet +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow +import java.util.* + +@MangaSourceParser("PRISMA_SCANS", "Prisma Scans", "pt") +internal class PrismaScansParser(context: MangaLoaderContext) : + Madara6Parser(context, MangaSource.PRISMA_SCANS, "prismascans.net") { + + override val tagPrefix = "manga-genre/" + + override fun getFaviconUrl(): String { + return "https://${getDomain()}/wp-content/uploads/2022/07/cropped-branca-1-192x192.png" + } + + override fun parseDetails(manga: Manga, body: Element, chapters: List): Manga { + val root = body.selectFirstOrThrow(".site-content") + val postContent = root.selectFirstOrThrow(".post-content") + val tags = postContent.getElementsContainingOwnText("Gênero") + .firstOrNull()?.tableValue() + ?.getElementsByAttributeValueContaining("href", tagPrefix) + ?.mapToSet { a -> a.asMangaTag() } ?: manga.tags + return manga.copy( + largeCoverUrl = root.selectFirst("picture") + ?.selectFirst("img[data-src]") + ?.attrAsAbsoluteUrlOrNull("data-src"), + description = root.selectFirstOrThrow(".description-summary").firstElementChild()?.html(), + author = postContent.getElementsContainingOwnText("Artista") + .firstOrNull()?.tableValue()?.text()?.trim(), + altTitle = postContent.getElementsContainingOwnText("Título Alternativo") + .firstOrNull()?.tableValue()?.text()?.trim(), + state = postContent.getElementsContainingOwnText("Status") + .firstOrNull()?.tableValue()?.text()?.asMangaState(), + tags = tags, + isNsfw = body.hasClass("adult-content"), + chapters = chapters, + ) + } + + override fun String.asMangaState() = when (trim().lowercase(sourceLocale ?: Locale.ROOT)) { + "em lançamento" -> MangaState.ONGOING + "completo", + "cancelado", + -> MangaState.FINISHED + + else -> null + } +} \ No newline at end of file