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

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

(cherry picked from commit 943555c0af49b48abaa18a47395e5516f3357bae)
This commit is contained in:
Roshan Varughese 2024-10-15 05:30:23 +13:00 committed by Cuong-Tran
parent 221508bc46
commit f839334c1c
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
9 changed files with 44 additions and 36 deletions

View file

@ -43,7 +43,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
@ -104,7 +104,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

@ -28,12 +28,11 @@ import okio.gzip
import okio.sink import okio.sink
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.data.DatabaseHandler
import tachiyomi.data.manga.MangaMapper
import tachiyomi.domain.backup.service.BackupPreferences 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
@ -50,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(),
@ -62,7 +62,6 @@ class BackupCreator(
// SY --> // SY -->
private val savedSearchBackupCreator: SavedSearchBackupCreator = SavedSearchBackupCreator(), private val savedSearchBackupCreator: SavedSearchBackupCreator = SavedSearchBackupCreator(),
private val getMergedManga: GetMergedManga = Injekt.get(), private val getMergedManga: GetMergedManga = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get(),
// SY <-- // SY <--
) { ) {
@ -90,13 +89,13 @@ class BackupCreator(
throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error)) throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error))
} }
val databaseManga = getFavorites.await() /* SY --> */ + val nonFavoriteManga = if (options.readEntries) mangaRepository.getReadMangaNotInLibrary() else emptyList()
if (options.readEntries) { // SY -->
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) } val mergedManga = getMergedManga.await()
} else { // SY <--
emptyList() val backupManga =
} + getMergedManga.await() // SY <-- backupMangas(getFavorites.await() + nonFavoriteManga /* SY --> */ + mergedManga /* SY <-- */, options)
val backupManga = backupMangas(databaseManga, options)
val backup = Backup( val backup = Backup(
backupManga = backupManga, backupManga = backupManga,
backupCategories = backupCategories(options), backupCategories = backupCategories(options),

View file

@ -12,13 +12,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 savedSearchesFeeds: Boolean = true, val savedSearchesFeeds: Boolean = true,
// SY <-- // SY <--
) { ) {
@ -29,13 +29,13 @@ data class BackupOptions(
chapters, chapters,
tracking, tracking,
history, history,
readEntries,
appSettings, appSettings,
extensionRepoSettings, extensionRepoSettings,
sourceSettings, sourceSettings,
privateSettings, privateSettings,
// SY --> // SY -->
customInfo, customInfo,
readEntries,
savedSearchesFeeds, savedSearchesFeeds,
// SY <-- // SY <--
) )
@ -73,6 +73,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,
@ -80,12 +86,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(
// KMK--> // KMK-->
label = KMR.strings.saved_searches_feeds, label = KMR.strings.saved_searches_feeds,
@ -126,13 +126,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],
savedSearchesFeeds = array[11], savedSearchesFeeds = 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

@ -540,6 +540,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>