diff --git a/src/en/immortalupdates/build.gradle b/src/en/immortalupdates/build.gradle deleted file mode 100644 index 5dd577af1..000000000 --- a/src/en/immortalupdates/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -ext { - extName = 'Immortal Updates' - extClass = '.ImmortalUpdates' - themePkg = 'madara' - baseUrl = 'https://immortalupdates.com' - overrideVersionCode = 6 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/en/immortalupdates/res/mipmap-hdpi/ic_launcher.png b/src/en/immortalupdates/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a77f2fc26..000000000 Binary files a/src/en/immortalupdates/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/immortalupdates/res/mipmap-mdpi/ic_launcher.png b/src/en/immortalupdates/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index eadb87d65..000000000 Binary files a/src/en/immortalupdates/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/immortalupdates/res/mipmap-xhdpi/ic_launcher.png b/src/en/immortalupdates/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 79bc9daf2..000000000 Binary files a/src/en/immortalupdates/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/immortalupdates/res/mipmap-xxhdpi/ic_launcher.png b/src/en/immortalupdates/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index cbc257b74..000000000 Binary files a/src/en/immortalupdates/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/immortalupdates/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/immortalupdates/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 380000090..000000000 Binary files a/src/en/immortalupdates/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/immortalupdates/src/eu/kanade/tachiyomi/extension/en/immortalupdates/ImmortalUpdates.kt b/src/en/immortalupdates/src/eu/kanade/tachiyomi/extension/en/immortalupdates/ImmortalUpdates.kt deleted file mode 100644 index 2ede5176b..000000000 --- a/src/en/immortalupdates/src/eu/kanade/tachiyomi/extension/en/immortalupdates/ImmortalUpdates.kt +++ /dev/null @@ -1,204 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.immortalupdates - -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.ColorMatrix -import android.graphics.ColorMatrixColorFilter -import android.graphics.Paint -import android.graphics.Rect -import app.cash.quickjs.QuickJs -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.Page -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.ResponseBody.Companion.toResponseBody -import org.jsoup.nodes.Document -import java.io.ByteArrayOutputStream -import java.io.InputStream - -class ImmortalUpdates : Madara("Immortal Updates", "https://immortalupdates.com", "en") { - - override val useNewChapterEndpoint: Boolean = true - - override val client = super.client.newBuilder() - .rateLimit(1, 2) - .addInterceptor { chain -> - val response = chain.proceed(chain.request()) - - if (response.request.url.fragment?.contains(DESCRAMBLE) != true) { - return@addInterceptor response - } - val fragment = response.request.url.fragment!! - val args = fragment.substringAfter("$DESCRAMBLE=").split(",") - - val image = unscrambleImage(response.body.byteStream(), args) - val body = image.toResponseBody("image/jpeg".toMediaTypeOrNull()) - return@addInterceptor response.newBuilder() - .body(body) - .build() - }.build() - - override fun pageListParse(document: Document): List { - val pageList = super.pageListParse(document).toMutableList() - - val unscramblingCallsPage = pageList.firstOrNull { it.imageUrl!!.contains("00-call") } - ?: return pageList - - val unscramblingCalls = client.newCall(GET(unscramblingCallsPage.imageUrl!!, headers)) - .execute() - .use { it.body.string() } - - unscramblingCalls.replace("\r", "").split("\n").filter { !it.isNullOrBlank() }.forEach { - val args = unfuckJs(it) - .substringAfter("[") - .substringBefore("]") - - val filenameFragment = args.split(",")[0].removeSurrounding("\"") - val page = pageList.firstOrNull { page -> page.imageUrl!!.contains(filenameFragment, ignoreCase = true) } - ?: return@forEach - val newPageUrl = page.imageUrl!!.toHttpUrl().newBuilder() - .fragment("$DESCRAMBLE=$args") - .build() - .toString() - pageList[page.index] = Page(page.index, document.location(), newPageUrl) - } - pageList.remove(unscramblingCallsPage) - return pageList - } - - // Converted from _0x3bc005: Find the CanvasRenderingContext2D.drawImage call basically - // - // `args` is the arguments of the original get_img call: - // get_img(file_to_match, indexer, iterations, sectionWidth, sectionHeight, isBackgroundBlack, shouldFillColor, ???, key, keyAddition, ???) - // - // The boolean after shouldFillColor seems to always be `false` so I have optimized it out for now. - // If it fucks up someone will make an issue anyways /shrug - // - // I assumed the last argument was to check if versions match or something (since it was 1.0.1) - // but it was used in some canvas thingy that I didn't bother to check - private fun unscrambleImage(image: InputStream, args: List): ByteArray { - val indexer = args[1].toInt() - val iterations = args[2].toInt() - val sectionWidth = args[3].toInt() - val sectionHeight = args[4].toInt() - val isBackgroundBlack = args[5] == "true" - val shouldFillColor = args[6] == "true" - val key = args[8].toInt() - val keyAddition = args[9].toInt() - - val bitmap = BitmapFactory.decodeStream(image) - - val result = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) - val canvas = Canvas(result) - - val heightSectionCount = bitmap.height / sectionHeight - val widthSectionCount = bitmap.width / sectionWidth - val sectionCount = heightSectionCount * widthSectionCount - val descramblingArray = createDescramblingArray(indexer, sectionCount, key, keyAddition, iterations) - - if (shouldFillColor) { - val backgroundColor = if (isBackgroundBlack) Color.BLACK else Color.WHITE - canvas.drawColor(backgroundColor) - } - - var i = 0 - for (vertical in 0 until heightSectionCount) { - for (horizontal in 0 until widthSectionCount) { - val swap = descramblingArray[i] - - val baseHeight = swap.floorDiv(widthSectionCount) - val baseWidth = swap - baseHeight * widthSectionCount - - val dx = baseWidth * sectionWidth - val dy = baseHeight * sectionHeight - - val sx = horizontal * sectionWidth - val sy = vertical * sectionHeight - - val srcRect = Rect(sx, sy, sx + sectionWidth, sy + sectionHeight) - val dstRect = Rect(dx, dy, dx + sectionWidth, dy + sectionHeight) - - canvas.drawBitmap(bitmap, srcRect, dstRect, null) - i += 1 - } - } - - if (isBackgroundBlack) { - val invertingPaint = Paint().apply { - colorFilter = ColorMatrixColorFilter( - ColorMatrix( - floatArrayOf( - -1.0f, 0.0f, 0.0f, 0.0f, 255.0f, - 0.0f, -1.0f, 0.0f, 0.0f, 255.0f, - 0.0f, 0.0f, -1.0f, 0.0f, 255.0f, - 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - ), - ), - ) - } - canvas.drawBitmap(result, 0f, 0f, invertingPaint) - } - - val output = ByteArrayOutputStream() - result.compress(Bitmap.CompressFormat.JPEG, 90, output) - - return output.toByteArray() - } - - // Converted from _0x144afb - // This should be called a little bit before the drawImage calls - private fun createDescramblingArray(indexer: Int, size: Int, key: Int, keyAddition: Int, iterations: Int = 2): List { - var indexerMut = indexer - val returnArray = mutableListOf() - - for (i in 0 until size) { - returnArray.add(i) - } - - for (i in 0 until size) { - for (o in 0 until iterations) { - indexerMut = (indexerMut * key + keyAddition) % size - - val tmp = returnArray[indexerMut] - returnArray[indexerMut] = returnArray[i] - returnArray[i] = tmp - } - } - - return returnArray - } - - private fun unfuckJs(jsf: String): String { - // String: ([]+[]) - // fontcolor: (![]+[])[+[]]+({}+[])[+!![]]+([][[]]+[])[+!![]]+(!![]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]] - // "undefined": []+[][[]] - // Quick hack so QuickJS doesn't complain about function being called with no args - val input = jsf.replace( - "([]+[])[(![]+[])[+[]]+({}+[])[+!![]]+([][[]]+[])[+!![]]+(!![]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]]()", - "([]+[])[(![]+[])[+[]]+({}+[])[+!![]]+([][[]]+[])[+!![]]+(!![]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]]([]+[][[]])", - ) - return QuickJs.create().use { - it.execute(jsfBoilerplate) - it.evaluate("get_img_data(${input.removePrefix("[]").removeSuffix("()")}[0])").toString() - } - } - - private val jsfBoilerplate: ByteArray by lazy { - QuickJs.create().use { - it.compile( - """ - var _0x56dfa1=_0x217c;(function(_0x458c1d,_0x5a2370){var _0x4d7856=_0x217c,_0x1fa20f=_0x458c1d();while(!![]){try{var _0x34da05=-parseInt(_0x4d7856(0xac))/0x1+-parseInt(_0x4d7856(0xbc))/0x2*(-parseInt(_0x4d7856(0xb3))/0x3)+-parseInt(_0x4d7856(0xb8))/0x4+-parseInt(_0x4d7856(0xbb))/0x5*(parseInt(_0x4d7856(0xbd))/0x6)+parseInt(_0x4d7856(0xba))/0x7*(-parseInt(_0x4d7856(0xae))/0x8)+-parseInt(_0x4d7856(0xb0))/0x9+-parseInt(_0x4d7856(0xb9))/0xa*(-parseInt(_0x4d7856(0xaf))/0xb);if(_0x34da05===_0x5a2370)break;else _0x1fa20f['push'](_0x1fa20f['shift']());}catch(_0x7dd169){_0x1fa20f['push'](_0x1fa20f['shift']());}}}(_0x3c3e,0x4ade6));class Location{constructor(_0x2d256a){var _0x3e4741=_0x217c;this[_0x3e4741(0xaa)]=_0x2d256a;}[_0x56dfa1(0xb7)](){var _0x6040fb=_0x56dfa1;return this[_0x6040fb(0xaa)];}}function _0x3c3e(){var _0x27ebfd=['mtu0nJCYmLfWu2Hpvq','C3rYAw5NAwz5','z2v0x2LTz19KyxrH','mJfyB3f3sxq','Bg9JyxrPB24','Ahr0Chm6lY8','sLnptG','Dg9tDhjPBMC','nta3mZq0rhbJv1jR','mtyYotaWnZb4rxnwAeO','mtK2nJa5qMHUBMTW','mJqYndaWnwjTsu5nAq','ndGWmtjpAgv4sKu','nKrSy1Pcza','AhjLzG','zxzHBa','mte3mdy1ywTgEuLY','z2v0x2LTzW','mty4vLP5D05q','mtfIBgDhyue'];_0x3c3e=function(){return _0x27ebfd;};return _0x3c3e();}function _0x217c(_0x3aa1f3,_0x1793f5){var _0x3c3edb=_0x3c3e();return _0x217c=function(_0x217ce0,_0x3d5a46){_0x217ce0=_0x217ce0-0xaa;var _0x14b4f0=_0x3c3edb[_0x217ce0];if(_0x217c['DsHVWi']===undefined){var _0x59277=function(_0x2d256a){var _0x54ceb8='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x41845c='',_0x15e7f4='';for(var _0x269688=0x0,_0x3cc079,_0x25d416,_0xa002fc=0x0;_0x25d416=_0x2d256a['charAt'](_0xa002fc++);~_0x25d416&&(_0x3cc079=_0x269688%0x4?_0x3cc079*0x40+_0x25d416:_0x25d416,_0x269688++%0x4)?_0x41845c+=String['fromCharCode'](0xff&_0x3cc079>>(-0x2*_0x269688&0x6)):0x0){_0x25d416=_0x54ceb8['indexOf'](_0x25d416);}for(var _0x1d6105=0x0,_0x225416=_0x41845c['length'];_0x1d6105<_0x225416;_0x1d6105++){_0x15e7f4+='%'+('00'+_0x41845c['charCodeAt'](_0x1d6105)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x15e7f4);};_0x217c['fyYiDX']=_0x59277,_0x3aa1f3=arguments,_0x217c['DsHVWi']=!![];}var _0x19df2c=_0x3c3edb[0x0],_0x478efb=_0x217ce0+_0x19df2c,_0x5309ed=_0x3aa1f3[_0x478efb];return!_0x5309ed?(_0x14b4f0=_0x217c['fyYiDX'](_0x14b4f0),_0x3aa1f3[_0x478efb]=_0x14b4f0):_0x14b4f0=_0x5309ed,_0x14b4f0;},_0x217c(_0x3aa1f3,_0x1793f5);}this[_0x56dfa1(0xb4)]=new Location(_0x56dfa1(0xb5)),this[_0x56dfa1(0xad)]=(..._0x54ceb8)=>[..._0x54ceb8],this[_0x56dfa1(0xb2)]=_0x41845c=>this[_0x56dfa1(0xb6)][_0x56dfa1(0xb1)](this[_0x56dfa1(0xab)](_0x41845c)); - """.trimIndent(), - "?", - ) - } - } - - companion object { - const val DESCRAMBLE = "descramble" - } -}