From 0208f5be97e52c526129350dd81e738532cde3ea Mon Sep 17 00:00:00 2001 From: devi Date: Sat, 16 Dec 2023 17:24:54 +0100 Subject: [PATCH] Add DatgarScanlation, HyoManga, HeckScans, MerlinScans, TraduccionesMoonlight, NinjaScan, Mi2MangaEs, ScanVfOrg Url InariManga Fix chapters MangaGeko --- .../kotatsu/parsers/site/en/MangaGeko.kt | 51 +++--- .../kotatsu/parsers/site/fr/ScanVfOrg.kt | 157 ++++++++++++++++++ .../parsers/site/madara/es/Mi2MangaEs.kt | 10 ++ .../parsers/site/madara/pt/NinjaScan.kt | 12 ++ .../parsers/site/mangareader/es/InariManga.kt | 5 +- .../mangareader/es/TraduccionesMoonlight.kt | 14 ++ .../site/mangareader/tr/MerlinScans.kt | 10 ++ .../site/zeistmanga/ZeistMangaParser.kt | 3 + .../site/zeistmanga/es/DatgarScanlation.kt | 10 ++ .../parsers/site/zeistmanga/id/HyoManga.kt | 12 ++ .../parsers/site/zeistmanga/pt/HeckScans.kt | 10 ++ 11 files changed, 269 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScanVfOrg.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mi2MangaEs.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TraduccionesMoonlight.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MerlinScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/es/DatgarScanlation.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/HyoManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/HeckScans.kt 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 3f333a3a..37248d35 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 @@ -1,5 +1,7 @@ package org.koitharu.kotatsu.parsers.site.en +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import okhttp3.Headers import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -98,10 +100,10 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context } } - override suspend fun getDetails(manga: Manga): Manga { + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val dateFormat = SimpleDateFormat("MMM dd, yyyy", sourceLocale) - return manga.copy( + val chaptersDeferred = async { loadChapters(manga.url) } + manga.copy( altTitle = doc.selectFirstOrThrow(".alternative-title").text(), state = when (doc.selectFirstOrThrow(".header-stats span:contains(Status) strong").text()) { "Ongoing" -> MangaState.ONGOING @@ -117,27 +119,34 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context }, author = doc.selectFirstOrThrow(".author").text(), description = doc.selectFirstOrThrow(".description").html(), - chapters = doc.requireElementById("chapters").select("ul.chapter-list li") - .mapChapters(reversed = true) { i, li -> - val a = li.selectFirstOrThrow("a") - val url = a.attrAsRelativeUrl("href") - val name = li.selectFirstOrThrow(".chapter-title").text() - val dateText = li.select(".chapter-update").attr("datetime").substringBeforeLast(',') - .replace(".", "").replace("Sept", "Sep") - MangaChapter( - id = generateUid(url), - name = name, - number = i + 1, - url = url, - scanlator = null, - uploadDate = dateFormat.tryParse(dateText), - branch = null, - source = source, - ) - }, + chapters = chaptersDeferred.await(), ) } + private suspend fun loadChapters(mangaUrl: String): List { + val urlChapter = mangaUrl + "all-chapters/" + val doc = webClient.httpGet(urlChapter.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("MMM dd, yyyy", sourceLocale) + return doc.requireElementById("chapters").select("ul.chapter-list li") + .mapChapters(reversed = true) { i, li -> + val a = li.selectFirstOrThrow("a") + val url = a.attrAsRelativeUrl("href") + val name = li.selectFirstOrThrow(".chapter-title").text() + val dateText = li.select(".chapter-update").attr("datetime").substringBeforeLast(',') + .replace(".", "").replace("Sept", "Sep") + MangaChapter( + id = generateUid(url), + name = name, + number = i + 1, + url = url, + scanlator = null, + uploadDate = dateFormat.tryParse(dateText), + branch = null, + source = source, + ) + } + } + override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScanVfOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScanVfOrg.kt new file mode 100644 index 00000000..026e537c --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScanVfOrg.kt @@ -0,0 +1,157 @@ +package org.koitharu.kotatsu.parsers.site.fr + +import androidx.collection.ArrayMap +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.koitharu.kotatsu.parsers.ErrorMessages +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("SCANVFORG", "ScanVfOrg", "fr") +internal class ScanVfOrg(context: MangaLoaderContext) : + PagedMangaParser(context, MangaSource.SCANVFORG, 0) { + + override val availableSortOrders: Set = + EnumSet.of(SortOrder.ALPHABETICAL, SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.RATING) + + override val configKeyDomain = ConfigKey.Domain("scanvf.org") + + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + + val url = buildString { + append("https://") + append(domain) + when (filter) { + is MangaListFilter.Search -> { + throw IllegalArgumentException(ErrorMessages.SEARCH_NOT_SUPPORTED) // TODO + } + + is MangaListFilter.Advanced -> { + + append("/manga") + append("?q=") + append( + when (filter.sortOrder) { + SortOrder.UPDATED -> "u" + SortOrder.ALPHABETICAL -> "a" + SortOrder.POPULARITY -> "p" + SortOrder.RATING -> "r" + else -> "u" + }, + ) + + filter.tags.forEach { + append("&search[tags][]=") + append(it.key) + } + + append("&page=") + append(page.toString()) + } + + null -> { + append("/manga?page=") + append(page.toString()) + } + } + } + + val doc = webClient.httpGet(url).parseHtml() + return doc.select(".series-paginated .series").map { div -> + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") + Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(div.host ?: domain), + coverUrl = div.selectFirst("img")?.src().orEmpty(), + title = div.selectFirstOrThrow(".link-series h3").text().orEmpty(), + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + author = null, + state = null, + source = source, + isNsfw = isNsfwSource, + ) + } + } + + private var tagCache: ArrayMap? = null + private val mutex = Mutex() + + override suspend fun getAvailableTags(): Set { + return getOrCreateTagMap().values.toSet() + } + + private suspend fun getOrCreateTagMap(): Map = mutex.withLock { + tagCache?.let { return@withLock it } + val tagMap = ArrayMap() + val tagElements = webClient.httpGet("https://$domain/manga").parseHtml() + .requireElementById("filter-wrapper").select(".form-filters div.form-check") + for (el in tagElements) { + val name = el.selectFirstOrThrow("label").text() + if (name.isEmpty()) continue + tagMap[name] = MangaTag( + key = el.selectFirstOrThrow("input").attr("value"), + title = name, + source = source, + ) + } + tagCache = tagMap + return@withLock tagMap + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val dateFormat = SimpleDateFormat("MM-dd-yyyy", sourceLocale) + val tagMap = getOrCreateTagMap() + val selectTag = doc.select(".card-series-detail .col-6:contains(Categories) div") + val tags = selectTag.mapNotNullToSet { tagMap[it.text()] } + return manga.copy( + rating = doc.selectFirst(".card-series-detail .rate-value span")?.ownText()?.toFloatOrNull()?.div(5f) + ?: RATING_UNKNOWN, + tags = tags, + author = doc.selectFirst(".card-series-detail .col-6:contains(Auteur) div")?.text(), + altTitle = doc.selectFirst(".card div.col-12.mb-4 h2")?.text().orEmpty(), + description = doc.selectFirst(".card div.col-12.mb-4 p")?.html().orEmpty(), + chapters = doc.select(".list-books .col-chapter").mapChapters(reversed = true) { i, div -> + val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") + MangaChapter( + id = generateUid(href), + name = div.selectFirstOrThrow("h5").html().substringBefore(""), + number = i + 1, + url = href, + scanlator = null, + uploadDate = dateFormat.tryParse(doc.selectFirstOrThrow("h5 div").text()), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val pages = ArrayList() + var n = 0 + while (true) { + ++n + val img = webClient.httpGet("$fullUrl/$n").parseHtml().selectFirst(".book-page .img-fluid")?.src() ?: break + pages.add( + MangaPage( + id = generateUid(img), + url = img, + preview = null, + source = source, + ), + ) + } + return pages + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mi2MangaEs.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mi2MangaEs.kt new file mode 100644 index 00000000..7b852406 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/es/Mi2MangaEs.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.madara.es + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("MI2MANGAES", "Mi2MangaEs", "es") +internal class Mi2MangaEs(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.MI2MANGAES, "es.mi2manga.com", 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt new file mode 100644 index 00000000..f056d7aa --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/NinjaScan.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.parsers.site.madara.pt + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("NINJASCAN", "NinjaScan", "pt") +internal class NinjaScan(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.NINJASCAN, "ninjascan.site") { + override val datePattern: String = "dd 'de' MMMMM 'de' yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt index 56b2d329..92b554fb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/InariManga.kt @@ -4,10 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import java.util.Locale @MangaSourceParser("INARIMANGA", "InariManga", "es") internal class InariManga(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.INARIMANGA, "inarimanga.com", pageSize = 20, searchPageSize = 10) { - override val sourceLocale: Locale = Locale.ENGLISH -} + MangaReaderParser(context, MangaSource.INARIMANGA, "inarimanga.net", pageSize = 20, searchPageSize = 10) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TraduccionesMoonlight.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TraduccionesMoonlight.kt new file mode 100644 index 00000000..cb8a9c8a --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/es/TraduccionesMoonlight.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.es + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser +import java.util.Locale + +@MangaSourceParser("TRADUCCIONESMOONLIGHT", "TraduccionesMoonlight", "es", ContentType.HENTAI) +internal class TraduccionesMoonlight(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.TRADUCCIONESMOONLIGHT, "tenkaiscan.net", 20, 10) { + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MerlinScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MerlinScans.kt new file mode 100644 index 00000000..18e9e4d6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MerlinScans.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.tr + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("MERLINSCANS", "MerlinScans", "tr") +internal class MerlinScans(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MERLINSCANS, "merlinscans.com", pageSize = 25, searchPageSize = 20) 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 a991ce4f..82a65774 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 @@ -43,6 +43,7 @@ internal abstract class ZeistMangaParser( "مستمر", "devam ediyor", "güncel", + "en emisión", ) @JvmField @@ -50,6 +51,7 @@ internal abstract class ZeistMangaParser( "completed", "completo", "tamamlandı", + "finalizado", ) @JvmField @@ -59,6 +61,7 @@ internal abstract class ZeistMangaParser( "dropado", "abandonado", "cancelado", + "suspendido", ) @JvmField diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/es/DatgarScanlation.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/es/DatgarScanlation.kt new file mode 100644 index 00000000..5df9353e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/es/DatgarScanlation.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.es + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser + +@MangaSourceParser("DATGARSCANLATION", "DatgarScanlation", "es") +internal class DatgarScanlation(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.DATGARSCANLATION, "datgarscanlation.blogspot.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/HyoManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/HyoManga.kt new file mode 100644 index 00000000..d0ee010b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/HyoManga.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.id + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser + +@MangaSourceParser("HYOMANGA", "HyoManga", "id") +internal class HyoManga(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.HYOMANGA, "www.hyomanga.my.id") { + override val mangaCategory = "Manga" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/HeckScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/HeckScans.kt new file mode 100644 index 00000000..90a12d42 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/HeckScans.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.pt + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser + +@MangaSourceParser("HECKSCANS", "HeckScans", "pt") +internal class HeckScans(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.HECKSCANS, "heckscans.blogspot.com")