From a35c2f339472d76f6f51444c9f914e326dcc99fc Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 15 Dec 2023 22:04:47 +0100 Subject: [PATCH 1/3] Add ZeistMangaParser and sources add urldecode ul VfScan --- .../site/all/NineNineNineHentaiParser.kt | 27 +- .../parsers/site/mangareader/fr/VfScan.kt | 2 +- .../parsers/site/mangareader/tr/MangaKings.kt | 10 + .../site/zeistmanga/ZeistMangaParser.kt | 331 ++++++++++++++++++ .../parsers/site/zeistmanga/ar/Hijala.kt | 10 + .../parsers/site/zeistmanga/ar/LonerTl.kt | 14 + .../parsers/site/zeistmanga/ar/MangaAiLand.kt | 30 ++ .../parsers/site/zeistmanga/ar/MangaSoul.kt | 10 + .../parsers/site/zeistmanga/ar/RocksManga.kt | 10 + .../parsers/site/zeistmanga/ar/YokaiTeam.kt | 10 + .../parsers/site/zeistmanga/id/AsupanKomik.kt | 10 + .../parsers/site/zeistmanga/id/KlManhua.kt | 28 ++ .../parsers/site/zeistmanga/id/KomikRealm.kt | 10 + .../parsers/site/zeistmanga/id/Mikoroku.kt | 29 ++ .../parsers/site/zeistmanga/id/ShiyuraSub.kt | 29 ++ .../parsers/site/zeistmanga/id/Sobatmanku.kt | 10 + .../parsers/site/zeistmanga/id/ToonCubus.kt | 44 +++ .../parsers/site/zeistmanga/pt/AnimeXNovel.kt | 48 +++ .../site/zeistmanga/pt/ElevenScanlator.kt | 14 + .../parsers/site/zeistmanga/pt/GalaxScans.kt | 15 + .../site/zeistmanga/pt/GuildaTierDraw.kt | 27 ++ .../parsers/site/zeistmanga/pt/TyrantScans.kt | 10 + .../site/zeistmanga/tr/Mikrokosmosfb.kt | 33 ++ .../site/zeistmanga/tr/SnscoeurTurkey.kt | 33 ++ .../koitharu/kotatsu/parsers/util/String.kt | 3 + 25 files changed, 784 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/Hijala.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/LonerTl.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaSoul.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/RocksManga.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/YokaiTeam.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/AsupanKomik.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikRealm.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Sobatmanku.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ToonCubus.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/ElevenScanlator.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GalaxScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TyrantScans.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt 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 0bd23373..8145040e 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 @@ -35,13 +35,14 @@ import java.util.EnumSet import java.util.Locale @MangaSourceParser("NINENINENINEHENTAI", "999Hentai", type = ContentType.HENTAI) -internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.NINENINENINEHENTAI, size), Interceptor { +internal class NineNineNineHentaiParser(context: MangaLoaderContext) : + PagedMangaParser(context, MangaSource.NINENINENINEHENTAI, size), Interceptor { override val configKeyDomain = ConfigKey.Domain("999hentai.net") override val availableSortOrders: EnumSet = EnumSet.of( SortOrder.POPULARITY, - SortOrder.NEWEST + SortOrder.NEWEST, ) override val isMultipleTagsSupported = false @@ -50,7 +51,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang Locale.ENGLISH, Locale.CHINESE, Locale.JAPANESE, - Locale("es") + Locale("es"), ) private fun Locale?.getSiteLang(): String { @@ -107,7 +108,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang MangaTag( title = name.toCamelCase(), key = name, - source = source + source = source, ) } } @@ -121,9 +122,11 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang getSearchList(page, null, filter.locale, filter.tags, filter.sortOrder) } } + is MangaListFilter.Search -> { getSearchList(page, filter.query, null, null, filter.sortOrder) } + else -> { getPopularList(page, null) } @@ -132,7 +135,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang private suspend fun getPopularList( page: Int, - locale: Locale? + locale: Locale?, ): List { val query = """ queryPopularChapters( @@ -268,7 +271,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang val tags = entry.optJSONArray("tags")?.mapJSON { SiteTag( name = it.getString("tagName"), - type = it.getStringOrNull("tagType") + type = it.getStringOrNull("tagType"), ) } return manga.copy( @@ -282,7 +285,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang MangaTag( title = it.name.toCamelCase(), key = it.name, - source = source + source = source, ) }.orEmpty(), state = null, @@ -307,15 +310,15 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang return@let locale.getDisplayLanguage(locale) }, - scanlator = when(entry.getStringOrNull("format")) { + scanlator = when (entry.getStringOrNull("format")) { "artistcg" -> "ArtistCG" "gamecg" -> "GameCG" "imageset" -> "ImageSet" else -> entry.getStringOrNull("format")?.toCamelCase() }, - source = source - ) - ) + source = source, + ), + ), ) } @@ -383,7 +386,7 @@ internal class NineNineNineHentaiParser(context: MangaLoaderContext) : PagedMang id = generateUid(img), url = cdn + img, preview = cdn + imgS, - source = source + source = source, ) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt index 491364d4..26f830a5 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/VfScan.kt @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser @MangaSourceParser("VFSCAN", "VfScan", "fr") internal class VfScan(context: MangaLoaderContext) : - MangaReaderParser(context, MangaSource.VFSCAN, "www.vfscan.com", pageSize = 18, searchPageSize = 18) + MangaReaderParser(context, MangaSource.VFSCAN, "www.vfscan.cc", pageSize = 18, searchPageSize = 18) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.kt new file mode 100644 index 00000000..9f56b20d --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/tr/MangaKings.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("MANGAKINGS", "MangaKings", "tr") +internal class MangaKings(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGAKINGS, "mangakings.com.tr", pageSize = 20, searchPageSize = 10) 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 new file mode 100644 index 00000000..a991ce4f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ZeistMangaParser.kt @@ -0,0 +1,331 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import org.json.JSONArray +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.ErrorMessages +import org.koitharu.kotatsu.parsers.MangaLoaderContext +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.getStringOrNull +import org.koitharu.kotatsu.parsers.util.json.toJSONList +import java.text.SimpleDateFormat +import java.util.* + +internal abstract class ZeistMangaParser( + context: MangaLoaderContext, + source: MangaSource, + domain: String, + pageSize: Int = 12, +) : PagedMangaParser(context, source, pageSize) { + + override val configKeyDomain = ConfigKey.Domain(domain) + + override val isMultipleTagsSupported = false + + override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) + + override val availableStates: Set = + EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED) + + protected open val datePattern = "yyyy-MM-dd" + + @JvmField + protected val ongoing: Set = hashSetOf( + "ongoing", + "en curso", + "ativo", + "lançando", + "مستمر", + "devam ediyor", + "güncel", + ) + + @JvmField + protected val finished: Set = hashSetOf( + "completed", + "completo", + "tamamlandı", + ) + + @JvmField + protected val abandoned: Set = hashSetOf( + "cancelled", + "dropped", + "dropado", + "abandonado", + "cancelado", + ) + + @JvmField + protected val paused: Set = hashSetOf( + "hiatus", + ) + + protected open val sateOngoing: String = "Ongoing" + protected open val sateFinished: String = "Completed" + protected open val sateAbandoned: String = "Cancelled" + + protected open val maxMangaResults: Int = 20 + + protected open val mangaCategory: String = "Series" + + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + val startIndex = maxMangaResults * (page - 1) + 1 + + val url = buildString { + append("https://") + append(domain) + append("/feeds/posts/default/-/") + when (filter) { + + is MangaListFilter.Search -> { + append(mangaCategory) + append("?alt=json&orderby=published&max-results=") + append((maxMangaResults + 1).toString()) + append("&start-index=") + append(startIndex.toString()) + append("&q=label:") + append(mangaCategory) + append("+") + append(filter.query.urlEncoded()) + } + + is MangaListFilter.Advanced -> { + + + if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { + throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED) + } + + if (filter.tags.isNotEmpty()) { + append(filter.tags.oneOrThrowIfMany()?.key.orEmpty()) + } else if (filter.states.isNotEmpty()) { + append( + filter.states.oneOrThrowIfMany().let { + when (it) { + MangaState.ONGOING -> sateOngoing + MangaState.FINISHED -> sateFinished + MangaState.ABANDONED -> sateAbandoned + else -> mangaCategory + } + }, + ) + } else { + append(mangaCategory) + } + + append("?alt=json&orderby=published&max-results=") + append((maxMangaResults + 1).toString()) + append("&start-index=") + append(startIndex.toString()) + } + + null -> { + append(mangaCategory) + append("?alt=json&orderby=published&max-results=") + append((maxMangaResults + 1).toString()) + append("&start-index=") + append(startIndex.toString()) + } + } + } + + val json = webClient.httpGet(url).parseJson().getJSONObject("feed") + + return if (json.toString().contains("\"entry\":")) { + parseMangaList(json.getJSONArray("entry")) + } else { + emptyList() + } + } + + protected open fun parseMangaList(json: JSONArray): List { + return json.toJSONList().map { j -> + val name = j.getJSONObject("title").getString("\$t") + val href = + j.getJSONArray("link").toJSONList().first { it.getString("rel") == "alternate" }.getString("href") + val urlImg = if (j.toString().contains("media\$thumbnail")) { + j.getJSONObject("media\$thumbnail").getStringOrNull("url") + ?.replace("""/s.+?-c/""".toRegex(), "/w600/") + ?.replace("""=s(?!.*=s).+?-c$""".toRegex(), "=w600") + ?.replace("""/s.+?-c-rw/""".toRegex(), "/w600/") + ?.replace("""=s(?!.*=s).+?-c-rw$""".toRegex(), "=w600") + } else { + Jsoup.parse(j.getJSONObject("content").getString("\$t")).selectFirstOrThrow("img").attr("src") + } + Manga( + id = generateUid(href), + url = href, + publicUrl = href, + coverUrl = urlImg.orEmpty(), + title = name, + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + author = null, + state = null, + source = source, + isNsfw = isNsfwSource, + ) + } + } + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet { + MangaTag( + key = it.selectFirstOrThrow("input").attr("value"), + title = it.selectFirstOrThrow("label").text(), + source = source, + ) + } + } + + protected open val selectTags = "article div.mt-15 a, .info-genre a" + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val state = + doc.selectFirst("div.y6x11p:contains(Status) .dt") + ?: doc.selectFirst("div.y6x11p:contains(Estado) .dt") + ?: doc.selectFirst("ul.infonime li:contains(Status) span") + ?: doc.selectFirst("ul.infonime li:contains(Estado) span") + ?: doc.selectFirst("span.status-novel") + val mangaState = state?.text()?.lowercase().let { + when (it) { + in ongoing -> MangaState.ONGOING + in finished -> MangaState.FINISHED + in abandoned -> MangaState.ABANDONED + in paused -> MangaState.PAUSED + else -> null + } + } + val desc = doc.getElementById("synopsis") ?: doc.getElementById("Sinopse") ?: doc.getElementById("sinopas") + val chaptersDeferred = async { loadChapters(manga.url, doc) } + manga.copy( + tags = doc.select(selectTags).mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").substringAfterLast("label/").substringBefore("?"), + title = a.text().toTitleCase(), + source = source, + ) + }, + description = desc?.text().orEmpty(), + state = mangaState, + chapters = chaptersDeferred.await(), + ) + } + + protected open suspend fun loadChapters(mangaUrl: String, doc: Document): List { + + val feed = if (doc.getElementById("myUL") != null) { + doc.requireElementById("myUL").selectFirstOrThrow("script").attr("src") + .substringAfterLast("/-/").substringBefore("?").urlDecode() + + } else if (doc.selectFirst("#latest > script") != null) { + val chapterRegex = """label\s*=\s*'([^']+)'""".toRegex() + val scriptSelector = "#latest > script" + val script = doc.selectFirstOrThrow(scriptSelector) + chapterRegex + .find(script.html()) + ?.groupValues?.get(1) + ?: throw Exception("Failed to find chapter feed") + + } else if (doc.selectFirst("#clwd > script") != null) { + val chapterRegex = """clwd\.run\('([^']+)'""".toRegex() + val scriptSelector = "#clwd > script" + val script = doc.selectFirstOrThrow(scriptSelector) + chapterRegex + .find(script.html()) + ?.groupValues?.get(1) + ?: throw Exception("Failed to find chapter feed") + + } else { + doc.selectFirstOrThrow("script:containsData(var label_chapter)").data() + .substringAfter("label_chapter = \"").substringBefore("\"") + } + + val url = buildString { + append("https://") + append(domain) + append("/feeds/posts/default/-/") + append(feed) + append("?alt=json&orderby=published&max-results=9999") + } + val json = + webClient.httpGet(url).parseJson().getJSONObject("feed").getJSONArray("entry").toJSONList().reversed() + val dateFormat = SimpleDateFormat(datePattern, sourceLocale) + return json.mapIndexedNotNull { i, j -> + val name = j.getJSONObject("title").getString("\$t") + val href = + j.getJSONArray("link").toJSONList().first { it.getString("rel") == "alternate" }.getString("href") + val dateText = j.getJSONObject("published").getString("\$t").substringBefore("T") + val slug = mangaUrl.substringAfterLast('/') + val slugChapter = href.substringAfterLast('/') + if (slug == slugChapter) { + return@mapIndexedNotNull null + } + MangaChapter( + id = generateUid(href), + url = href, + name = name, + number = i + 1, + branch = null, + uploadDate = dateFormat.tryParse(dateText), + scanlator = null, + source = source, + ) + } + } + + protected open val selectPage = "div.check-box img, article#reader .separator img, article.container .separator img" + + override suspend fun getPages(chapter: MangaChapter): List { + + val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() + + return if (doc.selectFirst("script:containsData(chapterImage =)") != null) { + doc.selectFirstOrThrow("script:containsData(chapterImage =)").data() + .substringAfter("[").substringBefore("]") + .replace(" ", "").replace("\"", "") + .split(",").map { url -> + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + + } else if (doc.selectFirst("script:containsData(const content = )") != null) { + doc.selectFirstOrThrow("script:containsData(const content = )").data() + .substringAfter("`").substringBefore("`;").split("src=\"").drop(1) + + .map { img -> + val url = img.substringBefore("\"") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + + } else { + doc.select(selectPage).map { img -> + val url = img.src() ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } + + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/Hijala.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/Hijala.kt new file mode 100644 index 00000000..3d68b2ae --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/Hijala.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +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("HIJALA", "Hijala", "ar") +internal class Hijala(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.HIJALA, "hijala.blogspot.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/LonerTl.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/LonerTl.kt new file mode 100644 index 00000000..c33e7657 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/LonerTl.kt @@ -0,0 +1,14 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +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("LONERTL", "LonerTranslations", "ar") +internal class LonerTl(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.LONERTL, "loner-tl.blogspot.com") { + override val sateOngoing: String = "مستمرة" + override val sateFinished: String = "مكتملة" + override val sateAbandoned: String = "متوقفة" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt new file mode 100644 index 00000000..09aee8ab --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaAiLand.kt @@ -0,0 +1,30 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById + +@MangaSourceParser("MANGAAILAND", "MangaAiLand", "ar") +internal class MangaAiLand(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.MANGAAILAND, "manga-ai-land.blogspot.com") { + override val sateOngoing: String = "مستمر" + override val sateFinished: String = "مكتملة" + override val sateAbandoned: String = "متوقفة" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").substringBefore("?").substringAfterLast('/'), + title = it.text(), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaSoul.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaSoul.kt new file mode 100644 index 00000000..6a7fda21 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/MangaSoul.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +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("MANGASOUL", "MangaSoul", "ar") +internal class MangaSoul(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.MANGASOUL, "www.manga-soul.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/RocksManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/RocksManga.kt new file mode 100644 index 00000000..08d3d771 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/RocksManga.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +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("ROCKSMANGA_COM", "RocksManga.com", "ar") +internal class RocksManga(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.ROCKSMANGA_COM, "www.rocks-manga.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/YokaiTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/YokaiTeam.kt new file mode 100644 index 00000000..df81ea01 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/ar/YokaiTeam.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.ar + +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("YOKAITEAM", "YokaiTeam", "ar") +internal class YokaiTeam(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.YOKAITEAM, "yokai-team.blogspot.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/AsupanKomik.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/AsupanKomik.kt new file mode 100644 index 00000000..7af12841 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/AsupanKomik.kt @@ -0,0 +1,10 @@ +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("ASUPANKOMIK", "AsupanKomik", "id") +internal class AsupanKomik(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.ASUPANKOMIK, "www.asupankomik.my.id") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt new file mode 100644 index 00000000..2ff408b6 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KlManhua.kt @@ -0,0 +1,28 @@ +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.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById + +@MangaSourceParser("KLMANHUA", "KlManhua", "id", ContentType.HENTAI) +internal class KlManhua(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.KLMANHUA, "klmanhua.blogspot.com") { + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").substringBefore("?").substringAfterLast('/'), + title = it.text(), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikRealm.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikRealm.kt new file mode 100644 index 00000000..a0109594 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/KomikRealm.kt @@ -0,0 +1,10 @@ +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("KOMIKREALM", "KomikRealm", "id") +internal class KomikRealm(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.KOMIKREALM, "www.komikrealm.my.id") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt new file mode 100644 index 00000000..cdf6b503 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/Mikoroku.kt @@ -0,0 +1,29 @@ +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.ContentType +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow + +@MangaSourceParser("MIKOROKU", "Mikoroku", "id", ContentType.HENTAI) +internal class Mikoroku(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.MIKOROKU, "www.mikoroku.web.id") { + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("Genre").select("div.items-center").mapNotNullToSet { + MangaTag( + key = it.selectFirstOrThrow("input").attr("value"), + title = it.selectFirstOrThrow("label").text().substringBefore('('), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt new file mode 100644 index 00000000..891a5b01 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/id/ShiyuraSub.kt @@ -0,0 +1,29 @@ +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.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml + +@MangaSourceParser("SHIYURASUB", "ShiyuraSub", "id") +internal class ShiyuraSub(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.SHIYURASUB, "shiyurasub.blogspot.com") { + + override val selectTags = ".leading-8 div.my-5.gap-2 a" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.select("div.list-label-widget-content ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").removeSuffix("/").substringAfterLast('/'), + title = it.html().substringBefore(" { + val doc = webClient.httpGet("https://$domain/p/genre-list.html").parseHtml() + return doc.select(".dzdes-genre ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").removeSuffix("/").substringAfterLast("/"), + title = it.selectFirstOrThrow("span").text(), + source = source, + ) + } + } + + override suspend fun loadChapters(mangaUrl: String, doc: Document): List { + return doc.selectFirstOrThrow("ul.series-chapterlist").select("div.flexch-infoz") + .mapChapters(reversed = true) { i, div -> + val url = div.selectFirstOrThrow("a").attr("href") + val name = div.selectFirstOrThrow("a span").text() + MangaChapter( + id = generateUid(url), + url = url, + name = name, + number = i + 1, + branch = null, + uploadDate = 0, + scanlator = null, + source = source, + ) + } + } +} 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 new file mode 100644 index 00000000..18690f48 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/AnimeXNovel.kt @@ -0,0 +1,48 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.pt + +import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.model.MangaChapter +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.* + +@MangaSourceParser("ANIMEXNOVEL", "AnimeXNovel", "pt") +internal class AnimeXNovel(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.ANIMEXNOVEL, "www.animexnovel.com") { + + override val sateOngoing: String = "Ativo" + override val sateFinished: String = "Completo" + override val sateAbandoned: String = "Dropado" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").removeSuffix("/").substringAfterLast('/'), + title = it.text(), + source = source, + ) + } + } + + override suspend fun loadChapters(mangaUrl: String, doc: Document): List { + return doc.select("div:has(> .list-judul:contains(Lista de Capítulos)) div#latest ul > li, div.tab:has(> label:contains(Lista de Capítulos)) div.tab-content ul > li") + .mapChapters(reversed = true) { i, li -> + val url = li.selectFirstOrThrow("a").attr("href") + val name = li.selectFirstOrThrow("a span").text() + MangaChapter( + id = generateUid(url), + url = url, + name = name, + number = i + 1, + branch = null, + uploadDate = 0, + scanlator = null, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/ElevenScanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/ElevenScanlator.kt new file mode 100644 index 00000000..80857a9b --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/ElevenScanlator.kt @@ -0,0 +1,14 @@ +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("ELEVENSCANLATOR", "ElevenScanlator", "pt") +internal class ElevenScanlator(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.ELEVENSCANLATOR, "elevenscanlator.blogspot.com") { + override val sateOngoing: String = "Lançando" + override val sateFinished: String = "Completo" + override val sateAbandoned: String = "Dropado" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GalaxScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GalaxScans.kt new file mode 100644 index 00000000..d4de4f42 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GalaxScans.kt @@ -0,0 +1,15 @@ +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("GALAXSCANS", "GalaxScans", "pt") +internal class GalaxScans(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.GALAXSCANS, "galaxscans.blogspot.com") { + override val mangaCategory = "Recentes" + override val sateOngoing: String = "Lançando" + override val sateFinished: String = "Completo" + override val sateAbandoned: String = "Dropado" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt new file mode 100644 index 00000000..cd849f49 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/GuildaTierDraw.kt @@ -0,0 +1,27 @@ +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.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById + +@MangaSourceParser("GUILDATIERDRAW", "GuildaTierDraw", "pt") +internal class GuildaTierDraw(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.GUILDATIERDRAW, "www.guildatierdraw.com") { + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("LinkList2").select("ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").substringBefore("?").substringAfterLast('/'), + title = it.text(), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TyrantScans.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TyrantScans.kt new file mode 100644 index 00000000..25d3e7e4 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/TyrantScans.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("TYRANTSCANS", "TyrantScans", "pt") +internal class TyrantScans(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.TYRANTSCANS, "www.tyrantscans.com") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt new file mode 100644 index 00000000..4559b98e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/Mikrokosmosfb.kt @@ -0,0 +1,33 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.tr + +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.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow + +@MangaSourceParser("MIKROKOSMOSFB", "Mikrokosmosfb", "tr", ContentType.HENTAI) +internal class Mikrokosmosfb(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.MIKROKOSMOSFB, "mikrokosmosfb.blogspot.com") { + override val sateOngoing: String = "Devam Ediyor" + override val sateFinished: String = "Tamamlandı" + override val sateAbandoned: String = "Güncel" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + val tags = doc.selectFirstOrThrow("script:containsData(label: )").data() + .substringAfter("label: [").substringBefore("]").replace("\"", "").split(", ") + return tags.mapNotNullToSet { + MangaTag( + key = it, + title = it, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt new file mode 100644 index 00000000..e369fa18 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/SnscoeurTurkey.kt @@ -0,0 +1,33 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.tr + +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.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow + +@MangaSourceParser("SNSCOEURTURKEY", "SnscoeurTurkey", "tr", ContentType.HENTAI) +internal class SnscoeurTurkey(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.SNSCOEURTURKEY, "snscoeurturkey.blogspot.com") { + override val sateOngoing: String = "Güncel" + override val sateFinished: String = "Final" + override val sateAbandoned: String = "Düzenleniyor" + + override val mangaCategory = "Seriler" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain/p/gelismis-arama.html").parseHtml() + return doc.selectFirstOrThrow("div.filter").select("ul li").mapNotNullToSet { + MangaTag( + key = it.selectFirstOrThrow("input").attr("value"), + title = it.selectFirstOrThrow("label").text(), + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt index c18c3fdb..e6efece6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt @@ -5,6 +5,7 @@ package org.koitharu.kotatsu.parsers.util import androidx.annotation.FloatRange import androidx.collection.arraySetOf import java.math.BigInteger +import java.net.URLDecoder import java.net.URLEncoder import java.security.MessageDigest import java.util.* @@ -98,6 +99,8 @@ fun String.splitTwoParts(delimiter: Char): Pair? { fun String.urlEncoded(): String = URLEncoder.encode(this, Charsets.UTF_8.name()) +fun String.urlDecode(): String = URLDecoder.decode(this, Charsets.UTF_8.name()) + fun ByteArray.byte2HexFormatted(): String { val str = StringBuilder(size * 2) for (i in indices) { From 49d0c8007003530c441393f4445b56288d14239d Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 15 Dec 2023 22:51:27 +0100 Subject: [PATCH 2/3] Add MaxgsScan, WolfScanBr --- .../kotatsu/parsers/site/zeistmanga/pt/MaxgsScan.kt | 10 ++++++++++ .../kotatsu/parsers/site/zeistmanga/pt/WolfScanBr.kt | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/MaxgsScan.kt create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/WolfScanBr.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/MaxgsScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/MaxgsScan.kt new file mode 100644 index 00000000..8fddc698 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/MaxgsScan.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("MAXGSSCAN", "MaxgsScan", "pt") +internal class MaxgsScan(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.MAXGSSCAN, "www.maxgsscan.online") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/WolfScanBr.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/WolfScanBr.kt new file mode 100644 index 00000000..6268f823 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/pt/WolfScanBr.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("WOLFSCANBR", "WolfScanBr", "pt") +internal class WolfScanBr(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.WOLFSCANBR, "wolfscanbr.blogspot.com") From 1b1a167580bb2724607bfeb2b2cacecd9c7776e6 Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 15 Dec 2023 23:06:29 +0100 Subject: [PATCH 3/3] Add EpikMan --- .../parsers/site/zeistmanga/tr/EpikMan.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt new file mode 100644 index 00000000..c48cab99 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/zeistmanga/tr/EpikMan.kt @@ -0,0 +1,30 @@ +package org.koitharu.kotatsu.parsers.site.zeistmanga.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.model.MangaTag +import org.koitharu.kotatsu.parsers.site.zeistmanga.ZeistMangaParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.mapNotNullToSet +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.requireElementById + +@MangaSourceParser("EPIKMAN", "EpikMan", "tr") +internal class EpikMan(context: MangaLoaderContext) : + ZeistMangaParser(context, MangaSource.EPIKMAN, "www.epikman.ga") { + override val sateOngoing = "Devam Ediyor" + override val sateFinished = "Tamamlandı" + override val mangaCategory = "Seri" + + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain").parseHtml() + return doc.requireElementById("LinkList1").select("ul li a").mapNotNullToSet { + MangaTag( + key = it.attr("href").substringBefore("?").substringAfterLast('/'), + title = it.text(), + source = source, + ) + } + } +}