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
This commit is contained in:
Ivan Iskandar 2022-06-10 20:33:59 +07:00 committed by GitHub
parent 20c14a0a00
commit 59837bbb90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 36 deletions

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.data.cache package eu.kanade.tachiyomi.data.cache
import android.content.Context import android.content.Context
import coil.imageLoader
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import java.io.File 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 { private fun getCacheDir(dir: String): File {
return context.getExternalFilesDir(dir) return context.getExternalFilesDir(dir)
?: File(context.filesDir, dir).also { it.mkdirs() } ?: File(context.filesDir, dir).also { it.mkdirs() }

View file

@ -42,28 +42,32 @@ import java.net.HttpURLConnection
* - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true * - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true
*/ */
class MangaCoverFetcher( class MangaCoverFetcher(
private val manga: Manga, private val url: String?,
private val sourceLazy: Lazy<HttpSource?>, private val isLibraryManga: Boolean,
private val options: Options, private val options: Options,
private val coverCache: CoverCache, private val coverFileLazy: Lazy<File?>,
private val customCoverFileLazy: Lazy<File>,
private val diskCacheKeyLazy: Lazy<String>,
private val sourceLazy: Lazy<HttpSource?>,
private val callFactoryLazy: Lazy<Call.Factory>, private val callFactoryLazy: Lazy<Call.Factory>,
private val diskCacheLazy: Lazy<DiskCache>, private val diskCacheLazy: Lazy<DiskCache>,
) : Fetcher { ) : Fetcher {
// For non-custom cover private val diskCacheKey: String
private val diskCacheKey: String? by lazy { MangaCoverKeyer().key(manga, options) } get() = diskCacheKeyLazy.value
private lateinit var url: String
override suspend fun fetch(): FetchResult { override suspend fun fetch(): FetchResult {
// Use custom cover if exists // Use custom cover if exists
val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true
val customCoverFile = coverCache.getCustomCoverFile(manga.id) if (useCustomCover) {
if (useCustomCover && customCoverFile.exists()) { val customCoverFile = customCoverFileLazy.value
if (customCoverFile.exists()) {
return fileLoader(customCoverFile) return fileLoader(customCoverFile)
} }
}
// diskCacheKey is thumbnail_url // diskCacheKey is thumbnail_url
url = diskCacheKey ?: error("No cover specified") if (url == null) error("No cover specified")
return when (getResourceType(url)) { return when (getResourceType(url)) {
Type.URL -> httpLoader() Type.URL -> httpLoader()
Type.File -> fileLoader(File(url.substringAfter("file://"))) Type.File -> fileLoader(File(url.substringAfter("file://")))
@ -81,8 +85,8 @@ class MangaCoverFetcher(
private suspend fun httpLoader(): FetchResult { private suspend fun httpLoader(): FetchResult {
// Only cache separately if it's a library item // Only cache separately if it's a library item
val libraryCoverCacheFile = if (manga.favorite) { val libraryCoverCacheFile = if (isLibraryManga) {
coverCache.getCoverFile(manga.thumbnail_url) ?: error("No cover specified") coverFileLazy.value ?: error("No cover specified")
} else { } else {
null null
} }
@ -156,7 +160,7 @@ class MangaCoverFetcher(
private fun newRequest(): Request { private fun newRequest(): Request {
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url!!)
.headers(sourceLazy.value?.headers ?: options.headers) .headers(sourceLazy.value?.headers ?: options.headers)
// Support attaching custom data to the network request. // Support attaching custom data to the network request.
.tag(Parameters::class.java, options.parameters) .tag(Parameters::class.java, options.parameters)
@ -188,7 +192,7 @@ class MangaCoverFetcher(
fileSystem.source(snapshot.data).use { input -> fileSystem.source(snapshot.data).use { input ->
writeSourceToCoverCache(input, cacheFile) writeSourceToCoverCache(input, cacheFile)
} }
remove(diskCacheKey!!) remove(diskCacheKey)
} }
cacheFile.takeIf { it.exists() } cacheFile.takeIf { it.exists() }
} catch (e: Exception) { } catch (e: Exception) {
@ -224,7 +228,7 @@ class MangaCoverFetcher(
} }
private fun readFromDiskCache(): DiskCache.Snapshot? { 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( private fun writeToDiskCache(
@ -238,7 +242,7 @@ class MangaCoverFetcher(
val editor = if (snapshot != null) { val editor = if (snapshot != null) {
snapshot.closeAndEdit() snapshot.closeAndEdit()
} else { } else {
diskCacheLazy.value.edit(diskCacheKey!!) diskCacheLazy.value.edit(diskCacheKey)
} ?: return null } ?: return null
try { try {
diskCacheLazy.value.fileSystem.write(editor.data) { diskCacheLazy.value.fileSystem.write(editor.data) {
@ -280,8 +284,17 @@ class MangaCoverFetcher(
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher { override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher {
val source = lazy { sourceManager.get(data.source) as? HttpSource } return MangaCoverFetcher(
return MangaCoverFetcher(data, source, options, coverCache, callFactoryLazy, diskCacheLazy) 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,
)
} }
} }

View file

@ -3,9 +3,14 @@ package eu.kanade.tachiyomi.data.coil
import coil.key.Keyer import coil.key.Keyer
import coil.request.Options import coil.request.Options
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.hasCustomCover
class MangaCoverKeyer : Keyer<Manga> { class MangaCoverKeyer : Keyer<Manga> {
override fun key(data: Manga, options: Options): String? { override fun key(data: Manga, options: Options): String {
return data.thumbnail_url?.takeIf { it.isNotBlank() } return if (data.hasCustomCover()) {
"${data.id};${data.cover_last_modified}"
} else {
"${data.thumbnail_url};${data.cover_last_modified}"
}
} }
} }

View file

@ -473,7 +473,6 @@ class LibraryUpdateService(
.awaitAll() .awaitAll()
} }
coverCache.clearMemoryCache()
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }

View file

@ -290,7 +290,6 @@ class LocalSource(
} }
} }
} }
.also { coverCache.clearMemoryCache() }
} }
sealed class Format { sealed class Format {

View file

@ -316,12 +316,11 @@ class MangaPresenter(
LocalSource.updateCover(context, manga, it) LocalSource.updateCover(context, manga, it)
manga.updateCoverLastModified(db) manga.updateCoverLastModified(db)
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
coverCache.clearMemoryCache()
} else if (manga.favorite) { } else if (manga.favorite) {
coverCache.setCustomCoverToCache(manga, it) coverCache.setCustomCoverToCache(manga, it)
manga.updateCoverLastModified(db) manga.updateCoverLastModified(db)
coverCache.clearMemoryCache()
} }
true
} }
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -337,7 +336,6 @@ class MangaPresenter(
.fromCallable { .fromCallable {
coverCache.deleteCustomCover(manga.id) coverCache.deleteCustomCover(manga.id)
manga.updateCoverLastModified(db) manga.updateCoverLastModified(db)
coverCache.clearMemoryCache()
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View file

@ -704,13 +704,11 @@ class ReaderPresenter(
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
LocalSource.updateCover(context, manga, it) LocalSource.updateCover(context, manga, it)
manga.updateCoverLastModified(db) manga.updateCoverLastModified(db)
coverCache.clearMemoryCache()
SetAsCoverResult.Success SetAsCoverResult.Success
} else { } else {
if (manga.favorite) { if (manga.favorite) {
coverCache.setCustomCoverToCache(manga, it) coverCache.setCustomCoverToCache(manga, it)
manga.updateCoverLastModified(db) manga.updateCoverLastModified(db)
coverCache.clearMemoryCache()
SetAsCoverResult.Success SetAsCoverResult.Success
} else { } else {
SetAsCoverResult.AddToLibraryFirst SetAsCoverResult.AddToLibraryFirst

View file

@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
fun Manga.isLocal() = source == LocalSource.ID 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() return coverCache.getCustomCoverFile(id).exists()
} }