Przeglądaj źródła

Fix download indexing with changed storage locations

Fixes #10218
arkon 1 rok temu
rodzic
commit
36f400d542

+ 1 - 1
app/build.gradle.kts

@@ -22,7 +22,7 @@ android {
     defaultConfig {
         applicationId = "eu.kanade.tachiyomi"
 
-        versionCode = 112
+        versionCode = 113
         versionName = "0.14.7"
 
         buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")

+ 0 - 2
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt

@@ -41,7 +41,6 @@ import eu.kanade.tachiyomi.data.backup.BackupCreateJob
 import eu.kanade.tachiyomi.data.backup.BackupFileValidator
 import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
 import eu.kanade.tachiyomi.data.cache.ChapterCache
-import eu.kanade.tachiyomi.data.download.DownloadCache
 import eu.kanade.tachiyomi.util.storage.DiskUtil
 import eu.kanade.tachiyomi.util.system.DeviceUtil
 import eu.kanade.tachiyomi.util.system.copyToClipboard
@@ -98,7 +97,6 @@ object SettingsDataScreen : SearchableSettings {
                 UniFile.fromUri(context, uri)?.let {
                     storageDirPref.set(it.uri.toString())
                 }
-                Injekt.get<DownloadCache>().invalidateCache()
             }
         }
     }

+ 4 - 6
app/src/main/java/eu/kanade/tachiyomi/Migrations.kt

@@ -381,12 +381,7 @@ object Migrations {
                     newKey = { Preference.privateKey(it) },
                 )
             }
