Fix/downloader manager persisting queue (#639)

* Extract reorder logic into function

* Save download queue everytime a download was finished

The download queue was never saved after a download was finished.
This caused finished download to be restored on a server start, which caused unnecessary "downloads" which most of the time would just finish immediately since the pages were still in the cache

* Wait for download queue save process to be finished

Since multiple downloaders could be finished at the same time, the download queue should be saved synchronously

* Remove unnecessary download queue save trigger

This gets called everytime a downloader finished downloading all chapters of its source.
Since the queue is now saved everytime a download is finished, this is trigger is not needed anymore
This commit is contained in:
schroda
2023-08-07 05:21:21 +02:00
committed by GitHub
parent b56b4fa813
commit 2889029b70
2 changed files with 33 additions and 17 deletions
@@ -20,7 +20,9 @@ import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -70,6 +72,10 @@ object DownloadManager {
.apply()
}
private fun triggerSaveDownloadQueue() {
scope.launch { saveQueueFlow.emit(Unit) }
}
fun restoreAndResumeDownloads() {
scope.launch {
logger.debug { "restoreAndResumeDownloads: Restore download queue..." }
@@ -126,6 +132,11 @@ object DownloadManager {
}
}
private val saveQueueFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
init {
saveQueueFlow.onEach { saveDownloadQueue() }.launchIn(scope)
}
private fun sendStatusToAllClients() {
val status = getStatus()
clients.forEach {
@@ -186,7 +197,6 @@ object DownloadManager {
private fun refreshDownloaders() {
scope.launch {
downloaderWatch.emit(Unit)
saveDownloadQueue()
}
}
@@ -196,7 +206,8 @@ object DownloadManager {
sourceId = sourceId,
downloadQueue = downloadQueue,
notifier = ::notifyAllClients,
onComplete = ::refreshDownloaders
onComplete = ::refreshDownloaders,
onDownloadFinished = ::triggerSaveDownloadQueue
)
}
@@ -276,7 +287,7 @@ object DownloadManager {
manga
)
downloadQueue.add(newDownloadChapter)
saveDownloadQueue()
triggerSaveDownloadQueue()
logger.debug { "Added chapter ${chapter.id} to download queue ($newDownloadChapter)" }
return newDownloadChapter
}
@@ -308,30 +319,33 @@ object DownloadManager {
logger.debug { "dequeue ${chapterDownloads.size} chapters [${chapterDownloads.joinToString(separator = ", ") { "$it" }}]" }
downloadQueue.removeAll(chapterDownloads)
saveDownloadQueue()
triggerSaveDownloadQueue()
notifyAllClients()
}
fun reorder(chapterIndex: Int, mangaId: Int, to: Int) {
require(to >= 0) { "'to' must be over or equal to 0" }
val download = downloadQueue.find { it.mangaId == mangaId && it.chapterIndex == chapterIndex }
?: return
reorder(download, to)
}
fun reorder(chapterId: Int, to: Int) {
val download = downloadQueue.find { it.chapter.id == chapterId }
?: return
reorder(download, to)
}
private fun reorder(download: DownloadChapter, to: Int) {
require(to >= 0) { "'to' must be over or equal to 0" }
logger.debug { "reorder download $download from ${downloadQueue.indexOf(download)} to $to" }
downloadQueue -= download
downloadQueue.add(to, download)
saveDownloadQueue()
}
fun reorder(chapterId: Int, to: Int) {
require(to >= 0) { "'to' must be over or equal to 0" }
val download = downloadQueue.find { it.chapter.id == chapterId }
?: return
downloadQueue -= download
downloadQueue.add(to, download)
saveDownloadQueue()
triggerSaveDownloadQueue()
}
fun start() {
@@ -360,7 +374,7 @@ object DownloadManager {
stop()
downloadQueue.clear()
saveDownloadQueue()
triggerSaveDownloadQueue()
notifyAllClients()
}
}
@@ -34,7 +34,8 @@ class Downloader(
val sourceId: String,
private val downloadQueue: CopyOnWriteArrayList<DownloadChapter>,
private val notifier: (immediate: Boolean) -> Unit,
private val onComplete: () -> Unit
private val onComplete: () -> Unit,
private val onDownloadFinished: () -> Unit
) {
private val logger = KotlinLogging.logger("${Downloader::class.java.name} source($sourceId)")
@@ -112,6 +113,7 @@ class Downloader(
downloadQueue.removeIf { it.mangaId == download.mangaId && it.chapterIndex == download.chapterIndex }
step(null, false)
downloadLogger.debug { "finished" }
onDownloadFinished()
} catch (e: CancellationException) {
logger.debug("Downloader was stopped")
availableSourceDownloads.filter { it.state == Downloading }.forEach { it.state = Queued }