xCaliBR Scans: Fix Split and Mirrored Images (#10250)
This commit is contained in:
parent
6b9b581d8d
commit
190e1410e3
|
@ -0,0 +1,61 @@
|
|||
package eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class MirrorImageInterceptor : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
if (!chain.request().url.toString().endsWith(MIRRORED_IMAGE_SUFFIX)) {
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
val imageUrl = chain.request().url.toString()
|
||||
.removeSuffix(MIRRORED_IMAGE_SUFFIX)
|
||||
|
||||
val request = chain.request().newBuilder().url(imageUrl).build()
|
||||
val response = chain.proceed(request)
|
||||
|
||||
val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream())
|
||||
|
||||
val result = bitmap.flipHorizontally()
|
||||
|
||||
val output = ByteArrayOutputStream()
|
||||
result.compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||
|
||||
val responseBody = output.toByteArray().toResponseBody("image/png".toMediaType())
|
||||
|
||||
return Response.Builder()
|
||||
.code(200)
|
||||
.protocol(Protocol.HTTP_1_1)
|
||||
.request(chain.request())
|
||||
.message("OK")
|
||||
.body(responseBody)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun Bitmap.flipHorizontally(): Bitmap {
|
||||
val matrix = Matrix().apply {
|
||||
postScale(
|
||||
-1F,
|
||||
1F,
|
||||
this@flipHorizontally.width / 2F,
|
||||
this@flipHorizontally.height / 2F
|
||||
)
|
||||
}
|
||||
return Bitmap.createBitmap(this, 0, 0, this.width, this.height, matrix, true)
|
||||
}
|
||||
}
|
||||
|
||||
const val MIRRORED_IMAGE_SUFFIX = "?mirrored"
|
||||
|
||||
fun String.prepareMirrorImageForInterceptor(): String {
|
||||
return "$this$MIRRORED_IMAGE_SUFFIX"
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor
|
||||
|
||||
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.Protocol
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class SplittedImageInterceptor : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
if (!chain.request().url.toString().endsWith(SPLITTED_IMAGE_SUFFIX)) {
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
val imageUrls = chain.request().url.toString()
|
||||
.removeSuffix(SPLITTED_IMAGE_SUFFIX)
|
||||
.split("%7C")
|
||||
|
||||
var width = 0
|
||||
var height = 0
|
||||
|
||||
val imageBitmaps = imageUrls.map { imageUrl ->
|
||||
val request = chain.request().newBuilder().url(imageUrl).build()
|
||||
val response = chain.proceed(request)
|
||||
|
||||
val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream())
|
||||
|
||||
width += bitmap.width
|
||||
height = bitmap.height
|
||||
|
||||
bitmap
|
||||
}
|
||||
|
||||
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(result)
|
||||
|
||||
var left = 0
|
||||
|
||||
imageBitmaps.forEach { bitmap ->
|
||||
val srcRect = Rect(0, 0, bitmap.width, bitmap.height)
|
||||
val dstRect = Rect(left, 0, left + bitmap.width, bitmap.height)
|
||||
|
||||
canvas.drawBitmap(bitmap, srcRect, dstRect, null)
|
||||
|
||||
left += bitmap.width
|
||||
}
|
||||
|
||||
val output = ByteArrayOutputStream()
|
||||
result.compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||
|
||||
val responseBody = output.toByteArray().toResponseBody("image/png".toMediaType())
|
||||
|
||||
return Response.Builder()
|
||||
.code(200)
|
||||
.protocol(Protocol.HTTP_1_1)
|
||||
.request(chain.request())
|
||||
.message("OK")
|
||||
.body(responseBody)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
const val SPLITTED_IMAGE_SUFFIX = "?splitted"
|
||||
|
||||
fun List<String>.prepareSplittedImageForInterceptor(): String {
|
||||
return "${this.joinToString("|")}$SPLITTED_IMAGE_SUFFIX"
|
||||
}
|
|
@ -1,18 +1,92 @@
|
|||
package eu.kanade.tachiyomi.extension.en.xcalibrscans
|
||||
|
||||
import eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor.MirrorImageInterceptor
|
||||
import eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor.SplittedImageInterceptor
|
||||
import eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor.prepareMirrorImageForInterceptor
|
||||
import eu.kanade.tachiyomi.extension.en.xcalibrscans.interceptor.prepareSplittedImageForInterceptor
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jsoup.nodes.Document
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class xCaliBRScans : WPMangaStream("xCaliBR Scans", "https://xcalibrscans.com", "en") {
|
||||
private val rateLimitInterceptor = RateLimitInterceptor(2)
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.addNetworkInterceptor(rateLimitInterceptor)
|
||||
.addNetworkInterceptor(RateLimitInterceptor(2))
|
||||
.addNetworkInterceptor(SplittedImageInterceptor())
|
||||
.addNetworkInterceptor(MirrorImageInterceptor())
|
||||
.build()
|
||||
|
||||
override val hasProjectPage = true
|
||||
|
||||
override val pageSelector = "div#readerarea > p, div#readerarea > div"
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val htmlPages = mutableListOf<Page>()
|
||||
|
||||
document.select(pageSelector)
|
||||
.filterNot {
|
||||
it.select("img").all { imgEl ->
|
||||
imgEl.attr("abs:src").isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
.map { el ->
|
||||
if (el.tagName() == "div") {
|
||||
when {
|
||||
el.hasClass("kage") -> {
|
||||
el.select("img").map { imgEl ->
|
||||
val index = htmlPages.size
|
||||
val imageUrl =
|
||||
imgEl.attr("abs:src").prepareMirrorImageForInterceptor()
|
||||
htmlPages.add(Page(index, "", imageUrl))
|
||||
}
|
||||
}
|
||||
el.hasClass("row") -> {
|
||||
val index = htmlPages.size
|
||||
val imageUrls = el.select("img").map { imgEl ->
|
||||
imgEl.attr("abs:src")
|
||||
}.prepareSplittedImageForInterceptor()
|
||||
htmlPages.add(Page(index, "", imageUrls))
|
||||
}
|
||||
else -> {
|
||||
val index = htmlPages.size
|
||||
Page(index, "", el.select("img").attr("abs:src"))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val index = htmlPages.size
|
||||
Page(index, "", el.select("img").attr("abs:src"))
|
||||
}
|
||||
}
|
||||
|
||||
val docString = document.toString()
|
||||
val imageListRegex = Regex("\\\"images.*?:.*?(\\[.*?\\])")
|
||||
val imageListJson = imageListRegex.find(docString)!!.destructured.toList()[0]
|
||||
|
||||
val imageList = json.parseToJsonElement(imageListJson).jsonArray
|
||||
val baseResolver = baseUrl.toHttpUrl()
|
||||
|
||||
val scriptPages = imageList.mapIndexed { i, jsonEl ->
|
||||
val imageUrl = jsonEl.jsonPrimitive.content
|
||||
Page(i, "", baseResolver.resolve(imageUrl).toString())
|
||||
}
|
||||
|
||||
if (htmlPages.size < scriptPages.size) {
|
||||
htmlPages += scriptPages
|
||||
}
|
||||
|
||||
countViews(document)
|
||||
|
||||
return htmlPages.distinctBy { it.imageUrl }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
|
|||
SingleLang("Mihentai", "https://mihentai.com", "en", isNsfw = true, overrideVersionCode = 1),
|
||||
SingleLang("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en", className = "KumaScans", overrideVersionCode = 1),
|
||||
SingleLang("Tempest Manga", "https://manga.tempestfansub.com", "tr"),
|
||||
SingleLang("xCaliBR Scans", "https://xcalibrscans.com", "en", overrideVersionCode = 2),
|
||||
SingleLang("xCaliBR Scans", "https://xcalibrscans.com", "en", overrideVersionCode = 3),
|
||||
SingleLang("NoxSubs", "https://noxsubs.com", "tr"),
|
||||
SingleLang("The Apollo Team", "https://theapollo.team", "en"),
|
||||
SingleLang("Sekte Doujin", "https://sektedoujin.xyz", "id", isNsfw = true, overrideVersionCode = 2),
|
||||
|
|
Loading…
Reference in New Issue