Download ahead (#7226)

This commit is contained in:
nzoba 2022-08-22 23:37:54 +02:00 committed by GitHub
parent 2e81e1b7d8
commit f207e87722
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 11 deletions

View File

@ -157,6 +157,20 @@ class DownloadManager(
downloader.queueChapters(manga, chapters, autoStart) 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<Download>) {
val wasEmpty = queue.isEmpty()
queue.toMutableList().apply {
addAll(0, downloads)
reorderQueue(this)
}
if (wasEmpty) startDownloads()
}
/** /**
* Builds the page list of a downloaded chapter. * Builds the page list of a downloaded chapter.
* *

View File

@ -275,11 +275,13 @@ class PreferencesHelper(val context: Context) {
fun pinnedSources() = flowPrefs.getStringSet("pinned_catalogues", emptySet()) 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 downloadNewChapterCategories() = flowPrefs.getStringSet("download_new_categories", emptySet())
fun downloadNewChapterCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", 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 defaultCategory() = prefs.getInt(Keys.defaultCategory, -1)
fun categorizedDisplaySettings() = flowPrefs.getBoolean("categorized_display", false) fun categorizedDisplaySettings() = flowPrefs.getBoolean("categorized_display", false)

View File

@ -19,8 +19,10 @@ import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.data.database.models.Manga 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.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadManager 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.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.saver.Image import eu.kanade.tachiyomi.data.saver.Image
import eu.kanade.tachiyomi.data.saver.ImageSaver 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.source.model.Page
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader 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.loader.HttpPageLoader
import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
@ -63,6 +66,7 @@ import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import eu.kanade.domain.manga.model.Manga as DomainManga 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. * Presenter used by the activity to perform background operations.
@ -119,6 +123,8 @@ class ReaderPresenter(
private val imageSaver: ImageSaver by injectLazy() 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 * 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. * time in a background thread to avoid blocking the UI.
@ -191,6 +197,9 @@ class ReaderPresenter(
if (currentChapters != null) { if (currentChapters != null) {
currentChapters.unref() currentChapters.unref()
saveReadingProgress(currentChapters.currChapter) saveReadingProgress(currentChapters.currChapter)
chapterDownload?.let {
downloadManager.addDownloadsToStartOfQueue(listOf(it))
}
} }
} }
@ -318,6 +327,7 @@ class ReaderPresenter(
newChapters.ref() newChapters.ref()
oldChapters?.unref() oldChapters?.unref()
chapterDownload = deleteChapterFromDownloadQueue(newChapters.currChapter)
viewerChaptersRelay.call(newChapters) viewerChaptersRelay.call(newChapters)
} }
} }
@ -416,7 +426,6 @@ class ReaderPresenter(
selectedChapter.chapter.read = true selectedChapter.chapter.read = true
updateTrackChapterRead(selectedChapter) updateTrackChapterRead(selectedChapter)
deleteChapterIfNeeded(selectedChapter) deleteChapterIfNeeded(selectedChapter)
deleteChapterFromDownloadQueue(currentChapters.currChapter)
} }
if (selectedChapter != currentChapters.currChapter) { if (selectedChapter != currentChapters.currChapter) {
@ -425,15 +434,56 @@ class ReaderPresenter(
setReadStartTime() setReadStartTime()
loadNewChapter(selectedChapter) 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<DbChapter> {
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<DbChapter>) {
downloadManager.downloadChapters(manga?.toDomainManga()!!, chapters)
} }
/** /**
* Removes [currentChapter] from download queue * Removes [currentChapter] from download queue
* if setting is enabled and [currentChapter] is queued for download * if setting is enabled and [currentChapter] is queued for download
*/ */
private fun deleteChapterFromDownloadQueue(currentChapter: ReaderChapter) { private fun deleteChapterFromDownloadQueue(currentChapter: ReaderChapter): Download? {
downloadManager.getChapterDownloadOrNull(currentChapter.chapter)?.let { download -> return downloadManager.getChapterDownloadOrNull(currentChapter.chapter)?.apply {
downloadManager.deletePendingDownload(download) downloadManager.deletePendingDownload(this)
} }
} }
@ -448,6 +498,9 @@ class ReaderPresenter(
val removeAfterReadSlots = preferences.removeAfterReadSlots() val removeAfterReadSlots = preferences.removeAfterReadSlots()
val chapterToDelete = chapterList.getOrNull(currentChapterPosition - 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 // Check if deleting option is enabled and chapter exists
if (removeAfterReadSlots != -1 && chapterToDelete != null) { if (removeAfterReadSlots != -1 && chapterToDelete != null) {
enqueueDeleteReadChapters(chapterToDelete) enqueueDeleteReadChapters(chapterToDelete)

View File

@ -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.bindTo
import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.entriesRes 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.intListPreference
import eu.kanade.tachiyomi.util.preference.multiSelectListPreference import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.onClick
@ -129,10 +130,10 @@ class SettingsDownloadController : SettingsController() {
} }
preferenceCategory { preferenceCategory {
titleRes = R.string.pref_category_auto_download titleRes = R.string.pref_download_new
switchPreference { switchPreference {
bindTo(preferences.downloadNewChapter()) bindTo(preferences.downloadNewChapters())
titleRes = R.string.pref_download_new titleRes = R.string.pref_download_new
} }
preference { preference {
@ -142,7 +143,7 @@ class SettingsDownloadController : SettingsController() {
DownloadCategoriesDialog().showDialog(router) DownloadCategoriesDialog().showDialog(router)
} }
visibleIf(preferences.downloadNewChapter()) { it } visibleIf(preferences.downloadNewChapters()) { it }
fun updateSummary() { fun updateSummary() {
val selectedCategories = preferences.downloadNewChapterCategories().get() val selectedCategories = preferences.downloadNewChapterCategories().get()
@ -178,6 +179,25 @@ class SettingsDownloadController : SettingsController() {
.launchIn(viewScope) .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?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@ -57,8 +57,8 @@ fun DomainManga.shouldDownloadNewChapters(dbCategories: List<Long>, preferences:
val categories = dbCategories.ifEmpty { listOf(0L) } val categories = dbCategories.ifEmpty { listOf(0L) }
// Boolean to determine if user wants to automatically download new chapters. // Boolean to determine if user wants to automatically download new chapters.
val downloadNewChapter = preferences.downloadNewChapter().get() val downloadNewChapters = preferences.downloadNewChapters().get()
if (!downloadNewChapter) return false if (!downloadNewChapters) return false
val includedCategories = preferences.downloadNewChapterCategories().get().map { it.toLong() } val includedCategories = preferences.downloadNewChapterCategories().get().map { it.toLong() }
val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get().map { it.toLong() } val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get().map { it.toLong() }

View File

@ -417,6 +417,13 @@
<string name="pref_category_auto_download">Auto-download</string> <string name="pref_category_auto_download">Auto-download</string>
<string name="pref_download_new">Download new chapters</string> <string name="pref_download_new">Download new chapters</string>
<string name="pref_download_new_categories_details">Manga in excluded categories will not be downloaded even if they are also in included categories.</string> <string name="pref_download_new_categories_details">Manga in excluded categories will not be downloaded even if they are also in included categories.</string>
<string name="download_ahead">Download ahead</string>
<string name="auto_download_while_reading">Auto download while reading</string>
<plurals name="next_unread_chapters">
<item quantity="one">Next unread chapter</item>
<item quantity="other">Next %d unread chapters</item>
</plurals>
<string name="download_ahead_info">Only works on entries in library and if the current chapter plus the next one are already downloaded</string>
<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>