From ac980a4dbfa3400af4a18e975ca3f8937bb4a336 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Fri, 8 Apr 2022 23:10:06 +0700 Subject: [PATCH] MangaCoverFetcher: Handle moving cover cache after adding to library (#6885) Move cover cache to separate cache dir after the parent manga is added to library --- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 0c8b6f43c..59de4bdf5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -16,12 +16,15 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.system.logcat +import logcat.LogPriority import okhttp3.CacheControl import okhttp3.Call import okhttp3.Request import okhttp3.Response import okhttp3.internal.closeQuietly import okio.Path.Companion.toOkioPath +import okio.Source import okio.buffer import okio.sink import uy.kohesive.injekt.injectLazy @@ -78,19 +81,26 @@ class MangaCoverFetcher( private suspend fun httpLoader(): FetchResult { // Only cache separately if it's a library item - val coverCacheFile = if (manga.favorite) { + val libraryCoverCacheFile = if (manga.favorite) { coverCache.getCoverFile(manga) ?: error("No cover specified") } else { null } - if (coverCacheFile?.exists() == true && options.diskCachePolicy.readEnabled) { - return fileLoader(coverCacheFile) + if (libraryCoverCacheFile?.exists() == true && options.diskCachePolicy.readEnabled) { + return fileLoader(libraryCoverCacheFile) } var snapshot = readFromDiskCache() try { // Fetch from disk cache if (snapshot != null) { + val snapshotCoverCache = moveSnapshotToCoverCache(snapshot, libraryCoverCacheFile) + if (snapshotCoverCache != null) { + // Read from cover cache after added to library + return fileLoader(snapshotCoverCache) + } + + // Read from snapshot return SourceResult( source = snapshot.toImageSource(), mimeType = "image/*", @@ -102,13 +112,14 @@ class MangaCoverFetcher( val response = executeNetworkRequest() val responseBody = checkNotNull(response.body) { "Null response source" } try { - snapshot = writeToDiskCache(snapshot, response) - - if (coverCacheFile != null) { - writeToCoverCache(coverCacheFile, response) + // Read from cover cache after library manga cover updated + val responseCoverCache = writeResponseToCoverCache(response, libraryCoverCacheFile) + if (responseCoverCache != null) { + return fileLoader(responseCoverCache) } // Read from disk cache + snapshot = writeToDiskCache(snapshot, response) if (snapshot != null) { return SourceResult( source = snapshot.toImageSource(), @@ -133,21 +144,6 @@ class MangaCoverFetcher( } } - private fun writeToCoverCache(cacheFile: File, response: Response) { - if (!options.diskCachePolicy.writeEnabled) return - try { - response.body!!.source().use { input -> - cacheFile.parentFile?.mkdirs() - if (cacheFile.exists()) { - cacheFile.delete() - } - cacheFile.sink().buffer().use { output -> - output.writeAll(input) - } - } - } catch (_: Exception) {} - } - private suspend fun executeNetworkRequest(): Response { val client = sourceLazy.value?.client ?: callFactoryLazy.value val response = client.newCall(newRequest()).await() @@ -185,6 +181,48 @@ class MangaCoverFetcher( return request.build() } + private fun moveSnapshotToCoverCache(snapshot: DiskCache.Snapshot, cacheFile: File?): File? { + if (cacheFile == null) return null + return try { + diskCacheLazy.value.run { + fileSystem.source(snapshot.data).use { input -> + writeSourceToCoverCache(input, cacheFile) + } + remove(diskCacheKey!!) + } + cacheFile.takeIf { it.exists() } + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) { "Failed to write snapshot data to cover cache ${cacheFile.name}" } + null + } + } + + private fun writeResponseToCoverCache(response: Response, cacheFile: File?): File? { + if (cacheFile == null || !options.diskCachePolicy.writeEnabled) return null + return try { + response.peekBody(Long.MAX_VALUE).source().use { input -> + writeSourceToCoverCache(input, cacheFile) + } + cacheFile.takeIf { it.exists() } + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) { "Failed to write response data to cover cache ${cacheFile.name}" } + null + } + } + + private fun writeSourceToCoverCache(input: Source, cacheFile: File) { + cacheFile.parentFile?.mkdirs() + cacheFile.delete() + try { + cacheFile.sink().buffer().use { output -> + output.writeAll(input) + } + } catch (e: Exception) { + cacheFile.delete() + throw e + } + } + private fun readFromDiskCache(): DiskCache.Snapshot? { return if (options.diskCachePolicy.readEnabled) diskCacheLazy.value[diskCacheKey!!] else null }