feat(download): auto-download chapters during library update
Add configurable library-update download modes (disabled, next X unread, all) with settings UI and enqueue downloads after chapter sync completes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4fce1fa940
commit
a81d717ca3
8 changed files with 106 additions and 3 deletions
|
|
@ -27,6 +27,7 @@ import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
|
||||||
import eu.kanade.domain.track.interactor.TrackChapter
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
import mihon.data.repository.ExtensionRepoRepositoryImpl
|
import mihon.data.repository.ExtensionRepoRepositoryImpl
|
||||||
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
||||||
|
import mihon.domain.chapter.interactor.GetChaptersForLibraryUpdateDownload
|
||||||
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
|
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
|
||||||
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
|
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
|
||||||
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
|
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
|
||||||
|
|
@ -171,6 +172,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
|
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
|
||||||
addFactory { GetAvailableScanlators(get()) }
|
addFactory { GetAvailableScanlators(get()) }
|
||||||
addFactory { FilterChaptersForDownload(get(), get(), get(), get()) }
|
addFactory { FilterChaptersForDownload(get(), get(), get(), get()) }
|
||||||
|
addFactory { GetChaptersForLibraryUpdateDownload(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||||
addFactory { GetHistory(get()) }
|
addFactory { GetHistory(get()) }
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import kotlinx.collections.immutable.toImmutableMap
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
|
import tachiyomi.domain.download.service.LibraryUpdateDownloadMode
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.kmk.KMR
|
import tachiyomi.i18n.kmk.KMR
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
|
|
@ -77,6 +78,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
||||||
allCategories = allCategories,
|
allCategories = allCategories,
|
||||||
),
|
),
|
||||||
getDownloadAheadGroup(downloadPreferences = downloadPreferences),
|
getDownloadAheadGroup(downloadPreferences = downloadPreferences),
|
||||||
|
getLibraryUpdateDownloadGroup(downloadPreferences = downloadPreferences),
|
||||||
// KMK -->
|
// KMK -->
|
||||||
getDownloadCacheRenewInterval(downloadPreferences = downloadPreferences),
|
getDownloadCacheRenewInterval(downloadPreferences = downloadPreferences),
|
||||||
// KMK <--
|
// KMK <--
|
||||||
|
|
@ -216,6 +218,39 @@ object SettingsDownloadScreen : SearchableSettings {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getLibraryUpdateDownloadGroup(
|
||||||
|
downloadPreferences: DownloadPreferences,
|
||||||
|
): Preference.PreferenceGroup {
|
||||||
|
val modePref = downloadPreferences.libraryUpdateDownloadMode()
|
||||||
|
val amountPref = downloadPreferences.libraryUpdateDownloadAmount()
|
||||||
|
val mode by modePref.collectAsState()
|
||||||
|
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = stringResource(KMR.strings.library_update_download),
|
||||||
|
preferenceItems = persistentListOf(
|
||||||
|
Preference.PreferenceItem.ListPreference(
|
||||||
|
preference = modePref,
|
||||||
|
entries = persistentMapOf(
|
||||||
|
LibraryUpdateDownloadMode.DISABLED to stringResource(MR.strings.disabled),
|
||||||
|
LibraryUpdateDownloadMode.NEXT_X to stringResource(KMR.strings.library_update_download_mode_next_x),
|
||||||
|
LibraryUpdateDownloadMode.ALL to stringResource(KMR.strings.library_update_download_mode_all),
|
||||||
|
),
|
||||||
|
title = stringResource(KMR.strings.library_update_download_mode),
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.ListPreference(
|
||||||
|
preference = amountPref,
|
||||||
|
entries = listOf(2, 3, 5, 10)
|
||||||
|
.associateWith { pluralStringResource(MR.plurals.next_unread_chapters, count = it, it) }
|
||||||
|
.toImmutableMap(),
|
||||||
|
title = stringResource(KMR.strings.library_update_download_amount),
|
||||||
|
enabled = mode == LibraryUpdateDownloadMode.NEXT_X,
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.InfoPreference(stringResource(KMR.strings.library_update_download_info)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// KMK -->
|
// KMK -->
|
||||||
@Composable
|
@Composable
|
||||||
private fun getDownloadCacheRenewInterval(
|
private fun getDownloadCacheRenewInterval(
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ import kotlinx.coroutines.sync.Semaphore
|
||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
||||||
|
import mihon.domain.chapter.interactor.GetChaptersForLibraryUpdateDownload
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
@ -119,6 +120,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get()
|
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get()
|
||||||
private val fetchInterval: FetchInterval = Injekt.get()
|
private val fetchInterval: FetchInterval = Injekt.get()
|
||||||
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get()
|
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get()
|
||||||
|
private val getChaptersForLibraryUpdateDownload: GetChaptersForLibraryUpdateDownload = Injekt.get()
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val getFavorites: GetFavorites = Injekt.get()
|
private val getFavorites: GetFavorites = Injekt.get()
|
||||||
|
|
@ -465,6 +467,12 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
// Convert to the manga that contains new chapters
|
// Convert to the manga that contains new chapters
|
||||||
newUpdates.add(manga to newChapters.toTypedArray())
|
newUpdates.add(manga to newChapters.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val libraryUpdateChapters = getChaptersForLibraryUpdateDownload.await(manga)
|
||||||
|
if (libraryUpdateChapters.isNotEmpty()) {
|
||||||
|
downloadChapters(manga, libraryUpdateChapters)
|
||||||
|
hasDownloads.store(true)
|
||||||
|
}
|
||||||
clearErrorFromDB(mangaId = manga.id)
|
clearErrorFromDB(mangaId = manga.id)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
val errorMessage = when (e) {
|
val errorMessage = when (e) {
|
||||||
|
|
@ -493,10 +501,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
|
|
||||||
if (newUpdates.isNotEmpty()) {
|
if (newUpdates.isNotEmpty()) {
|
||||||
notifier.showUpdateNotifications(newUpdates)
|
notifier.showUpdateNotifications(newUpdates)
|
||||||
|
}
|
||||||
|
|
||||||
if (hasDownloads.load()) {
|
if (hasDownloads.load()) {
|
||||||
downloadManager.startDownloads()
|
downloadManager.startDownloads()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (failedUpdates.isNotEmpty()) {
|
if (failedUpdates.isNotEmpty()) {
|
||||||
notifier.showUpdateErrorNotification(
|
notifier.showUpdateErrorNotification(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package mihon.domain.chapter.interactor
|
||||||
|
|
||||||
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
|
import tachiyomi.domain.download.service.LibraryUpdateDownloadMode
|
||||||
|
import tachiyomi.domain.history.interactor.GetNextChapters
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
|
class GetChaptersForLibraryUpdateDownload(
|
||||||
|
private val downloadPreferences: DownloadPreferences,
|
||||||
|
private val getNextChapters: GetNextChapters,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(manga: Manga): List<Chapter> {
|
||||||
|
if (!manga.favorite) return emptyList()
|
||||||
|
|
||||||
|
val mode = downloadPreferences.libraryUpdateDownloadMode().get()
|
||||||
|
if (mode == LibraryUpdateDownloadMode.DISABLED) return emptyList()
|
||||||
|
|
||||||
|
val chapters = getNextChapters.await(manga.id)
|
||||||
|
if (chapters.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
return when (mode) {
|
||||||
|
LibraryUpdateDownloadMode.NEXT_X -> {
|
||||||
|
chapters.take(downloadPreferences.libraryUpdateDownloadAmount().get())
|
||||||
|
}
|
||||||
|
LibraryUpdateDownloadMode.ALL -> chapters
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -47,6 +47,13 @@ class DownloadPreferences(
|
||||||
|
|
||||||
// KMK -->
|
// KMK -->
|
||||||
fun downloadCacheRenewInterval() = preferenceStore.getInt("download_cache_renew_interval", 1)
|
fun downloadCacheRenewInterval() = preferenceStore.getInt("download_cache_renew_interval", 1)
|
||||||
|
|
||||||
|
fun libraryUpdateDownloadMode() = preferenceStore.getInt(
|
||||||
|
"library_update_download_mode",
|
||||||
|
LibraryUpdateDownloadMode.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun libraryUpdateDownloadAmount() = preferenceStore.getInt("library_update_download_amount", 10)
|
||||||
// KMK <--
|
// KMK <--
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package tachiyomi.domain.download.service
|
||||||
|
|
||||||
|
object LibraryUpdateDownloadMode {
|
||||||
|
const val DISABLED = 0
|
||||||
|
const val NEXT_X = 1
|
||||||
|
const val ALL = 2
|
||||||
|
}
|
||||||
|
|
@ -113,6 +113,12 @@
|
||||||
<string name="download_cache_renew_interval_12hour">12 hours</string>
|
<string name="download_cache_renew_interval_12hour">12 hours</string>
|
||||||
<string name="download_cache_renew_interval_24hour">1 day</string>
|
<string name="download_cache_renew_interval_24hour">1 day</string>
|
||||||
<string name="download_cache_renew_interval_info">If set to "manual", either refresh each manga or using the "Reindex downloads" action in "Advanced"</string>
|
<string name="download_cache_renew_interval_info">If set to "manual", either refresh each manga or using the "Reindex downloads" action in "Advanced"</string>
|
||||||
|
<string name="library_update_download">Download on library update</string>
|
||||||
|
<string name="library_update_download_mode">Download chapters on library update</string>
|
||||||
|
<string name="library_update_download_mode_next_x">Next unread chapters from current position</string>
|
||||||
|
<string name="library_update_download_mode_all">All unread chapters from current position</string>
|
||||||
|
<string name="library_update_download_amount">Number of chapters to download</string>
|
||||||
|
<string name="library_update_download_info">Downloads unread chapters from your current reading position when the library is updated. Already downloaded chapters are skipped.</string>
|
||||||
<!-- Tracking section -->
|
<!-- Tracking section -->
|
||||||
<string name="pref_auto_sync_progress_from_trackers">Auto sync progress from Trackers</string>
|
<string name="pref_auto_sync_progress_from_trackers">Auto sync progress from Trackers</string>
|
||||||
<string name="sync_progress_from_trackers_up_to_chapter">Sync progress from Trackers up to chapter %d</string>
|
<string name="sync_progress_from_trackers_up_to_chapter">Sync progress from Trackers up to chapter %d</string>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@
|
||||||
<string name="download_cache_renew_interval_12hour">12 Stunden</string>
|
<string name="download_cache_renew_interval_12hour">12 Stunden</string>
|
||||||
<string name="download_cache_renew_interval_24hour">1 Tag</string>
|
<string name="download_cache_renew_interval_24hour">1 Tag</string>
|
||||||
<string name="download_cache_renew_interval_info">Wenn auf \"Manuell\" gesetzt, aktualisieren Sie entweder jedes Manga oder verwenden Sie die Aktion \"Downloads neu indizieren\" in \"Erweitert\"</string>
|
<string name="download_cache_renew_interval_info">Wenn auf \"Manuell\" gesetzt, aktualisieren Sie entweder jedes Manga oder verwenden Sie die Aktion \"Downloads neu indizieren\" in \"Erweitert\"</string>
|
||||||
|
<string name="library_update_download">Bei Bibliotheksaktualisierung herunterladen</string>
|
||||||
|
<string name="library_update_download_mode">Kapitel bei Bibliotheksaktualisierung herunterladen</string>
|
||||||
|
<string name="library_update_download_mode_next_x">Nächste ungelesene Kapitel ab aktueller Leseposition</string>
|
||||||
|
<string name="library_update_download_mode_all">Alle ungelesenen Kapitel ab aktueller Leseposition</string>
|
||||||
|
<string name="library_update_download_amount">Anzahl der herunterzuladenden Kapitel</string>
|
||||||
|
<string name="library_update_download_info">Lädt ungelesene Kapitel ab deiner aktuellen Leseposition herunter, wenn die Bibliothek aktualisiert wird. Bereits heruntergeladene Kapitel werden übersprungen.</string>
|
||||||
<string name="pref_clean_invalid_downloads">Ungültige Downloads bereinigen</string>
|
<string name="pref_clean_invalid_downloads">Ungültige Downloads bereinigen</string>
|
||||||
<string name="pref_clean_invalid_downloads_summary">Finden und entfernen Sie alle Downloads, Dateien, Ordner die nicht in Ihrer Bibliothek gespeichert sind</string>
|
<string name="pref_clean_invalid_downloads_summary">Finden und entfernen Sie alle Downloads, Dateien, Ordner die nicht in Ihrer Bibliothek gespeichert sind</string>
|
||||||
<string name="invalid_downloads_cleaned">Ungültige Downloads bereinigt</string>
|
<string name="invalid_downloads_cleaned">Ungültige Downloads bereinigt</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue