Browse Source

Split up BackupCreator into smaller classes

arkon 1 year ago
parent
commit
1a559124eb

+ 26 - 153
app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt

@@ -6,47 +6,27 @@ import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.data.backup.BackupFileValidator
 import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_APP_PREFS
 import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CATEGORY
-import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CHAPTER
-import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_HISTORY
 import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_SOURCE_PREFS
-import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_TRACK
+import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator
+import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator
+import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator
+import eu.kanade.tachiyomi.data.backup.create.creators.SourcesBackupCreator
 import eu.kanade.tachiyomi.data.backup.models.Backup
 import eu.kanade.tachiyomi.data.backup.models.BackupCategory
-import eu.kanade.tachiyomi.data.backup.models.BackupChapter
-import eu.kanade.tachiyomi.data.backup.models.BackupHistory
 import eu.kanade.tachiyomi.data.backup.models.BackupManga
 import eu.kanade.tachiyomi.data.backup.models.BackupPreference
 import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
 import eu.kanade.tachiyomi.data.backup.models.BackupSource
 import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
-import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
-import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
-import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
-import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
-import eu.kanade.tachiyomi.source.ConfigurableSource
-import eu.kanade.tachiyomi.source.preferenceKey
-import eu.kanade.tachiyomi.source.sourcePreferences
 import kotlinx.serialization.protobuf.ProtoBuf
 import logcat.LogPriority
 import okio.buffer
 import okio.gzip
 import okio.sink
 import tachiyomi.core.i18n.stringResource
-import tachiyomi.core.preference.Preference
-import tachiyomi.core.preference.PreferenceStore
 import tachiyomi.core.util.system.logcat
-import tachiyomi.data.DatabaseHandler
-import tachiyomi.domain.category.interactor.GetCategories
-import tachiyomi.domain.category.model.Category
-import tachiyomi.domain.history.interactor.GetHistory
 import tachiyomi.domain.manga.interactor.GetFavorites
 import tachiyomi.domain.manga.model.Manga
