From 7bcc624a74511914ac0750fa0b30a9bac81b2639 Mon Sep 17 00:00:00 2001 From: devi Date: Wed, 24 Jul 2024 19:17:06 +0200 Subject: [PATCH] Add some sources Fix some sources close #929 --- .../parsers/site/en/AsuraScansParser.kt | 177 ++++++++++++++++++ .../parsers/site/madara/tr/Grimelek.kt | 2 +- .../parsers/site/mangareader/en/LuaScans.kt | 3 +- .../mangareader/en/ReaperScansUnoriginal.kt | 16 ++ .../en/{AsuraScansParser.kt => TecnoScans.kt} | 8 +- 5 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReaperScansUnoriginal.kt rename src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/{AsuraScansParser.kt => TecnoScans.kt} (50%) 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 new file mode 100644 index 00000000..4421128e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/AsuraScansParser.kt @@ -0,0 +1,177 @@ +package org.koitharu.kotatsu.parsers.site.en + +import androidx.collection.ArrayMap +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +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 org.koitharu.kotatsu.parsers.util.json.toJSONList +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("ASURASCANS", "AsuraComic", "en") +internal class AsuraScansParser(context: MangaLoaderContext) : + PagedMangaParser(context, MangaParserSource.ASURASCANS, pageSize = 30) { + + override val availableSortOrders: Set = EnumSet.of( + SortOrder.RATING, + SortOrder.UPDATED, + SortOrder.NEWEST, + SortOrder.ALPHABETICAL_DESC, + SortOrder.ALPHABETICAL, + ) + + override val availableStates: Set = EnumSet.allOf(MangaState::class.java) + + override val configKeyDomain = ConfigKey.Domain("asuracomic.net") + + override val isMultipleTagsSupported = true + + // &types=-1&order=desc + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + val url = buildString { + append("https://") + append(domain) + append("/series?page=") + append(page) + + when (filter) { + is MangaListFilter.Search -> { + append("&name=") + append(filter.query.urlEncoded()) + } + + is MangaListFilter.Advanced -> { + + if (filter.tags.isNotEmpty()) { + append("&genres=") + append(filter.tags.joinToString(separator = ",") { it.key }) + } + + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "3" + MangaState.ABANDONED -> "4" + MangaState.PAUSED -> "2" + MangaState.UPCOMING -> "6" + }, + ) + } + + append("&types=-1&order=") + when (filter.sortOrder) { + SortOrder.RATING -> append("rating") + SortOrder.UPDATED -> append("update") + SortOrder.NEWEST -> append("latest") + SortOrder.ALPHABETICAL_DESC -> append("desc") + SortOrder.ALPHABETICAL -> append("asc") + else -> append("Updated") + } + } + + null -> append("&genres=&status=-1&order=update&types=-1") + } + } + val doc = webClient.httpGet(url).parseHtml() + return doc.select("div.grid > a[href]").map { a -> + val href = "/" + a.attrAsRelativeUrl("href") + Manga( + id = generateUid(href), + url = href, + publicUrl = href.toAbsoluteUrl(domain), + coverUrl = a.selectFirst("img")?.src().orEmpty(), + title = a.selectFirst("div.block > span.block")?.text().orEmpty(), + altTitle = null, + rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN, + tags = emptySet(), + author = null, + state = when (a.selectLastOrThrow("span.status").text()) { + "Ongoing" -> MangaState.ONGOING + "Completed" -> MangaState.FINISHED + "Hiatus" -> MangaState.PAUSED + "Dropped" -> MangaState.ABANDONED + "Coming Soon" -> MangaState.UPCOMING + else -> 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 json = + webClient.httpGet("https://gg.$domain/api/series/filters").parseJson().getJSONArray("genres").toJSONList() + for (el in json) { + if (el.getString("name").isEmpty()) continue + tagMap[el.getString("name")] = MangaTag( + key = el.getInt("id").toString(), + title = el.getString("name"), + source = source, + ) + } + tagCache = tagMap + return@withLock tagMap + } + + private val regexDate = """(\d+)(st|nd|rd|th)""".toRegex() + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val tagMap = getOrCreateTagMap() + val selectTag = doc.select("div[class^=space] > div.flex > button.text-white") + val tags = selectTag.mapNotNullToSet { tagMap[it.text()] } + return manga.copy( + description = doc.selectFirst("span.font-medium.text-sm")?.text().orEmpty(), + tags = tags, + author = doc.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Author)) > h3:eq(1)")?.text(), + chapters = doc.select("div.scrollbar-thumb-themecolor > a.block").mapChapters(reversed = true) { i, a -> + val urlRelative = "/series/" + a.attrAsRelativeUrl("href") + val url = urlRelative.toAbsoluteUrl(domain) + val date = a.selectFirst("h3:eq(1)")!!.ownText() + val cleanDate = date.replace(regexDate, "$1") + MangaChapter( + id = generateUid(url), + name = a.selectFirstOrThrow("h3:eq(0)").text(), + number = i + 1f, + volume = 0, + url = url, + scanlator = null, + uploadDate = SimpleDateFormat("MMMM d yyyy", Locale.US) + .tryParse(cleanDate), + branch = null, + source = source, + ) + }, + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() + return doc.select("div > img[alt=chapter]").map { img -> + val urlPage = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(urlPage), + url = urlPage, + preview = null, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Grimelek.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Grimelek.kt index da57ec50..2d6524fa 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Grimelek.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/Grimelek.kt @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser //This source requires an account. @MangaSourceParser("GRIMELEK", "Grimelek", "tr") internal class Grimelek(context: MangaLoaderContext) : - MadaraParser(context, MangaParserSource.GRIMELEK, "grimelek.org", 20) { + MadaraParser(context, MangaParserSource.GRIMELEK, "grimelek.mom", 20) { override val datePattern = "d MMMM yyyy" override val listUrl = "seri/" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaScans.kt index 1469e16e..0e43067e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaScans.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/LuaScans.kt @@ -5,9 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -// redirect to @KAISCANS @MangaSourceParser("LUASCANS", "LuaScans", "en") internal class LuaScans(context: MangaLoaderContext) : - MangaReaderParser(context, MangaParserSource.LUASCANS, "kaiscans.org", pageSize = 20, searchPageSize = 10) { + MangaReaderParser(context, MangaParserSource.LUASCANS, "luacomic.net", pageSize = 20, searchPageSize = 10) { override val isTagsExclusionSupported = false } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReaperScansUnoriginal.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReaperScansUnoriginal.kt new file mode 100644 index 00000000..f5ccd7e9 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/ReaperScansUnoriginal.kt @@ -0,0 +1,16 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.en + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaParserSource +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser + +@MangaSourceParser("REAPERSCANSUNORIGINAL", "ReaperScansUnoriginal", "en") +internal class ReaperScansUnoriginal(context: MangaLoaderContext) : + MangaReaderParser( + context, + MangaParserSource.REAPERSCANSUNORIGINAL, + "reaper-scans.com", + pageSize = 30, + searchPageSize = 42, + ) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt similarity index 50% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt index e0d3ca4d..dfc74821 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/AsuraScansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/TecnoScans.kt @@ -5,10 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("ASURASCANS", "AsuraScans", "en") -internal class AsuraScansParser(context: MangaLoaderContext) : - MangaReaderParser(context, MangaParserSource.ASURASCANS, "asuratoon.com", pageSize = 20, searchPageSize = 10) { - override val selectPage = "#readerarea img:not(.asurascans)" - override val selectTestScript = "force html" +@MangaSourceParser("TECNOSCANS", "TecnoScans", "en") +internal class TecnoScans(context: MangaLoaderContext) : + MangaReaderParser(context, MangaParserSource.TECNOSCANS, "tecnoscans.xyz", pageSize = 20, searchPageSize = 10) { override val isTagsExclusionSupported = false }