Răsfoiți Sursa

Always renew download cache if no sources have been loaded yet

Fixes #7705. Somewhat janky solution to handle when loading the sources on app start is slower than
the initial download cache priming.
arkon 2 ani în urmă
părinte
comite
558aad1a71

+ 86 - 88
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt

@@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit
 
 /**
  * Cache where we dump the downloads directory from the filesystem. This class is needed because
- * directory checking is expensive and it slowdowns the app. The cache is invalidated by the time
+ * directory checking is expensive and it slows down the app. The cache is invalidated by the time
  * defined in [renewInterval] as we don't have any control over the filesystem and the user can
  * delete the folders at any time without the app noticing.
  *
@@ -41,27 +41,16 @@ class DownloadCache(
      */
     private var lastRenew = 0L
 
-    /**
-     * The root directory for downloads.
-     */
-    private var rootDir = RootDirectory(getDirectoryFromPreference())
+    private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
 
     init {
         downloadPreferences.downloadsDirectory().changes()
             .onEach {
                 lastRenew = 0L // invalidate cache
-                rootDir = RootDirectory(getDirectoryFromPreference())
+                rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
             }
     }
 
-    /**
-     * Returns the downloads directory from the user's preferences.
-     */
-    private fun getDirectoryFromPreference(): UniFile {
-        val dir = downloadPreferences.downloadsDirectory().get()
-        return UniFile.fromUri(context, dir.toUri())
-    }
-
     /**
      * Returns true if the chapter is downloaded.
      *
@@ -83,9 +72,9 @@ class DownloadCache(
             return provider.findChapterDir(chapterName, chapterScanlator, mangaTitle, source) != null
         }
 
-        checkRenew()
+        renewCache()
 
-        val sourceDir = rootDir.files[sourceId]
+        val sourceDir = rootDownloadsDir.files[sourceId]
         if (sourceDir != null) {
             val mangaDir = sourceDir.files[provider.getMangaDirName(mangaTitle)]
             if (mangaDir != null) {
@@ -101,9 +90,9 @@ class DownloadCache(
      * @param manga the manga to check.
      */
     fun getDownloadCount(manga: Manga): Int {
-        checkRenew()
+        renewCache()
 
-        val sourceDir = rootDir.files[manga.source]
+        val sourceDir = rootDownloadsDir.files[manga.source]
         if (sourceDir != null) {
             val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)]
             if (mangaDir != null) {
@@ -113,54 +102,6 @@ class DownloadCache(
         return 0
     }
 
-    /**
-     * Checks if the cache needs a renewal and performs it if needed.
-     */
-    @Synchronized
-    private fun checkRenew() {
-        if (lastRenew + renewInterval < System.currentTimeMillis()) {
-            renew()
-            lastRenew = System.currentTimeMillis()
-        }
-    }
-
-    /**
-     * Renews the downloads cache.
-     */
-    private fun renew() {
-        val sources = sourceManager.getOnlineSources() + sourceManager.getStubSources()
-
-        val sourceDirs = rootDir.dir.listFiles()
-            .orEmpty()
-            .associate { it.name to SourceDirectory(it) }
-            .mapNotNullKeys { entry ->
-                sources.find { provider.getSourceDirName(it).equals(entry.key, ignoreCase = true) }?.id
-            }
-
-        rootDir.files = sourceDirs
-
-        sourceDirs.values.forEach { sourceDir ->
-            val mangaDirs = sourceDir.dir.listFiles()
-                .orEmpty()
-                .associateNotNullKeys { it.name to MangaDirectory(it) }
-
-            sourceDir.files = mangaDirs
-
-            mangaDirs.values.forEach { mangaDir ->
-                val chapterDirs = mangaDir.dir.listFiles()
-                    .orEmpty()
-                    .mapNotNull { chapterDir ->
-                        chapterDir.name
-                            ?.replace(".cbz", "")
-                            ?.takeUnless { it.endsWith(Downloader.TMP_DIR_SUFFIX) }
-                    }
-                    .toHashSet()
-
-                mangaDir.files = chapterDirs
-            }
-        }
-    }
-
     /**
      * Adds a chapter that has just been download to this cache.
      *
@@ -171,12 +112,12 @@ class DownloadCache(
     @Synchronized
     fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
         // Retrieve the cached source directory or cache a new one
-        var sourceDir = rootDir.files[manga.source]
+        var sourceDir = rootDownloadsDir.files[manga.source]
         if (sourceDir == null) {
             val source = sourceManager.get(manga.source) ?: return
             val sourceUniFile = provider.findSourceDir(source) ?: return
             sourceDir = SourceDirectory(sourceUniFile)
-            rootDir.files += manga.source to sourceDir
+            rootDownloadsDir.files += manga.source to sourceDir
         }
 
         // Retrieve the cached manga directory or cache a new one
@@ -199,7 +140,7 @@ class DownloadCache(
      */
     @Synchronized
     fun removeChapter(chapter: Chapter, manga: Manga) {
-        val sourceDir = rootDir.files[manga.source] ?: return
+        val sourceDir = rootDownloadsDir.files[manga.source] ?: return
         val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)] ?: return
         provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
             if (it in mangaDir.files) {
@@ -216,7 +157,7 @@ class DownloadCache(
      */
     @Synchronized
     fun removeChapters(chapters: List<Chapter>, manga: Manga) {
-        val sourceDir = rootDir.files[manga.source] ?: return
+        val sourceDir = rootDownloadsDir.files[manga.source] ?: return
         val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)] ?: return
         chapters.forEach { chapter ->
             provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
@@ -234,7 +175,7 @@ class DownloadCache(
      */
     @Synchronized
     fun removeManga(manga: Manga) {
-        val sourceDir = rootDir.files[manga.source] ?: return
+        val sourceDir = rootDownloadsDir.files[manga.source] ?: return
         val mangaDirName = provider.getMangaDirName(manga.title)
         if (mangaDirName in sourceDir.files) {
             sourceDir.files -= mangaDirName
@@ -242,28 +183,61 @@ class DownloadCache(
     }
 
     /**
-     * Class to store the files under the root downloads directory.
+     * Returns the downloads directory from the user's preferences.
      */
-    private class RootDirectory(
-        val dir: UniFile,
-        var files: Map<Long, SourceDirectory> = hashMapOf(),
-    )
+    private fun getDirectoryFromPreference(): UniFile {
+        val dir = downloadPreferences.downloadsDirectory().get()
+        return UniFile.fromUri(context, dir.toUri())
+    }
 
     /**
-     * Class to store the files under a source directory.
+     * Renews the downloads cache.
      */
-    private class SourceDirectory(
-        val dir: UniFile,
-        var files: Map<String, MangaDirectory> = hashMapOf(),
-    )
+    @Synchronized
+    private fun renewCache() {
+        if (lastRenew + renewInterval >= System.currentTimeMillis()) {
+            return
+        }
 
-    /**
-     * Class to store the files under a manga directory.
-     */
-    private class MangaDirectory(
-        val dir: UniFile,
-        var files: Set<String> = hashSetOf(),
-    )
+        val sources = sourceManager.getOnlineSources() + sourceManager.getStubSources()
+
+        // Ensure we try again later if no sources have been loaded
+        if (sources.isEmpty()) {
+            return
+        }
+
+        val sourceDirs = rootDownloadsDir.dir.listFiles()
+            .orEmpty()
+            .associate { it.name to SourceDirectory(it) }
+            .mapNotNullKeys { entry ->
+                sources.find { provider.getSourceDirName(it).equals(entry.key, ignoreCase = true) }?.id
+            }
+
+        rootDownloadsDir.files = sourceDirs
+
+        sourceDirs.values.forEach { sourceDir ->
+            val mangaDirs = sourceDir.dir.listFiles()
+                .orEmpty()
+                .associateNotNullKeys { it.name to MangaDirectory(it) }
+
+            sourceDir.files = mangaDirs
+
+            mangaDirs.values.forEach { mangaDir ->
+                val chapterDirs = mangaDir.dir.listFiles()
+                    .orEmpty()
+                    .mapNotNull { chapterDir ->
+                        chapterDir.name
+                            ?.replace(".cbz", "")
+                            ?.takeUnless { it.endsWith(Downloader.TMP_DIR_SUFFIX) }
+                    }
+                    .toHashSet()
+
+                mangaDir.files = chapterDirs
+            }
+        }
+
+        lastRenew = System.currentTimeMillis()
+    }
 
     /**
      * Returns a new map containing only the key entries of [transform] that are not null.
@@ -288,3 +262,27 @@ class DownloadCache(
         return destination
     }
 }
+
+/**
+ * Class to store the files under the root downloads directory.
+ */
+private class RootDirectory(
+    val dir: UniFile,
+    var files: Map<Long, SourceDirectory> = hashMapOf(),
+)
+
+/**
+ * Class to store the files under a source directory.
+ */
+private class SourceDirectory(
+    val dir: UniFile,
+    var files: Map<String, MangaDirectory> = hashMapOf(),
+)
+
+/**
+ * Class to store the files under a manga directory.
+ */
+private class MangaDirectory(
+    val dir: UniFile,
+    var files: Set<String> = hashSetOf(),
+)