Fix downloading slowdown

This commit is contained in:
Koitharu
2024-11-04 16:04:18 +02:00
parent 38b342b721
commit e2f8d8e022
3 changed files with 29 additions and 15 deletions

View File

@@ -1,16 +1,20 @@
package org.koitharu.kotatsu.download.ui.worker
import android.os.SystemClock
import androidx.collection.MutableObjectLongMap
import kotlinx.coroutines.delay
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.parsers.model.MangaSource
import javax.inject.Inject
import javax.inject.Singleton
class DownloadSlowdownDispatcher(
@Singleton
class DownloadSlowdownDispatcher @Inject constructor(
private val mangaRepositoryFactory: MangaRepository.Factory,
private val defaultDelay: Long,
) {
private val timeMap = MutableObjectLongMap<MangaSource>()
private val defaultDelay = 1_600L
suspend fun delay(source: MangaSource) {
val repo = mangaRepositoryFactory.create(source) as? ParserMangaRepository ?: return
@@ -19,11 +23,11 @@ class DownloadSlowdownDispatcher(
}
val lastRequest = synchronized(timeMap) {
val res = timeMap.getOrDefault(source, 0L)
timeMap[source] = System.currentTimeMillis()
timeMap[source] = SystemClock.elapsedRealtime()
res
}
if (lastRequest != 0L) {
delay(lastRequest + defaultDelay - System.currentTimeMillis())
delay(lastRequest + defaultDelay - SystemClock.elapsedRealtime())
}
}
}

View File

@@ -101,6 +101,7 @@ class DownloadWorker @AssistedInject constructor(
private val mangaRepositoryFactory: MangaRepository.Factory,
private val settings: AppSettings,
@LocalStorageChanges private val localStorageChanges: MutableSharedFlow<LocalManga?>,
private val slowdownDispatcher: DownloadSlowdownDispatcher,
private val imageProxyInterceptor: ImageProxyInterceptor,
notificationFactoryFactory: DownloadNotificationFactory.Factory,
) : CoroutineWorker(appContext, params) {
@@ -110,7 +111,6 @@ class DownloadWorker @AssistedInject constructor(
isSilent = params.inputData.getBoolean(IS_SILENT, false),
)
private val notificationManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private val slowdownDispatcher = DownloadSlowdownDispatcher(mangaRepositoryFactory, SLOWDOWN_DELAY)
@Volatile
private var lastPublishedState: DownloadState? = null
@@ -569,7 +569,6 @@ class DownloadWorker @AssistedInject constructor(
const val MAX_PAGES_PARALLELISM = 4
const val DOWNLOAD_ERROR_DELAY = 2_000L
const val MAX_RETRY_DELAY = 7_200_000L // 2 hours
const val SLOWDOWN_DELAY = 200L
const val MANGA_ID = "manga_id"
const val CHAPTERS_IDS = "chapters"
const val IS_SILENT = "silent"

View File

@@ -1,10 +1,8 @@
package org.koitharu.kotatsu.reader.domain
import android.content.ContentResolver.MimeTypeInfo
import android.content.Context
import android.graphics.Rect
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.annotation.AnyThread
import androidx.collection.LongSparseArray
import androidx.collection.set
@@ -29,6 +27,7 @@ import kotlinx.coroutines.sync.withPermit
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.use
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
@@ -46,11 +45,13 @@ import org.koitharu.kotatsu.core.util.ext.exists
import org.koitharu.kotatsu.core.util.ext.getCompletionResultOrNull
import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode
import org.koitharu.kotatsu.core.util.ext.isTargetNotEmpty
import org.koitharu.kotatsu.core.util.ext.mimeType
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.ramAvailable
import org.koitharu.kotatsu.core.util.ext.use
import org.koitharu.kotatsu.core.util.ext.withProgress
import org.koitharu.kotatsu.core.util.progress.ProgressDeferred
import org.koitharu.kotatsu.download.ui.worker.DownloadSlowdownDispatcher
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.local.data.isFileUri
import org.koitharu.kotatsu.local.data.isZipUri
@@ -59,8 +60,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mimeType
import org.koitharu.kotatsu.parsers.util.requireBody
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
import org.koitharu.kotatsu.core.util.ext.mimeType
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import java.util.LinkedList
import java.util.concurrent.atomic.AtomicInteger
@@ -79,6 +78,7 @@ class PageLoader @Inject constructor(
private val settings: AppSettings,
private val mangaRepositoryFactory: MangaRepository.Factory,
private val imageProxyInterceptor: ImageProxyInterceptor,
private val downloadSlowdownDispatcher: DownloadSlowdownDispatcher,
) {
val loaderScope = RetainedLifecycleCoroutineScope(lifecycle) + InternalErrorHandler() + Dispatchers.Default
@@ -127,7 +127,7 @@ class PageLoader @Inject constructor(
} else if (task?.isCancelled == false) {
return task
}
task = loadPageAsyncImpl(page, force)
task = loadPageAsyncImpl(page, skipCache = force, isPrefetch = false)
synchronized(tasks) {
tasks[page.id] = task
}
@@ -186,7 +186,7 @@ class PageLoader @Inject constructor(
val page = prefetchQueue.pollFirst() ?: return@launch
if (cache.get(page.url) == null) {
synchronized(tasks) {
tasks[page.id] = loadPageAsyncImpl(page, false)
tasks[page.id] = loadPageAsyncImpl(page, skipCache = false, isPrefetch = true)
}
return@launch
}
@@ -194,7 +194,11 @@ class PageLoader @Inject constructor(
}
}
private fun loadPageAsyncImpl(page: MangaPage, skipCache: Boolean): ProgressDeferred<Uri, Float> {
private fun loadPageAsyncImpl(
page: MangaPage,
skipCache: Boolean,
isPrefetch: Boolean,
): ProgressDeferred<Uri, Float> {
val progress = MutableStateFlow(PROGRESS_UNDEFINED)
val deferred = loaderScope.async {
if (!skipCache) {
@@ -202,7 +206,7 @@ class PageLoader @Inject constructor(
}
counter.incrementAndGet()
try {
loadPageImpl(page, progress)
loadPageImpl(page, progress, isPrefetch)
} finally {
if (counter.decrementAndGet() == 0) {
onIdle()
@@ -222,7 +226,11 @@ class PageLoader @Inject constructor(
}
}
private suspend fun loadPageImpl(page: MangaPage, progress: MutableStateFlow<Float>): Uri = semaphore.withPermit {
private suspend fun loadPageImpl(
page: MangaPage,
progress: MutableStateFlow<Float>,
isPrefetch: Boolean,
): Uri = semaphore.withPermit {
val pageUrl = getPageUrl(page)
check(pageUrl.isNotBlank()) { "Cannot obtain full image url for $page" }
val uri = Uri.parse(pageUrl)
@@ -235,6 +243,9 @@ class PageLoader @Inject constructor(
uri.isFileUri() -> uri
else -> {
if (isPrefetch) {
downloadSlowdownDispatcher.delay(page.source)
}
val request = createPageRequest(pageUrl, page.source)
imageProxyInterceptor.interceptPageRequest(request, okHttp).ensureSuccess().use { response ->
response.requireBody().withProgress(progress).use {