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 e341151d8e..4bed1dc957 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 @@ -19,6 +19,7 @@ import uy.kohesive.injekt.api.get import java.io.File import java.io.IOException import java.io.InputStream +import java.util.concurrent.TimeUnit /** * Class used to create cover cache. @@ -33,6 +34,7 @@ class CoverCache(val context: Context) { companion object { private const val COVERS_DIR = "covers" private const val CUSTOM_COVERS_DIR = "covers/custom" + private const val ONLINE_COVERS_DIR = "online_covers" } /** Cache directory used for cache management.*/ @@ -41,6 +43,19 @@ class CoverCache(val context: Context) { /** Cache directory used for custom cover cache management.*/ private val customCoverCacheDir = getCacheDir(CUSTOM_COVERS_DIR) + /** Cache directory used for covers not in library management.*/ + private val onlineCoverDirectory = File(context.cacheDir, ONLINE_COVERS_DIR).also { it.mkdirs() } + + private val maxOnlineCacheSize = 50L * 1024L * 1024L // 50 MB + + private var lastClean = 0L + + /** + * The interval after which this cache should be invalidated. 1 hour shouldn't cause major + * issues, as the cache is only used for UI feedback. + */ + private val renewInterval = TimeUnit.HOURS.toMillis(1) + fun getChapterCacheSize(): String { return Formatter.formatFileSize(context, DiskUtil.getDirectorySize(cacheDir)) } @@ -71,6 +86,33 @@ class CoverCache(val context: Context) { } } + /** + * Clear out online covers until its under a certain size + */ + fun deleteCachedCovers() { + if (lastClean + renewInterval < System.currentTimeMillis()) { + GlobalScope.launch(Dispatchers.IO) { + val directory = onlineCoverDirectory + val size = DiskUtil.getDirectorySize(directory) + if (size <= maxOnlineCacheSize) { + return@launch + } + var deletedSize = 0L + val files = directory.listFiles()?.sortedBy { it.lastModified() }?.iterator() + ?: return@launch + while (files.hasNext()) { + val file = files.next() + deletedSize += file.length() + file.delete() + if (size - deletedSize <= maxOnlineCacheSize) { + break + } + } + } + lastClean = System.currentTimeMillis() + } + } + /** * Returns the custom cover from cache. * @@ -117,7 +159,15 @@ class CoverCache(val context: Context) { * @return cover image. */ fun getCoverFile(manga: Manga): File { - return File(cacheDir, manga.key()) + return if (manga.favorite) { + File(cacheDir, manga.key()) + } else { + getOnlineCoverFile(manga) + } + } + + fun getOnlineCoverFile(manga: Manga): File { + return File(onlineCoverDirectory, manga.key()) } fun deleteFromCache(name: String?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt index f484b490bb..a5dea33785 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt @@ -7,7 +7,6 @@ import coil.ImageLoader import coil.decode.GifDecoder import coil.decode.ImageDecoderDecoder import coil.decode.SvgDecoder -import coil.util.CoilUtils import com.chuckerteam.chucker.api.ChuckerInterceptor import okhttp3.OkHttpClient @@ -29,7 +28,6 @@ class CoilSetup(context: Context) { add(ByteArrayFetcher()) }.okHttpClient { OkHttpClient.Builder() - .cache(CoilUtils.createDefaultCache(context)) .addInterceptor(ChuckerInterceptor(context)) .build() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaFetcher.kt index dca14e12c3..0d284251c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaFetcher.kt @@ -27,6 +27,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.io.File +import java.util.Date class MangaFetcher : Fetcher { @@ -63,29 +64,31 @@ class MangaFetcher : Fetcher { } val coverFile = coverCache.getCoverFile(manga) if (coverFile.exists() && options.diskCachePolicy.readEnabled) { - return fileLoader(coverFile) - } - if (!manga.favorite) { - val (response, body) = awaitGetCall(manga) - - return SourceResult( - source = body.source(), - mimeType = getMimeType(manga.thumbnail_url!!, body), - dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK - ) - } else { - val (_, body) = awaitGetCall(manga, !options.networkCachePolicy.readEnabled) - - val tmpFile = File(coverFile.absolutePath + "_tmp") - body.source().use { input -> - tmpFile.sink().buffer().use { output -> - output.writeAll(input) - } + if (!manga.favorite) { + coverFile.setLastModified(Date().time) } - - tmpFile.renameTo(coverFile) return fileLoader(coverFile) } + val (_, body) = awaitGetCall( + manga, if (manga.favorite) { + !options.networkCachePolicy.readEnabled + } else { + false + } + ) + + val tmpFile = File(coverFile.absolutePath + "_tmp") + body.source().use { input -> + tmpFile.sink().buffer().use { output -> + output.writeAll(input) + } + } + + tmpFile.renameTo(coverFile) + if (manga.favorite) { + coverCache.deleteCachedCovers() + } + return fileLoader(coverFile) } private suspend fun awaitGetCall(manga: Manga, onlyCache: Boolean = false): Pair