Yuri Garden: Add intercept, apply unscrambleImage for getPages

master
dragonx943 9 months ago
parent 31999d97a0
commit 7f39798a8c
No known key found for this signature in database
GPG Key ID: 48DD99A2C5421C1C

@ -38,7 +38,7 @@ internal class CuuTruyenParser(context: MangaLoaderContext) :
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) { override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys) super.onCreateConfig(keys)
keys.add(userAgentKey) keys.remove(userAgentKey)
} }
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(

@ -4,15 +4,23 @@ import androidx.collection.arraySetOf
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.Response
import okio.IOException
import org.json.JSONArray
import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.bitmap.Bitmap
import org.koitharu.kotatsu.parsers.bitmap.Rect
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.core.PagedMangaParser import org.koitharu.kotatsu.parsers.core.PagedMangaParser
import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy import org.koitharu.kotatsu.parsers.network.UserAgents
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.json.JSONObject import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy
import java.util.* import java.util.*
internal abstract class YuriGardenParser( internal abstract class YuriGardenParser(
@ -26,12 +34,18 @@ 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"
private val cdnSuffix = "db.$domain/storage/v1/object/public/yuri-garden-store"
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) .add("User-Agent", UserAgents.KOTATSU)
.build() .build()
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.remove(userAgentKey)
}
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST, SortOrder.NEWEST,
SortOrder.NEWEST_ASC, SortOrder.NEWEST_ASC,
@ -95,7 +109,7 @@ internal abstract class YuriGardenParser(
} }
append("&full=true") append("&full=true")
if (filter.tags.isNotEmpty()) { if (filter.tags.isNotEmpty()) {
append("&genre=") append("&genre=")
append(filter.tags.joinToString(separator = ",") { it.key }) append(filter.tags.joinToString(separator = ",") { it.key })
@ -128,7 +142,7 @@ internal abstract class YuriGardenParser(
allTags.find { x -> x.key == g } allTags.find { x -> x.key == g }
} }
}.orEmpty() }.orEmpty()
Manga( Manga(
id = generateUid(id), id = generateUid(id),
url = "/comics/$id", url = "/comics/$id",
@ -193,19 +207,74 @@ internal abstract class YuriGardenParser(
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val json = webClient.httpGet("https://$apiSuffix/chapters/${chapter.url}").parseJson() val json = webClient.httpGet("https://$apiSuffix/chapters/${chapter.url}").parseJson()
val pages = json.getJSONArray("pages").asTypedList<JSONObject>() val pages = json.getJSONArray("pages").asTypedList<JSONObject>()
return pages.mapIndexed { index, page -> return pages.mapIndexed { index, page ->
val pageUrl = page.getString("url") val rawUrl = page.getString("url")
MangaPage(
id = generateUid(index.toLong()), if (rawUrl.startsWith("comics")) {
url = pageUrl, val key = page.optString("key", null)
preview = null, val url = "https://$cdnSuffix/$rawUrl".toHttpUrl().newBuilder().apply {
source = source, if (!key.isNullOrEmpty()) {
) fragment("KEY=$key")
} }
} }
MangaPage(
id = generateUid(index.toLong()),
url = url.build().toString(),
preview = null,
source = source,
)
} else {
val url = rawUrl.toHttpUrlOrNull()?.toString() ?: rawUrl
MangaPage(
id = generateUid(index.toLong()),
url = url,
preview = null,
source = source,
)
}
}
}
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val fragment = response.request.url.fragment ?: return response
if (!fragment.contains("KEY=")) return response
return context.redrawImageResponse(response) { bitmap ->
val url = fragment.substringBefore("KEY=")
val key = fragment.substringAfter("KEY=")
kotlinx.coroutines.runBlocking {
unscrambleImage(url, bitmap, key)
}
}
}
private suspend fun unscrambleImage(url: String, bitmap: Bitmap, key: String): Bitmap {
val js = """
(function(Q0,Q1,Q2){"use strict";const A=(()=>{const L=[49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,74,75,76,77,78,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,109,110,111,112,113,114,115,116,117,118,119,120,121,122];return L.map(c=>String.fromCharCode(c)).join("")})();const F=(()=>{let f=[1];for(let i=1;i<=10;i++)f[i]=f[i-1]*i;return f})();const _I=(E,P)=>{let n=[...Array(P).keys()],r=[];for(let a=P-1;a>=0;a--){let i=F[a],s=Math.floor(E/i);E%=i;r.push(n.splice(s,1)[0])}return r};const _S=str=>{let t=0;for(let ch of str){let r=A.indexOf(ch);if(r<0)throw Error("Invalid Base58 char");t=t*58+r}return t};const _U=(enc,p)=>{if(!/^H[1-9A-HJ-NP-Za-km-z]+${'$'}/.test(enc))throw Error("Bad Base58");let t=enc.slice(1,-1),n=enc.slice(-1),r=_S(t);if(A[r%58]!==n)throw Error("Checksum mismatch");return _I(r,p)};const _P=(h,p)=>{let n=Math.floor(h/p),r=h%p,a=[];for(let i=0;i<p;i++)a.push(n+(i<r?1:0));return a};const _D=e=>{let t=Array(e.length);e.forEach((v,i)=>t[v]=i);return t};const _X=(K,H,P)=>{let e=_U(K.slice(4),P),s=_D(e),u=_P(H-4*(P-1),P),m=e.map(i=>u[i]),pts=[0];for(let i=0;i<m.length;i++)pts[i+1]=pts[i]+m[i];let f=[];for(let i=0;i<m.length;i++)f.push({y:i?pts[i]+4*i:0,h:m[i]});return s.map(i=>f[i])};return _X(Q0,Q1,Q2)})("$key",${bitmap.height},10);
""".trimIndent()
val result = context.evaluateJs(url, js)
?: throw IOException("Oops! Đã xảy ra lỗi khi biên dịch")
val arr = JSONArray(result)
val out = context.createBitmap(bitmap.width, bitmap.height)
var dy = 0
for (i in 0 until arr.length()) {
val o = arr.getJSONObject(i)
val sy = o.getInt("y")
val h = o.getInt("h")
val src = Rect(0, sy, bitmap.width, sy + h)
val dst = Rect(0, dy, bitmap.width, dy + h)
out.drawBitmap(bitmap, src, dst)
dy += h
}
return out
}
private suspend fun fetchTags(): Set<MangaTag> { private suspend fun fetchTags(): Set<MangaTag> {
val json = webClient.httpGet("https://$apiSuffix/resources/systems_vi.json").parseJson() val json = webClient.httpGet("https://$apiSuffix/resources/systems_vi.json").parseJson()

Loading…
Cancel
Save