From f207e8772299b55ffc08aa62d06fae8acd23a06d Mon Sep 17 00:00:00 2001 From: nzoba <55888232+nzoba@users.noreply.github.com> Date: Mon, 22 Aug 2022 23:37:54 +0200 Subject: [PATCH] Download ahead (#7226) --- .../data/download/DownloadManager.kt | 14 +++++ .../data/preference/PreferencesHelper.kt | 4 +- .../tachiyomi/ui/reader/ReaderPresenter.kt | 61 +++++++++++++++++-- .../ui/setting/SettingsDownloadController.kt | 26 +++++++- .../kanade/tachiyomi/util/MangaExtensions.kt | 4 +- app/src/main/res/values/strings.xml | 9 ++- 6 files changed, 107 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 85c0447500..374d926239 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -157,6 +157,20 @@ class DownloadManager( downloader.queueChapters(manga, chapters, autoStart) } + /** + * Tells the downloader to enqueue the given list of downloads at the start of the queue. + * + * @param downloads the list of downloads to enqueue. + */ + fun addDownloadsToStartOfQueue(downloads: List) { + val wasEmpty = queue.isEmpty() + queue.toMutableList().apply { + addAll(0, downloads) + reorderQueue(this) + } + if (wasEmpty) startDownloads() + } + /** * Builds the page list of a downloaded chapter. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 82a1e65223..ee5aaf8114 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -275,11 +275,13 @@ class PreferencesHelper(val context: Context) { fun pinnedSources() = flowPrefs.getStringSet("pinned_catalogues", emptySet()) - fun downloadNewChapter() = flowPrefs.getBoolean("download_new", false) + fun downloadNewChapters() = flowPrefs.getBoolean("download_new", false) fun downloadNewChapterCategories() = flowPrefs.getStringSet("download_new_categories", emptySet()) fun downloadNewChapterCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", emptySet()) + fun autoDownloadWhileReading() = flowPrefs.getInt("auto_download_while_reading", 0) + fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1) fun categorizedDisplaySettings() = flowPrefs.getBoolean("categorized_display", false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index 902e313858..3095bf84af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -19,8 +19,10 @@ import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.model.toDbTrack import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.saver.Image import eu.kanade.tachiyomi.data.saver.ImageSaver @@ -32,6 +34,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader +import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter @@ -63,6 +66,7 @@ import uy.kohesive.injekt.injectLazy import java.util.Date import java.util.concurrent.TimeUnit import eu.kanade.domain.manga.model.Manga as DomainManga +import eu.kanade.tachiyomi.data.database.models.Chapter as DbChapter /** * Presenter used by the activity to perform background operations. @@ -119,6 +123,8 @@ class ReaderPresenter( private val imageSaver: ImageSaver by injectLazy() + private var chapterDownload: Download? = null + /** * Chapter list for the active manga. It's retrieved lazily and should be accessed for the first * time in a background thread to avoid blocking the UI. @@ -191,6 +197,9 @@ class ReaderPresenter( if (currentChapters != null) { currentChapters.unref() saveReadingProgress(currentChapters.currChapter) + chapterDownload?.let { + downloadManager.addDownloadsToStartOfQueue(listOf(it)) + } } } @@ -318,6 +327,7 @@ class ReaderPresenter( newChapters.ref() oldChapters?.unref() + chapterDownload = deleteChapterFromDownloadQueue(newChapters.currChapter) viewerChaptersRelay.call(newChapters) } } @@ -416,7 +426,6 @@ class ReaderPresenter( selectedChapter.chapter.read = true updateTrackChapterRead(selectedChapter) deleteChapterIfNeeded(selectedChapter) - deleteChapterFromDownloadQueue(currentChapters.currChapter) } if (selectedChapter != currentChapters.currChapter) { @@ -425,15 +434,56 @@ class ReaderPresenter( setReadStartTime() loadNewChapter(selectedChapter) } + val pages = page.chapter.pages ?: return + val inDownloadRange = page.number.toDouble() / pages.size > 0.2 + if (inDownloadRange) { + downloadNextChapters() + } + } + + private fun downloadNextChapters() { + val manga = manga ?: return + if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return + val nextChapter = viewerChaptersRelay.value?.nextChapter?.chapter ?: return + val chaptersNumberToDownload = preferences.autoDownloadWhileReading().get() + if (chaptersNumberToDownload == 0 || !manga.favorite) return + val isNextChapterDownloaded = + downloadManager.isChapterDownloaded(nextChapter.name, nextChapter.scanlator, manga.title, manga.source) + if (isNextChapterDownloaded) { + downloadAutoNextChapters(chaptersNumberToDownload, nextChapter.id) + } + } + + private fun downloadAutoNextChapters(choice: Int, nextChapterId: Long?) { + val chaptersToDownload = getNextUnreadChaptersSorted(nextChapterId).take(choice - 1) + if (chaptersToDownload.isNotEmpty()) { + downloadChapters(chaptersToDownload) + } + } + + private fun getNextUnreadChaptersSorted(nextChapterId: Long?): List { + return chapterList.map { it.chapter.toDomainChapter()!! } + .filter { !it.read || it.id == nextChapterId } + .sortedWith(getChapterSort(manga?.toDomainManga()!!, false)) + .map { it.toDbChapter() } + .takeLastWhile { it.id != nextChapterId } + } + + /** + * Downloads the given list of chapters with the manager. + * @param chapters the list of chapters to download. + */ + private fun downloadChapters(chapters: List) { + downloadManager.downloadChapters(manga?.toDomainManga()!!, chapters) } /** * Removes [currentChapter] from download queue * if setting is enabled and [currentChapter] is queued for download */ - private fun deleteChapterFromDownloadQueue(currentChapter: ReaderChapter) { - downloadManager.getChapterDownloadOrNull(currentChapter.chapter)?.let { download -> - downloadManager.deletePendingDownload(download) + private fun deleteChapterFromDownloadQueue(currentChapter: ReaderChapter): Download? { + return downloadManager.getChapterDownloadOrNull(currentChapter.chapter)?.apply { + downloadManager.deletePendingDownload(this) } } @@ -448,6 +498,9 @@ class ReaderPresenter( val removeAfterReadSlots = preferences.removeAfterReadSlots() val chapterToDelete = chapterList.getOrNull(currentChapterPosition - removeAfterReadSlots) + if (removeAfterReadSlots != 0 && chapterDownload != null) { + downloadManager.addDownloadsToStartOfQueue(listOf(chapterDownload!!)) + } // Check if deleting option is enabled and chapter exists if (removeAfterReadSlots != -1 && chapterToDelete != null) { enqueueDeleteReadChapters(chapterToDelete) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt index da41c89331..c0b7ad20fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt @@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.preference.bindTo import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.entriesRes +import eu.kanade.tachiyomi.util.preference.infoPreference import eu.kanade.tachiyomi.util.preference.intListPreference import eu.kanade.tachiyomi.util.preference.multiSelectListPreference import eu.kanade.tachiyomi.util.preference.onClick @@ -129,10 +130,10 @@ class SettingsDownloadController : SettingsController() { } preferenceCategory { - titleRes = R.string.pref_category_auto_download + titleRes = R.string.pref_download_new switchPreference { - bindTo(preferences.downloadNewChapter()) + bindTo(preferences.downloadNewChapters()) titleRes = R.string.pref_download_new } preference { @@ -142,7 +143,7 @@ class SettingsDownloadController : SettingsController() { DownloadCategoriesDialog().showDialog(router) } - visibleIf(preferences.downloadNewChapter()) { it } + visibleIf(preferences.downloadNewChapters()) { it } fun updateSummary() { val selectedCategories = preferences.downloadNewChapterCategories().get() @@ -178,6 +179,25 @@ class SettingsDownloadController : SettingsController() { .launchIn(viewScope) } } + + preferenceCategory { + titleRes = R.string.download_ahead + + intListPreference { + bindTo(preferences.autoDownloadWhileReading()) + titleRes = R.string.auto_download_while_reading + entries = arrayOf( + context.getString(R.string.disabled), + context.resources.getQuantityString(R.plurals.next_unread_chapters, 2, 2), + context.resources.getQuantityString(R.plurals.next_unread_chapters, 3, 3), + context.resources.getQuantityString(R.plurals.next_unread_chapters, 5, 5), + context.resources.getQuantityString(R.plurals.next_unread_chapters, 10, 10), + ) + entryValues = arrayOf("0", "2", "3", "5", "10") + summary = "%s" + } + infoPreference(R.string.download_ahead_info) + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt index 76089f4379..03a2b202ca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -57,8 +57,8 @@ fun DomainManga.shouldDownloadNewChapters(dbCategories: List, preferences: val categories = dbCategories.ifEmpty { listOf(0L) } // Boolean to determine if user wants to automatically download new chapters. - val downloadNewChapter = preferences.downloadNewChapter().get() - if (!downloadNewChapter) return false + val downloadNewChapters = preferences.downloadNewChapters().get() + if (!downloadNewChapters) return false val includedCategories = preferences.downloadNewChapterCategories().get().map { it.toLong() } val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get().map { it.toLong() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 013f61eb58..f633ebf443 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -417,11 +417,18 @@ Auto-download Download new chapters Manga in excluded categories will not be downloaded even if they are also in included categories. + Download ahead + Auto download while reading + + Next unread chapter + Next %d unread chapters + + Only works on entries in library and if the current chapter plus the next one are already downloaded Save as CBZ archive Split tall images Improves reader performance - + Tracking guide Update progress after reading Services