Defer library download counts if not needed

(cherry picked from commit 93827aba34e98c6f70397d0e767580f3aaf5136b)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt
This commit is contained in:
arkon 2022-10-20 23:20:32 -04:00 committed by Jobobby04
parent 65d523e311
commit 5ff3c39d68
3 changed files with 76 additions and 56 deletions

View File

@ -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(/* SY --> */ manga.ogTitle /* SY <-- */)]
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)]
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(/* SY --> */ manga.ogTitle /* SY <-- */)
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,22 +149,22 @@ class DownloadCache(
*/
@Synchronized
fun removeChapter(chapter: Chapter, manga: Manga) {
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
val mangaDir = sourceDir.files[provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)] ?: return
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)] ?: return
provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
if (it in mangaDir.files) {
mangaDir.files -= it
if (it in mangaDir.chapterDirs) {
mangaDir.chapterDirs -= it
}
}
}
// SY -->
fun removeFolders(folders: List<String>, manga: Manga) {
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
val mangaDir = sourceDir.files[provider.getMangaDirName(manga.ogTitle)] ?: return
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.ogTitle)] ?: return
folders.forEach { chapter ->
if (chapter in mangaDir.files) {
mangaDir.files -= chapter
if (chapter in mangaDir.chapterDirs) {
mangaDir.chapterDirs -= chapter
}
}
}
@ -175,12 +179,12 @@ class DownloadCache(
*/
@Synchronized
fun removeChapters(chapters: List<Chapter>, manga: Manga) {
val sourceDir = rootDownloadsDir.files[manga.source] ?: return
val mangaDir = sourceDir.files[provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)] ?: return
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)] ?: 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
}
}
}
@ -193,10 +197,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(/* SY --> */ manga.ogTitle /* SY <-- */)
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
}
}
@ -235,14 +248,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()
@ -254,7 +267,7 @@ class DownloadCache(
}
.toHashSet()
mangaDir.files = chapterDirs
mangaDir.chapterDirs = chapterDirs
}
}
@ -290,7 +303,7 @@ class DownloadCache(
*/
private class RootDirectory(
val dir: UniFile,
var files: Map<Long, SourceDirectory> = hashMapOf(),
var sourceDirs: Map<Long, SourceDirectory> = hashMapOf(),
)
/**
@ -298,7 +311,7 @@ private class RootDirectory(
*/
private class SourceDirectory(
val dir: UniFile,
var files: Map<String, MangaDirectory> = hashMapOf(),
var mangaDirs: Map<String, MangaDirectory> = hashMapOf(),
)
/**
@ -306,5 +319,5 @@ private class SourceDirectory(
*/
private class MangaDirectory(
val dir: UniFile,
var files: Set<String> = hashSetOf(),
var chapterDirs: Set<String> = hashSetOf(),
)

View File

@ -289,10 +289,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

View File

@ -493,34 +493,44 @@ 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 = /* SY --> */ if (libraryManga.manga.source == MERGED_SOURCE_ID) {
downloadCount = if (downloadBadgePref) {
// SY -->
if (libraryManga.manga.source == MERGED_SOURCE_ID) {
runBlocking {
getMergedMangaById.await(libraryManga.manga.id)
}.sumOf { downloadManager.getDownloadCount(it) }.toLong()
} else {
downloadManager.getDownloadCount(libraryManga.manga).toLong()
} /* SY <-- */
}
// SY <--
} 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 }
.groupBy { it.libraryManga.category }
}
return combine(getCategories.subscribe(), libraryMangasFlow) { categories, libraryManga ->
val displayCategories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
categories.filterNot { it.isSystemCategory }
} else {
dbCategories
categories
}
// SY -->
state.ogCategories = categories
state.ogCategories = displayCategories
// SY <--
Library(categories, libraryManga)
}