Bato.to: Cleanup `pageListParse()` (#13047)

Also move to QuickJS

The change was tested on https://bato.to/chapter/1 and many recent chapters
This commit is contained in:
AntsyLich 2022-08-17 22:13:39 +06:00 committed by GitHub
parent 372fa06b85
commit a83e04f237
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 109 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Bato.to'
pkgNameSuffix = 'all.batoto'
extClass = '.BatoToFactory'
extVersionCode = 28
extVersionCode = 29
isNsfw = true
}

View File

@ -5,7 +5,7 @@ import android.content.SharedPreferences
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import com.squareup.duktape.Duktape
import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
@ -95,49 +95,6 @@ open class BatoTo(
private fun getMirrorPref(): String? = preferences.getString("${MIRROR_PREF_KEY}_$lang", MIRROR_PREF_DEFAULT_VALUE)
private fun getAltChapterListPref(): Boolean = preferences.getBoolean("${ALT_CHAPTER_LIST_PREF_KEY}_$lang", ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE)
companion object {
private const val MIRROR_PREF_KEY = "MIRROR"
private const val MIRROR_PREF_TITLE = "Mirror"
private val MIRROR_PREF_ENTRIES = arrayOf(
"bato.to",
"batocc.com",
"batotoo.com",
"batotwo.com",
"battwo.com",
"comiko.net",
"mangatoto.com",
"mangatoto.net",
"mangatoto.org",
"mycordant.co.uk",
"dto.to",
"hto.to",
"mto.to",
"wto.to"
)
private val MIRROR_PREF_ENTRY_VALUES = arrayOf(
"https://bato.to",
"https://batocc.com",
"https://batotoo.com",
"https://batotwo.com",
"https://battwo.com",
"https://comiko.net",
"https://mangatoto.com",
"https://mangatoto.net",
"https://mangatoto.org",
"https://mycordant.co.uk",
"https://dto.to",
"https://hto.to",
"https://mto.to",
"https://wto.to"
)
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
private const val ALT_CHAPTER_LIST_PREF_KEY = "ALT_CHAPTER_LIST"
private const val ALT_CHAPTER_LIST_PREF_TITLE = "Alternative Chapter List"
private const val ALT_CHAPTER_LIST_PREF_SUMMARY = "If checked, uses an alternate chapter list"
private const val ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE = false
}
override val supportsLatest = true
private val json: Json by injectLazy()
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
@ -185,7 +142,7 @@ open class BatoTo(
val id = query.substringAfter("ID:")
client.newCall(GET("$baseUrl/series/$id", headers)).asObservableSuccess()
.map { response ->
queryIDParse(response, id)
queryIDParse(response)
}
}
query.isNotBlank() -> {
@ -199,6 +156,7 @@ open class BatoTo(
url.addQueryParameter("mode", "letter")
}
}
else -> { /* Do Nothing */ }
}
}
client.newCall(GET(url.build().toString(), headers)).asObservableSuccess()
@ -263,6 +221,7 @@ open class BatoTo(
}
is MinChapterTextFilter -> min = filter.state
is MaxChapterTextFilter -> max = filter.state
else -> { /* Do Nothing */ }
}
}
url.addQueryParameter("page", page.toString())
@ -279,7 +238,7 @@ open class BatoTo(
}
}
private fun queryIDParse(response: Response, id: String): MangasPage {
private fun queryIDParse(response: Response): MangasPage {
val document = response.asJsoup()
val infoElement = document.select("div#mainer div.container-fluid")
val manga = SManga.create()
@ -496,76 +455,26 @@ open class BatoTo(
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
val script = document.selectFirst("script:containsData(imgHttpLis):containsData(batoWord):containsData(batoPass)")?.html()
?: throw RuntimeException("Couldn't find script with image data.")
val script = document.select("script").html()
val imgHttpLisString = script.substringAfter("const imgHttpLis =").substringBefore(";").trim()
val imgHttpLis = json.parseToJsonElement(imgHttpLisString).jsonArray.map { it.jsonPrimitive.content }
val batoWord = script.substringAfter("const batoWord =").substringBefore(";").trim()
val batoPass = script.substringAfter("const batoPass =").substringBefore(";").trim()
if (script.contains("var images =")) {
/*
* During kotlinx.serialization migration, the pre-existing code seemed to not work
* Could not find a case where code would run in practice, so it was commented out.
*/
throw RuntimeException("Unexpected Branch: Please File A Bug Report describing this issue")
// val imgJson = json.parseToJsonElement(script.substringAfter("var images = ").substringBefore(";")).jsonObject
// imgJson.keys.forEachIndexed { i, s -> pages.add(Page(i, imageUrl = imgJson[s]!!.jsonPrimitive.content)) }
} else if (script.contains("const server =")) { // bato.to
val duktape = Duktape.create()
val encryptedServer = script.substringAfter("const server = ").substringBefore(";")
val batojs = duktape.evaluate(script.substringAfter("const batojs = ").substringBefore(";")).toString()
val decryptScript = cryptoJS + "CryptoJS.AES.decrypt($encryptedServer, \"$batojs\").toString(CryptoJS.enc.Utf8);"
val server = duktape.evaluate(decryptScript).toString().replace("\"", "")
duktape.close()
val decryptScript = cryptoJS + "CryptoJS.AES.decrypt($batoWord, $batoPass).toString(CryptoJS.enc.Utf8);"
json.parseToJsonElement(script.substringAfter("const images = ").substringBefore(";")).jsonArray
.forEachIndexed { i, it ->
val imgUrl = it.jsonPrimitive.content
if (script.contains("bato.to/images")) {
pages.add(Page(i, imageUrl = imgUrl))
} else {
pages.add(Page(i, imageUrl = if (server.startsWith("http")) "${server}$imgUrl" else "https:${server}$imgUrl"))
}
}
} else if (script.contains("const imgHttpLis = ") && script.contains("const batoWord = ") && script.contains(
"const batoPass = "
)
) {
val duktape = Duktape.create()
val imgHttpLis = json.parseToJsonElement(
script.substringAfter("const imgHttpLis = ").substringBefore(";")
).jsonArray
val batoWord = script.substringAfter("const batoWord = ").substringBefore(";")
val batoPass =
duktape.evaluate(script.substringAfter("const batoPass = ").substringBefore(";"))
.toString()
val input =
cryptoJS + "CryptoJS.AES.decrypt($batoWord, \"$batoPass\").toString(CryptoJS.enc.Utf8);"
val imgWordLis = json.parseToJsonElement(duktape.evaluate(input).toString()).jsonArray
duktape.close()
val imgAccListString = QuickJs.create().use { it.evaluate(decryptScript).toString() }
val imgAccList = json.parseToJsonElement(imgAccListString).jsonArray.map { it.jsonPrimitive.content }
if (imgHttpLis.size == imgWordLis.size) {
imgHttpLis.forEachIndexed { i: Int, item ->
val imageUrl =
"${item.jsonPrimitive.content}?${imgWordLis.get(i).jsonPrimitive.content}"
pages.add(
Page(
i,
imageUrl = imageUrl
)
)
}
}
return imgHttpLis.zip(imgAccList).mapIndexed { i, (imgUrl, imgAcc) ->
Page(i, imageUrl = "$imgUrl?$imgAcc")
}
return pages
}
private val cryptoJS by lazy {
client.newCall(
GET(
"https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js",
headers
)
).execute().body!!.string()
client.newCall(GET(CryptoJSUrl, headers)).execute().body!!.string()
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
@ -1001,4 +910,49 @@ open class BatoTo(
CheckboxFilterOption("eu", "Basque"),
CheckboxFilterOption("pt-PT", "Portuguese (Portugal)"),
).filterNot { it.value == siteLang }
companion object {
const val CryptoJSUrl = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"
private const val MIRROR_PREF_KEY = "MIRROR"
private const val MIRROR_PREF_TITLE = "Mirror"
private val MIRROR_PREF_ENTRIES = arrayOf(
"bato.to",
"batocc.com",
"batotoo.com",
"batotwo.com",
"battwo.com",
"comiko.net",
"mangatoto.com",
"mangatoto.net",
"mangatoto.org",
"mycordant.co.uk",
"dto.to",
"hto.to",
"mto.to",
"wto.to"
)
private val MIRROR_PREF_ENTRY_VALUES = arrayOf(
"https://bato.to",
"https://batocc.com",
"https://batotoo.com",
"https://batotwo.com",
"https://battwo.com",
"https://comiko.net",
"https://mangatoto.com",
"https://mangatoto.net",
"https://mangatoto.org",
"https://mycordant.co.uk",
"https://dto.to",
"https://hto.to",
"https://mto.to",
"https://wto.to"
)
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
private const val ALT_CHAPTER_LIST_PREF_KEY = "ALT_CHAPTER_LIST"
private const val ALT_CHAPTER_LIST_PREF_TITLE = "Alternative Chapter List"
private const val ALT_CHAPTER_LIST_PREF_SUMMARY = "If checked, uses an alternate chapter list"
private const val ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE = false
}
}