-import tachiyomi.domain.source.service.SourceManager
 import tachiyomi.i18n.MR
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
@@ -54,15 +34,13 @@ import java.io.FileOutputStream
 
 class BackupCreator(
     private val context: Context,
+    private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
+    private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
+    private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(),
+    private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
+    private val getFavorites: GetFavorites = Injekt.get(),
 ) {
 
-    private val handler: DatabaseHandler = Injekt.get()
-    private val sourceManager: SourceManager = Injekt.get()
-    private val getCategories: GetCategories = Injekt.get()
-    private val getFavorites: GetFavorites = Injekt.get()
-    private val getHistory: GetHistory = Injekt.get()
-    private val preferenceStore: PreferenceStore = Injekt.get()
-
     internal val parser = ProtoBuf
 
     /**
@@ -72,16 +50,6 @@ class BackupCreator(
      * @param isAutoBackup backup called from scheduled backup job
      */
     suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
-        val databaseManga = getFavorites.await()
-        val backup = Backup(
-            backupMangas(databaseManga, flags),
-            backupCategories(flags),
-            emptyList(),
-            prepExtensionInfoForSync(databaseManga),
-            backupAppPreferences(flags),
-            backupSourcePreferences(flags),
-        )
-
         var file: UniFile? = null
         try {
             file = (
@@ -108,6 +76,15 @@ class BackupCreator(
                 throw IllegalStateException("Failed to get handle on a backup file")
             }
 
+            val databaseManga = getFavorites.await()
+            val backup = Backup(
+                backupManga = backupMangas(databaseManga, flags),
+                backupCategories = backupCategories(flags),
+                backupSources = backupSources(databaseManga),
+                backupPreferences = backupAppPreferences(flags),
+                backupSourcePreferences = backupSourcePreferences(flags),
+            )
+
             val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
             if (byteArray.isEmpty()) {
                 throw IllegalStateException(context.stringResource(MR.strings.empty_backup_error))
@@ -130,134 +107,30 @@ class BackupCreator(
         }
     }
 
-    private fun prepExtensionInfoForSync(mangas: List<Manga>): List<BackupSource> {
-        return mangas
-            .asSequence()
-            .map(Manga::source)
-            .distinct()
-            .map(sourceManager::getOrStub)
-            .map(BackupSource::copyFrom)
-            .toList()
-    }
-
-    /**
-     * Backup the categories of library
-     *
-     * @return list of [BackupCategory] to be backed up
-     */
     private suspend fun backupCategories(options: Int): List<BackupCategory> {
-        // Check if user wants category information in backup
-        return if (options and BACKUP_CATEGORY == BACKUP_CATEGORY) {
-            getCategories.await()
-                .filterNot(Category::isSystemCategory)
-                .map(backupCategoryMapper)
-        } else {
-            emptyList()
-        }
+        if (options and BACKUP_CATEGORY != BACKUP_CATEGORY) return emptyList()
+
+        return categoriesBackupCreator.backupCategories()
     }
 
     private suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
-        return mangas.map {
-            backupManga(it, flags)
-        }
+        return mangaBackupCreator.backupMangas(mangas, flags)
     }
 
-    /**
-     * Convert a manga to Json
-     *
-     * @param manga manga that gets converted
-     * @param options options for the backup
-     * @return [BackupManga] containing manga in a serializable form
-     */
-    private suspend fun backupManga(manga: Manga, options: Int): BackupManga {
-        // Entry for this manga
-        val mangaObject = BackupManga.copyFrom(manga)
-
-        // Check if user wants chapter information in backup
-        if (options and BACKUP_CHAPTER == BACKUP_CHAPTER) {
-            // Backup all the chapters
-            handler.awaitList {
-                chaptersQueries.getChaptersByMangaId(
-                    mangaId = manga.id,
-                    applyScanlatorFilter = 0, // false
-                    mapper = backupChapterMapper,
-                )
-            }
-                .takeUnless(List<BackupChapter>::isEmpty)
-                ?.let { mangaObject.chapters = it }
-        }
-
-        // Check if user wants category information in backup
-        if (options and BACKUP_CATEGORY == BACKUP_CATEGORY) {
-            // Backup categories for this manga
-            val categoriesForManga = getCategories.await(manga.id)
-            if (categoriesForManga.isNotEmpty()) {
-                mangaObject.categories = categoriesForManga.map { it.order }
-            }
-        }
-
-        // Check if user wants track information in backup
-        if (options and BACKUP_TRACK == BACKUP_TRACK) {
-            val tracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id, backupTrackMapper) }
-            if (tracks.isNotEmpty()) {
-                mangaObject.tracking = tracks
-            }
-        }
-
-        // Check if user wants history information in backup
-        if (options and BACKUP_HISTORY == BACKUP_HISTORY) {
-            val historyByMangaId = getHistory.await(manga.id)
-            if (historyByMangaId.isNotEmpty()) {
-                val history = historyByMangaId.map { history ->
-                    val chapter = handler.awaitOne { chaptersQueries.getChapterById(history.chapterId) }
-                    BackupHistory(chapter.url, history.readAt?.time ?: 0L, history.readDuration)
-                }
-                if (history.isNotEmpty()) {
-                    mangaObject.history = history
-                }
-            }
-        }
-
-        return mangaObject
+    private fun backupSources(mangas: List<Manga>): List<BackupSource> {
+        return sourcesBackupCreator.backupSources(mangas)
     }
 
     private fun backupAppPreferences(flags: Int): List<BackupPreference> {
         if (flags and BACKUP_APP_PREFS != BACKUP_APP_PREFS) return emptyList()
 
-        return preferenceStore.getAll().toBackupPreferences()
+        return preferenceBackupCreator.backupAppPreferences()
     }
 
     private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> {
         if (flags and BACKUP_SOURCE_PREFS != BACKUP_SOURCE_PREFS) return emptyList()
 
-        return sourceManager.getCatalogueSources()
-            .filterIsInstance<ConfigurableSource>()
-            .map {
-                BackupSourcePreferences(
-                    it.preferenceKey(),
-                    it.sourcePreferences().all.toBackupPreferences(),
-                )
-            }
-    }
-
-    @Suppress("UNCHECKED_CAST")
-    private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
-        return this.filterKeys {
-            !Preference.isPrivate(it) && !Preference.isAppState(it)
-        }
-            .mapNotNull { (key, value) ->
-                when (value) {
-                    is Int -> BackupPreference(key, IntPreferenceValue(value))
-                    is Long -> BackupPreference(key, LongPreferenceValue(value))
-                    is Float -> BackupPreference(key, FloatPreferenceValue(value))
-                    is String -> BackupPreference(key, StringPreferenceValue(value))
-                    is Boolean -> BackupPreference(key, BooleanPreferenceValue(value))
-                    is Set<*> -> (value as? Set<String>)?.let {
-                        BackupPreference(key, StringSetPreferenceValue(it))
-                    }
-                    else -> null
-                }
-            }
+        return preferenceBackupCreator.backupSourcePreferences()
     }
 }
 

+ 19 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/CategoriesBackupCreator.kt

@@ -0,0 +1,19 @@
+package eu.kanade.tachiyomi.data.backup.create.creators
+
+import eu.kanade.tachiyomi.data.backup.models.BackupCategory
+import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
+import tachiyomi.domain.category.interactor.GetCategories
+import tachiyomi.domain.category.model.Category
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class CategoriesBackupCreator(
+    private val getCategories: GetCategories = Injekt.get(),
+) {
+
+    suspend fun backupCategories(): List<BackupCategory> {
+        return getCategories.await()
+            .filterNot(Category::isSystemCategory)
+            .map(backupCategoryMapper)
+    }
+}

+ 79 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt

@@ -0,0 +1,79 @@
+package eu.kanade.tachiyomi.data.backup.create.creators
+
+import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
+import eu.kanade.tachiyomi.data.backup.models.BackupChapter
+import eu.kanade.tachiyomi.data.backup.models.BackupHistory
+import eu.kanade.tachiyomi.data.backup.models.BackupManga
+import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
+import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
+import tachiyomi.data.DatabaseHandler
+import tachiyomi.domain.category.interactor.GetCategories
+import tachiyomi.domain.history.interactor.GetHistory
+import tachiyomi.domain.manga.model.Manga
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class MangaBackupCreator(
+    private val handler: DatabaseHandler = Injekt.get(),
+    private val getCategories: GetCategories = Injekt.get(),
+    private val getHistory: GetHistory = Injekt.get(),
+) {
+
+    suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
+        return mangas.map {
+            backupManga(it, flags)
+        }
+    }
+
+    private suspend fun backupManga(manga: Manga, options: Int): BackupManga {
+        // Entry for this manga
+        val mangaObject = BackupManga.copyFrom(manga)
+
+        // Check if user wants chapter information in backup
+        if (options and BackupCreateFlags.BACKUP_CHAPTER == BackupCreateFlags.BACKUP_CHAPTER) {
+            // Backup all the chapters
+            handler.awaitList {
+                chaptersQueries.getChaptersByMangaId(
+                    mangaId = manga.id,
+                    applyScanlatorFilter = 0, // false
+                    mapper = backupChapterMapper,
+                )
+            }
+                .takeUnless(List<BackupChapter>::isEmpty)
+                ?.let { mangaObject.chapters = it }
+        }
+
+        // Check if user wants category information in backup
+        if (options and BackupCreateFlags.BACKUP_CATEGORY == BackupCreateFlags.BACKUP_CATEGORY) {
+            // Backup categories for this manga
+            val categoriesForManga = getCategories.await(manga.id)
+            if (categoriesForManga.isNotEmpty()) {
+                mangaObject.categories = categoriesForManga.map { it.order }
+            }
+        }
+
+        // Check if user wants track information in backup
+        if (options and BackupCreateFlags.BACKUP_TRACK == BackupCreateFlags.BACKUP_TRACK) {
+            val tracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id, backupTrackMapper) }
+            if (tracks.isNotEmpty()) {
+                mangaObject.tracking = tracks
+            }
+        }
+
+        // Check if user wants history information in backup
+        if (options and BackupCreateFlags.BACKUP_HISTORY == BackupCreateFlags.BACKUP_HISTORY) {
+            val historyByMangaId = getHistory.await(manga.id)
+            if (historyByMangaId.isNotEmpty()) {
+                val history = historyByMangaId.map { history ->
+                    val chapter = handler.awaitOne { chaptersQueries.getChapterById(history.chapterId) }
+                    BackupHistory(chapter.url, history.readAt?.time ?: 0L, history.readDuration)
+                }
+                if (history.isNotEmpty()) {
+                    mangaObject.history = history
+                }
+            }
+        }
+
+        return mangaObject
+    }
+}

