Fix pages loading issues

This commit is contained in:
Koitharu
2024-10-21 13:34:33 +03:00
parent 9559e148c6
commit 5bccc595a8
6 changed files with 48 additions and 19 deletions

View File

@@ -136,7 +136,7 @@ dependencies {
implementation 'io.coil-kt:coil-base:2.7.0'
implementation 'io.coil-kt:coil-svg:2.7.0'
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:e04098de68'
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:ac7360c5e3'
implementation 'com.github.solkin:disk-lru-cache:1.4'
implementation 'io.noties.markwon:core:4.6.2'

View File

@@ -26,8 +26,10 @@ class ProgressResponseBody(
override fun contentType(): MediaType? = delegate.contentType()
override fun source(): BufferedSource {
return bufferedSource ?: ProgressSource(delegate.source(), contentLength(), progressState).buffer().also {
bufferedSource = it
return bufferedSource ?: synchronized(this) {
bufferedSource ?: ProgressSource(delegate.source(), contentLength(), progressState).buffer().also {
bufferedSource = it
}
}
}

View File

@@ -104,10 +104,9 @@ class MangaPageFetcher(
if (!response.isSuccessful) {
throw HttpException(response)
}
val body = response.requireBody()
val mimeType = response.mimeType
val file = body.use {
pagesCache.put(pageUrl, it.source())
val file = response.requireBody().use {
pagesCache.put(pageUrl, it.source(), mimeType)
}
SourceResult(
source = ImageSource(

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.local.data
import android.content.Context
import android.graphics.Bitmap
import android.os.StatFs
import android.webkit.MimeTypeMap
import com.tomclaw.cache.DiskLruCache
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
@@ -15,7 +16,7 @@ import okio.use
import org.koitharu.kotatsu.core.exceptions.NoDataReceivedException
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ext.compressToPNG
import org.koitharu.kotatsu.core.util.ext.longHashCode
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.subdir
import org.koitharu.kotatsu.core.util.ext.takeIfReadable
@@ -24,6 +25,7 @@ import org.koitharu.kotatsu.core.util.ext.writeAllCancellable
import org.koitharu.kotatsu.parsers.util.SuspendLazy
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.io.File
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
@@ -50,15 +52,15 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
}.getOrThrow()
}
suspend fun get(url: String): File? {
suspend fun get(url: String): File? = withContext(Dispatchers.IO) {
val cache = lruCache.get()
return runInterruptible(Dispatchers.IO) {
runInterruptible {
cache.get(url)?.takeIfReadable()
}
}
suspend fun put(url: String, source: Source): File = withContext(Dispatchers.IO) {
val file = File(cacheDir.get().parentFile, url.longHashCode().toString())
suspend fun put(url: String, source: Source, mimeType: String?): File = withContext(Dispatchers.IO) {
val file = createBufferFile(url, mimeType)
try {
val bytes = file.sink(append = false).buffer().use {
it.writeAllCancellable(source)
@@ -66,17 +68,23 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
if (bytes == 0L) {
throw NoDataReceivedException(url)
}
lruCache.get().put(url, file)
val cache = lruCache.get()
runInterruptible {
cache.put(url, file)
}
} finally {
file.delete()
}
}
suspend fun put(url: String, bitmap: Bitmap): File = withContext(Dispatchers.IO) {
val file = File(cacheDir.get().parentFile, url.longHashCode().toString())
val file = createBufferFile(url, "image/png")
try {
bitmap.compressToPNG(file)
lruCache.get().put(url, file)
val cache = lruCache.get()
runInterruptible {
cache.put(url, file)
}
} finally {
file.delete()
}
@@ -90,12 +98,24 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
}
private suspend fun getAvailableSize(): Long = runCatchingCancellable {
val statFs = StatFs(cacheDir.get().absolutePath)
statFs.availableBytes
val dir = cacheDir.get()
runInterruptible(Dispatchers.IO) {
val statFs = StatFs(dir.absolutePath)
statFs.availableBytes
}
}.onFailure {
it.printStackTraceDebug()
}.getOrDefault(SIZE_DEFAULT)
private suspend fun createBufferFile(url: String, mimeType: String?): File {
val ext = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) }
?: MimeTypeMap.getFileExtensionFromUrl(url).ifNullOrEmpty { "dat" }
val cacheDir = cacheDir.get()
val rootDir = checkNotNull(cacheDir.parentFile) { "Cannot get parent for ${cacheDir.absolutePath}" }
val name = UUID.randomUUID().toString() + "." + ext
return File(rootDir, name)
}
private companion object {
val SIZE_MIN

View File

@@ -3,8 +3,10 @@ package org.koitharu.kotatsu.reader.domain
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import androidx.annotation.AnyThread
import androidx.collection.LongSparseArray
import androidx.collection.set
@@ -56,6 +58,7 @@ import org.koitharu.kotatsu.local.data.isFileUri
import org.koitharu.kotatsu.local.data.isZipUri
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mimeType
import org.koitharu.kotatsu.parsers.util.requireBody
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
@@ -149,9 +152,13 @@ class PageLoader @Inject constructor(
cache.put(uri.toString(), bitmap).toUri()
} else {
val file = uri.toFile()
context.ensureRamAtLeast(file.length() * 2)
runInterruptible(Dispatchers.IO) {
checkBitmapNotNull(BitmapFactory.decodeFile(file.absolutePath))
context.ensureRamAtLeast(file.length() * 2)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(file))
} else {
checkBitmapNotNull(BitmapFactory.decodeFile(file.absolutePath))
}
}.use { image ->
image.compressToPNG(file)
}
@@ -235,7 +242,7 @@ class PageLoader @Inject constructor(
val request = createPageRequest(pageUrl, page.source)
imageProxyInterceptor.interceptPageRequest(request, okHttp).ensureSuccess().use { response ->
response.requireBody().withProgress(progress).use {
cache.put(pageUrl, it.source())
cache.put(pageUrl, it.source(), response.mimeType)
}
}.toUri()
}

View File

@@ -152,6 +152,7 @@ class PageHolderDelegate(
} catch (ce: CancellationException) {
throw ce
} catch (e2: Throwable) {
e2.printStackTrace()
e.addSuppressed(e2)
state = State.ERROR
callback.onError(e)