mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Start cleaning up backup/restore code
The abstraction was useful for handling 2 systems, but it's no longer needed. Cleaning it up will make migrating to domain models easier down the line.
This commit is contained in:
		| @@ -1,10 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
|  | ||||
| import android.content.Context | ||||
| import android.net.Uri | ||||
|  | ||||
| abstract class AbstractBackupRestoreValidator { | ||||
|     abstract fun validate(context: Context, uri: Uri): Results | ||||
|  | ||||
|     data class Results(val missingSources: List<String>, val missingTrackers: List<String>) | ||||
| } | ||||
| @@ -13,7 +13,6 @@ import androidx.work.WorkManager | ||||
| import androidx.work.WorkerParameters | ||||
| import androidx.work.workDataOf | ||||
| import com.hippo.unifile.UniFile | ||||
| import eu.kanade.tachiyomi.data.backup.full.FullBackupManager | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.util.system.logcat | ||||
| @@ -36,7 +35,7 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet | ||||
|  | ||||
|         context.notificationManager.notify(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build()) | ||||
|         return try { | ||||
|             val location = FullBackupManager(context).createBackup(uri, flags, isAutoBackup) | ||||
|             val location = BackupManager(context).createBackup(uri, flags, isAutoBackup) | ||||
|             if (!isAutoBackup) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())) | ||||
|             Result.success() | ||||
|         } catch (e: Exception) { | ||||
|   | ||||
| @@ -1,10 +1,9 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.net.Uri | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.AbstractBackupRestoreValidator | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import okio.buffer | ||||
| @@ -13,10 +12,10 @@ import okio.source | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| 
 | ||||
| class FullBackupRestoreValidator : AbstractBackupRestoreValidator() { | ||||
| 
 | ||||
|     private val sourceManager: SourceManager = Injekt.get() | ||||
|     private val trackManager: TrackManager = Injekt.get() | ||||
| class BackupFileValidator( | ||||
|     private val sourceManager: SourceManager = Injekt.get(), | ||||
|     private val trackManager: TrackManager = Injekt.get(), | ||||
| ) { | ||||
| 
 | ||||
|     /** | ||||
|      * Checks for critical backup file data. | ||||
| @@ -24,8 +23,8 @@ class FullBackupRestoreValidator : AbstractBackupRestoreValidator() { | ||||
|      * @throws Exception if manga cannot be found. | ||||
|      * @return List of missing sources or missing trackers. | ||||
|      */ | ||||
|     override fun validate(context: Context, uri: Uri): Results { | ||||
|         val backupManager = FullBackupManager(context) | ||||
|     fun validate(context: Context, uri: Uri): Results { | ||||
|         val backupManager = BackupManager(context) | ||||
| 
 | ||||
|         val backup = try { | ||||
|             val backupString = | ||||
| @@ -63,4 +62,6 @@ class FullBackupRestoreValidator : AbstractBackupRestoreValidator() { | ||||
| 
 | ||||
|         return Results(missingSources, missingTrackers) | ||||
|     } | ||||
| 
 | ||||
|     data class Results(val missingSources: List<String>, val missingTrackers: List<String>) | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.net.Uri | ||||
| @@ -9,7 +9,6 @@ import eu.kanade.data.category.categoryMapper | ||||
| import eu.kanade.domain.category.model.Category | ||||
| import eu.kanade.domain.history.model.HistoryUpdate | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.AbstractBackupManager | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER | ||||
| @@ -18,16 +17,15 @@ import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.Backup | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupFull | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupManga | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupSource | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.backupCategoryMapper | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.backupChapterMapper | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.backupTrackMapper | ||||
| import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupCategory | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupHistory | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupManga | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSource | ||||
| 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.data.database.models.Chapter | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.database.models.Track | ||||
| @@ -42,7 +40,7 @@ import java.util.Date | ||||
| import kotlin.math.max | ||||
| import eu.kanade.domain.manga.model.Manga as DomainManga | ||||
| 
 | ||||
| class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
| class BackupManager(context: Context) : AbstractBackupManager(context) { | ||||
| 
 | ||||
|     val parser = ProtoBuf | ||||
| 
 | ||||
| @@ -52,6 +50,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * @param uri path of Uri | ||||
|      * @param isAutoBackup backup called from scheduled backup job | ||||
|      */ | ||||
|     @Suppress("BlockingMethodInNonBlockingContext") | ||||
|     override suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String { | ||||
|         // Create root object | ||||
|         var backup: Backup? = null | ||||
| @@ -59,7 +58,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|         val databaseManga = getFavoriteManga() | ||||
| 
 | ||||
|         backup = Backup( | ||||
|             backupManga(databaseManga, flags), | ||||
|             backupMangas(databaseManga, flags), | ||||
|             backupCategories(flags), | ||||
|             emptyList(), | ||||
|             backupExtensionInfo(databaseManga), | ||||
| @@ -83,7 +82,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|                         .forEach { it.delete() } | ||||
| 
 | ||||
|                     // Create new file to place backup | ||||
|                     dir.createFile(BackupFull.getDefaultFilename()) | ||||
|                     dir.createFile(Backup.getBackupFilename()) | ||||
|                 } else { | ||||
|                     UniFile.fromUri(context, uri) | ||||
|                 } | ||||
| @@ -106,7 +105,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|             val fileUri = file.uri | ||||
| 
 | ||||
|             // Make sure it's a valid backup file | ||||
|             FullBackupRestoreValidator().validate(context, fileUri) | ||||
|             BackupFileValidator().validate(context, fileUri) | ||||
| 
 | ||||
|             return fileUri.toString() | ||||
|         } catch (e: Exception) { | ||||
| @@ -116,12 +115,6 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun backupManga(mangas: List<DomainManga>, flags: Int): List<BackupManga> { | ||||
|         return mangas.map { | ||||
|             backupMangaObject(it, flags) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun backupExtensionInfo(mangas: List<DomainManga>): List<BackupSource> { | ||||
|         return mangas | ||||
|             .asSequence() | ||||
| @@ -148,6 +141,12 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun backupMangas(mangas: List<DomainManga>, flags: Int): List<BackupManga> { | ||||
|         return mangas.map { | ||||
|             backupManga(it, flags) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Convert a manga to Json | ||||
|      * | ||||
| @@ -155,7 +154,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * @param options options for the backup | ||||
|      * @return [BackupManga] containing manga in a serializable form | ||||
|      */ | ||||
|     private suspend fun backupMangaObject(manga: DomainManga, options: Int): BackupManga { | ||||
|     private suspend fun backupManga(manga: DomainManga, options: Int): BackupManga { | ||||
|         // Entry for this manga | ||||
|         val mangaObject = BackupManga.copyFrom(manga) | ||||
| 
 | ||||
| @@ -202,7 +201,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|         return mangaObject | ||||
|     } | ||||
| 
 | ||||
|     suspend fun restoreMangaNoFetch(manga: Manga, dbManga: Mangas) { | ||||
|     suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) { | ||||
|         manga.id = dbManga._id | ||||
|         manga.copyFrom(dbManga) | ||||
|         updateManga(manga) | ||||
| @@ -214,7 +213,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * @param manga manga that needs updating | ||||
|      * @return Updated manga info. | ||||
|      */ | ||||
|     suspend fun restoreManga(manga: Manga): Manga { | ||||
|     suspend fun restoreNewManga(manga: Manga): Manga { | ||||
|         return manga.also { | ||||
|             it.initialized = it.description != null | ||||
|             it.id = insertManga(it) | ||||
| @@ -267,7 +266,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * @param manga the manga whose categories have to be restored. | ||||
|      * @param categories the categories to restore. | ||||
|      */ | ||||
|     internal suspend fun restoreCategoriesForManga(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) { | ||||
|     internal suspend fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) { | ||||
|         val dbCategories = handler.awaitList { categoriesQueries.getCategories() } | ||||
|         val mangaCategoriesToUpdate = mutableListOf<Pair<Long, Long>>() | ||||
| 
 | ||||
| @@ -299,7 +298,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * | ||||
|      * @param history list containing history to be restored | ||||
|      */ | ||||
|     internal suspend fun restoreHistoryForManga(history: List<BackupHistory>) { | ||||
|     internal suspend fun restoreHistory(history: List<BackupHistory>) { | ||||
|         // List containing history to be updated | ||||
|         val toUpdate = mutableListOf<HistoryUpdate>() | ||||
|         for ((url, lastRead, readDuration) in history) { | ||||
| @@ -349,7 +348,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|      * @param manga the manga whose sync have to be restored. | ||||
|      * @param tracks the track list to restore. | ||||
|      */ | ||||
|     internal suspend fun restoreTrackForManga(manga: Manga, tracks: List<Track>) { | ||||
|     internal suspend fun restoreTracking(manga: Manga, tracks: List<Track>) { | ||||
|         // Fix foreign keys with the current manga id | ||||
|         tracks.map { it.manga_id = manga.id!! } | ||||
| 
 | ||||
| @@ -427,7 +426,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal suspend fun restoreChaptersForManga(manga: Manga, chapters: List<Chapter>) { | ||||
|     internal suspend fun restoreChapters(manga: Manga, chapters: List<Chapter>) { | ||||
|         val dbChapters = handler.awaitList { chaptersQueries.getChaptersByMangaId(manga.id!!) } | ||||
| 
 | ||||
|         chapters.forEach { chapter -> | ||||
| @@ -8,7 +8,6 @@ import android.os.IBinder | ||||
| import android.os.PowerManager | ||||
| import androidx.core.content.ContextCompat | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.full.FullBackupRestore | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.util.system.acquireWakeLock | ||||
| import eu.kanade.tachiyomi.util.system.isServiceRunning | ||||
| @@ -70,7 +69,7 @@ class BackupRestoreService : Service() { | ||||
|     private lateinit var wakeLock: PowerManager.WakeLock | ||||
|  | ||||
|     private lateinit var ioScope: CoroutineScope | ||||
|     private var backupRestore: AbstractBackupRestore<*>? = null | ||||
|     private var restorer: AbstractBackupRestore<*>? = null | ||||
|     private lateinit var notifier: BackupNotifier | ||||
|  | ||||
|     override fun onCreate() { | ||||
| @@ -94,7 +93,7 @@ class BackupRestoreService : Service() { | ||||
|     } | ||||
|  | ||||
|     private fun destroyJob() { | ||||
|         backupRestore?.job?.cancel() | ||||
|         restorer?.job?.cancel() | ||||
|         ioScope.cancel() | ||||
|         if (wakeLock.isHeld) { | ||||
|             wakeLock.release() | ||||
| @@ -118,26 +117,26 @@ class BackupRestoreService : Service() { | ||||
|         val uri = intent?.getParcelableExtra<Uri>(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY | ||||
|  | ||||
|         // Cancel any previous job if needed. | ||||
|         backupRestore?.job?.cancel() | ||||
|         restorer?.job?.cancel() | ||||
|  | ||||
|         backupRestore = FullBackupRestore(this, notifier) | ||||
|         restorer = BackupRestorer(this, notifier) | ||||
|  | ||||
|         val handler = CoroutineExceptionHandler { _, exception -> | ||||
|             logcat(LogPriority.ERROR, exception) | ||||
|             backupRestore?.writeErrorLog() | ||||
|             restorer?.writeErrorLog() | ||||
|  | ||||
|             notifier.showRestoreError(exception.message) | ||||
|             stopSelf(startId) | ||||
|         } | ||||
|         val job = ioScope.launch(handler) { | ||||
|             if (backupRestore?.restoreBackup(uri) == false) { | ||||
|             if (restorer?.restoreBackup(uri) == false) { | ||||
|                 notifier.showRestoreError(getString(R.string.restoring_backup_canceled)) | ||||
|             } | ||||
|         } | ||||
|         job.invokeOnCompletion { | ||||
|             stopSelf(startId) | ||||
|         } | ||||
|         backupRestore?.job = job | ||||
|         restorer?.job = job | ||||
|  | ||||
|         return START_NOT_STICKY | ||||
|     } | ||||
|   | ||||
| @@ -1,15 +1,13 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.net.Uri | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.AbstractBackupRestore | ||||
| import eu.kanade.tachiyomi.data.backup.BackupNotifier | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupManga | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupSource | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupCategory | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupHistory | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupManga | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSerializer | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSource | ||||
| import eu.kanade.tachiyomi.data.database.models.Chapter | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.database.models.Track | ||||
| @@ -18,12 +16,12 @@ import okio.gzip | ||||
| import okio.source | ||||
| import java.util.Date | ||||
| 
 | ||||
| class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<FullBackupManager>(context, notifier) { | ||||
| class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<BackupManager>(context, notifier) { | ||||
| 
 | ||||
|     @Suppress("BlockingMethodInNonBlockingContext") | ||||
|     override suspend fun performRestore(uri: Uri): Boolean { | ||||
|         backupManager = FullBackupManager(context) | ||||
|         backupManager = BackupManager(context) | ||||
| 
 | ||||
|         @Suppress("BlockingMethodInNonBlockingContext") | ||||
|         val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() } | ||||
|         val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString) | ||||
| 
 | ||||
| @@ -68,7 +66,17 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa | ||||
|         val tracks = backupManga.getTrackingImpl() | ||||
| 
 | ||||
|         try { | ||||
|             restoreMangaData(manga, chapters, categories, history, tracks, backupCategories) | ||||
|             val dbManga = backupManager.getMangaFromDatabase(manga.url, manga.source) | ||||
|             if (dbManga == null) { | ||||
|                 // Manga not in database | ||||
|                 restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories) | ||||
|             } else { | ||||
|                 // Manga in database | ||||
|                 // Copy information from manga already in database | ||||
|                 backupManager.restoreExistingManga(manga, dbManga) | ||||
|                 // Fetch rest of manga information | ||||
|                 restoreNewManga(manga, chapters, categories, history, tracks, backupCategories) | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             val sourceName = sourceMapping[manga.source] ?: manga.source.toString() | ||||
|             errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}") | ||||
| @@ -78,36 +86,6 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa | ||||
|         showRestoreProgress(restoreProgress, restoreAmount, manga.title) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a manga restore observable | ||||
|      * | ||||
|      * @param manga manga data from json | ||||
|      * @param chapters chapters data from json | ||||
|      * @param categories categories data from json | ||||
|      * @param history history data from json | ||||
|      * @param tracks tracking data from json | ||||
|      */ | ||||
|     private suspend fun restoreMangaData( | ||||
|         manga: Manga, | ||||
|         chapters: List<Chapter>, | ||||
|         categories: List<Int>, | ||||
|         history: List<BackupHistory>, | ||||
|         tracks: List<Track>, | ||||
|         backupCategories: List<BackupCategory>, | ||||
|     ) { | ||||
|         val dbManga = backupManager.getMangaFromDatabase(manga.url, manga.source) | ||||
|         if (dbManga == null) { | ||||
|             // Manga not in database | ||||
|             restoreMangaFetch(manga, chapters, categories, history, tracks, backupCategories) | ||||
|         } else { | ||||
|             // Manga in database | ||||
|             // Copy information from manga already in database | ||||
|             backupManager.restoreMangaNoFetch(manga, dbManga) | ||||
|             // Fetch rest of manga information | ||||
|             restoreMangaNoFetch(manga, chapters, categories, history, tracks, backupCategories) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches manga information | ||||
|      * | ||||
| @@ -115,7 +93,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa | ||||
|      * @param chapters chapters of manga that needs updating | ||||
|      * @param categories categories that need updating | ||||
|      */ | ||||
|     private suspend fun restoreMangaFetch( | ||||
|     private suspend fun restoreExistingManga( | ||||
|         manga: Manga, | ||||
|         chapters: List<Chapter>, | ||||
|         categories: List<Int>, | ||||
| @@ -123,19 +101,14 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa | ||||
|         tracks: List<Track>, | ||||
|         backupCategories: List<BackupCategory>, | ||||
|     ) { | ||||
|         try { | ||||
|             val fetchedManga = backupManager.restoreManga(manga) | ||||
|             fetchedManga.id ?: return | ||||
|         val fetchedManga = backupManager.restoreNewManga(manga) | ||||
|         fetchedManga.id ?: return | ||||
| 
 | ||||
|             backupManager.restoreChaptersForManga(fetchedManga, chapters) | ||||
| 
 | ||||
|             restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories) | ||||
|         } catch (e: Exception) { | ||||
|             errors.add(Date() to "${manga.title} - ${e.message}") | ||||
|         } | ||||
|         backupManager.restoreChapters(fetchedManga, chapters) | ||||
|         restoreExtras(fetchedManga, categories, history, tracks, backupCategories) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun restoreMangaNoFetch( | ||||
|     private suspend fun restoreNewManga( | ||||
|         backupManga: Manga, | ||||
|         chapters: List<Chapter>, | ||||
|         categories: List<Int>, | ||||
| @@ -143,19 +116,13 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa | ||||
|         tracks: List<Track>, | ||||
|         backupCategories: List<BackupCategory>, | ||||
|     ) { | ||||
|         backupManager.restoreChaptersForManga(backupManga, chapters) | ||||
| 
 | ||||
|         restoreExtraForManga(backupManga, categories, history, tracks, backupCategories) | ||||
|         backupManager.restoreChapters(backupManga, chapters) | ||||
|         restoreExtras(backupManga, categories, history, tracks, backupCategories) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { | ||||
|         // Restore categories | ||||
|         backupManager.restoreCategoriesForManga(manga, categories, backupCategories) | ||||
| 
 | ||||
|         // Restore history | ||||
|         backupManager.restoreHistoryForManga(history) | ||||
| 
 | ||||
|         // Restore tracking | ||||
|         backupManager.restoreTrackForManga(manga, tracks) | ||||
|     private suspend fun restoreExtras(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { | ||||
|         backupManager.restoreCategories(manga, categories, backupCategories) | ||||
|         backupManager.restoreHistory(history) | ||||
|         backupManager.restoreTracking(manga, tracks) | ||||
|     } | ||||
| } | ||||
| @@ -1,12 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
|  | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.Date | ||||
| import java.util.Locale | ||||
|  | ||||
| object BackupFull { | ||||
|     fun getDefaultFilename(): String { | ||||
|         val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date()) | ||||
|         return "tachiyomi_$date.proto.gz" | ||||
|     } | ||||
| } | ||||
| @@ -1,7 +1,10 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.protobuf.ProtoNumber | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.Date | ||||
| import java.util.Locale | ||||
| 
 | ||||
| @Serializable | ||||
| data class Backup( | ||||
| @@ -10,4 +13,12 @@ data class Backup( | ||||
|     // Bump by 100 to specify this is a 0.x value | ||||
|     @ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(), | ||||
|     @ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(), | ||||
| ) | ||||
| ) { | ||||
| 
 | ||||
|     companion object { | ||||
|         fun getBackupFilename(): String { | ||||
|             val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date()) | ||||
|             return "tachiyomi_$date.proto.gz" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import eu.kanade.domain.category.model.Category | ||||
| import kotlinx.serialization.Serializable | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.data.database.models.ChapterImpl | ||||
| import kotlinx.serialization.Serializable | ||||
| @@ -1,18 +1,19 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.protobuf.ProtoNumber | ||||
| 
 | ||||
| @Serializable | ||||
| data class BrokenBackupHistory( | ||||
|     @ProtoNumber(0) var url: String, | ||||
|     @ProtoNumber(1) var lastRead: Long, | ||||
|     @ProtoNumber(2) var readDuration: Long = 0, | ||||
| ) | ||||
| 
 | ||||
| @Serializable | ||||
| data class BackupHistory( | ||||
|     @ProtoNumber(1) var url: String, | ||||
|     @ProtoNumber(2) var lastRead: Long, | ||||
|     @ProtoNumber(3) var readDuration: Long = 0, | ||||
| ) | ||||
| 
 | ||||
| @Deprecated("Replaced with BackupHistory. This is retained for legacy reasons.") | ||||
| @Serializable | ||||
| data class BrokenBackupHistory( | ||||
|     @ProtoNumber(0) var url: String, | ||||
|     @ProtoNumber(1) var lastRead: Long, | ||||
|     @ProtoNumber(2) var readDuration: Long = 0, | ||||
| ) | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import eu.kanade.domain.manga.model.Manga | ||||
| import eu.kanade.tachiyomi.data.database.models.ChapterImpl | ||||
| @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.protobuf.ProtoNumber | ||||
| 
 | ||||
| @Suppress("DEPRECATION") | ||||
| @Serializable | ||||
| data class BackupManga( | ||||
|     // in 1.x some of these values have different names | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import kotlinx.serialization.Serializer | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import kotlinx.serialization.Serializable | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.full.models | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.data.database.models.TrackImpl | ||||
| import kotlinx.serialization.Serializable | ||||
| @@ -21,9 +21,9 @@ import com.hippo.unifile.UniFile | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst | ||||
| import eu.kanade.tachiyomi.data.backup.BackupCreatorJob | ||||
| import eu.kanade.tachiyomi.data.backup.BackupFileValidator | ||||
| import eu.kanade.tachiyomi.data.backup.BackupRestoreService | ||||
| import eu.kanade.tachiyomi.data.backup.full.FullBackupRestoreValidator | ||||
| import eu.kanade.tachiyomi.data.backup.full.models.BackupFull | ||||
| import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe | ||||
| import eu.kanade.tachiyomi.util.preference.bindTo | ||||
| @@ -206,11 +206,10 @@ class SettingsBackupController : SettingsController() { | ||||
|     fun createBackup(flags: Int) { | ||||
|         backupFlags = flags | ||||
|         try { | ||||
|             // Use Android's built-in file creator | ||||
|             val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) | ||||
|                 .addCategory(Intent.CATEGORY_OPENABLE) | ||||
|                 .setType("application/*") | ||||
|                 .putExtra(Intent.EXTRA_TITLE, BackupFull.getDefaultFilename()) | ||||
|                 .putExtra(Intent.EXTRA_TITLE, Backup.getBackupFilename()) | ||||
|  | ||||
|             startActivityForResult(intent, CODE_BACKUP_CREATE) | ||||
|         } catch (e: ActivityNotFoundException) { | ||||
| @@ -270,7 +269,7 @@ class SettingsBackupController : SettingsController() { | ||||
|             val uri: Uri = args.getParcelable(KEY_URI)!! | ||||
|  | ||||
|             return try { | ||||
|                 val results = FullBackupRestoreValidator().validate(activity, uri) | ||||
|                 val results = BackupFileValidator().validate(activity, uri) | ||||
|  | ||||
|                 var message = activity.getString(R.string.backup_restore_content_full) | ||||
|                 if (results.missingSources.isNotEmpty()) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user