mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-09 18:48:55 +01:00
Add option to customize concurrent downloads, increase page concurrency (#2637)
This commit is contained in:
@@ -13,9 +13,11 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### 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))
|
- 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
|
### Changed
|
||||||
- Delegate Suwayomi tracker authentication to extension ([@cpiber](https://github.com/cpiber)) ([#2476](https://github.com/mihonapp/mihon/pull/2476))
|
- 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
|
### 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))
|
- Spoofing of `X-Requested-With` header to support newer WebView versions ([@Guzmazow](https://github.com/Guzmazow)) ([#2491](https://github.com/mihonapp/mihon/pull/2491))
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
||||||
|
|
||||||
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
||||||
|
val parallelSourceLimit by downloadPreferences.parallelSourceLimit().collectAsState()
|
||||||
|
val parallelPageLimit by downloadPreferences.parallelPageLimit().collectAsState()
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
preference = downloadPreferences.downloadOnlyOverWifi(),
|
preference = downloadPreferences.downloadOnlyOverWifi(),
|
||||||
@@ -51,6 +53,19 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.split_tall_images),
|
title = stringResource(MR.strings.split_tall_images),
|
||||||
subtitle = stringResource(MR.strings.split_tall_images_summary),
|
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(
|
getDeleteChaptersGroup(
|
||||||
downloadPreferences = downloadPreferences,
|
downloadPreferences = downloadPreferences,
|
||||||
categories = allCategories,
|
categories = allCategories,
|
||||||
|
|||||||
@@ -191,15 +191,17 @@ class Downloader(
|
|||||||
if (isRunning) return
|
if (isRunning) return
|
||||||
|
|
||||||
downloaderJob = scope.launch {
|
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) {
|
while (true) {
|
||||||
val activeDownloads = queue.asSequence()
|
val activeDownloads = queue.asSequence()
|
||||||
// Ignore completed downloads, leave them in the queue
|
// Ignore completed downloads, leave them in the queue
|
||||||
.filter { it.status.value <= Download.State.DOWNLOADING.value }
|
.filter { it.status.value <= Download.State.DOWNLOADING.value }
|
||||||
.groupBy { it.source }
|
.groupBy { it.source }
|
||||||
.toList()
|
.toList()
|
||||||
// Concurrently download from 5 different sources
|
.take(parallelCount)
|
||||||
.take(5)
|
|
||||||
.map { (_, downloads) -> downloads.first() }
|
.map { (_, downloads) -> downloads.first() }
|
||||||
emit(activeDownloads)
|
emit(activeDownloads)
|
||||||
|
|
||||||
@@ -211,7 +213,8 @@ class Downloader(
|
|||||||
}.filter { it }
|
}.filter { it }
|
||||||
activeDownloadsErroredFlow.first()
|
activeDownloadsErroredFlow.first()
|
||||||
}
|
}
|
||||||
}.distinctUntilChanged()
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
|
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
@@ -366,24 +369,23 @@ class Downloader(
|
|||||||
download.status = Download.State.DOWNLOADING
|
download.status = Download.State.DOWNLOADING
|
||||||
|
|
||||||
// Start downloading images, consider we can have downloaded images already
|
// Start downloading images, consider we can have downloaded images already
|
||||||
// Concurrently do 2 pages at a time
|
pageList.asFlow().flatMapMerge(concurrency = downloadPreferences.parallelPageLimit().get()) { page ->
|
||||||
pageList.asFlow()
|
flow {
|
||||||
.flatMapMerge(concurrency = 2) { page ->
|
// Fetch image URL if necessary
|
||||||
flow {
|
if (page.imageUrl.isNullOrEmpty()) {
|
||||||
// Fetch image URL if necessary
|
page.status = Page.State.LoadPage
|
||||||
if (page.imageUrl.isNullOrEmpty()) {
|
try {
|
||||||
page.status = Page.State.LoadPage
|
page.imageUrl = download.source.getImageUrl(page)
|
||||||
try {
|
} catch (e: Throwable) {
|
||||||
page.imageUrl = download.source.getImageUrl(page)
|
page.status = Page.State.Error(e)
|
||||||
} catch (e: Throwable) {
|
|
||||||
page.status = Page.State.Error(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
withIOContext { getOrDownloadImage(page, download, tmpDir) }
|
withIOContext { getOrDownloadImage(page, download, tmpDir) }
|
||||||
emit(page)
|
emit(page)
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
}
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
}
|
||||||
.collect {
|
.collect {
|
||||||
// Do when page is downloaded.
|
// Do when page is downloaded.
|
||||||
notifier.onProgressChange(download)
|
notifier.onProgressChange(download)
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ class DownloadPreferences(
|
|||||||
|
|
||||||
fun downloadNewUnreadChaptersOnly() = preferenceStore.getBoolean("download_new_unread_chapters_only", false)
|
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 {
|
companion object {
|
||||||
private const val REMOVE_EXCLUDE_CATEGORIES_PREF_KEY = "remove_exclude_categories"
|
private const val REMOVE_EXCLUDE_CATEGORIES_PREF_KEY = "remove_exclude_categories"
|
||||||
private const val DOWNLOAD_NEW_CATEGORIES_PREF_KEY = "download_new_categories"
|
private const val DOWNLOAD_NEW_CATEGORIES_PREF_KEY = "download_new_categories"
|
||||||
|
|||||||
@@ -523,6 +523,9 @@
|
|||||||
<string name="save_chapter_as_cbz">Save as CBZ archive</string>
|
<string name="save_chapter_as_cbz">Save as CBZ archive</string>
|
||||||
<string name="split_tall_images">Split tall images</string>
|
<string name="split_tall_images">Split tall images</string>
|
||||||
<string name="split_tall_images_summary">Improves reader performance</string>
|
<string name="split_tall_images_summary">Improves reader performance</string>
|
||||||
|
<string name="pref_download_concurrent_sources">Concurrent source downloads</string>
|
||||||
|
<string name="pref_download_concurrent_pages">Concurrent page downloads</string>
|
||||||
|
<string name="pref_download_concurrent_pages_summary">Pages downloaded simultaneously per source</string>
|
||||||
|
|
||||||
<!-- Tracking section -->
|
<!-- Tracking section -->
|
||||||
<string name="tracking_guide">Tracking guide</string>
|
<string name="tracking_guide">Tracking guide</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user