Option to slowdown downloads and configure parallelism
This commit is contained in:
@@ -141,6 +141,12 @@ class AppSettings(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
val isDownloadsSlowdownEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_DOWNLOADS_SLOWDOWN, false)
|
||||
|
||||
val downloadsParallelism: Int
|
||||
get() = prefs.getInt(KEY_DOWNLOADS_PARALLELISM, 2)
|
||||
|
||||
val isSuggestionsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_SUGGESTIONS, false)
|
||||
|
||||
@@ -261,6 +267,8 @@ class AppSettings(context: Context) {
|
||||
const val KEY_SUGGESTIONS_EXCLUDE_NSFW = "suggestions_exclude_nsfw"
|
||||
const val KEY_SUGGESTIONS_EXCLUDE_TAGS = "suggestions_exclude_tags"
|
||||
const val KEY_SEARCH_SINGLE_SOURCE = "search_single_source"
|
||||
const val KEY_DOWNLOADS_PARALLELISM = "downloads_parallelism"
|
||||
const val KEY_DOWNLOADS_SLOWDOWN = "downloads_slowdown"
|
||||
|
||||
// About
|
||||
const val KEY_APP_UPDATE = "app_update"
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.local.data.PagesCache
|
||||
import org.koitharu.kotatsu.local.domain.CbzMangaOutput
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
@@ -30,8 +31,8 @@ import org.koitharu.kotatsu.utils.progress.ProgressJob
|
||||
import java.io.File
|
||||
|
||||
private const val MAX_DOWNLOAD_ATTEMPTS = 3
|
||||
private const val MAX_PARALLEL_DOWNLOADS = 2
|
||||
private const val DOWNLOAD_ERROR_DELAY = 500L
|
||||
private const val SLOWDOWN_DELAY = 200L
|
||||
|
||||
class DownloadManager(
|
||||
private val coroutineScope: CoroutineScope,
|
||||
@@ -40,9 +41,10 @@ class DownloadManager(
|
||||
private val okHttp: OkHttpClient,
|
||||
private val cache: PagesCache,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val settings: AppSettings,
|
||||
) {
|
||||
|
||||
private val connectivityManager = context.applicationContext.getSystemService(
|
||||
private val connectivityManager = context.getSystemService(
|
||||
Context.CONNECTIVITY_SERVICE
|
||||
) as ConnectivityManager
|
||||
private val coverWidth = context.resources.getDimensionPixelSize(
|
||||
@@ -51,7 +53,7 @@ class DownloadManager(
|
||||
private val coverHeight = context.resources.getDimensionPixelSize(
|
||||
androidx.core.R.dimen.compat_notification_large_icon_max_height
|
||||
)
|
||||
private val semaphore = Semaphore(MAX_PARALLEL_DOWNLOADS)
|
||||
private val semaphore = Semaphore(settings.downloadsParallelism)
|
||||
|
||||
fun downloadManga(
|
||||
manga: Manga,
|
||||
@@ -141,6 +143,10 @@ class DownloadManager(
|
||||
totalPages = pages.size,
|
||||
currentPage = pageIndex,
|
||||
)
|
||||
|
||||
if (settings.isDownloadsSlowdownEnabled) {
|
||||
delay(SLOWDOWN_DELAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
outState.value = DownloadState.PostProcessing(startId, data, cover)
|
||||
@@ -206,4 +212,24 @@ class DownloadManager(
|
||||
error = throwable,
|
||||
)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val context: Context,
|
||||
private val imageLoader: ImageLoader,
|
||||
private val okHttp: OkHttpClient,
|
||||
private val cache: PagesCache,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val settings: AppSettings,
|
||||
) {
|
||||
|
||||
fun create(coroutineScope: CoroutineScope) = DownloadManager(
|
||||
coroutineScope = coroutineScope,
|
||||
context = context,
|
||||
imageLoader = imageLoader,
|
||||
okHttp = okHttp,
|
||||
cache = cache,
|
||||
localMangaRepository = localMangaRepository,
|
||||
settings = settings,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -49,13 +49,8 @@ class DownloadService : BaseService() {
|
||||
notificationSwitcher = ForegroundNotificationSwitcher(this)
|
||||
val wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
|
||||
downloadManager = DownloadManager(
|
||||
downloadManager = get<DownloadManager.Factory>().create(
|
||||
coroutineScope = lifecycleScope + WakeLockNode(wakeLock, TimeUnit.HOURS.toMillis(1)),
|
||||
context = this,
|
||||
imageLoader = get(),
|
||||
okHttp = get(),
|
||||
cache = get(),
|
||||
localMangaRepository = get(),
|
||||
)
|
||||
DownloadNotification.createChannel(this)
|
||||
registerReceiver(controlReceiver, IntentFilter(ACTION_DOWNLOAD_CANCEL))
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.local
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.download.domain.DownloadManager
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.local.ui.LocalListViewModel
|
||||
@@ -16,5 +17,7 @@ val localModule
|
||||
|
||||
factory { ExternalStorageHelper(androidContext()) }
|
||||
|
||||
factory { DownloadManager.Factory(androidContext(), get(), get(), get(), get(), get()) }
|
||||
|
||||
viewModel { LocalListViewModel(get(), get(), get(), get()) }
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -13,8 +12,10 @@ import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.settings.utils.SliderPreference
|
||||
import org.koitharu.kotatsu.utils.ext.getStorageName
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
import java.io.File
|
||||
|
||||
class ContentSettingsFragment :
|
||||
BasePreferenceFragment(R.string.content),
|
||||
@@ -29,6 +30,13 @@ class ContentSettingsFragment :
|
||||
findPreference<Preference>(AppSettings.KEY_SUGGESTIONS)?.setSummary(
|
||||
if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled
|
||||
)
|
||||
findPreference<SliderPreference>(AppSettings.KEY_DOWNLOADS_PARALLELISM)?.run {
|
||||
summary = value.toString()
|
||||
setOnPreferenceChangeListener { preference, newValue ->
|
||||
preference.summary = newValue.toString()
|
||||
true
|
||||
}
|
||||
}
|
||||
bindRemoteSourcesSummary()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ suspend fun ConnectivityManager.waitForNetwork(): Network {
|
||||
return suspendCancellableCoroutine { cont ->
|
||||
val callback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
unregisterNetworkCallback(this)
|
||||
if (cont.isActive) {
|
||||
cont.resume(network)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user