Defer library download counts if not needed
This commit is contained in:
parent
3318314c4a
commit
93827aba34
3 changed files with 65 additions and 47 deletions
|
@ -6,8 +6,10 @@ import com.hippo.unifile.UniFile
|
|||
import eu.kanade.domain.download.service.DownloadPreferences
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -32,7 +34,7 @@ class DownloadCache(
|
|||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||
) {
|
||||
|
||||
private val scope = MainScope()
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* The interval after which this cache should be invalidated. 1 hour shouldn't cause major
|
||||
|
@ -50,8 +52,10 @@ class DownloadCache(
|
|||
init {
|
||||
downloadPreferences.downloadsDirectory().changes()
|
||||
.onEach {
|
||||
lastRenew = 0L // invalidate cache
|
||||
rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
|
||||
|
||||
// Invalidate cache
|
||||
lastRenew = 0L
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
@ -79,11 +83,11 @@ class DownloadCache(
|
|||
|
||||
renewCache()
|
||||
|
||||
val sourceDir = rootDownloadsDir.files[sourceId]
|
||||
val sourceDir = rootDownloadsDir.sourceDirs[sourceId]
|
||||
if (sourceDir != null) {
|
||||
val mangaDir = sourceDir.files[provider.getMangaDirName(mangaTitle)]
|
||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
||||
if (mangaDir != null) {
|
||||
return provider.getValidChapterDirNames(chapterName, chapterScanlator).any { it in mangaDir.files }
|
||||
return provider.getValidChapterDirNames(chapterName, chapterScanlator).any { it in mangaDir.chapterDirs }
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -97,11 +101,11 @@ class DownloadCache(
|
|||
fun getDownloadCount(manga: Manga): Int {
|
||||
renewCache()
|
||||
|
||||
val sourceDir = rootDownloadsDir.files[manga.source]
|
||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
||||
if (sourceDir != null) {
|
||||
val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)]
|
||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)]
|
||||
if (mangaDir != null) {
|
||||
return mangaDir.files.size
|
||||
return mangaDir.chapterDirs.size
|
||||
}
|
||||
}
|
||||
return 0
|
||||
|
@ -117,24 +121,24 @@ class DownloadCache(
|
|||
@Synchronized
|
||||
fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
|
||||
// Retrieve the cached source directory or cache a new one
|
||||
var sourceDir = rootDownloadsDir.files[manga.source]
|
||||
var sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
||||
if (sourceDir == null) {
|
||||
val source = sourceManager.get(manga.source) ?: return
|
||||
val sourceUniFile = provider.findSourceDir(source) ?: return
|
||||
sourceDir = SourceDirectory(sourceUniFile)
|
||||
rootDownloadsDir.files += manga.source to sourceDir
|
||||
rootDownloadsDir.sourceDirs += manga.source to sourceDir
|
||||
}
|
||||
|
||||
// Retrieve the cached manga directory or cache a new one
|
||||
val mangaDirName = provider.getMangaDirName(manga.title)
|
||||
var mangaDir = sourceDir.files[mangaDirName]
|
||||
var mangaDir = sourceDir.mangaDirs[mangaDirName]
|
||||
if (mangaDir == null) {
|
||||
mangaDir = MangaDirectory(mangaUniFile)
|
||||
sourceDir.files += mangaDirName to mangaDir
|
||||
sourceDir.mangaDirs += mangaDirName to mangaDir
|
||||
}
|
||||
|
||||
// Save the chapter directory
|
||||
mangaDir.files += chapterDirName
|
||||
mangaDir.chapterDirs += chapterDirName
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,11 +149,11 @@ class DownloadCache(
|
|||
*/
|
||||
@Synchronized
|
||||
fun removeChapter(chapter: Chapter, manga: Manga) {
|
||||
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
|
||||
val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)] ?: return
|
||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
||||
provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
|
||||
if (it in mangaDir.files) {
|
||||
mangaDir.files -= it
|
||||
if (it in mangaDir.chapterDirs) {
|
||||
mangaDir.chapterDirs -= it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,12 +166,12 @@ class DownloadCache(
|
|||
*/
|
||||
@Synchronized
|
||||
fun removeChapters(chapters: List<Chapter>, manga: Manga) {
|
||||
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
|
||||
val mangaDir = sourceDir.files[provider.getMangaDirName(manga.title)] ?: return
|
||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
||||
chapters.forEach { chapter ->
|
||||
provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
|
||||
if (it in mangaDir.files) {
|
||||
mangaDir.files -= it
|
||||
if (it in mangaDir.chapterDirs) {
|
||||
mangaDir.chapterDirs -= it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,10 +184,19 @@ class DownloadCache(
|
|||
*/
|
||||
@Synchronized
|
||||
fun removeManga(manga: Manga) {
|
||||
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
|
||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||
val mangaDirName = provider.getMangaDirName(manga.title)
|
||||
if (mangaDirName in sourceDir.files) {
|
||||
sourceDir.files -= mangaDirName
|
||||
if (mangaDirName in sourceDir.mangaDirs) {
|
||||
sourceDir.mangaDirs -= mangaDirName
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeSourceIfEmpty(source: Source) {
|
||||
val sourceDir = provider.findSourceDir(source)
|
||||
if (sourceDir?.listFiles()?.isEmpty() == true) {
|
||||
sourceDir.delete()
|
||||
rootDownloadsDir.sourceDirs -= source.id
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,14 +233,14 @@ class DownloadCache(
|
|||
}?.id
|
||||
}
|
||||
|
||||
rootDownloadsDir.files = sourceDirs
|
||||
rootDownloadsDir.sourceDirs = sourceDirs
|
||||
|
||||
sourceDirs.values.forEach { sourceDir ->
|
||||
val mangaDirs = sourceDir.dir.listFiles()
|
||||
.orEmpty()
|
||||
.associateNotNullKeys { it.name to MangaDirectory(it) }
|
||||
|
||||
sourceDir.files = mangaDirs
|
||||
sourceDir.mangaDirs = mangaDirs
|
||||
|
||||
mangaDirs.values.forEach { mangaDir ->
|
||||
val chapterDirs = mangaDir.dir.listFiles()
|
||||
|
@ -239,7 +252,7 @@ class DownloadCache(
|
|||
}
|
||||
.toHashSet()
|
||||
|
||||
mangaDir.files = chapterDirs
|
||||
mangaDir.chapterDirs = chapterDirs
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,7 +288,7 @@ class DownloadCache(
|
|||
*/
|
||||
private class RootDirectory(
|
||||
val dir: UniFile,
|
||||
var files: Map<Long, SourceDirectory> = hashMapOf(),
|
||||
var sourceDirs: Map<Long, SourceDirectory> = hashMapOf(),
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -283,7 +296,7 @@ private class RootDirectory(
|
|||
*/
|
||||
private class SourceDirectory(
|
||||
val dir: UniFile,
|
||||
var files: Map<String, MangaDirectory> = hashMapOf(),
|
||||
var mangaDirs: Map<String, MangaDirectory> = hashMapOf(),
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -291,5 +304,5 @@ private class SourceDirectory(
|
|||
*/
|
||||
private class MangaDirectory(
|
||||
val dir: UniFile,
|
||||
var files: Set<String> = hashSetOf(),
|
||||
var chapterDirs: Set<String> = hashSetOf(),
|
||||
)
|
||||
|
|
|
@ -287,10 +287,7 @@ class DownloadManager(
|
|||
}
|
||||
|
||||
// Delete source directory if empty
|
||||
val sourceDir = provider.findSourceDir(source)
|
||||
if (sourceDir?.listFiles()?.isEmpty() == true) {
|
||||
sourceDir.delete()
|
||||
}
|
||||
cache.removeSourceIfEmpty(source)
|
||||
}
|
||||
|
||||
return filteredChapters
|
||||
|
|
|
@ -338,27 +338,35 @@ class LibraryPresenter(
|
|||
* @return an observable of the categories and its manga.
|
||||
*/
|
||||
private fun getLibraryFlow(): Flow<Library> {
|
||||
val categoriesFlow = getCategories.subscribe()
|
||||
val libraryMangasFlow = getLibraryManga.subscribe()
|
||||
.map { list ->
|
||||
list.map { libraryManga ->
|
||||
val libraryMangasFlow = combine(
|
||||
getLibraryManga.subscribe(),
|
||||
libraryPreferences.downloadBadge().changes(),
|
||||
) { libraryMangaList, downloadBadgePref ->
|
||||
libraryMangaList
|
||||
.map { libraryManga ->
|
||||
// Display mode based on user preference: take it from global library setting or category
|
||||
LibraryItem(libraryManga).apply {
|
||||
downloadCount = downloadManager.getDownloadCount(libraryManga.manga).toLong()
|
||||
downloadCount = if (downloadBadgePref) {
|
||||
downloadManager.getDownloadCount(libraryManga.manga).toLong()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
unreadCount = libraryManga.unreadCount
|
||||
isLocal = libraryManga.manga.isLocal()
|
||||
sourceLanguage = sourceManager.getOrStub(libraryManga.manga.source).lang
|
||||
}
|
||||
}.groupBy { it.libraryManga.category }
|
||||
}
|
||||
return combine(categoriesFlow, libraryMangasFlow) { dbCategories, libraryManga ->
|
||||
val categories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
|
||||
dbCategories.filterNot { it.isSystemCategory }
|
||||
} else {
|
||||
dbCategories
|
||||
.groupBy { it.libraryManga.category }
|
||||
}
|
||||
|
||||
state.categories = categories
|
||||
return combine(getCategories.subscribe(), libraryMangasFlow) { categories, libraryManga ->
|
||||
val displayCategories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
|
||||
categories.filterNot { it.isSystemCategory }
|
||||
} else {
|
||||
categories
|
||||
}
|
||||
|
||||
state.categories = displayCategories
|
||||
Library(categories, libraryManga)
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue