From cecba7623e9c5361b6da37e31692f36003368828 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 25 Nov 2023 14:44:04 +0200 Subject: [PATCH] [ComicK] Fixes and status filter support --- .../parsers/site/all/ComickFunParser.kt | 109 ++++++++++++------ .../kotatsu/parsers/util/Collection.kt | 7 ++ 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt index 586c5734..26dcbf3e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/ComickFunParser.kt @@ -2,10 +2,11 @@ package org.koitharu.kotatsu.parsers.site.all import androidx.collection.ArraySet import androidx.collection.SparseArrayCompat +import org.json.JSONArray import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser 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.* @@ -17,11 +18,10 @@ import java.util.* * https://api.comick.fun/docs/static/index.html */ -private const val PAGE_SIZE = 20 private const val CHAPTERS_LIMIT = 99999 @MangaSourceParser("COMICK_FUN", "ComicK") -internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(context, MangaSource.COMICK_FUN) { +internal class ComickFunParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.COMICK_FUN, 20) { override val configKeyDomain = ConfigKey.Domain("comick.app") @@ -31,46 +31,56 @@ internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(contex SortOrder.RATING, ) + override val availableStates: Set = EnumSet.allOf(MangaState::class.java) + @Volatile private var cachedTags: SparseArrayCompat? = null - override suspend fun getList( - offset: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, - ): List { + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { val domain = domain - val url = buildString { - append("https://api.") - append(domain) - append("/v1.0/search?tachiyomi=true") - if (!query.isNullOrEmpty()) { - if (offset > 0) { - return emptyList() - } - append("&q=") - append(query.urlEncoded()) - } else { - append("&limit=") - append(PAGE_SIZE) - append("&page=") - append((offset / PAGE_SIZE) + 1) - if (!tags.isNullOrEmpty()) { - append("&genres=") - appendAll(tags, "&genres=", MangaTag::key) + val url = urlBuilder() + .host("api.$domain") + .addPathSegment("v1.0") + .addPathSegment("search") + .addQueryParameter("type", "comic") + .addQueryParameter("tachiyomi", "true") + .addQueryParameter("limit", pageSize.toString()) + .addQueryParameter("page", page.toString()) + when (filter) { + is MangaListFilter.Search -> { + url.addQueryParameter("q", filter.query) + } + + null -> { + url.addQueryParameter("sort", "view") + } + + is MangaListFilter.Advanced -> { + filter.tags.forEach { tag -> + url.addQueryParameter("genres", tag.key) } - append("&sort=") // view, uploaded, rating, follow, user_follow_count - append( - when (sortOrder) { + url.addQueryParameter( + "sort", + when (filter.sortOrder) { SortOrder.POPULARITY -> "view" SortOrder.RATING -> "rating" else -> "uploaded" }, ) + filter.states.forEach { + url.addQueryParameter( + "status", + when (it) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "2" + MangaState.ABANDONED -> "3" + MangaState.PAUSED -> "4" + }, + ) + } } } - val ja = webClient.httpGet(url).parseJsonArray() + val ja = webClient.httpGet(url.build()).parseJsonArray() val tagsMap = cachedTags ?: loadTags() return ja.mapJSON { jo -> val slug = jo.getString("slug") @@ -108,7 +118,7 @@ internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(contex title = comic.getString("title"), altTitle = null, // TODO isNsfw = jo.getBoolean("matureContent") || comic.getBoolean("hentai"), - description = comic.getStringOrNull("parsed") ?: comic.getString("desc"), + description = comic.getStringOrNull("parsed") ?: comic.getStringOrNull("desc"), tags = manga.tags + comic.getJSONArray("md_comic_md_genres").mapJSONToSet { val g = it.getJSONObject("md_genres") MangaTag( @@ -168,9 +178,32 @@ internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(contex url = "https://api.${domain}/comic/$hid/chapters?limit=$CHAPTERS_LIMIT", ).parseJson().getJSONArray("chapters") val dateFormat = SimpleDateFormat("yyyy-MM-dd") - val list = ja.toJSONList().reversed() + val counters = HashMap() + return ja.toJSONList().reversed().mapChapters { _, jo -> + val vol = jo.getStringOrNull("vol") + val chap = jo.getStringOrNull("chap") + val locale = Locale.forLanguageTag(jo.getString("lang")) + val group = jo.optJSONArray("group_name")?.joinToString(", ") + val branch = locale.getDisplayName(locale).toTitleCase(locale) + group + MangaChapter( + id = generateUid(jo.getLong("id")), + name = buildString { + vol?.let { append("Vol ").append(it).append(' ') } + chap?.let { append("Chap ").append(it) } + jo.getStringOrNull("title")?.let { append(": ").append(it) } + }, + number = counters.incrementAndGet(branch), + url = jo.getString("hid"), + scanlator = jo.optJSONArray("group_name")?.asIterable()?.joinToString() + ?.takeUnless { it.isBlank() }, + uploadDate = dateFormat.tryParse(jo.getString("created_at").substringBefore('T')), + branch = branch, + source = source, + ) + } - val chaptersBuilder = ChaptersListBuilder(list.size) + + /*val chaptersBuilder = ChaptersListBuilder(list.size) val branchedChapters = HashMap, MangaChapter>>() for (jo in list) { val vol = jo.getStringOrNull("vol") @@ -201,7 +234,7 @@ internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(contex branchedChapters.getOrPut(branch, ::HashMap)[volChap] = chapter } } - return chaptersBuilder.toList() + return chaptersBuilder.toList()*/ } private fun JSONObject.selectGenres(tags: SparseArrayCompat): Set { @@ -209,9 +242,13 @@ internal class ComickFunParser(context: MangaLoaderContext) : MangaParser(contex val res = ArraySet(array.length()) for (i in 0 until array.length()) { val id = array.getInt(i) - val tag = tags.get(id) ?: continue + val tag = tags[id] ?: continue res.add(tag) } return res } + + private fun JSONArray.joinToString(separator: String): String { + return (0 until length()).joinToString(separator) { i -> getString(i) } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt index 3024e4c3..45cdc948 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt @@ -73,3 +73,10 @@ inline fun Collection.associateGrouping(transform: (T) -> Pair MutableMap.incrementAndGet(key: K): Int { + var value = get(key) ?: 0 + value++ + put(key, value) + return value +}