diff --git a/src/all/batoto/build.gradle b/src/all/batoto/build.gradle index 49a9115e7..46796c79f 100644 --- a/src/all/batoto/build.gradle +++ b/src/all/batoto/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Bato.to' pkgNameSuffix = 'all.batoto' extClass = '.BatoToFactory' - extVersionCode = 28 + extVersionCode = 29 isNsfw = true } diff --git a/src/all/batoto/src/eu/kanade/tachiyomi/extension/all/batoto/BatoTo.kt b/src/all/batoto/src/eu/kanade/tachiyomi/extension/all/batoto/BatoTo.kt index f8a3eee07..6a0f96fd0 100644 --- a/src/all/batoto/src/eu/kanade/tachiyomi/extension/all/batoto/BatoTo.kt +++ b/src/all/batoto/src/eu/kanade/tachiyomi/extension/all/batoto/BatoTo.kt @@ -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 { - val pages = mutableListOf() + 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 + } }