Change method of work (#15140)
* Change method of work * always update bytecode
This commit is contained in:
parent
d67b542c21
commit
ff22db6290
|
@ -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 {
|
||||||
.joinToString("")
|
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
|
||||||
|
}
|
||||||
|
|
||||||
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)) {
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in New Issue