Change method of work (#15140)

* Change method of work

* always update bytecode
This commit is contained in:
beerpsi 2023-01-28 03:06:33 +07:00 committed by GitHub
parent d67b542c21
commit ff22db6290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 18 deletions

View File

@ -8,11 +8,13 @@ import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Rect import android.graphics.Rect
import android.util.Log import android.util.Log
import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
@ -97,20 +99,14 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarsca
val tsDataEncrypted = TS_DATA_RE.find(obfuscatedCode)?.groupValues?.get(1) val tsDataEncrypted = TS_DATA_RE.find(obfuscatedCode)?.groupValues?.get(1)
if (tsDataEncrypted != null) { if (tsDataEncrypted != null) {
val descrambledData = descrambleString(tsDataEncrypted).trim() val descrambledData = descrambleString(tsDataEncrypted).trim()
Log.d("constellarscans", "decrypted chapter, data: $descrambledData")
val match = DESCRAMBLING_KEY_RE.find(descrambledData)?.value val match = DESCRAMBLING_KEY_RE.find(descrambledData)?.value
?: throw Exception("Did not receive valid decryption key. Try opening the chapter again.") ?: throw Exception("Did not receive valid decryption key. Try opening the chapter again.")
Log.d("constellarscans", "device-limited chapter: $match") Log.d("constellarscans", "device-limited chapter: $match")
return decodeDeviceLimitedChapter(match) return decodeDeviceLimitedChapter(match)
} }
val scripts = document.select("script").html() val tsData = descrambleString(getDecryptionKey(document)).trim()
val tsData = JS_FUNC_RE.findAll(obfuscatedCode).firstNotNullOf {
val func = it.groupValues[1]
val tsDataFuncRe = Regex("""$func\s*\(\s*['"]([\da-z]+?)['"]\s*\)""", RegexOption.IGNORE_CASE)
val match = tsDataFuncRe.find(scripts)?.groupValues?.get(1)
?: return@firstNotNullOf null
descrambleString(match).trim()
}
val tsDataObject = json.parseToJsonElement(tsData).jsonObject val tsDataObject = json.parseToJsonElement(tsData).jsonObject
return tsDataObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { index, jsonElement -> return tsDataObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { index, jsonElement ->
Page(index, imageUrl = jsonElement.jsonPrimitive.content.replace("http://", "https://")) Page(index, imageUrl = jsonElement.jsonPrimitive.content.replace("http://", "https://"))
@ -124,16 +120,63 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarsca
.header("Sec-Fetch-Site", "same-origin") .header("Sec-Fetch-Site", "same-origin")
.build() .build()
private fun sumOfDigits(input: String): Int = input.sumOf { it.toString().toInt() } private var descramblingBytecode: ByteArray? = null
private fun descrambleString(input: String): String = /**
input.replace(NOT_DIGIT_RE, "") * Also updates `descramblingBytecode` as a side effect
.chunked(6) */
.map { private fun getDecryptionKey(document: Document?): String {
val charCode = sumOfDigits(it.substring(0..2)) * 10 + sumOfDigits(it.substring(3)) + 32 val doc = if (document == null) {
charCode.toChar() val req = pageListRequest(
SChapter.create().apply {
url = "/the-dignity-of-sister-in-law-chapter-60/"
}
)
client.newCall(req).execute().asJsoup()
} else {
document
}
val obfuscatedScript = doc.selectFirst("script:containsData(_0x)").data()
val scripts = doc.select("script:containsData(_0x) ~ script").html()
val (decodingSymbol, tsData) = JS_FUNC_RE.findAll(obfuscatedScript).firstNotNullOf {
val func = it.groupValues[1]
val tsDataFuncRe = Regex("""$func\s*\(\s*['"]([\da-z]+?)['"]\s*\)""", RegexOption.IGNORE_CASE)
val tsData = tsDataFuncRe.find(scripts)?.groupValues?.get(1)
?: return@firstNotNullOf null
func to tsData
}
Log.d("constellarscans", "decoding symbol: $decodingSymbol")
descramblingBytecode = QuickJs.create().use {
it.compile(
"""
ts_reader = {}
ts_reader.run = arg => { throw Error(arg) }
JSON.parse = arg => arg
${obfuscatedScript.replace(decodingSymbol, "decode")}
""".trimIndent(),
"#"
)
}
return tsData
}
private fun descrambleString(input: String): String {
if (descramblingBytecode == null) getDecryptionKey(null)
return QuickJs.create().use {
it.execute(descramblingBytecode!!)
it.evaluate(
"""
try {
decode("$input")
} catch (e) {
e.message
}
""".trimIndent()
)
} as String
} }
.joinToString("")
private fun decodeDeviceLimitedChapter(fullKey: String): List<Page> { private fun decodeDeviceLimitedChapter(fullKey: String): List<Page> {
if (!DESCRAMBLING_KEY_RE.matches(fullKey)) { if (!DESCRAMBLING_KEY_RE.matches(fullKey)) {

View File

@ -25,7 +25,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator {
SingleLang("Azure Scans", "https://azuremanga.com", "en", overrideVersionCode = 1), SingleLang("Azure Scans", "https://azuremanga.com", "en", overrideVersionCode = 1),
SingleLang("Boosei", "https://boosei.net", "id", overrideVersionCode = 2), SingleLang("Boosei", "https://boosei.net", "id", overrideVersionCode = 2),
SingleLang("Clayrer", "https://clayrer.net", "es"), SingleLang("Clayrer", "https://clayrer.net", "es"),
SingleLang("Constellar Scans", "https://constellarscans.com", "en", isNsfw = true, overrideVersionCode = 7), SingleLang("Constellar Scans", "https://constellarscans.com", "en", isNsfw = true, overrideVersionCode = 8),
SingleLang("Cosmic Scans", "https://cosmicscans.com", "en", overrideVersionCode = 1), SingleLang("Cosmic Scans", "https://cosmicscans.com", "en", overrideVersionCode = 1),
SingleLang("Diskus Scan", "https://diskusscan.com", "pt-BR", overrideVersionCode = 7), SingleLang("Diskus Scan", "https://diskusscan.com", "pt-BR", overrideVersionCode = 7),
SingleLang("Dojing.net", "https://dojing.net", "id", isNsfw = true, className = "DojingNet"), SingleLang("Dojing.net", "https://dojing.net", "id", isNsfw = true, className = "DojingNet"),