[优化] 下载优化

This commit is contained in:
HapeLee
2026-04-29 01:42:08 +08:00
parent 12b20ce5d8
commit 00dc733ec8
2 changed files with 58 additions and 65 deletions
@@ -679,8 +679,8 @@ private fun BookshelfManageScreen(
val cacheCount = remember(renderVersion, book.bookUrl) {
viewModel.getCacheCount(book.bookUrl) ?: 0
}
val downloadState = state.downloadStates[book.bookUrl]
val isPreparingDownload = state.pendingDownloadBookUrls.contains(book.bookUrl)
val downloadState = viewModel.getBookDownloadState(book.bookUrl)
val isPreparingDownload = viewModel.isBookPreparingDownload(book.bookUrl)
val waitingDownloadCount = downloadState?.waitingCount ?: 0
val runningDownloadCount = downloadState?.runningIndices?.size ?: 0
val isDownloadingInCacheModel = viewModel.isBookDownloading(book.bookUrl)
@@ -688,7 +688,7 @@ private fun BookshelfManageScreen(
waitingDownloadCount > 0 ||
runningDownloadCount > 0 ||
isDownloadingInCacheModel
val downloadFailureText = state.downloadFailureMessages[book.bookUrl]?.let {
val downloadFailureText = viewModel.getDownloadFailureMessage(book.bookUrl)?.let {
stringResource(R.string.cache_download_failed, it)
}
val isSelected = selectedBookUrls.contains(book.bookUrl)
@@ -73,9 +73,6 @@ data class BookshelfManageScreenUiState(
val bookSort: Int = BookshelfConfig.bookshelfSort,
val bookSortOrder: Int = BookshelfConfig.bookshelfSortOrder,
val isDownloadRunning: Boolean = false,
val pendingDownloadBookUrls: Set<String> = emptySet(),
val downloadStates: Map<String, CacheBookDownloadState> = emptyMap(),
val downloadFailureMessages: Map<String, String> = emptyMap(),
val isChangingSource: Boolean = false,
val changeSourceProgress: String? = null,
val changeSourceMessage: String? = null,
@@ -172,6 +169,9 @@ class BookshelfManageScreenViewModel(
val effects = _effects.asSharedFlow()
private val cacheCounts = ConcurrentHashMap<String, Int>()
private val downloadStates = ConcurrentHashMap<String, CacheBookDownloadState>()
private val pendingDownloadBookUrls = ConcurrentHashMap.newKeySet<String>()
private val downloadFailureMessages = ConcurrentHashMap<String, String>()
private var booksJob: Job? = null
private var groupsJob: Job? = null
private var cacheLoadJob: Job? = null
@@ -309,6 +309,18 @@ class BookshelfManageScreenViewModel(
return CacheBook.cacheBookMap[bookUrl]?.isStop() == false
}
fun getBookDownloadState(bookUrl: String): CacheBookDownloadState? {
return downloadStates[bookUrl]
}
fun getDownloadFailureMessage(bookUrl: String): String? {
return downloadFailureMessages[bookUrl]
}
fun isBookPreparingDownload(bookUrl: String): Boolean {
return pendingDownloadBookUrls.contains(bookUrl)
}
private fun initialize(groupId: Long) {
_uiState.update { it.copy(groupId = groupId) }
syncExportConfig()
@@ -385,31 +397,27 @@ class BookshelfManageScreenViewModel(
}
viewModelScope.launch {
CacheBook.downloadStateFlow.collect { downloadState ->
_uiState.update { state ->
val successfulBookUrls = downloadState.books
.filterValues {
it.successCount > 0 &&
it.failedIndices.isEmpty() &&
it.failureMessage == null
}
.keys
val failureMessages = downloadState.books.mapNotNull { (bookUrl, bookState) ->
val message = bookState.failureMessage ?: if (bookState.failedIndices.isNotEmpty()) {
"${bookState.failedIndices.size}"
} else {
null
}
message?.let { bookUrl to it }
}.toMap()
state.copy(
isDownloadRunning = downloadState.isRunning,
pendingDownloadBookUrls = state.pendingDownloadBookUrls - downloadState.books.keys,
downloadStates = downloadState.books,
downloadFailureMessages = (
state.downloadFailureMessages - successfulBookUrls
) + failureMessages,
)
}
val successfulBookUrls = downloadState.books
.filterValues {
it.successCount > 0 &&
it.failedIndices.isEmpty() &&
it.failureMessage == null
}
.keys
val failureMsgs = downloadState.books.mapNotNull { (bookUrl, bookState) ->
val message = bookState.failureMessage ?: if (bookState.failedIndices.isNotEmpty()) {
"${bookState.failedIndices.size}"
} else {
null
}
message?.let { bookUrl to it }
}.toMap()
pendingDownloadBookUrls.removeAll(downloadState.books.keys)
downloadStates.clear()
downloadStates.putAll(downloadState.books)
successfulBookUrls.forEach { downloadFailureMessages.remove(it) }
downloadFailureMessages.putAll(failureMsgs)
_uiState.update { it.copy(isDownloadRunning = downloadState.isRunning) }
downloadState.books.keys.forEach { bookUrl ->
scheduleDownloadStatusRefresh(bookUrl)
}
@@ -475,13 +483,17 @@ class BookshelfManageScreenViewModel(
cacheCounts.remove(bookUrl)
}
}
val changedBookUrls = linkedSetOf<String>()
books.forEach { book ->
if (!book.isLocal && !cacheCounts.containsKey(book.bookUrl)) {
cacheCounts[book.bookUrl] = calculateCacheCount(book)
emitBookChanged(book.bookUrl)
changedBookUrls.add(book.bookUrl)
}
ensureActive()
}
if (changedBookUrls.isNotEmpty()) {
_uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) }
}
}
}
@@ -539,9 +551,7 @@ class BookshelfManageScreenViewModel(
private fun startDownloadForVisibleBooks(books: List<Book>, downloadAllChapters: Boolean) {
val bookUrls = books.mapTo(hashSetOf()) { it.bookUrl }
_uiState.update {
it.copy(downloadFailureMessages = it.downloadFailureMessages - bookUrls)
}
bookUrls.forEach { downloadFailureMessages.remove(it) }
execute {
batchCacheDownloadUseCase.execute(
bookUrls = bookUrls,
@@ -555,40 +565,28 @@ class BookshelfManageScreenViewModel(
private fun toggleBookDownload(book: Book) {
if (book.isLocal) return
if (isBookDownloading(book.bookUrl) || uiState.value.pendingDownloadBookUrls.contains(book.bookUrl)) {
if (isBookDownloading(book.bookUrl) || pendingDownloadBookUrls.contains(book.bookUrl)) {
CacheBook.remove(context, book.bookUrl)
_uiState.update {
it.copy(pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl)
}
pendingDownloadBookUrls.remove(book.bookUrl)
emitBookChanged(book.bookUrl)
syncDownloadRunning()
} else {
_uiState.update {
it.copy(
pendingDownloadBookUrls = it.pendingDownloadBookUrls + book.bookUrl,
downloadFailureMessages = it.downloadFailureMessages - book.bookUrl
)
}
pendingDownloadBookUrls.add(book.bookUrl)
downloadFailureMessages.remove(book.bookUrl)
emitBookChanged(book.bookUrl)
execute {
cacheBookChaptersUseCase.executeRange(book.bookUrl, 0, book.lastChapterIndex)
}.onSuccess { count ->
if (count <= 0) {
_uiState.update {
it.copy(
pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl,
downloadFailureMessages = it.downloadFailureMessages +
(book.bookUrl to "没有可缓存的章节")
)
}
pendingDownloadBookUrls.remove(book.bookUrl)
downloadFailureMessages[book.bookUrl] = "没有可缓存的章节"
emitBookChanged(book.bookUrl)
_effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("没有可缓存的章节"))
}
}.onError { error ->
_uiState.update {
it.copy(
pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl,
downloadFailureMessages = it.downloadFailureMessages +
(book.bookUrl to (error.localizedMessage ?: "未知错误"))
)
}
pendingDownloadBookUrls.remove(book.bookUrl)
downloadFailureMessages[book.bookUrl] = error.localizedMessage ?: "未知错误"
emitBookChanged(book.bookUrl)
_effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("缓存失败\n${error.localizedMessage}"))
}.onFinally {
syncDownloadRunning()
@@ -661,9 +659,7 @@ class BookshelfManageScreenViewModel(
private fun downloadBooks(bookUrls: Set<String>, downloadAllChapters: Boolean) {
if (bookUrls.isEmpty()) return
_uiState.update {
it.copy(downloadFailureMessages = it.downloadFailureMessages - bookUrls)
}
bookUrls.forEach { downloadFailureMessages.remove(it) }
execute {
batchCacheDownloadUseCase.execute(
bookUrls = bookUrls,
@@ -972,9 +968,6 @@ class BookshelfManageScreenViewModel(
private fun emitBooksChanged(bookUrls: Set<String>) {
if (bookUrls.isEmpty()) return
_uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) }
bookUrls.forEach { bookUrl ->
_effects.tryEmit(BookshelfManageScreenEffect.NotifyBookChanged(bookUrl))
}
}
}