+ 59 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt

@@ -0,0 +1,59 @@
+package eu.kanade.tachiyomi.data.backup.create.creators
+
+import eu.kanade.tachiyomi.data.backup.models.BackupPreference
+import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
+import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
+import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
+import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
+import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
+import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
+import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
+import eu.kanade.tachiyomi.source.ConfigurableSource
+import eu.kanade.tachiyomi.source.preferenceKey
+import eu.kanade.tachiyomi.source.sourcePreferences
+import tachiyomi.core.preference.Preference
+import tachiyomi.core.preference.PreferenceStore
+import tachiyomi.domain.source.service.SourceManager
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class PreferenceBackupCreator(
+    private val sourceManager: SourceManager = Injekt.get(),
+    private val preferenceStore: PreferenceStore = Injekt.get(),
+) {
+
+    fun backupAppPreferences(): List<BackupPreference> {
+        return preferenceStore.getAll().toBackupPreferences()
+    }
+
+    fun backupSourcePreferences(): List<BackupSourcePreferences> {
+        return sourceManager.getCatalogueSources()
+            .filterIsInstance<ConfigurableSource>()
+            .map {
+                BackupSourcePreferences(
+                    it.preferenceKey(),
+                    it.sourcePreferences().all.toBackupPreferences(),
+                )
+            }
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
+        return this.filterKeys {
+            !Preference.isPrivate(it) && !Preference.isAppState(it)
+        }
+            .mapNotNull { (key, value) ->
+                when (value) {
+                    is Int -> BackupPreference(key, IntPreferenceValue(value))
+                    is Long -> BackupPreference(key, LongPreferenceValue(value))
+                    is Float -> BackupPreference(key, FloatPreferenceValue(value))
+                    is String -> BackupPreference(key, StringPreferenceValue(value))
+                    is Boolean -> BackupPreference(key, BooleanPreferenceValue(value))
+                    is Set<*> -> (value as? Set<String>)?.let {
+                        BackupPreference(key, StringSetPreferenceValue(it))
+                    }
+                    else -> null
+                }
+            }
+    }
+}

+ 22 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SourcesBackupCreator.kt

@@ -0,0 +1,22 @@
+package eu.kanade.tachiyomi.data.backup.create.creators
+
+import eu.kanade.tachiyomi.data.backup.models.BackupSource
+import tachiyomi.domain.manga.model.Manga
+import tachiyomi.domain.source.service.SourceManager
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class SourcesBackupCreator(
+    private val sourceManager: SourceManager = Injekt.get(),
+) {
+
+    fun backupSources(mangas: List<Manga>): List<BackupSource> {
+        return mangas
+            .asSequence()
+            .map(Manga::source)
+            .distinct()
+            .map(sourceManager::getOrStub)
+            .map(BackupSource::copyFrom)
+            .toList()
+    }
+}

+ 8 - 8
app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupSource.kt

@@ -4,14 +4,6 @@ import eu.kanade.tachiyomi.source.Source
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.protobuf.ProtoNumber
 
-@Serializable
-data class BrokenBackupSource(
-    @ProtoNumber(0) var name: String = "",
-    @ProtoNumber(1) var sourceId: Long,
-) {
-    fun toBackupSource() = BackupSource(name, sourceId)
-}
-
 @Serializable
 data class BackupSource(
     @ProtoNumber(1) var name: String = "",
@@ -26,3 +18,11 @@ data class BackupSource(
         }
     }
 }
+
+@Serializable
+data class BrokenBackupSource(
+    @ProtoNumber(0) var name: String = "",
+    @ProtoNumber(1) var sourceId: Long,
+) {
+    fun toBackupSource() = BackupSource(name, sourceId)
+}

+ 3 - 16
app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt

@@ -7,6 +7,9 @@ import eu.kanade.tachiyomi.data.backup.models.BackupCategory
 import eu.kanade.tachiyomi.data.backup.models.BackupManga
 import eu.kanade.tachiyomi.data.backup.models.BackupPreference
 import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
+import eu.kanade.tachiyomi.data.backup.restore.restorers.CategoriesRestorer
+import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaRestorer
+import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceRestorer
 import eu.kanade.tachiyomi.util.BackupUtil
 import eu.kanade.tachiyomi.util.system.createFileInCacheDir
 import kotlinx.coroutines.CoroutineScope
@@ -170,19 +173,3 @@ class BackupRestorer(
         return File("")
     }
 }
-
-data class RestoreOptions(
-    val appSettings: Boolean = true,
-    val sourceSettings: Boolean = true,
-    val library: Boolean = true,
-) {
-    fun toBooleanArray() = booleanArrayOf(appSettings, sourceSettings, library)
-
-    companion object {
-        fun fromBooleanArray(booleanArray: BooleanArray) = RestoreOptions(
-            appSettings = booleanArray[0],
-            sourceSettings = booleanArray[1],
-            library = booleanArray[2],
-        )
-    }
-}

+ 17 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt

@@ -0,0 +1,17 @@
+package eu.kanade.tachiyomi.data.backup.restore
+
+data class RestoreOptions(
+    val appSettings: Boolean = true,
+    val sourceSettings: Boolean = true,
+    val library: Boolean = true,
+) {
+    fun toBooleanArray() = booleanArrayOf(appSettings, sourceSettings, library)
+
+    companion object {
+        fun fromBooleanArray(booleanArray: BooleanArray) = RestoreOptions(
+            appSettings = booleanArray[0],
+            sourceSettings = booleanArray[1],
+            library = booleanArray[2],
+        )
+    }
+}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/CategoriesRestorer.kt → app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt

@@ -1,4 +1,4 @@
-package eu.kanade.tachiyomi.data.backup.restore
+package eu.kanade.tachiyomi.data.backup.restore.restorers
 
 import eu.kanade.tachiyomi.data.backup.models.BackupCategory
 import tachiyomi.data.DatabaseHandler

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/MangaRestorer.kt → app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt

@@ -1,4 +1,4 @@
-package eu.kanade.tachiyomi.data.backup.restore
+package eu.kanade.tachiyomi.data.backup.restore.restorers
 
 import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.tachiyomi.data.backup.models.BackupCategory

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/PreferenceRestorer.kt → app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt

@@ -1,4 +1,4 @@
-package eu.kanade.tachiyomi.data.backup.restore
+package eu.kanade.tachiyomi.data.backup.restore.restorers
 
 import android.content.Context
 import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob