From 768cd979feb1675cc30d628d8e14aad5a2e8f0c3 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sat, 16 Sep 2023 13:28:54 +0200 Subject: [PATCH] Handle finished downloads that weren't removed from the queue In case a download was finished, but the downloader got stopped before it was able to remove the finished download from the queue, the downloader got stuck in an endless loop of starting and pausing downloads. This was caused by selecting the next chapter to download and then recognizing in "Downloader::step", that there is another chapter to download before the current one in the queue. However, since this recognized chapter is already downloaded, the downloader selected the next queued chapter again. It was then stuck in this loop until the finished chapter was manually removed from the queue. --- .../manga/impl/download/Downloader.kt | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt index f97c92503..99f6af97a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import mu.KLogger import mu.KotlinLogging import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction @@ -83,10 +84,17 @@ class Downloader( logger.debug { "stopped" } } + private suspend fun finishDownload(logger: KLogger, download: DownloadChapter) { + downloadQueue.removeIf { it.mangaId == download.mangaId && it.chapterIndex == download.chapterIndex } + step(null, false) + logger.debug { "finished" } + onDownloadFinished() + } + private suspend fun run() { while (downloadQueue.isNotEmpty() && currentCoroutineContext().isActive) { val download = availableSourceDownloads.firstOrNull { - (it.state == Queued || (it.state == Error && it.tries < 3)) // 3 re-tries per download + (it.state == Queued || it.state == Finished || (it.state == Error && it.tries < 3)) // 3 re-tries per download } ?: break val logContext = "${logger.name} - downloadChapter($download))" @@ -94,6 +102,14 @@ class Downloader( downloadLogger.debug { "start" } + // handle cases were the downloader was stopped before the finished download could be removed from the queue + // otherwise, it will create an endless loop, due to never removing the finished chapter and thinking that the + // current download chapter was moved down in the queue + if (download.state == Finished) { + finishDownload(downloadLogger, download) + break + } + try { download.state = Downloading step(download, true) @@ -109,11 +125,7 @@ class Downloader( } } step(download, true) - - downloadQueue.removeIf { it.mangaId == download.mangaId && it.chapterIndex == download.chapterIndex } - step(null, false) - downloadLogger.debug { "finished" } - onDownloadFinished() + finishDownload(downloadLogger, download) } catch (e: CancellationException) { logger.debug("Downloader was stopped") availableSourceDownloads.filter { it.state == Downloading }.forEach { it.state = Queued }