diff --git a/app/src/main/java/org/koitharu/kotatsu/download/DownloadService.kt b/app/src/main/java/org/koitharu/kotatsu/download/DownloadService.kt index fd533a2b1..a434571cb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/download/DownloadService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/download/DownloadService.kt @@ -93,7 +93,7 @@ class DownloadService : BaseService() { checkNotNull(destination) { getString(R.string.cannot_find_available_storage) } var output: MangaZip? = null try { - val repo = manga.source.repository + val repo = mangaRepositoryOf(manga.source) val cover = runCatching { imageLoader.execute( ImageRequest.Builder(this@DownloadService) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 59dfde78a..acdd7bca7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -3,16 +3,20 @@ package org.koitharu.kotatsu.reader.domain import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri -import android.util.ArrayMap +import androidx.collection.LongSparseArray +import androidx.collection.set import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import okhttp3.OkHttpClient -import org.koitharu.kotatsu.core.model.RequestDraft +import org.koin.core.component.KoinComponent +import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.utils.CacheUtils import org.koitharu.kotatsu.utils.ext.await +import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf import java.io.File import java.util.zip.ZipFile @@ -20,45 +24,61 @@ class PageLoader( scope: CoroutineScope, private val okHttp: OkHttpClient, private val cache: PagesCache -) : CoroutineScope by scope { +) : CoroutineScope by scope, KoinComponent { - private val tasks = ArrayMap>() + private var repository: MangaRepository? = null + private val tasks = LongSparseArray>() private val convertLock = Mutex() - suspend fun loadFile(requestDraft: RequestDraft, force: Boolean): File { + suspend fun loadPage(page: MangaPage, force: Boolean): File { if (!force) { - cache[requestDraft.url]?.let { + cache[page.url]?.let { return it } } - val task = - tasks[requestDraft.url]?.takeUnless { it.isCancelled || (force && it.isCompleted) } - return (task ?: loadAsync(requestDraft).also { tasks[requestDraft.url] = it }).await() + var task = tasks[page.id] + if (force) { + task?.cancel() + } else if (task?.isCancelled == false) { + return task.await() + } + task = loadAsync(page) + tasks[page.id] = task + return task.await() } - private fun loadAsync(requestDraft: RequestDraft) = async(Dispatchers.IO) { - val uri = Uri.parse(requestDraft.url) - if (uri.scheme == "cbz") { - val zip = ZipFile(uri.schemeSpecificPart) - val entry = zip.getEntry(uri.fragment) - zip.getInputStream(entry).use { - cache.put(requestDraft.url, it) - } - } else { - val request = requestDraft.newBuilder() - .header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") - .cacheControl(CacheUtils.CONTROL_DISABLED) - .build() - okHttp.newCall(request).await().use { response -> - check(response.isSuccessful) { - "Invalid response: ${response.code} ${response.message}" - } - val body = checkNotNull(response.body) { - "Null response" - } - body.byteStream().use { + private fun loadAsync(page: MangaPage): Deferred { + var repo = repository + if (repo?.javaClass != page.source.cls) { + repo = mangaRepositoryOf(page.source) + repository = repo + } + return async(Dispatchers.IO) { + val requestDraft = repo.getPageRequest(page) + check(requestDraft.isValid) { "Cannot obtain full image url" } + val uri = Uri.parse(requestDraft.url) + if (uri.scheme == "cbz") { + val zip = ZipFile(uri.schemeSpecificPart) + val entry = zip.getEntry(uri.fragment) + zip.getInputStream(entry).use { cache.put(requestDraft.url, it) } + } else { + val request = requestDraft.newBuilder() + .header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") + .cacheControl(CacheUtils.CONTROL_DISABLED) + .build() + okHttp.newCall(request).await().use { response -> + check(response.isSuccessful) { + "Invalid response: ${response.code} ${response.message}" + } + val body = checkNotNull(response.body) { + "Null response" + } + body.byteStream().use { + cache.put(requestDraft.url, it) + } + } } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index 272969966..e9f9598c8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -88,11 +88,7 @@ class PageHolderDelegate( error = null callback.onLoadingStarted() try { - val file = withContext(Dispatchers.IO) { - val pageRequest = data.source.repository.getPageRequest(data) - check(pageRequest.isValid) { "Cannot obtain full image url" } - loader.loadFile(pageRequest, force) - } + val file = loader.loadPage(data, force) this@PageHolderDelegate.file = file state = State.LOADED callback.onImageReady(file.toUri()) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt index ad8b053ea..cf788051a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/thumbnails/PagesThumbnailsSheet.kt @@ -18,6 +18,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.SheetPagesBinding import org.koitharu.kotatsu.list.ui.MangaListSpanResolver import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter +import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf import org.koitharu.kotatsu.utils.ext.resolveDp import org.koitharu.kotatsu.utils.ext.viewLifecycleScope import org.koitharu.kotatsu.utils.ext.withArgs @@ -36,7 +37,7 @@ class PagesThumbnailsSheet : BaseBottomSheet(), return } val current = arguments?.getInt(ARG_CURRENT, -1) ?: -1 - val repository = pages.first().source.repository + val repository = mangaRepositoryOf(pages.first().source) thumbnails = pages.mapIndexed { i, x -> PageThumbnail( number = i + 1, diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt index a4fa0e443..70c05516e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt @@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.settings.utils.EditTextSummaryProvider +import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf import org.koitharu.kotatsu.utils.ext.withArgs class SourceSettingsFragment : PreferenceFragmentCompat() { @@ -24,7 +25,7 @@ class SourceSettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.sharedPreferencesName = source.name - val repo = source.repository as? RemoteMangaRepository ?: return + val repo = mangaRepositoryOf(source) as? RemoteMangaRepository ?: return val keys = repo.onCreatePreferences() addPreferencesFromResource(R.xml.pref_source) for (i in 0 until preferenceScreen.preferenceCount) { diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index 1267734c7..f80dcac07 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -21,6 +21,7 @@ import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.tracker.domain.TrackingRepository +import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf import org.koitharu.kotatsu.utils.ext.toBitmapOrNull import org.koitharu.kotatsu.utils.ext.toUriOrNull import java.util.concurrent.TimeUnit @@ -51,7 +52,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) : var success = 0 for (track in tracks) { val details = runCatching { - track.manga.source.repository.getDetails(track.manga) + mangaRepositoryOf(track.manga.source).getDetails(track.manga) }.getOrNull() val chapters = details?.chapters ?: continue when { diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/KoinExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/KoinExt.kt new file mode 100644 index 000000000..8d71b09bb --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/KoinExt.kt @@ -0,0 +1,19 @@ +package org.koitharu.kotatsu.utils.ext + +import android.content.ComponentCallbacks +import org.koin.android.ext.android.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get +import org.koin.core.qualifier.named +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.parser.MangaRepository + +@Suppress("NOTHING_TO_INLINE") +inline fun ComponentCallbacks.mangaRepositoryOf(source: MangaSource): MangaRepository { + return get(named(source)) +} + +@Suppress("NOTHING_TO_INLINE") +inline fun KoinComponent.mangaRepositoryOf(source: MangaSource): MangaRepository { + return get(named(source)) +} \ No newline at end of file