From e03d0efe7138e5eeefc16787fe228fb40b0983ab Mon Sep 17 00:00:00 2001 From: devi Date: Fri, 5 Jan 2024 19:24:36 +0100 Subject: [PATCH] Fix FlixScansOrg and add filter --- .../kotatsu/parsers/site/en/FlixScansOrg.kt | 169 ++++++++++++------ 1 file changed, 110 insertions(+), 59 deletions(-) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt index 40da667a..85ec3a8e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/FlixScansOrg.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.parsers.site.en +import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +import org.json.JSONArray import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -9,7 +11,7 @@ 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.mapJSON -import java.text.DateFormat +import org.koitharu.kotatsu.parsers.util.json.toJSONList import java.text.SimpleDateFormat import java.util.* @@ -18,6 +20,7 @@ internal class FlixScansOrg(context: MangaLoaderContext) : PagedMangaParser(cont override val availableSortOrders: Set = EnumSet.of(SortOrder.UPDATED) override val availableStates: Set = EnumSet.allOf(MangaState::class.java) + override val availableContentRating: Set = EnumSet.of(ContentRating.ADULT) override val configKeyDomain = ConfigKey.Domain("flixscans.org") override val isSearchSupported = false @@ -29,17 +32,45 @@ internal class FlixScansOrg(context: MangaLoaderContext) : PagedMangaParser(cont } is MangaListFilter.Advanced -> { + val url = buildString { append("https://api.") append(domain) - append("/api/v1/webtoon/homepage/latest/home?page=") + append("/api/v1/search/advance?=&serie_type=webtoon&page=") append(page.toString()) + + append("&genres=") + append(filter.tags.joinToString(separator = ",") { it.key }) + + filter.states.oneOrThrowIfMany()?.let { + append("&status=") + append( + when (it) { + MangaState.ONGOING -> "ongoing" + MangaState.FINISHED -> "completed" + MangaState.ABANDONED -> "droped" + MangaState.PAUSED -> "onhold" + MangaState.UPCOMING -> "soon" + }, + ) + } + + filter.contentRating.oneOrThrowIfMany()?.let { + append("&adult=") + append( + when (it) { + ContentRating.ADULT -> "true" + else -> "" + }, + ) + } + append("&serie_type=webtoon") } webClient.httpGet(url).parseJson().getJSONArray("data") } null -> { - val url = "https://api.$domain/api/v1/webtoon/homepage/latest/home?page=$page" + val url = "https://api.$domain/api/v1/search/advance?=&serie_type=webtoon&page=$page" webClient.httpGet(url).parseJson().getJSONArray("data") } } @@ -69,80 +100,100 @@ internal class FlixScansOrg(context: MangaLoaderContext) : PagedMangaParser(cont } } - override suspend fun getAvailableTags(): Set = emptySet() + override suspend fun getAvailableTags(): Set { + val doc = webClient.httpGet("https://$domain/search/advance").parseHtml() + val json = JSONArray(doc.requireElementById("__NUXT_DATA__").data()) + val tagsList = json.getJSONArray(3).toString().replace("[", "").replace("]", "").split(",") + return tagsList.mapNotNullToSet { idTag -> + val id = idTag.toInt() + val idKey = json.getJSONObject(id).getInt("id") + val key = json.getInt(idKey).toString() + val idName = json.getJSONObject(id).getInt("name") + val name = json.getString(idName) + MangaTag( + key = key, + title = name, + source = source, + ) + } + } override suspend fun getDetails(manga: Manga): Manga = coroutineScope { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val dateFormat = SimpleDateFormat("dd/MM/yyyy", sourceLocale) + val chaptersDeferred = async { loadChapters(manga.url) } + val json = JSONArray(doc.requireElementById("__NUXT_DATA__").data()) + val descId = json.getJSONObject(6).getInt("story") + val desc = json.getString(descId) + val tagsId = json.getJSONObject(6).getInt("genres") + val tagsList = json.getJSONArray(tagsId).toString().replace("[", "").replace("]", "").split(",") + val ratingId = json.getJSONObject(6).getInt("rating") + val rating = json.getString(ratingId) + val nsfwId = json.getJSONObject(6).getInt("nsfw") + val nsfw = json.getBoolean(nsfwId) manga.copy( - description = doc.selectFirst("div.text-base")?.text(), - author = doc.selectFirst("div.gap-1:contains(Authors) span.MuiChip-label")?.text(), - altTitle = doc.select("div.gap-1:contains(Other names) span.MuiChip-label") - .joinToString(" / ") { it.text() }, - chapters = doc.select("div.nox-scrollbar a").mapChapters(reversed = true) { i, a -> - val url = a.attrAsRelativeUrl("href") - val name = a.selectFirstOrThrow("div.font-medium").text() - val dateText = a.selectLastOrThrow("div").text() - MangaChapter( - id = generateUid(url), - url = url, - name = name, - number = i + 1, - branch = null, - uploadDate = parseChapterDate( - dateFormat, - dateText, - ), - scanlator = null, + description = desc, + tags = tagsList.mapToSet { idTag -> + val id = idTag.toInt() + val idKey = json.getJSONObject(id).getInt("id") + val key = json.get(idKey).toString() + val idName = json.getJSONObject(id).getInt("name") + val name = json.get(idName).toString() + MangaTag( + key = key, + title = name, source = source, ) }, + rating = rating?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN, + isNsfw = nsfw, + chapters = chaptersDeferred.await(), ) } - private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { - val d = date?.lowercase() ?: return 0 - return when { - d.endsWith(" ago") -> parseRelativeDate(date) - else -> dateFormat.tryParse(date) - } - } + private val dateFormat = SimpleDateFormat("yyyy-MM-dd", sourceLocale) - private fun parseRelativeDate(date: String): Long { - val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 - val cal = Calendar.getInstance() - return when { - WordSet("second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis - WordSet("minute", "minutes", "mins", "min").anyWordIn(date) -> cal.apply { - add( - Calendar.MINUTE, - -number, - ) - }.timeInMillis - - WordSet("hour", "hours").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis - WordSet("day", "days").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis - WordSet("week", "weeks").anyWordIn(date) -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis - WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis - WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis - else -> 0 + private suspend fun loadChapters(baseUrl: String): List { + val key = baseUrl.substringAfter("-").substringBefore("-") + val seriesKey = baseUrl.substringAfterLast("/").substringBefore("-") + val json = JSONArray( + webClient.httpGet("https://api.$domain/api/v1/webtoon/chapters/$key-desc").parseRaw(), + ).toJSONList().reversed() + return json.mapChapters { i, j -> + val url = "https://$domain/read/webtoon/$seriesKey-${j.getString("id")}-${j.getString("slug")}" + val date = j.getString("createdAt").substringBeforeLast("T") + MangaChapter( + id = generateUid(url), + url = url, + name = j.getString("slug").replace('-', ' '), + number = i + 1, + branch = null, + uploadDate = dateFormat.tryParse(date), + scanlator = null, + source = source, + ) } } - override suspend fun getPages(chapter: MangaChapter): List { val fullUrl = chapter.url.toAbsoluteUrl(domain) val doc = webClient.httpGet(fullUrl).parseHtml() - val urls = doc.selectFirstOrThrow("script:containsData(chapterData)").data().replace("\\", "") - .substringAfterLast("\"webtoon\":[\"").substringBeforeLast("\"]").split("\",\"") - return urls.map { url -> - val urlImg = "https://media.$domain/$url" - MangaPage( - id = generateUid(urlImg), - url = urlImg, - preview = null, - source = source, + val json = JSONArray(doc.requireElementById("__NUXT_DATA__").data()) + val chapterData = json.getJSONObject(6).getInt("chapterData") + val pageLocate = json.getJSONObject(chapterData).getInt("webtoon") + val jsonPages = json.getJSONArray(pageLocate) + val pages = ArrayList(jsonPages.length()) + for (i in 0 until jsonPages.length()) { + val id = jsonPages.getInt(i) + val url = "https://media.$domain/" + json.getString(id) + pages.add( + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ), ) } + return pages } }