Add option to customize concurrent downloads, increase page concurrency (#2637)

This commit is contained in:
AntsyLich
2025-11-01 20:07:30 +06:00
committed by GitHub
parent 7e880014b0
commit 643762f913
5 changed files with 45 additions and 19 deletions

View File

@@ -37,6 +37,8 @@ object SettingsDownloadScreen : SearchableSettings {
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
val parallelSourceLimit by downloadPreferences.parallelSourceLimit().collectAsState()
val parallelPageLimit by downloadPreferences.parallelPageLimit().collectAsState()
return listOf(
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.downloadOnlyOverWifi(),
@@ -51,6 +53,19 @@ object SettingsDownloadScreen : SearchableSettings {
title = stringResource(MR.strings.split_tall_images),
subtitle = stringResource(MR.strings.split_tall_images_summary),
),
Preference.PreferenceItem.SliderPreference(
value = parallelSourceLimit,
valueRange = 1..10,
title = stringResource(MR.strings.pref_download_concurrent_sources),
onValueChanged = { downloadPreferences.parallelSourceLimit().set(it) },
),
Preference.PreferenceItem.SliderPreference(
value = parallelPageLimit,
valueRange = 1..15,
title = stringResource(MR.strings.pref_download_concurrent_pages),
subtitle = stringResource(MR.strings.pref_download_concurrent_pages_summary),
onValueChanged = { downloadPreferences.parallelPageLimit().set(it) },
),
getDeleteChaptersGroup(
downloadPreferences = downloadPreferences,
categories = allCategories,

View File

@@ -191,15 +191,17 @@ class Downloader(
if (isRunning) return
downloaderJob = scope.launch {
val activeDownloadsFlow = queueState.transformLatest { queue ->
val activeDownloadsFlow = combine(
queueState,
downloadPreferences.parallelSourceLimit().changes(),
) { a, b -> a to b }.transformLatest { (queue, parallelCount) ->
while (true) {
val activeDownloads = queue.asSequence()
// Ignore completed downloads, leave them in the queue
.filter { it.status.value <= Download.State.DOWNLOADING.value }
.groupBy { it.source }
.toList()
// Concurrently download from 5 different sources
.take(5)
.take(parallelCount)
.map { (_, downloads) -> downloads.first() }
emit(activeDownloads)
@@ -211,7 +213,8 @@ class Downloader(
}.filter { it }
activeDownloadsErroredFlow.first()
}
}.distinctUntilChanged()
}
.distinctUntilChanged()
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
supervisorScope {
@@ -366,24 +369,23 @@ class Downloader(
download.status = Download.State.DOWNLOADING
// Start downloading images, consider we can have downloaded images already
// Concurrently do 2 pages at a time
pageList.asFlow()
.flatMapMerge(concurrency = 2) { page ->
flow {
// Fetch image URL if necessary
if (page.imageUrl.isNullOrEmpty()) {
page.status = Page.State.LoadPage
try {
page.imageUrl = download.source.getImageUrl(page)
} catch (e: Throwable) {
page.status = Page.State.Error(e)
}
pageList.asFlow().flatMapMerge(concurrency = downloadPreferences.parallelPageLimit().get()) { page ->
flow {
// Fetch image URL if necessary
if (page.imageUrl.isNullOrEmpty()) {
page.status = Page.State.LoadPage
try {
page.imageUrl = download.source.getImageUrl(page)
} catch (e: Throwable) {
page.status = Page.State.Error(e)
}
}
withIOContext { getOrDownloadImage(page, download, tmpDir) }
emit(page)
}.flowOn(Dispatchers.IO)
withIOContext { getOrDownloadImage(page, download, tmpDir) }
emit(page)
}
.flowOn(Dispatchers.IO)
}
.collect {
// Do when page is downloaded.
notifier.onProgressChange(download)