Wait for a canceled downloader job to finish before starting another

Page writes block, so a canceled job could still be writing pages when the next one started on the same pages, leaving duplicate "(1)" files and failed chapters.
This commit is contained in:
AntsyLich
2026-10-07 01:15:18 +06:00
parent a5d584eeb0
commit 6490c41e63
@@ -22,6 +22,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@@ -41,6 +42,7 @@ import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import logcat.LogPriority
import mihon.core.archive.ZipWriter
import nl.adaptivity.xmlutil.serialization.XML
@@ -91,6 +93,8 @@ class Downloader(
val queueState = _queueState.asStateFlow()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
// Kept when canceled, so the next job can wait for it to finish
private var downloaderJob: Job? = null
/**
@@ -183,7 +187,12 @@ class Downloader(
private fun launchDownloaderJob() {
if (isRunning) return
val previousJob = downloaderJob
downloaderJob = scope.launch {
// Page writes block, so a canceled job can still be writing the pages this one would start on. Not
// cancelable, so a job canceled while waiting still finishes after the one before it.
withContext(NonCancellable) { previousJob?.join() }
val activeDownloadsFlow = combine(
queueState,
downloadPreferences.parallelSourceLimit.changes(),
@@ -253,7 +262,6 @@ class Downloader(
*/
private fun cancelDownloaderJob() {
downloaderJob?.cancel()
downloaderJob = null
}
/**