Add option to backup non-library read entries (#1324)

Co-authored-by: jobobby04 <jobobby04@gmail.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit de36357da834bff4110dbb56dd7ce7aad04d3c7d)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt
#	data/src/main/sqldelight/tachiyomi/data/mangas.sq
This commit is contained in:
Roshan Varughese 2024-10-15 05:30:23 +13:00 committed by Jobobby04
parent 216bc2c57d
commit 943555c0af
9 changed files with 43 additions and 35 deletions

View File

@ -45,7 +45,7 @@ import tachiyomi.domain.manga.interactor.GetMergedManga
import tachiyomi.domain.manga.interactor.GetMergedMangaById import tachiyomi.domain.manga.interactor.GetMergedMangaById
import tachiyomi.domain.manga.interactor.GetMergedMangaForDownloading import tachiyomi.domain.manga.interactor.GetMergedMangaForDownloading
import tachiyomi.domain.manga.interactor.GetMergedReferencesById import tachiyomi.domain.manga.interactor.GetMergedReferencesById
import tachiyomi.domain.manga.interactor.GetReadMangaNotInLibrary import tachiyomi.domain.manga.interactor.GetReadMangaNotInLibraryView
import tachiyomi.domain.manga.interactor.GetSearchMetadata import tachiyomi.domain.manga.interactor.GetSearchMetadata
import tachiyomi.domain.manga.interactor.GetSearchTags import tachiyomi.domain.manga.interactor.GetSearchTags
import tachiyomi.domain.manga.interactor.GetSearchTitles import tachiyomi.domain.manga.interactor.GetSearchTitles
@ -101,7 +101,7 @@ class SYDomainModule : InjektModule {
addFactory { GetPagePreviews(get(), get()) } addFactory { GetPagePreviews(get(), get()) }
addFactory { SearchEngine() } addFactory { SearchEngine() }
addFactory { IsTrackUnfollowed() } addFactory { IsTrackUnfollowed() }
addFactory { GetReadMangaNotInLibrary(get()) } addFactory { GetReadMangaNotInLibraryView(get()) }
// Required for [MetadataSource] // Required for [MetadataSource]
addFactory<MetadataSource.GetMangaId> { GetManga(get()) } addFactory<MetadataSource.GetMangaId> { GetManga(get()) }

View File

@ -32,6 +32,7 @@ import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.interactor.GetMergedManga import tachiyomi.domain.manga.interactor.GetMergedManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -48,6 +49,7 @@ class BackupCreator(
private val parser: ProtoBuf = Injekt.get(), private val parser: ProtoBuf = Injekt.get(),
private val getFavorites: GetFavorites = Injekt.get(), private val getFavorites: GetFavorites = Injekt.get(),
private val backupPreferences: BackupPreferences = Injekt.get(), private val backupPreferences: BackupPreferences = Injekt.get(),
private val mangaRepository: MangaRepository = Injekt.get(),
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(), private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(), private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
@ -85,15 +87,12 @@ class BackupCreator(
throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error)) throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error))
} }
val backupManga = backupMangas( val nonFavoriteManga = if (options.readEntries) mangaRepository.getReadMangaNotInLibrary() else emptyList()
getFavorites.await() /* SY --> */ + // SY -->
if (options.readEntries) { val mergedManga = getMergedManga.await()
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) } // SY <--
} else { val backupManga = backupMangas(getFavorites.await() + nonFavoriteManga /* SY --> */ + mergedManga /* SY <-- */, options)
emptyList()
} + getMergedManga.await(), // SY <--
options,
)
val backup = Backup( val backup = Backup(
backupManga = backupManga, backupManga = backupManga,
backupCategories = backupCategories(options), backupCategories = backupCategories(options),

View File

@ -11,13 +11,13 @@ data class BackupOptions(
val chapters: Boolean = true, val chapters: Boolean = true,
val tracking: Boolean = true, val tracking: Boolean = true,
val history: Boolean = true, val history: Boolean = true,
val readEntries: Boolean = true,
val appSettings: Boolean = true, val appSettings: Boolean = true,
val extensionRepoSettings: Boolean = true, val extensionRepoSettings: Boolean = true,
val sourceSettings: Boolean = true, val sourceSettings: Boolean = true,
val privateSettings: Boolean = false, val privateSettings: Boolean = false,
// SY --> // SY -->
val customInfo: Boolean = true, val customInfo: Boolean = true,
val readEntries: Boolean = true,
val savedSearches: Boolean = true, val savedSearches: Boolean = true,
// SY <-- // SY <--
) { ) {
@ -28,13 +28,13 @@ data class BackupOptions(
chapters, chapters,
tracking, tracking,
history, history,
readEntries,
appSettings, appSettings,
extensionRepoSettings, extensionRepoSettings,
sourceSettings, sourceSettings,
privateSettings, privateSettings,
// SY --> // SY -->
customInfo, customInfo,
readEntries,
savedSearches, savedSearches,
// SY <-- // SY <--
) )
@ -72,6 +72,12 @@ data class BackupOptions(
getter = BackupOptions::categories, getter = BackupOptions::categories,
setter = { options, enabled -> options.copy(categories = enabled) }, setter = { options, enabled -> options.copy(categories = enabled) },
), ),
Entry(
label = MR.strings.non_library_settings,
getter = BackupOptions::readEntries,
setter = { options, enabled -> options.copy(readEntries = enabled) },
enabled = { it.libraryEntries },
),
// SY --> // SY -->
Entry( Entry(
label = SYMR.strings.custom_entry_info, label = SYMR.strings.custom_entry_info,
@ -79,12 +85,6 @@ data class BackupOptions(
setter = { options, enabled -> options.copy(customInfo = enabled) }, setter = { options, enabled -> options.copy(customInfo = enabled) },
enabled = { it.libraryEntries }, enabled = { it.libraryEntries },
), ),
Entry(
label = SYMR.strings.all_read_entries,
getter = BackupOptions::readEntries,
setter = { options, enabled -> options.copy(readEntries = enabled) },
enabled = { it.libraryEntries },
),
Entry( Entry(
label = SYMR.strings.saved_searches, label = SYMR.strings.saved_searches,
getter = BackupOptions::savedSearches, getter = BackupOptions::savedSearches,
@ -123,13 +123,13 @@ data class BackupOptions(
chapters = array[2], chapters = array[2],
tracking = array[3], tracking = array[3],
history = array[4], history = array[4],
appSettings = array[5], readEntries = array[5],
extensionRepoSettings = array[6], appSettings = array[6],
sourceSettings = array[7], extensionRepoSettings = array[7],
privateSettings = array[8], sourceSettings = array[8],
privateSettings = array[9],
// SY --> // SY -->
customInfo = array[9], customInfo = array[10],
readEntries = array[10],
savedSearches = array[11], savedSearches = array[11],
// SY <-- // SY <--
) )

View File

@ -24,7 +24,7 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_HAS_U
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ
import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.interactor.GetReadMangaNotInLibrary import tachiyomi.domain.manga.interactor.GetReadMangaNotInLibraryView
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.model.Track
import tachiyomi.source.local.isLocal import tachiyomi.source.local.isLocal
@ -39,7 +39,7 @@ class StatsScreenModel(
private val preferences: LibraryPreferences = Injekt.get(), private val preferences: LibraryPreferences = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(), private val trackerManager: TrackerManager = Injekt.get(),
// SY --> // SY -->
private val getReadMangaNotInLibrary: GetReadMangaNotInLibrary = Injekt.get(), private val getReadMangaNotInLibraryView: GetReadMangaNotInLibraryView = Injekt.get(),
// SY <-- // SY <--
) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) { ) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {
@ -55,7 +55,7 @@ class StatsScreenModel(
_allRead.onEach { allRead -> _allRead.onEach { allRead ->
mutableState.update { StatsScreenState.Loading } mutableState.update { StatsScreenState.Loading }
val libraryManga = getLibraryManga.await() + if (allRead) { val libraryManga = getLibraryManga.await() + if (allRead) {
getReadMangaNotInLibrary.await() getReadMangaNotInLibraryView.await()
} else { } else {
emptyList() emptyList()
} }

View File

@ -51,6 +51,10 @@ class MangaRepositoryImpl(
return handler.awaitList { mangasQueries.getFavorites(MangaMapper::mapManga) } return handler.awaitList { mangasQueries.getFavorites(MangaMapper::mapManga) }
} }
override suspend fun getReadMangaNotInLibrary(): List<Manga> {
return handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
}
override suspend fun getLibraryManga(): List<LibraryManga> { override suspend fun getLibraryManga(): List<LibraryManga> {
return handler.awaitListExecutable { return handler.awaitListExecutable {
(handler as AndroidDatabaseHandler).getLibraryQuery() (handler as AndroidDatabaseHandler).getLibraryQuery()
@ -198,7 +202,7 @@ class MangaRepositoryImpl(
handler.await { mangasQueries.deleteById(mangaId) } handler.await { mangasQueries.deleteById(mangaId) }
} }
override suspend fun getReadMangaNotInLibrary(): List<LibraryManga> { override suspend fun getReadMangaNotInLibraryView(): List<LibraryManga> {
return handler.awaitListExecutable { return handler.awaitListExecutable {
(handler as AndroidDatabaseHandler).getLibraryQuery("M.favorite = 0 AND C.readCount != 0") (handler as AndroidDatabaseHandler).getLibraryQuery("M.favorite = 0 AND C.readCount != 0")
}.map(MangaMapper::mapLibraryView) }.map(MangaMapper::mapLibraryView)

View File

@ -80,10 +80,12 @@ FROM mangas
WHERE favorite = 1; WHERE favorite = 1;
getReadMangaNotInLibrary: getReadMangaNotInLibrary:
SELECT mangas.* SELECT *
FROM mangas FROM mangas
WHERE favorite = 0 AND _id IN( WHERE favorite = 0 AND _id IN (
SELECT chapters.manga_id FROM chapters WHERE read = 1 OR last_page_read != 0 SELECT DISTINCT chapters.manga_id
FROM chapters
WHERE read = 1 OR last_page_read != 0
); );
getAllManga: getAllManga:

View File

@ -3,11 +3,11 @@ package tachiyomi.domain.manga.interactor
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
class GetReadMangaNotInLibrary( class GetReadMangaNotInLibraryView(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
) { ) {
suspend fun await(): List<LibraryManga> { suspend fun await(): List<LibraryManga> {
return mangaRepository.getReadMangaNotInLibrary() return mangaRepository.getReadMangaNotInLibraryView()
} }
} }

View File

@ -17,6 +17,8 @@ interface MangaRepository {
suspend fun getFavorites(): List<Manga> suspend fun getFavorites(): List<Manga>
suspend fun getReadMangaNotInLibrary(): List<Manga>
suspend fun getLibraryManga(): List<LibraryManga> suspend fun getLibraryManga(): List<LibraryManga>
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>> fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
@ -44,6 +46,6 @@ interface MangaRepository {
suspend fun deleteManga(mangaId: Long) suspend fun deleteManga(mangaId: Long)
suspend fun getReadMangaNotInLibrary(): List<LibraryManga> suspend fun getReadMangaNotInLibraryView(): List<LibraryManga>
// SY <-- // SY <--
} }

View File

@ -546,6 +546,7 @@
<string name="source_settings">Source settings</string> <string name="source_settings">Source settings</string>
<string name="extensionRepo_settings">Extension repos</string> <string name="extensionRepo_settings">Extension repos</string>
<string name="private_settings">Include sensitive settings (e.g., tracker login tokens)</string> <string name="private_settings">Include sensitive settings (e.g., tracker login tokens)</string>
<string name="non_library_settings">All read entries</string>
<string name="creating_backup">Creating backup</string> <string name="creating_backup">Creating backup</string>
<string name="creating_backup_error">Backup failed</string> <string name="creating_backup_error">Backup failed</string>
<string name="missing_storage_permission">Storage permissions not granted</string> <string name="missing_storage_permission">Storage permissions not granted</string>