Check if page file valid before page display

pull/377/head
Koitharu 3 years ago
parent 12d8d3e2d1
commit 02d5dfb375
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -26,6 +26,8 @@ fun File.takeIfReadable() = takeIf { it.exists() && it.canRead() }
fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() } fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() }
fun File.isNotEmpty() = length() != 0L
fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use { fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use {
it.readText() it.readText()
} }

@ -87,3 +87,6 @@ fun Throwable.isWebViewUnavailable(): Boolean {
return (this is AndroidRuntimeException && message?.contains("WebView") == true) || return (this is AndroidRuntimeException && message?.contains("WebView") == true) ||
cause?.isWebViewUnavailable() == true cause?.isWebViewUnavailable() == true
} }
@Suppress("FunctionName")
fun NoSpaceLeftException() = IOException(MSG_NO_SPACE_LEFT)

@ -1,6 +1,7 @@
package org.koitharu.kotatsu.local.data package org.koitharu.kotatsu.local.data
import android.content.Context import android.content.Context
import android.os.StatFs
import com.tomclaw.cache.DiskLruCache import com.tomclaw.cache.DiskLruCache
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -33,7 +34,8 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
} }
private val lruCache = SuspendLazy { private val lruCache = SuspendLazy {
val dir = cacheDir.get() val dir = cacheDir.get()
val size = FileSize.MEGABYTES.convert(200, FileSize.BYTES) val availableSize = getAvailableSizeMb()
val size = SIZE_DEFAULT.coerceIn(SIZE_MIN, availableSize)
runCatchingCancellable { runCatchingCancellable {
DiskLruCache.create(dir, size) DiskLruCache.create(dir, size)
}.recoverCatching { error -> }.recoverCatching { error ->
@ -63,4 +65,20 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
file.delete() file.delete()
} }
} }
private suspend fun getAvailableSizeMb(): Long = runCatchingCancellable {
val statFs = StatFs(cacheDir.get().absolutePath)
FileSize.BYTES.convert(statFs.availableBytes, FileSize.MEGABYTES)
}.onFailure {
it.printStackTraceDebug()
}.getOrDefault(SIZE_DEFAULT)
private companion object {
val SIZE_MIN
get() = FileSize.MEGABYTES.convert(20, FileSize.BYTES)
val SIZE_DEFAULT
get() = FileSize.MEGABYTES.convert(200, FileSize.BYTES)
}
} }

@ -12,6 +12,7 @@ import dagger.hilt.android.lifecycle.RetainedLifecycle
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ActivityRetainedScoped import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -32,6 +33,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope
import org.koitharu.kotatsu.core.util.ext.ensureSuccess import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.isNotEmpty
import org.koitharu.kotatsu.core.util.ext.ramAvailable import org.koitharu.kotatsu.core.util.ext.ramAvailable
import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.ext.withProgress
import org.koitharu.kotatsu.core.util.progress.ProgressDeferred import org.koitharu.kotatsu.core.util.progress.ProgressDeferred
@ -103,7 +105,7 @@ class PageLoader @Inject constructor(
} }
fun loadPageAsync(page: MangaPage, force: Boolean): ProgressDeferred<File, Float> { fun loadPageAsync(page: MangaPage, force: Boolean): ProgressDeferred<File, Float> {
var task = tasks[page.id] var task = tasks[page.id]?.takeIf { it.isValid() }
if (force) { if (force) {
task?.cancel() task?.cancel()
} else if (task?.isCancelled == false) { } else if (task?.isCancelled == false) {
@ -216,6 +218,15 @@ class PageLoader @Inject constructor(
return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES) return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES)
} }
private fun Deferred<File>.isValid(): Boolean {
return if (isCompleted) {
val file = getCompleted()
file.exists() && file.isNotEmpty()
} else {
true
}
}
private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
CoroutineExceptionHandler { CoroutineExceptionHandler {

Loading…
Cancel
Save