diff --git a/README.md b/README.md index b52db6c74..abfbf9784 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ JVM and Android applications. ## Projects that use the library - [Kotatsu](https://github.com/KotatsuApp/Kotatsu) +- [Doki](https://github.com/DokiTeam/Doki) - [kotatsu-dl](https://github.com/KotatsuApp/kotatsu-dl) - [Shirizu (WIP)](https://github.com/ztimms73/shirizu) - [OtakuWorld](https://github.com/jakepurple13/OtakuWorld) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/yurigarden/YuriGardenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/yurigarden/YuriGardenParser.kt index e61f8b04b..d2b2c9d16 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/yurigarden/YuriGardenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/yurigarden/YuriGardenParser.kt @@ -12,9 +12,7 @@ import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.json.* -import org.koitharu.kotatsu.parsers.util.CryptoAES import org.json.JSONObject -import org.json.JSONArray import java.util.* internal abstract class YuriGardenParser( @@ -29,15 +27,9 @@ internal abstract class YuriGardenParser( override val configKeyDomain = ConfigKey.Domain(domain) private val apiSuffix = "api.$domain" - override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_MOBILE) - - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } - override fun getRequestHeaders(): Headers = Headers.Builder() .add("x-app-origin", "https://$domain") + .add("User-Agent", UserAgents.KOTATSU) .build() override val availableSortOrders: Set = EnumSet.of( @@ -60,6 +52,7 @@ internal abstract class YuriGardenParser( MangaState.FINISHED, MangaState.ABANDONED, MangaState.PAUSED, + MangaState.UPCOMING, ), ) } @@ -95,11 +88,12 @@ internal abstract class YuriGardenParser( MangaState.FINISHED -> "completed" MangaState.PAUSED -> "hiatus" MangaState.ABANDONED -> "cancelled" + MangaState.UPCOMING -> "oncoming" else -> "all" }) } - append("&full=true") + append("&full=true") if (filter.tags.isNotEmpty()) { append("&genre=") @@ -107,27 +101,34 @@ internal abstract class YuriGardenParser( } } - val raw = webClient.httpGet(url).parseRaw() - val json = JSONObject(decryptIfNeeded(raw)) + val json = webClient.httpGet(url).parseJson() val data = json.getJSONArray("comics") return data.mapJSON { jo -> val id = jo.getLong("id") + val allTags = fetchTags().orEmpty() + val tags = allTags.let { allTags -> + jo.optJSONArray("genres")?.asTypedList()?.mapNotNullToSet { g -> + allTags.find { x -> x.key == g } + } + }.orEmpty() + Manga( id = generateUid(id), url = "/comics/$id", publicUrl = "https://$domain/comic/$id", title = jo.getString("title"), - altTitles = emptySet(), + altTitles = setOf(jo.getString("anotherName")), coverUrl = jo.getString("thumbnail"), largeCoverUrl = jo.getString("thumbnail"), authors = emptySet(), - tags = emptySet(), + tags = tags, state = when(jo.getString("status")) { "ongoing" -> MangaState.ONGOING "completed" -> MangaState.FINISHED "hiatus" -> MangaState.PAUSED "cancelled" -> MangaState.ABANDONED + "oncoming" -> MangaState.UPCOMING else -> null }, description = jo.getString("description"), @@ -139,75 +140,50 @@ internal abstract class YuriGardenParser( } override suspend fun getDetails(manga: Manga): Manga = coroutineScope { - val url = "https://" + apiSuffix + manga.url - val chaptersDeferred = async { - val raw = webClient.httpGet("$url/chapters").parseRaw() - JSONArray(decryptIfNeeded(raw)) - } - val raw = webClient.httpGet(url).parseRaw() - val json = JSONObject(decryptIfNeeded(raw)) + val id = manga.url.substringAfter("/comics/") + val json = webClient.httpGet("https://$apiSuffix/comics/${id}").parseJson() val authors = json.optJSONArray("authors")?.mapJSONToSet { jo -> jo.getString("name") }.orEmpty() - val team = json.optJSONArray("teams")?.getJSONObject(0)?.getString("name") - - val allTags = fetchTags().orEmpty() - val tags = allTags.let { allTags -> - json.optJSONArray("genres")?.asTypedList()?.mapNotNullToSet { g -> - allTags.find { x -> x.key == g } - } - }.orEmpty() + val chaptersDeferred = async { + webClient.httpGet("https://$apiSuffix/chapters/comic/${id}").parseJsonArray() + } manga.copy( - title = json.getString("title"), - altTitles = setOf(json.getString("anotherName")), - contentRating = if (json.getBooleanOrDefault("r18", false)) { - ContentRating.ADULT - } else { - ContentRating.SUGGESTIVE - }, authors = authors, - tags = tags, - description = json.getString("description"), - state = when(json.getString("status")) { - "ongoing" -> MangaState.ONGOING - "completed" -> MangaState.FINISHED - "hiatus" -> MangaState.PAUSED - "cancelled" -> MangaState.ABANDONED - else -> null - }, chapters = chaptersDeferred.await().mapChapters() { _, jo -> - val chapterId = jo.getLong("id") - val pageUrls = jo.getJSONArray("pages").mapJSON { page -> - page.getString("url") - } + val chapId = jo.getLong("id") MangaChapter( - id = generateUid(chapterId), + id = generateUid(chapId), title = jo.getString("name"), - number = jo.getString("order").toFloat(), + number = jo.getFloatOrDefault("order", 0f), volume = 0, - url = pageUrls.joinToString("\n"), - scanlator = team, + url = "$chapId", + scanlator = null, uploadDate = jo.getLong("lastUpdated"), branch = null, source = source, ) - }, + } ) } override suspend fun getPages(chapter: MangaChapter): List { - return chapter.url.split("\n").mapIndexed { index, url -> - MangaPage( - id = generateUid(index.toLong()), - url = url, - preview = null, - source = source, - ) - } - } + val json = webClient.httpGet("https://$apiSuffix/chapters/${chapter.url}").parseJson() + val pages = json.getJSONArray("pages").asTypedList() + + return pages.mapIndexed { index, page -> + val pageUrl = page.getString("url") + MangaPage( + id = generateUid(index.toLong()), + url = pageUrl, + preview = null, + source = source, + ) + } + } private suspend fun fetchTags(): Set { val json = webClient.httpGet("https://$apiSuffix/resources/systems_vi.json").parseJson() @@ -221,15 +197,4 @@ internal abstract class YuriGardenParser( ) } } - - private fun decryptIfNeeded(raw: String): String { - val json = raw.toJSONObjectOrNull() ?: return raw - if (json.optBoolean("encrypted", false)) { - val data = json.optString("data") - if (data.isNullOrEmpty()) return raw - val decrypted = CryptoAES(context).decrypt(data, "d7p3FBmASBpaWP") - return decrypted - } - return raw - } }