Minor download location cleanup
This commit is contained in:
parent
f1778ac5b4
commit
21ae04d25d
2 changed files with 54 additions and 41 deletions
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.download
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
@ -19,6 +18,7 @@ import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
@ -64,7 +64,7 @@ class DownloadCache(
|
||||||
private val provider: DownloadProvider = Injekt.get(),
|
private val provider: DownloadProvider = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
private val storagePreferences: StoragePreferences = Injekt.get(),
|
storagePreferences: StoragePreferences = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.IO)
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
@ -95,16 +95,9 @@ class DownloadCache(
|
||||||
get() = File(context.cacheDir, "dl_index_cache")
|
get() = File(context.cacheDir, "dl_index_cache")
|
||||||
|
|
||||||
private val rootDownloadsDirLock = Mutex()
|
private val rootDownloadsDirLock = Mutex()
|
||||||
private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
|
private var rootDownloadsDir = RootDirectory(provider.downloadsDir)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
storagePreferences.baseStorageDirectory().changes()
|
|
||||||
.onEach {
|
|
||||||
rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
|
|
||||||
invalidateCache()
|
|
||||||
}
|
|
||||||
.launchIn(scope)
|
|
||||||
|
|
||||||
// Attempt to read cache file
|
// Attempt to read cache file
|
||||||
scope.launch {
|
scope.launch {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirLock.withLock {
|
||||||
|
@ -119,6 +112,14 @@ class DownloadCache(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storagePreferences.baseStorageDirectory().changes()
|
||||||
|
.drop(1)
|
||||||
|
.onEach {
|
||||||
|
rootDownloadsDir = RootDirectory(provider.downloadsDir)
|
||||||
|
invalidateCache()
|
||||||
|
}
|
||||||
|
.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,17 +294,6 @@ class DownloadCache(
|
||||||
renewalJob?.cancel()
|
renewalJob?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the downloads directory from the user's preferences.
|
|
||||||
*/
|
|
||||||
private fun getDirectoryFromPreference(): UniFile {
|
|
||||||
return storagePreferences.baseStorageDirectory().get().let {
|
|
||||||
UniFile.fromUri(context, it.toUri()).also {
|
|
||||||
it?.createDirectory(StoragePreferences.DOWNLOADS_DIR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renews the downloads cache.
|
* Renews the downloads cache.
|
||||||
*/
|
*/
|
||||||
|
@ -335,7 +325,7 @@ class DownloadCache(
|
||||||
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
||||||
|
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirLock.withLock {
|
||||||
val sourceDirs = rootDownloadsDir.dir.listFiles().orEmpty()
|
val sourceDirs = rootDownloadsDir.dir?.listFiles().orEmpty()
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
.mapNotNull { dir ->
|
.mapNotNull { dir ->
|
||||||
val sourceId = sourceMap[dir.name!!.lowercase()]
|
val sourceId = sourceMap[dir.name!!.lowercase()]
|
||||||
|
@ -348,12 +338,12 @@ class DownloadCache(
|
||||||
sourceDirs.values
|
sourceDirs.values
|
||||||
.map { sourceDir ->
|
.map { sourceDir ->
|
||||||
async {
|
async {
|
||||||
sourceDir.mangaDirs = sourceDir.dir.listFiles().orEmpty()
|
sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty()
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
.associate { it.name!! to MangaDirectory(it) }
|
.associate { it.name!! to MangaDirectory(it) }
|
||||||
|
|
||||||
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
||||||
val chapterDirs = mangaDir.dir.listFiles().orEmpty()
|
val chapterDirs = mangaDir.dir?.listFiles().orEmpty()
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
when {
|
when {
|
||||||
// Ignore incomplete downloads
|
// Ignore incomplete downloads
|
||||||
|
@ -430,7 +420,7 @@ class DownloadCache(
|
||||||
@Serializable
|
@Serializable
|
||||||
private class RootDirectory(
|
private class RootDirectory(
|
||||||
@Serializable(with = UniFileAsStringSerializer::class)
|
@Serializable(with = UniFileAsStringSerializer::class)
|
||||||
val dir: UniFile,
|
val dir: UniFile?,
|
||||||
var sourceDirs: Map<Long, SourceDirectory> = mapOf(),
|
var sourceDirs: Map<Long, SourceDirectory> = mapOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -440,7 +430,7 @@ private class RootDirectory(
|
||||||
@Serializable
|
@Serializable
|
||||||
private class SourceDirectory(
|
private class SourceDirectory(
|
||||||
@Serializable(with = UniFileAsStringSerializer::class)
|
@Serializable(with = UniFileAsStringSerializer::class)
|
||||||
val dir: UniFile,
|
val dir: UniFile?,
|
||||||
var mangaDirs: Map<String, MangaDirectory> = mapOf(),
|
var mangaDirs: Map<String, MangaDirectory> = mapOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -450,17 +440,26 @@ private class SourceDirectory(
|
||||||
@Serializable
|
@Serializable
|
||||||
private class MangaDirectory(
|
private class MangaDirectory(
|
||||||
@Serializable(with = UniFileAsStringSerializer::class)
|
@Serializable(with = UniFileAsStringSerializer::class)
|
||||||
val dir: UniFile,
|
val dir: UniFile?,
|
||||||
var chapterDirs: MutableSet<String> = mutableSetOf(),
|
var chapterDirs: MutableSet<String> = mutableSetOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private object UniFileAsStringSerializer : KSerializer<UniFile> {
|
private object UniFileAsStringSerializer : KSerializer<UniFile?> {
|
||||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UniFile", PrimitiveKind.STRING)
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UniFile", PrimitiveKind.STRING)
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: UniFile) {
|
override fun serialize(encoder: Encoder, value: UniFile?) {
|
||||||
return encoder.encodeString(value.uri.toString())
|
return if (value == null) {
|
||||||
|
encoder.encodeNull()
|
||||||
|
} else {
|
||||||
|
encoder.encodeString(value.uri.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override fun deserialize(decoder: Decoder): UniFile {
|
|
||||||
return UniFile.fromUri(Injekt.get<Application>(), Uri.parse(decoder.decodeString()))
|
override fun deserialize(decoder: Decoder): UniFile? {
|
||||||
|
return if (decoder.decodeNotNullMark()) {
|
||||||
|
UniFile.fromUri(Injekt.get<Application>(), Uri.parse(decoder.decodeString()))
|
||||||
|
} else {
|
||||||
|
decoder.decodeNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,10 @@ import androidx.core.net.toUri
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
|
@ -23,17 +27,27 @@ import uy.kohesive.injekt.api.get
|
||||||
*/
|
*/
|
||||||
class DownloadProvider(
|
class DownloadProvider(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val storagePreferences: StoragePreferences = Injekt.get(),
|
storagePreferences: StoragePreferences = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val downloadsDir: UniFile?
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
get() = storagePreferences.baseStorageDirectory().get().let {
|
|
||||||
UniFile.fromUri(context, it.toUri())
|
private var _downloadsDir: UniFile? =
|
||||||
?.createDirectory(StoragePreferences.DOWNLOADS_DIR)
|
storagePreferences.baseStorageDirectory().get().let(::getDownloadsLocation)
|
||||||
?.also { dir ->
|
val downloadsDir: UniFile?
|
||||||
DiskUtil.createNoMediaFile(dir, context)
|
get() = _downloadsDir
|
||||||
}
|
|
||||||
}
|
init {
|
||||||
|
storagePreferences.baseStorageDirectory().changes()
|
||||||
|
.onEach { _downloadsDir = getDownloadsLocation(it) }
|
||||||
|
.launchIn(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDownloadsLocation(dir: String): UniFile? {
|
||||||
|
return UniFile.fromUri(context, dir.toUri())
|
||||||
|
?.createDirectory(StoragePreferences.DOWNLOADS_DIR)
|
||||||
|
?.also { DiskUtil.createNoMediaFile(it, context) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the download directory for a manga. For internal use only.
|
* Returns the download directory for a manga. For internal use only.
|
||||||
|
|
Reference in a new issue