-            if (oldVersion < 111) {
-                File(context.cacheDir, "dl_index_cache")
-                    .takeIf { it.exists() }
-                    ?.delete()
-            }
-            if (oldVersion < 112) {
+            if (oldVersion < 113) {
                 val prefsToReplace = listOf(
                     "pref_download_only",
                     "incognito_mode",
@@ -406,6 +401,9 @@ object Migrations {
                     filterPredicate = { it.key in prefsToReplace },
                     newKey = { Preference.appStateKey(it) },
                 )
+
+                // Deleting old download cache index files, but might as well clear it all out
+                context.cacheDir.deleteRecursively()
             }
             return true
         }

+ 22 - 23
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt

@@ -18,7 +18,6 @@ import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
@@ -48,7 +47,7 @@ import tachiyomi.core.util.system.logcat
 import tachiyomi.domain.chapter.model.Chapter
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.domain.source.service.SourceManager
-import tachiyomi.domain.storage.service.StoragePreferences
+import tachiyomi.domain.storage.service.StorageManager
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import java.io.File
@@ -66,7 +65,7 @@ class DownloadCache(
     private val provider: DownloadProvider = Injekt.get(),
     private val sourceManager: SourceManager = Injekt.get(),
     private val extensionManager: ExtensionManager = Injekt.get(),
-    storagePreferences: StoragePreferences = Injekt.get(),
+    private val storageManager: StorageManager = Injekt.get(),
 ) {
 
     private val scope = CoroutineScope(Dispatchers.IO)
@@ -74,7 +73,7 @@ class DownloadCache(
     private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
     val changes = _changes.receiveAsFlow()
         .onStart { emit(Unit) }
-        .shareIn(scope, SharingStarted.Eagerly, 1)
+        .shareIn(scope, SharingStarted.Lazily, 1)
 
     /**
      * The interval after which this cache should be invalidated. 1 hour shouldn't cause major
@@ -94,10 +93,10 @@ class DownloadCache(
         .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     private val diskCacheFile: File
-        get() = File(context.cacheDir, "dl_index_cache_v2")
+        get() = File(context.cacheDir, "dl_index_cache_v3")
 
     private val rootDownloadsDirLock = Mutex()
-    private var rootDownloadsDir = RootDirectory(provider.downloadsDir)
+    private var rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
 
     init {
         // Attempt to read cache file
@@ -115,12 +114,8 @@ class DownloadCache(
             }
         }
 
-        storagePreferences.baseStorageDirectory().changes()
-            .drop(1)
-            .onEach {
-                rootDownloadsDir = RootDirectory(provider.downloadsDir)
-                invalidateCache()
-            }
+        storageManager.changes
+            .onEach { invalidateCache() }
             .launchIn(scope)
     }
 
@@ -294,6 +289,8 @@ class DownloadCache(
     fun invalidateCache() {
         lastRenew = 0L
         renewalJob?.cancel()
+        diskCacheFile.delete()
+        renewCache()
     }
 
     /**
@@ -310,23 +307,26 @@ class DownloadCache(
                 _isInitializing.emit(true)
             }
 
-            var sources = getSources()
-
             // Try to wait until extensions and sources have loaded
-            withTimeoutOrNull(30.seconds) {
-                while (!extensionManager.isInitialized) {
-                    delay(2.seconds)
-                }
+            var sources = getSources()
+            if (sources.isEmpty()) {
+                withTimeoutOrNull(30.seconds) {
+                    while (!extensionManager.isInitialized) {
+                        delay(2.seconds)
+                    }
 
-                while (sources.isEmpty()) {
-                    delay(2.seconds)
-                    sources = getSources()
+                    while (extensionManager.availableExtensionsFlow.value.isNotEmpty() && sources.isEmpty()) {
+                        delay(2.seconds)
+                        sources = getSources()
+                    }
                 }
             }
 
             val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
 
             rootDownloadsDirLock.withLock {
+                rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
+
                 val sourceDirs = rootDownloadsDir.dir?.listFiles().orEmpty()
                     .filter { it.isDirectory && !it.name.isNullOrBlank() }
                     .mapNotNull { dir ->
@@ -371,10 +371,9 @@ class DownloadCache(
         }.also {
             it.invokeOnCompletion(onCancelling = true) { exception ->
                 if (exception != null && exception !is CancellationException) {
-                    logcat(LogPriority.ERROR, exception) { "Failed to create download cache" }
+                    logcat(LogPriority.ERROR, exception) { "DownloadCache: failed to create cache" }
                 }
                 lastRenew = System.currentTimeMillis()
-
                 notifyChanges()
             }
         }

+ 27 - 12
domain/src/main/java/tachiyomi/domain/storage/service/StorageManager.kt

@@ -6,8 +6,14 @@ import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.util.storage.DiskUtil
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.shareIn
 
 class StorageManager(
     private val context: Context,
@@ -16,24 +22,33 @@ class StorageManager(
 
     private val scope = CoroutineScope(Dispatchers.IO)
 
-    private var baseDir: UniFile? = storagePreferences.baseStorageDirectory().get().let(::getBaseDir)
+    private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory().get())
+
+    private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
+    val changes = _changes.receiveAsFlow()
+        .shareIn(scope, SharingStarted.Lazily, 1)
 
     init {
         storagePreferences.baseStorageDirectory().changes()
-            .onEach { baseDir = getBaseDir(it) }
+            .drop(1)
+            .distinctUntilChanged()
+            .onEach { uri ->
+                baseDir = getBaseDir(uri)
+                baseDir?.let { parent ->
+                    parent.createDirectory(AUTOMATIC_BACKUPS_PATH)
+                    parent.createDirectory(LOCAL_SOURCE_PATH)
+                    parent.createDirectory(DOWNLOADS_PATH).also {
+                        DiskUtil.createNoMediaFile(it, context)
+                    }
+                }
+                _changes.send(Unit)
+            }
             .launchIn(scope)
     }
 
-    private fun getBaseDir(path: String): UniFile? {
-        val file = UniFile.fromUri(context, path.toUri())
-
-        return file.takeIf { it?.exists() == true }?.also { parent ->
-            parent.createDirectory(AUTOMATIC_BACKUPS_PATH)
-            parent.createDirectory(LOCAL_SOURCE_PATH)
-            parent.createDirectory(DOWNLOADS_PATH).also {
-                DiskUtil.createNoMediaFile(it, context)
-            }
-        }
+    private fun getBaseDir(uri: String): UniFile? {
+        return UniFile.fromUri(context, uri.toUri())
+            .takeIf { it?.exists() == true }
     }
 
     fun getAutomaticBackupsDirectory(): UniFile? {