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.
Cette révision appartient à :
Parent
d6cbff2837
révision
558aad1a71
1 fichiers modifiés avec 86 ajouts et 88 suppressions
|
@ -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(),
|
||||
)
|
||||
|
|
Référencer dans un nouveau ticket