stevenyomi 2ba79a0094
Add MangaFire and create MangaReader multisrc (#16138)
* Add MangaFire.to

* Fixes
Updated 'searchMangaNextPageSelector'
Added throwing 'UnsupportedOperationException' for unused selector

* Fixed naming of filters and fixed parameter's name of minimal chapters' filter

* Fixed language parameter in query

* Clean up MangaFire filters

* Clean up MangaFire descrambler

* Create MangaReader multisrc theme

* Move MangaFire to overrides

* Refactor MangaFire for multisrc

* Update MangaReader changelog

* Remove duplicate filter entry

Co-authored-by: Druzai <70586473+Druzai@users.noreply.github.com>

---------

Co-authored-by: Druzai <g9code@yandex.ru>
Co-authored-by: Druzai <70586473+Druzai@users.noreply.github.com>
2023-04-23 19:12:26 -04:00

80 lines
2.8 KiB
Kotlin

package eu.kanade.tachiyomi.extension.all.mangafire
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Rect
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.io.ByteArrayOutputStream
import java.io.InputStream
import kotlin.math.min
object ImageInterceptor : Interceptor {
const val SCRAMBLED = "scrambled"
private const val PIECE_SIZE = 200
private const val MIN_SPLIT_COUNT = 5
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val fragment = request.url.fragment ?: return response
if (SCRAMBLED !in fragment) return response
val offset = fragment.substringAfterLast('_').toInt()
val image = response.body.byteStream().use { descramble(it, offset) }
val body = image.toResponseBody("image/jpeg".toMediaType())
return response.newBuilder().body(body).build()
}
private fun descramble(image: InputStream, offset: Int): ByteArray {
// obfuscated code: https://mangafire.to/assets/t1/min/all.js
// it shuffles arrays of the image slices
val bitmap = BitmapFactory.decodeStream(image)
val width = bitmap.width
val height = bitmap.height
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
val pieceWidth = min(PIECE_SIZE, width.ceilDiv(MIN_SPLIT_COUNT))
val pieceHeight = min(PIECE_SIZE, height.ceilDiv(MIN_SPLIT_COUNT))
val xMax = width.ceilDiv(pieceWidth) - 1
val yMax = height.ceilDiv(pieceHeight) - 1
for (y in 0..yMax) {
for (x in 0..xMax) {
val xDst = pieceWidth * x
val yDst = pieceHeight * y
val w = min(pieceWidth, width - xDst)
val h = min(pieceHeight, height - yDst)
val xSrc = pieceWidth * when (x) {
xMax -> x // margin
else -> (xMax - x + offset) % xMax
}
val ySrc = pieceHeight * when (y) {
yMax -> y // margin
else -> (yMax - y + offset) % yMax
}
val srcRect = Rect(xSrc, ySrc, xSrc + w, ySrc + h)
val dstRect = Rect(xDst, yDst, xDst + w, yDst + h)
canvas.drawBitmap(bitmap, srcRect, dstRect, null)
}
}
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 90, output)
return output.toByteArray()
}
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.ceilDiv(other: Int) = (this + (other - 1)) / other
}