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