mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	@@ -22,7 +22,7 @@ android {
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId = "eu.kanade.tachiyomi"
 | 
			
		||||
 | 
			
		||||
        versionCode = 112
 | 
			
		||||
        versionCode = 113
 | 
			
		||||
        versionName = "0.14.7"
 | 
			
		||||
 | 
			
		||||
        buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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? {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user