[YuriGarden] Fixes (#1934)

Draken 10 months ago committed by GitHub
parent 67b556c2eb
commit 1d2ee20d17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -61,6 +61,7 @@ JVM and Android applications.
## Projects that use the library ## Projects that use the library
- [Kotatsu](https://github.com/KotatsuApp/Kotatsu) - [Kotatsu](https://github.com/KotatsuApp/Kotatsu)
- [Doki](https://github.com/DokiTeam/Doki)
- [kotatsu-dl](https://github.com/KotatsuApp/kotatsu-dl) - [kotatsu-dl](https://github.com/KotatsuApp/kotatsu-dl)
- [Shirizu (WIP)](https://github.com/ztimms73/shirizu) - [Shirizu (WIP)](https://github.com/ztimms73/shirizu)
- [OtakuWorld](https://github.com/jakepurple13/OtakuWorld) - [OtakuWorld](https://github.com/jakepurple13/OtakuWorld)

@ -12,9 +12,7 @@ import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.* import org.koitharu.kotatsu.parsers.util.json.*
import org.koitharu.kotatsu.parsers.util.CryptoAES
import org.json.JSONObject import org.json.JSONObject
import org.json.JSONArray
import java.util.* import java.util.*
internal abstract class YuriGardenParser( internal abstract class YuriGardenParser(
@ -29,15 +27,9 @@ internal abstract class YuriGardenParser(
override val configKeyDomain = ConfigKey.Domain(domain) override val configKeyDomain = ConfigKey.Domain(domain)
private val apiSuffix = "api.$domain" private val apiSuffix = "api.$domain"
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_MOBILE)
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override fun getRequestHeaders(): Headers = Headers.Builder() override fun getRequestHeaders(): Headers = Headers.Builder()
.add("x-app-origin", "https://$domain") .add("x-app-origin", "https://$domain")
.add("User-Agent", UserAgents.KOTATSU)
.build() .build()
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
@ -60,6 +52,7 @@ internal abstract class YuriGardenParser(
MangaState.FINISHED, MangaState.FINISHED,
MangaState.ABANDONED, MangaState.ABANDONED,
MangaState.PAUSED, MangaState.PAUSED,
MangaState.UPCOMING,
), ),
) )
} }
@ -95,6 +88,7 @@ internal abstract class YuriGardenParser(
MangaState.FINISHED -> "completed" MangaState.FINISHED -> "completed"
MangaState.PAUSED -> "hiatus" MangaState.PAUSED -> "hiatus"
MangaState.ABANDONED -> "cancelled" MangaState.ABANDONED -> "cancelled"
MangaState.UPCOMING -> "oncoming"
else -> "all" else -> "all"
}) })
} }
@ -107,27 +101,34 @@ internal abstract class YuriGardenParser(
} }
} }
val raw = webClient.httpGet(url).parseRaw() val json = webClient.httpGet(url).parseJson()
val json = JSONObject(decryptIfNeeded(raw))
val data = json.getJSONArray("comics") val data = json.getJSONArray("comics")
return data.mapJSON { jo -> return data.mapJSON { jo ->
val id = jo.getLong("id") val id = jo.getLong("id")
val allTags = fetchTags().orEmpty()
val tags = allTags.let { allTags ->
jo.optJSONArray("genres")?.asTypedList<String>()?.mapNotNullToSet { g ->
allTags.find { x -> x.key == g }
}
}.orEmpty()
Manga( Manga(
id = generateUid(id), id = generateUid(id),
url = "/comics/$id", url = "/comics/$id",
publicUrl = "https://$domain/comic/$id", publicUrl = "https://$domain/comic/$id",
title = jo.getString("title"), title = jo.getString("title"),
altTitles = emptySet(), altTitles = setOf(jo.getString("anotherName")),
coverUrl = jo.getString("thumbnail"), coverUrl = jo.getString("thumbnail"),
largeCoverUrl = jo.getString("thumbnail"), largeCoverUrl = jo.getString("thumbnail"),
authors = emptySet(), authors = emptySet(),
tags = emptySet(), tags = tags,
state = when(jo.getString("status")) { state = when(jo.getString("status")) {
"ongoing" -> MangaState.ONGOING "ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED "completed" -> MangaState.FINISHED
"hiatus" -> MangaState.PAUSED "hiatus" -> MangaState.PAUSED
"cancelled" -> MangaState.ABANDONED "cancelled" -> MangaState.ABANDONED
"oncoming" -> MangaState.UPCOMING
else -> null else -> null
}, },
description = jo.getString("description"), description = jo.getString("description"),
@ -139,70 +140,45 @@ internal abstract class YuriGardenParser(
} }
override suspend fun getDetails(manga: Manga): Manga = coroutineScope { override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val url = "https://" + apiSuffix + manga.url val id = manga.url.substringAfter("/comics/")
val chaptersDeferred = async { val json = webClient.httpGet("https://$apiSuffix/comics/${id}").parseJson()
val raw = webClient.httpGet("$url/chapters").parseRaw()
JSONArray(decryptIfNeeded(raw))
}
val raw = webClient.httpGet(url).parseRaw()
val json = JSONObject(decryptIfNeeded(raw))
val authors = json.optJSONArray("authors")?.mapJSONToSet { jo -> val authors = json.optJSONArray("authors")?.mapJSONToSet { jo ->
jo.getString("name") jo.getString("name")
}.orEmpty() }.orEmpty()
val team = json.optJSONArray("teams")?.getJSONObject(0)?.getString("name") val chaptersDeferred = async {
webClient.httpGet("https://$apiSuffix/chapters/comic/${id}").parseJsonArray()
val allTags = fetchTags().orEmpty()
val tags = allTags.let { allTags ->
json.optJSONArray("genres")?.asTypedList<String>()?.mapNotNullToSet { g ->
allTags.find { x -> x.key == g }
} }
}.orEmpty()
manga.copy( manga.copy(
title = json.getString("title"),
altTitles = setOf(json.getString("anotherName")),
contentRating = if (json.getBooleanOrDefault("r18", false)) {
ContentRating.ADULT
} else {
ContentRating.SUGGESTIVE
},
authors = authors, 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 -> chapters = chaptersDeferred.await().mapChapters() { _, jo ->
val chapterId = jo.getLong("id") val chapId = jo.getLong("id")
val pageUrls = jo.getJSONArray("pages").mapJSON { page ->
page.getString("url")
}
MangaChapter( MangaChapter(
id = generateUid(chapterId), id = generateUid(chapId),
title = jo.getString("name"), title = jo.getString("name"),
number = jo.getString("order").toFloat(), number = jo.getFloatOrDefault("order", 0f),
volume = 0, volume = 0,
url = pageUrls.joinToString("\n"), url = "$chapId",
scanlator = team, scanlator = null,
uploadDate = jo.getLong("lastUpdated"), uploadDate = jo.getLong("lastUpdated"),
branch = null, branch = null,
source = source, source = source,
) )
}, }
) )
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
return chapter.url.split("\n").mapIndexed { index, url -> val json = webClient.httpGet("https://$apiSuffix/chapters/${chapter.url}").parseJson()
val pages = json.getJSONArray("pages").asTypedList<JSONObject>()
return pages.mapIndexed { index, page ->
val pageUrl = page.getString("url")
MangaPage( MangaPage(
id = generateUid(index.toLong()), id = generateUid(index.toLong()),
url = url, url = pageUrl,
preview = null, preview = null,
source = source, source = source,
) )
@ -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
}
} }

Loading…
Cancel
Save