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:
parent
d55b796c50
commit
fa359b535b
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'BoyLove'
|
extName = 'BoyLove'
|
||||||
extClass = '.BoyLove'
|
extClass = '.BoyLove'
|
||||||
extVersionCode = 9
|
extVersionCode = 10
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue