feat: option to auto sync metadata and chapter list when adding to library (#1091)
* feat: auto-fetch chapter list when adding to library * simplify impl * IO context (gemini suggestion) * don't trigger NoChapterException * add option to settings * linting * fix: fetching on bulk * add kmk comment * fix exception handling * linting again * feat: also sync metadata * fix: rebase issues * remove KMK comment * Use `manga` instead of `new` so its title got updated with source's `getMangaDetails`; also reset chapterFlags --------- Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
parent
c7e0585c6f
commit
e7bdbb5794
5 changed files with 78 additions and 1 deletions
|
|
@ -292,6 +292,11 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||
preference = libraryPreferences.showEmptyCategoriesSearch(),
|
||||
title = stringResource(KMR.strings.pref_show_empty_categories_search),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = libraryPreferences.syncOnAdd(),
|
||||
title = stringResource(KMR.strings.pref_sync_manga_on_add),
|
||||
subtitle = stringResource(KMR.strings.pref_sync_manga_on_add_description),
|
||||
),
|
||||
// KMK <--
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedback
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
|
|
@ -9,7 +10,9 @@ import androidx.compose.ui.util.fastForEach
|
|||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.domain.track.interactor.AddTracks
|
||||
import eu.kanade.presentation.components.BulkSelectionToolbar
|
||||
import eu.kanade.presentation.manga.DuplicateMangaDialog
|
||||
|
|
@ -24,10 +27,13 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.preference.CheckboxState
|
||||
import tachiyomi.core.common.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||
import tachiyomi.domain.category.model.Category
|
||||
|
|
@ -53,6 +59,8 @@ class BulkFavoriteScreenModel(
|
|||
private val coverCache: CoverCache = Injekt.get(),
|
||||
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
||||
private val addTracks: AddTracks = Injekt.get(),
|
||||
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
|
||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||
) : StateScreenModel<BulkFavoriteScreenModel.State>(initialState) {
|
||||
|
||||
fun backHandler() {
|
||||
|
|
@ -243,7 +251,23 @@ class BulkFavoriteScreenModel(
|
|||
if (manga.favorite) return
|
||||
|
||||
screenModelScope.launchIO {
|
||||
updateManga.awaitUpdateFavorite(manga.id, true)
|
||||
try {
|
||||
val source = sourceManager.getOrStub(manga.source)
|
||||
setMangaDefaultChapterFlags.await(manga)
|
||||
addTracks.bindEnhancedTrackers(manga, source)
|
||||
updateManga.awaitUpdateFavorite(manga.id, true)
|
||||
if (libraryPreferences.syncOnAdd().get()) {
|
||||
val sManga = manga.toSManga()
|
||||
val remoteManga = source.getMangaDetails(sManga)
|
||||
val chapters = source.getChapterList(sManga)
|
||||
// Use `manga` instead of `new` so its title got updated with source's `getMangaDetails`
|
||||
updateManga.awaitUpdateFromSource(manga, remoteManga, false, coverCache)
|
||||
syncChaptersWithSource.await(chapters, manga, source, false)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
snackbarHostState.showSnackbar(message = "Failed to sync manga: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +351,21 @@ class BulkFavoriteScreenModel(
|
|||
}
|
||||
|
||||
updateManga.await(new.toMangaUpdate())
|
||||
if (new.favorite && libraryPreferences.syncOnAdd().get()) {
|
||||
withIOContext {
|
||||
try {
|
||||
val sManga = manga.toSManga()
|
||||
val remoteManga = source.getMangaDetails(sManga)
|
||||
val chapters = source.getChapterList(sManga)
|
||||
// Use `manga` instead of `new` so its title got updated with source's `getMangaDetails`
|
||||
updateManga.awaitUpdateFromSource(manga, remoteManga, false, coverCache)
|
||||
syncChaptersWithSource.await(chapters, manga, source, false)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
snackbarHostState.showSnackbar(message = "Failed to sync manga: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -16,7 +17,9 @@ import cafe.adriel.voyager.core.model.StateScreenModel
|
|||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.core.preference.asState
|
||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||
import eu.kanade.domain.source.interactor.GetIncognitoState
|
||||
import eu.kanade.domain.source.interactor.ToggleIncognito
|
||||
|
|
@ -57,11 +60,14 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.preference.CheckboxState
|
||||
import tachiyomi.core.common.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.lang.withUIContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||
import tachiyomi.domain.category.model.Category
|
||||
|
|
@ -113,6 +119,8 @@ open class BrowseSourceScreenModel(
|
|||
// KMK -->
|
||||
private val toggleIncognito: ToggleIncognito = Injekt.get(),
|
||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
|
||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||
// KMK <--
|
||||
|
||||
// SY -->
|
||||
|
|
@ -415,6 +423,25 @@ open class BrowseSourceScreenModel(
|
|||
}
|
||||
|
||||
updateManga.await(new.toMangaUpdate())
|
||||
// KMK -->
|
||||
if (new.favorite && libraryPreferences.syncOnAdd().get()) {
|
||||
withIOContext {
|
||||
try {
|
||||
val sManga = manga.toSManga()
|
||||
val remoteManga = source.getMangaDetails(sManga)
|
||||
val chapters = source.getChapterList(sManga)
|
||||
// Use `manga` instead of `new` so its title got updated with source's `getMangaDetails`
|
||||
updateManga.awaitUpdateFromSource(manga, remoteManga, false, coverCache)
|
||||
syncChaptersWithSource.await(chapters, manga, source, false)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
screenModelScope.launch {
|
||||
snackbarHostState.showSnackbar(message = "Failed to sync manga: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@ class LibraryPreferences(
|
|||
|
||||
fun autoUpdateMetadata() = preferenceStore.getBoolean("auto_update_metadata", false)
|
||||
|
||||
// KMK -->
|
||||
fun syncOnAdd() = preferenceStore.getBoolean("sync_on_add", false)
|
||||
// KMK <--
|
||||
|
||||
fun showContinueReadingButton() = preferenceStore.getBoolean(
|
||||
"display_continue_reading_button",
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@
|
|||
<string name="pref_library_filter_categories_details">Entries in excluded categories will not be shown even if they are also in included categories.</string>
|
||||
<string name="updating">Updating</string>
|
||||
<string name="pref_show_empty_categories_search">Show empty categories during search/filtering</string>
|
||||
<string name="pref_sync_manga_on_add">Sync manga when adding to library</string>
|
||||
<string name="pref_sync_manga_on_add_description">Automatically fetches the metadata and chapter list of a new manga when adding it to the library</string>
|
||||
<!-- Extension section -->
|
||||
<string name="extensions_page_need_refresh">Refresh extension page to update list.</string>
|
||||
<string name="extensions_page_more">More extensions...</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue