From 59837bbb903dcfc2cf824972977b6247c713507b Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Fri, 10 Jun 2022 20:33:59 +0700 Subject: [PATCH] Change cover memory key (#7276) Use different key for custom cover and add last modified time for updating cover without clearing the whole memory cache --- .../kanade/tachiyomi/data/cache/CoverCache.kt | 8 --- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 49 ++++++++++++------- .../tachiyomi/data/coil/MangaCoverKeyer.kt | 9 +++- .../data/library/LibraryUpdateService.kt | 1 - .../eu/kanade/tachiyomi/source/LocalSource.kt | 1 - .../tachiyomi/ui/manga/MangaPresenter.kt | 4 +- .../tachiyomi/ui/reader/ReaderPresenter.kt | 2 - .../kanade/tachiyomi/util/MangaExtensions.kt | 4 +- 8 files changed, 42 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 0788ef37d..6652c5898 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.data.cache import android.content.Context -import coil.imageLoader import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.storage.DiskUtil import java.io.File @@ -100,13 +99,6 @@ class CoverCache(private val context: Context) { } } - /** - * Clear coil's memory cache. - */ - fun clearMemoryCache() { - context.imageLoader.memoryCache?.clear() - } - private fun getCacheDir(dir: String): File { return context.getExternalFilesDir(dir) ?: File(context.filesDir, dir).also { it.mkdirs() } 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 870ced03e..494ab32db 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 @@ -42,28 +42,32 @@ import java.net.HttpURLConnection * - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true */ class MangaCoverFetcher( - private val manga: Manga, - private val sourceLazy: Lazy, + private val url: String?, + private val isLibraryManga: Boolean, private val options: Options, - private val coverCache: CoverCache, + private val coverFileLazy: Lazy, + private val customCoverFileLazy: Lazy, + private val diskCacheKeyLazy: Lazy, + private val sourceLazy: Lazy, private val callFactoryLazy: Lazy, private val diskCacheLazy: Lazy, ) : Fetcher { - // For non-custom cover - private val diskCacheKey: String? by lazy { MangaCoverKeyer().key(manga, options) } - private lateinit var url: String + private val diskCacheKey: String + get() = diskCacheKeyLazy.value override suspend fun fetch(): FetchResult { // Use custom cover if exists val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true - val customCoverFile = coverCache.getCustomCoverFile(manga.id) - if (useCustomCover && customCoverFile.exists()) { - return fileLoader(customCoverFile) + if (useCustomCover) { + val customCoverFile = customCoverFileLazy.value + if (customCoverFile.exists()) { + return fileLoader(customCoverFile) + } } // diskCacheKey is thumbnail_url - url = diskCacheKey ?: error("No cover specified") + if (url == null) error("No cover specified") return when (getResourceType(url)) { Type.URL -> httpLoader() Type.File -> fileLoader(File(url.substringAfter("file://"))) @@ -81,8 +85,8 @@ class MangaCoverFetcher( private suspend fun httpLoader(): FetchResult { // Only cache separately if it's a library item - val libraryCoverCacheFile = if (manga.favorite) { - coverCache.getCoverFile(manga.thumbnail_url) ?: error("No cover specified") + val libraryCoverCacheFile = if (isLibraryManga) { + coverFileLazy.value ?: error("No cover specified") } else { null } @@ -156,7 +160,7 @@ class MangaCoverFetcher( private fun newRequest(): Request { val request = Request.Builder() - .url(url) + .url(url!!) .headers(sourceLazy.value?.headers ?: options.headers) // Support attaching custom data to the network request. .tag(Parameters::class.java, options.parameters) @@ -188,7 +192,7 @@ class MangaCoverFetcher( fileSystem.source(snapshot.data).use { input -> writeSourceToCoverCache(input, cacheFile) } - remove(diskCacheKey!!) + remove(diskCacheKey) } cacheFile.takeIf { it.exists() } } catch (e: Exception) { @@ -224,7 +228,7 @@ class MangaCoverFetcher( } private fun readFromDiskCache(): DiskCache.Snapshot? { - return if (options.diskCachePolicy.readEnabled) diskCacheLazy.value[diskCacheKey!!] else null + return if (options.diskCachePolicy.readEnabled) diskCacheLazy.value[diskCacheKey] else null } private fun writeToDiskCache( @@ -238,7 +242,7 @@ class MangaCoverFetcher( val editor = if (snapshot != null) { snapshot.closeAndEdit() } else { - diskCacheLazy.value.edit(diskCacheKey!!) + diskCacheLazy.value.edit(diskCacheKey) } ?: return null try { diskCacheLazy.value.fileSystem.write(editor.data) { @@ -280,8 +284,17 @@ class MangaCoverFetcher( private val sourceManager: SourceManager by injectLazy() override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher { - val source = lazy { sourceManager.get(data.source) as? HttpSource } - return MangaCoverFetcher(data, source, options, coverCache, callFactoryLazy, diskCacheLazy) + return MangaCoverFetcher( + url = data.thumbnail_url, + isLibraryManga = data.favorite, + options = options, + coverFileLazy = lazy { coverCache.getCoverFile(data.thumbnail_url) }, + customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.id) }, + diskCacheKeyLazy = lazy { MangaCoverKeyer().key(data, options) }, + sourceLazy = lazy { sourceManager.get(data.source) as? HttpSource }, + callFactoryLazy = callFactoryLazy, + diskCacheLazy = diskCacheLazy, + ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt index 6f3ce70ae..dec62e337 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt @@ -3,9 +3,14 @@ package eu.kanade.tachiyomi.data.coil import coil.key.Keyer import coil.request.Options import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.util.hasCustomCover class MangaCoverKeyer : Keyer { - override fun key(data: Manga, options: Options): String? { - return data.thumbnail_url?.takeIf { it.isNotBlank() } + override fun key(data: Manga, options: Options): String { + return if (data.hasCustomCover()) { + "${data.id};${data.cover_last_modified}" + } else { + "${data.thumbnail_url};${data.cover_last_modified}" + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index 3989ed8cf..0c0143c07 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -473,7 +473,6 @@ class LibraryUpdateService( .awaitAll() } - coverCache.clearMemoryCache() notifier.cancelProgressNotification() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt index 70a4ca3fc..3bf95569c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -290,7 +290,6 @@ class LocalSource( } } } - .also { coverCache.clearMemoryCache() } } sealed class Format { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 7f8e94a2b..8f458beb4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -316,12 +316,11 @@ class MangaPresenter( LocalSource.updateCover(context, manga, it) manga.updateCoverLastModified(db) db.insertManga(manga).executeAsBlocking() - coverCache.clearMemoryCache() } else if (manga.favorite) { coverCache.setCustomCoverToCache(manga, it) manga.updateCoverLastModified(db) - coverCache.clearMemoryCache() } + true } } .subscribeOn(Schedulers.io()) @@ -337,7 +336,6 @@ class MangaPresenter( .fromCallable { coverCache.deleteCustomCover(manga.id) manga.updateCoverLastModified(db) - coverCache.clearMemoryCache() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index 14d0b4943..7e075bd41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -704,13 +704,11 @@ class ReaderPresenter( val context = Injekt.get() LocalSource.updateCover(context, manga, it) manga.updateCoverLastModified(db) - coverCache.clearMemoryCache() SetAsCoverResult.Success } else { if (manga.favorite) { coverCache.setCustomCoverToCache(manga, it) manga.updateCoverLastModified(db) - coverCache.clearMemoryCache() SetAsCoverResult.Success } else { SetAsCoverResult.AddToLibraryFirst diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt index 321e96c7e..d2c7ce142 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.model.SManga +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.util.Date fun Manga.isLocal() = source == LocalSource.ID @@ -36,7 +38,7 @@ fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSa } } -fun Manga.hasCustomCover(coverCache: CoverCache): Boolean { +fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean { return coverCache.getCustomCoverFile(id).exists() }