Merge branch 'master' into devel

This commit is contained in:
Koitharu
2024-12-19 18:30:16 +02:00
5 changed files with 26 additions and 12 deletions

View File

@@ -3,5 +3,5 @@ package org.koitharu.kotatsu.core.exceptions
import okio.IOException
class NoDataReceivedException(
url: String,
val url: String,
) : IOException("No data has been received from $url")

View File

@@ -5,6 +5,7 @@ import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.os.Build
import android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi
import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -32,19 +33,21 @@ object BitmapDecoderCompat {
}
@Blocking
fun decode(stream: InputStream, type: MediaType?): Bitmap {
fun decode(stream: InputStream, type: MediaType?, isMutable: Boolean = false): Bitmap {
val format = type?.subtype
if (format == FORMAT_AVIF) {
return decodeAvif(stream.toByteBuffer())
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return checkBitmapNotNull(BitmapFactory.decodeStream(stream), format)
val opts = BitmapFactory.Options()
opts.inMutable = isMutable
return checkBitmapNotNull(BitmapFactory.decodeStream(stream, null, opts), format)
}
val byteBuffer = stream.toByteBuffer()
return if (AvifDecoder.isAvifImage(byteBuffer)) {
decodeAvif(byteBuffer)
} else {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(byteBuffer))
ImageDecoder.decodeBitmap(ImageDecoder.createSource(byteBuffer), DecoderConfigListener(isMutable))
}
}
@@ -74,4 +77,18 @@ object BitmapDecoderCompat {
}
return bitmap
}
@RequiresApi(Build.VERSION_CODES.P)
private class DecoderConfigListener(
private val isMutable: Boolean,
) : ImageDecoder.OnHeaderDecodedListener {
override fun onHeaderDecoded(
decoder: ImageDecoder,
info: ImageDecoder.ImageInfo,
source: ImageDecoder.Source
) {
decoder.isMutableRequired = isMutable
}
}
}

View File

@@ -2,12 +2,10 @@ package org.koitharu.kotatsu.core.parser
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.BitmapFactory
import android.util.Base64
import android.webkit.WebView
import androidx.annotation.MainThread
import androidx.core.os.LocaleListCompat
import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
@@ -17,6 +15,7 @@ import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.ResponseBody.Companion.asResponseBody
import okio.Buffer
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.prefs.SourceSettings
@@ -31,7 +30,6 @@ import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.util.map
import org.koitharu.kotatsu.parsers.util.mimeType
import java.lang.ref.WeakReference
import java.util.Locale
import javax.inject.Inject
@@ -80,15 +78,13 @@ class MangaLoaderContextImpl @Inject constructor(
override fun redrawImageResponse(response: Response, redraw: (image: Bitmap) -> Bitmap): Response {
return response.map { body ->
val opts = BitmapFactory.Options()
opts.inMutable = true
BitmapFactory.decodeStream(body.byteStream(), null, opts)?.use { bitmap ->
BitmapDecoderCompat.decode(body.byteStream(), body.contentType(), isMutable = true).use { bitmap ->
(redraw(BitmapWrapper.create(bitmap)) as BitmapWrapper).use { result ->
Buffer().also {
result.compressTo(it.outputStream())
}.asResponseBody("image/jpeg".toMediaType())
}
} ?: throw ImageDecodeException(response.request.url.toString(), response.mimeType)
}
}
}

View File

@@ -142,6 +142,7 @@ fun Throwable.getCauseUrl(): String? = when (this) {
is NotFoundException -> url
is TooManyRequestExceptions -> url
is CaughtException -> cause?.getCauseUrl()
is NoDataReceivedException -> url
is CloudFlareBlockedException -> url
is CloudFlareProtectedException -> url
is HttpStatusException -> url