zh/BoyLove: Fix scrambled images (#548)

* fix: Fix scrambled images

* chore: Bump version

* refactor: Apply code suggestions

* refactor: Remove unnecessary .use block
This commit is contained in:
Claudemirovsky 2024-01-24 00:56:49 -03:00 committed by Draff
parent d55b796c50
commit fa359b535b
3 changed files with 101 additions and 6 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'BoyLove' extName = 'BoyLove'
extClass = '.BoyLove' extClass = '.BoyLove'
extVersionCode = 9 extVersionCode = 10
isNsfw = true isNsfw = true
} }

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.zh.boylove package eu.kanade.tachiyomi.extension.zh.boylove
import android.app.Application import android.app.Application
import android.content.SharedPreferences
import android.util.Log import android.util.Log
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
@ -19,8 +18,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.select.Evaluator import org.jsoup.select.Evaluator
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -37,8 +38,8 @@ class BoyLove : HttpSource(), ConfigurableSource {
private val json: Json by injectLazy() private val json: Json by injectLazy()
override val baseUrl = run { override val baseUrl by lazy {
val preferences: SharedPreferences = val preferences =
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
val mirrors = MIRRORS val mirrors = MIRRORS
@ -48,6 +49,7 @@ class BoyLove : HttpSource(), ConfigurableSource {
override val client = network.cloudflareClient.newBuilder() override val client = network.cloudflareClient.newBuilder()
.rateLimit(2) .rateLimit(2)
.addInterceptor(UnscramblerInterceptor())
.build() .build()
override fun popularMangaRequest(page: Int): Request = override fun popularMangaRequest(page: Int): Request =
@ -111,7 +113,8 @@ class BoyLove : HttpSource(), ConfigurableSource {
private fun fetchPageList(chapterUrl: String): Observable<List<Page>> = private fun fetchPageList(chapterUrl: String): Observable<List<Page>> =
client.newCall(GET(baseUrl + chapterUrl, headers)).asObservableSuccess().map { response -> client.newCall(GET(baseUrl + chapterUrl, headers)).asObservableSuccess().map { response ->
val root = response.asJsoup().selectFirst(Evaluator.Tag("section"))!! val doc = response.asJsoup()
val root = doc.selectFirst(Evaluator.Tag("section"))!!
val images = root.select(Evaluator.Class("reader-cartoon-image")) val images = root.select(Evaluator.Class("reader-cartoon-image"))
val urlList = if (images.isEmpty()) { val urlList = if (images.isEmpty()) {
root.select(Evaluator.Tag("img")).map { it.attr("src").trim().toImageUrl() } root.select(Evaluator.Tag("img")).map { it.attr("src").trim().toImageUrl() }
@ -121,9 +124,31 @@ class BoyLove : HttpSource(), ConfigurableSource {
.filter { it.attr("src").endsWith("load.png") } .filter { it.attr("src").endsWith("load.png") }
.map { it.attr("data-original").trim().toImageUrl() } .map { it.attr("data-original").trim().toImageUrl() }
} }
urlList.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } val parts = doc.getPartsCount()
urlList.mapIndexed { index, imageUrl ->
val url = if (parts == null) {
imageUrl
} else {
imageUrl.toHttpUrl().newBuilder()
.addQueryParameter(UnscramblerInterceptor.PARTS_COUNT_PARAM, parts.toString())
.build()
.toString()
}
Page(index, imageUrl = url)
}
} }
private fun Document.getPartsCount(): Int? {
return selectFirst("script:containsData(do_mergeImg):containsData(context0 =)")?.data()?.run {
substringBefore("canvas0.width")
.substringAfterLast("var ")
.substringBefore(';')
.trim()
.substringAfterLast(" ")
.toIntOrNull()
}
}
override fun pageListParse(response: Response) = throw UnsupportedOperationException() override fun pageListParse(response: Response) = throw UnsupportedOperationException()
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()

View File

@ -0,0 +1,70 @@
package eu.kanade.tachiyomi.extension.zh.boylove
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
class UnscramblerInterceptor : Interceptor {
companion object {
const val PARTS_COUNT_PARAM = "scrambled_parts_count"
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val parts = request.url.queryParameter(PARTS_COUNT_PARAM)?.toIntOrNull()
return if (parts == null) {
chain.proceed(request)
} else {
val newRequest = request.newBuilder()
.url(request.url.newBuilder().removeAllQueryParameters(PARTS_COUNT_PARAM).build())
.build()
val response = chain.proceed(newRequest)
val image = response.body.byteStream().use { descramble(it, parts) }
val body = image.toResponseBody("image/jpeg".toMediaType())
response.newBuilder().body(body).build()
}
}
private fun descramble(image: InputStream, partsCount: Int): ByteArray {
val srcBitmap = BitmapFactory.decodeStream(image)
val width = srcBitmap.width
val height = srcBitmap.height
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
for (partIndex in 1..partsCount) {
if (height >= 4000) {
val stripWidth = width / partsCount
val x1 = stripWidth * (partIndex - 1)
val rect = Rect(x1, 0, x1 + stripWidth, height)
canvas.drawBitmap(srcBitmap, rect, rect, null)
} else if (partIndex == partsCount) {
val stripWidth = width - ((width / partsCount) * (partsCount - 1))
val rectSrc = Rect(0, 0, stripWidth, height)
val rectDst = Rect(width - stripWidth, 0, width, height)
canvas.drawBitmap(srcBitmap, rectSrc, rectDst, null)
} else {
val stripWidth = width / partsCount
val xSrc = width - (stripWidth * partIndex)
val rectSrc = Rect(xSrc, 0, xSrc + stripWidth, height)
val xDst = stripWidth * (partIndex - 1)
val rectDst = Rect(xDst, 0, xDst + stripWidth, height)
canvas.drawBitmap(srcBitmap, rectSrc, rectDst, null)
}
}
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 90, output)
return output.toByteArray()
}
}