From a58947376cbca1cbce28944db98199d787c568e0 Mon Sep 17 00:00:00 2001 From: Draken <131387159+dragonx943@users.noreply.github.com> Date: Sat, 10 May 2025 20:20:13 +0700 Subject: [PATCH] [Misskon] Add source (#1772) --- .github/summary.yaml | 2 +- .../kotatsu/parsers/site/all/Misskon.kt | 148 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/Misskon.kt diff --git a/.github/summary.yaml b/.github/summary.yaml index 48b90516..3693821f 100644 --- a/.github/summary.yaml +++ b/.github/summary.yaml @@ -1 +1 @@ -total: 1225 \ No newline at end of file +total: 1226 \ No newline at end of file diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/Misskon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/Misskon.kt new file mode 100644 index 00000000..82e33981 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/Misskon.kt @@ -0,0 +1,148 @@ +package org.koitharu.kotatsu.parsers.site.all + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +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 java.util.* + +@MangaSourceParser("MISSKON", "MissKon", type = ContentType.OTHER) +internal class Misskon(context: MangaLoaderContext) : LegacyPagedMangaParser(context, MangaParserSource.MISSKON, 24) { + + override val configKeyDomain = ConfigKey.Domain("misskon.com") + + override fun onCreateConfig(keys: MutableCollection>) { + super.onCreateConfig(keys) + keys.add(userAgentKey) + } + + override val availableSortOrders: Set = EnumSet.of( + SortOrder.UPDATED, + SortOrder.POPULARITY + ) + + override val filterCapabilities: MangaListFilterCapabilities + get() = MangaListFilterCapabilities( isSearchSupported = true ) + + override suspend fun getFilterOptions() = MangaListFilterOptions() + + override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { + val url = buildString { + append("https://") + append(domain) + when { + !filter.query.isNullOrEmpty() -> { + append("/page/$page/") + append("?s=") + append(filter.query.urlEncoded()) + } + order == SortOrder.POPULARITY -> { + append("/top3/") + } + else -> { + append("/page/$page") + } + } + } + + val doc = webClient.httpGet(url).parseHtml() + return doc.select("article.item-list").map { article -> + val titleEl = article.selectFirst(".post-box-title")!! + val href = titleEl.selectFirst("a")?.attrAsRelativeUrl("href") + ?: article.parseFailed("Cannot find manga link") + + Manga( + id = generateUid(href), + title = titleEl.text(), + altTitles = emptySet(), + url = href, + publicUrl = href.toAbsoluteUrl(domain), + rating = RATING_UNKNOWN, + contentRating = ContentRating.ADULT, + coverUrl = article.selectFirst(".post-thumbnail img")?.absUrl("data-src").orEmpty(), + tags = setOf(), + state = null, + authors = emptySet(), + source = source, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga { + val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() + val postInnerEl = doc.selectFirst("article > .post-inner")!! + + return manga.copy( + tags = postInnerEl.select(".post-tag > a").mapToSet { a -> + MangaTag( + key = a.text().lowercase(), + title = a.text(), + source = source + ) + }, + chapters = listOf( + MangaChapter( + id = manga.id, + title = "Oneshot", // 1 album, idk + number = 1f, + volume = 0, + url = manga.url, + scanlator = null, + uploadDate = 0L, + branch = null, + source = source + ) + ) + ) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml() + val basePageUrl = doc.selectFirst("link[rel=canonical]")?.absUrl("href") + ?: chapter.url.toAbsoluteUrl(domain) + + val pages = mutableListOf() + val pageLinks = doc.select("div.post-inner div.page-link:nth-child(1) .post-page-numbers") + + if (pageLinks.isEmpty()) { + // Single page gallery + return doc.select("div.post-inner > div.entry > p > img") + .mapNotNull { img -> img.absUrl("data-src") } + .mapIndexed { i, url -> + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source + ) + } + } + + // Multi-page gallery + pageLinks.forEachIndexed { index, pageEl -> + val pageDoc = when (index) { + 0 -> doc + else -> { + val url = "$basePageUrl${pageEl.text()}/" + webClient.httpGet(url).parseHtml() + } + } + + pages.addAll( + pageDoc.select("div.post-inner > div.entry > p > img") + .mapNotNull { img -> img.absUrl("data-src") } + .map { url -> + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source + ) + } + ) + } + return pages + } +} \ No newline at end of file