diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d2e4fad..253dcb5ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,11 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co ## [Unreleased] ### Added - Advanced setting to limit download filenames to ASCII characters. This is provided only as a workaround for OSes that do not properly handle standard Unicode filenames. This setting is generally not recommended and should only be used as a last resort ([@raxod502](https://github.com/radian-software)) ([#2305](https://github.com/mihonapp/mihon/pull/2305)) +- Option to customize the number of concurrent source and page downloads ([@AntsyLich](https://github.com/AntsyLich)) ([#2637](https://github.com/mihonapp/mihon/pull/2637)) ### Changed - Delegate Suwayomi tracker authentication to extension ([@cpiber](https://github.com/cpiber)) ([#2476](https://github.com/mihonapp/mihon/pull/2476)) +- Increased default concurrent page downloads to 5 ([@AntsyLich](https://github.com/AntsyLich)) ([#2637](https://github.com/mihonapp/mihon/pull/2637)) ### Improved - Spoofing of `X-Requested-With` header to support newer WebView versions ([@Guzmazow](https://github.com/Guzmazow)) ([#2491](https://github.com/mihonapp/mihon/pull/2491)) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt index 46608659e..f66e40e27 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt @@ -37,6 +37,8 @@ object SettingsDownloadScreen : SearchableSettings { val allCategories by getCategories.subscribe().collectAsState(initial = emptyList()) val downloadPreferences = remember { Injekt.get() } + 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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index d9609d490..ee7f1d273 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -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) diff --git a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt index 7d24c181a..269d788a4 100644 --- a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt @@ -37,6 +37,10 @@ class DownloadPreferences( fun downloadNewUnreadChaptersOnly() = preferenceStore.getBoolean("download_new_unread_chapters_only", false) + fun parallelSourceLimit() = preferenceStore.getInt("download_parallel_source_limit", 5) + + fun parallelPageLimit() = preferenceStore.getInt("download_parallel_page_limit", 5) + companion object { private const val REMOVE_EXCLUDE_CATEGORIES_PREF_KEY = "remove_exclude_categories" private const val DOWNLOAD_NEW_CATEGORIES_PREF_KEY = "download_new_categories" diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 000c517f6..7bb1aaa8b 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -523,6 +523,9 @@ Save as CBZ archive Split tall images Improves reader performance + Concurrent source downloads + Concurrent page downloads + Pages downloaded simultaneously per source Tracking guide