From d1be221d7aaa811e50417235021c7e038704d276 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 29 May 2022 12:24:39 -0400 Subject: [PATCH] Remove logic for restoring legacy JSON backups - Protobuf backups have been around for 1.5 years now - The ability to restore online-dependant data from JSON backups gets harder as time goes on and sources drift - If users really need a way to restore them, they can use an older version of the app, or a separate tool for translating between the formats could be created --- .../data/backup/AbstractBackupManager.kt | 25 +- .../backup/AbstractBackupRestoreValidator.kt | 8 - .../tachiyomi/data/backup/BackupConst.kt | 5 - .../data/backup/BackupRestoreService.kt | 10 +- .../data/backup/full/FullBackupManager.kt | 36 +-- .../data/backup/full/FullBackupRestore.kt | 4 +- .../backup/full/FullBackupRestoreValidator.kt | 12 +- .../data/backup/legacy/LegacyBackupManager.kt | 252 ------------------ .../data/backup/legacy/LegacyBackupRestore.kt | 184 ------------- .../legacy/LegacyBackupRestoreValidator.kt | 66 ----- .../data/backup/legacy/models/Backup.kt | 37 --- .../data/backup/legacy/models/DHistory.kt | 3 - .../serializer/CategoryTypeSerializer.kt | 49 ---- .../serializer/ChapterTypeSerializer.kt | 66 ----- .../serializer/HistoryTypeSerializer.kt | 41 --- .../legacy/serializer/MangaTypeSerializer.kt | 56 ---- .../legacy/serializer/TrackTypeSerializer.kt | 68 ----- .../data/database/tables/TrackTable.kt | 8 - .../ui/setting/SettingsBackupController.kt | 18 +- app/src/main/res/values/strings.xml | 3 - 20 files changed, 47 insertions(+), 904 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/DHistory.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt index d23ce07309..cf1b4b8e6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt @@ -12,14 +12,15 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource -import uy.kohesive.injekt.injectLazy +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get abstract class AbstractBackupManager(protected val context: Context) { - internal val databaseHelper: DatabaseHelper by injectLazy() - internal val sourceManager: SourceManager by injectLazy() - internal val trackManager: TrackManager by injectLazy() - protected val preferences: PreferencesHelper by injectLazy() + internal val db: DatabaseHelper = Injekt.get() + internal val sourceManager: SourceManager = Injekt.get() + internal val trackManager: TrackManager = Injekt.get() + protected val preferences: PreferencesHelper = Injekt.get() abstract fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String @@ -29,7 +30,7 @@ abstract class AbstractBackupManager(protected val context: Context) { * @return [Manga], null if not found */ internal fun getMangaFromDatabase(manga: Manga): Manga? = - databaseHelper.getManga(manga.url, manga.source).executeAsBlocking() + db.getManga(manga.url, manga.source).executeAsBlocking() /** * Fetches chapter information. @@ -42,7 +43,7 @@ abstract class AbstractBackupManager(protected val context: Context) { internal suspend fun restoreChapters(source: Source, manga: Manga, chapters: List): Pair, List> { val fetchedChapters = source.getChapterList(manga.toMangaInfo()) .map { it.toSChapter() } - val syncedChapters = syncChaptersWithSource(databaseHelper, fetchedChapters, manga, source) + val syncedChapters = syncChaptersWithSource(db, fetchedChapters, manga, source) if (syncedChapters.first.isNotEmpty()) { chapters.forEach { it.manga_id = manga.id } updateChapters(chapters) @@ -56,7 +57,7 @@ abstract class AbstractBackupManager(protected val context: Context) { * @return [Manga] from library */ protected fun getFavoriteManga(): List = - databaseHelper.getFavoriteMangas().executeAsBlocking() + db.getFavoriteMangas().executeAsBlocking() /** * Inserts manga and returns id @@ -64,27 +65,27 @@ abstract class AbstractBackupManager(protected val context: Context) { * @return id of [Manga], null if not found */ internal fun insertManga(manga: Manga): Long? = - databaseHelper.insertManga(manga).executeAsBlocking().insertedId() + db.insertManga(manga).executeAsBlocking().insertedId() /** * Inserts list of chapters */ protected fun insertChapters(chapters: List) { - databaseHelper.insertChapters(chapters).executeAsBlocking() + db.insertChapters(chapters).executeAsBlocking() } /** * Updates a list of chapters */ protected fun updateChapters(chapters: List) { - databaseHelper.updateChaptersBackup(chapters).executeAsBlocking() + db.updateChaptersBackup(chapters).executeAsBlocking() } /** * Updates a list of chapters with known database ids */ protected fun updateKnownChapters(chapters: List) { - databaseHelper.updateKnownChaptersBackup(chapters).executeAsBlocking() + db.updateKnownChaptersBackup(chapters).executeAsBlocking() } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestoreValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestoreValidator.kt index 47f0fb1c84..b8d8f65dc8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestoreValidator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestoreValidator.kt @@ -2,17 +2,9 @@ package eu.kanade.tachiyomi.data.backup import android.content.Context import android.net.Uri -import eu.kanade.tachiyomi.data.track.TrackManager -import eu.kanade.tachiyomi.source.SourceManager -import uy.kohesive.injekt.injectLazy abstract class AbstractBackupRestoreValidator { - protected val sourceManager: SourceManager by injectLazy() - protected val trackManager: TrackManager by injectLazy() - abstract fun validate(context: Context, uri: Uri): Results data class Results(val missingSources: List, val missingTrackers: List) } - -class ValidatorParseException(e: Exception) : RuntimeException(e) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt index e185508d1d..f9e5d81305 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt @@ -6,11 +6,6 @@ object BackupConst { private const val NAME = "BackupRestoreServices" const val EXTRA_URI = "$ID.$NAME.EXTRA_URI" - const val EXTRA_FLAGS = "$ID.$NAME.EXTRA_FLAGS" - const val EXTRA_MODE = "$ID.$NAME.EXTRA_MODE" - - const val BACKUP_TYPE_LEGACY = 0 - const val BACKUP_TYPE_FULL = 1 // Filter options internal const val BACKUP_CATEGORY = 0x1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt index 8b912a8056..037efc6a95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt @@ -9,7 +9,6 @@ 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.backup.legacy.LegacyBackupRestore import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.util.system.acquireWakeLock import eu.kanade.tachiyomi.util.system.isServiceRunning @@ -44,11 +43,10 @@ class BackupRestoreService : Service() { * @param context context of application * @param uri path of Uri */ - fun start(context: Context, uri: Uri, mode: Int) { + fun start(context: Context, uri: Uri) { if (!isRunning(context)) { val intent = Intent(context, BackupRestoreService::class.java).apply { putExtra(BackupConst.EXTRA_URI, uri) - putExtra(BackupConst.EXTRA_MODE, mode) } ContextCompat.startForegroundService(context, intent) } @@ -118,15 +116,11 @@ class BackupRestoreService : Service() { */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val uri = intent?.getParcelableExtra(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY - val mode = intent.getIntExtra(BackupConst.EXTRA_MODE, BackupConst.BACKUP_TYPE_FULL) // Cancel any previous job if needed. backupRestore?.job?.cancel() - backupRestore = when (mode) { - BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier) - else -> LegacyBackupRestore(this, notifier) - } + backupRestore = FullBackupRestore(this, notifier) val handler = CoroutineExceptionHandler { _, exception -> logcat(LogPriority.ERROR, exception) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt index 586306023f..cb84c66ab2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt @@ -50,7 +50,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Create root object var backup: Backup? = null - databaseHelper.inTransaction { + db.inTransaction { val databaseManga = getFavoriteManga() backup = Backup( @@ -136,7 +136,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { private fun backupCategories(options: Int): List { // Check if user wants category information in backup return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) { - databaseHelper.getCategories() + db.getCategories() .executeAsBlocking() .map { BackupCategory.copyFrom(it) } } else { @@ -158,7 +158,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Check if user wants chapter information in backup if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) { // Backup all the chapters - val chapters = databaseHelper.getChapters(manga).executeAsBlocking() + val chapters = db.getChapters(manga).executeAsBlocking() if (chapters.isNotEmpty()) { mangaObject.chapters = chapters.map { BackupChapter.copyFrom(it) } } @@ -167,7 +167,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Check if user wants category information in backup if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) { // Backup categories for this manga - val categoriesForManga = databaseHelper.getCategoriesForManga(manga).executeAsBlocking() + val categoriesForManga = db.getCategoriesForManga(manga).executeAsBlocking() if (categoriesForManga.isNotEmpty()) { mangaObject.categories = categoriesForManga.mapNotNull { it.order } } @@ -175,7 +175,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Check if user wants track information in backup if (options and BACKUP_TRACK_MASK == BACKUP_TRACK) { - val tracks = databaseHelper.getTracks(manga).executeAsBlocking() + val tracks = db.getTracks(manga).executeAsBlocking() if (tracks.isNotEmpty()) { mangaObject.tracking = tracks.map { BackupTracking.copyFrom(it) } } @@ -183,10 +183,10 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Check if user wants history information in backup if (options and BACKUP_HISTORY_MASK == BACKUP_HISTORY) { - val historyForManga = databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking() + val historyForManga = db.getHistoryByMangaId(manga.id!!).executeAsBlocking() if (historyForManga.isNotEmpty()) { val history = historyForManga.mapNotNull { history -> - val url = databaseHelper.getChapter(history.chapter_id).executeAsBlocking()?.url + val url = db.getChapter(history.chapter_id).executeAsBlocking()?.url url?.let { BackupHistory(url, history.last_read) } } if (history.isNotEmpty()) { @@ -224,7 +224,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { */ internal fun restoreCategories(backupCategories: List) { // Get categories from file and from db - val dbCategories = databaseHelper.getCategories().executeAsBlocking() + val dbCategories = db.getCategories().executeAsBlocking() // Iterate over them backupCategories.map { it.getCategoryImpl() }.forEach { category -> @@ -244,7 +244,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { if (!found) { // Let the db assign the id category.id = null - val result = databaseHelper.insertCategory(category).executeAsBlocking() + val result = db.insertCategory(category).executeAsBlocking() category.id = result.insertedId()?.toInt() } } @@ -257,7 +257,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { * @param categories the categories to restore. */ internal fun restoreCategoriesForManga(manga: Manga, categories: List, backupCategories: List) { - val dbCategories = databaseHelper.getCategories().executeAsBlocking() + val dbCategories = db.getCategories().executeAsBlocking() val mangaCategoriesToUpdate = ArrayList(categories.size) categories.forEach { backupCategoryOrder -> backupCategories.firstOrNull { @@ -273,8 +273,8 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // Update database if (mangaCategoriesToUpdate.isNotEmpty()) { - databaseHelper.deleteOldMangasCategories(listOf(manga)).executeAsBlocking() - databaseHelper.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking() + db.deleteOldMangasCategories(listOf(manga)).executeAsBlocking() + db.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking() } } @@ -287,7 +287,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { // List containing history to be updated val historyToBeUpdated = ArrayList(history.size) for ((url, lastRead) in history) { - val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking() + val dbHistory = db.getHistoryByChapterUrl(url).executeAsBlocking() // Check if history already in database and update if (dbHistory != null) { dbHistory.apply { @@ -296,7 +296,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { historyToBeUpdated.add(dbHistory) } else { // If not in database create - databaseHelper.getChapter(url).executeAsBlocking()?.let { + db.getChapter(url).executeAsBlocking()?.let { val historyToAdd = History.create(it).apply { last_read = lastRead } @@ -304,7 +304,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { } } } - databaseHelper.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking() + db.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking() } /** @@ -318,7 +318,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { tracks.map { it.manga_id = manga.id!! } // Get tracks from database - val dbTracks = databaseHelper.getTracks(manga).executeAsBlocking() + val dbTracks = db.getTracks(manga).executeAsBlocking() val trackToUpdate = mutableListOf() tracks.forEach { track -> @@ -346,12 +346,12 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { } // Update database if (trackToUpdate.isNotEmpty()) { - databaseHelper.insertTracks(trackToUpdate).executeAsBlocking() + db.insertTracks(trackToUpdate).executeAsBlocking() } } internal fun restoreChaptersForManga(manga: Manga, chapters: List) { - val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking() + val dbChapters = db.getChapters(manga).executeAsBlocking() chapters.forEach { chapter -> val dbChapter = dbChapters.find { it.url == chapter.url } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt index 573d5f0bd2..acb633abbc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt @@ -34,8 +34,8 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa } // Store source mapping for error messages - var backupMaps = backup.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources - sourceMapping = backupMaps.map { it.sourceId to it.name }.toMap() + val backupMaps = backup.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources + sourceMapping = backupMaps.associate { it.sourceId to it.name } // Restore individual manga backup.backupManga.forEach { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestoreValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestoreValidator.kt index c938dc2bcc..351673f09f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestoreValidator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestoreValidator.kt @@ -4,14 +4,20 @@ 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.ValidatorParseException import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer +import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.source.SourceManager import okio.buffer import okio.gzip 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() + /** * Checks for critical backup file data. * @@ -27,11 +33,11 @@ class FullBackupRestoreValidator : AbstractBackupRestoreValidator() { .use { it.readByteArray() } backupManager.parser.decodeFromByteArray(BackupSerializer, backupString) } catch (e: Exception) { - throw ValidatorParseException(e) + throw IllegalStateException(e) } if (backup.backupManga.isEmpty()) { - throw Exception(context.getString(R.string.invalid_backup_file_missing_manga)) + throw IllegalStateException(context.getString(R.string.invalid_backup_file_missing_manga)) } val sources = backup.backupSources.associate { it.sourceId to it.name } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt deleted file mode 100644 index 8d42245e02..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt +++ /dev/null @@ -1,252 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy - -import android.content.Context -import android.net.Uri -import eu.kanade.tachiyomi.data.backup.AbstractBackupManager -import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.Companion.CURRENT_VERSION -import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory -import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryImplTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterImplTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.HistoryTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.MangaImplTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.MangaTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.TrackImplTypeSerializer -import eu.kanade.tachiyomi.data.backup.legacy.serializer.TrackTypeSerializer -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.History -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaCategory -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.toMangaInfo -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.model.toSManga -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.contextual -import kotlin.math.max - -class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) { - - val parser: Json = when (version) { - 2 -> Json { - // Forks may have added items to backup - ignoreUnknownKeys = true - - // Register custom serializers - serializersModule = SerializersModule { - contextual(MangaTypeSerializer) - contextual(MangaImplTypeSerializer) - contextual(ChapterTypeSerializer) - contextual(ChapterImplTypeSerializer) - contextual(CategoryTypeSerializer) - contextual(CategoryImplTypeSerializer) - contextual(TrackTypeSerializer) - contextual(TrackImplTypeSerializer) - contextual(HistoryTypeSerializer) - } - } - else -> throw Exception("Unknown backup version") - } - - /** - * Create backup Json file from database - * - * @param uri path of Uri - * @param isAutoBackup backup called from scheduled backup job - */ - override fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean) = - throw IllegalStateException("Legacy backup creation is not supported") - - fun restoreMangaNoFetch(manga: Manga, dbManga: Manga) { - manga.id = dbManga.id - manga.copyFrom(dbManga) - manga.favorite = true - insertManga(manga) - } - - /** - * Fetches manga information - * - * @param source source of manga - * @param manga manga that needs updating - * @return Updated manga. - */ - suspend fun fetchManga(source: Source, manga: Manga): Manga { - val networkManga = source.getMangaDetails(manga.toMangaInfo()) - return manga.also { - it.copyFrom(networkManga.toSManga()) - it.favorite = true - it.initialized = true - it.id = insertManga(manga) - } - } - - /** - * Restore the categories from Json - * - * @param backupCategories array containing categories - */ - internal fun restoreCategories(backupCategories: List) { - // Get categories from file and from db - val dbCategories = databaseHelper.getCategories().executeAsBlocking() - - // Iterate over them - backupCategories.forEach { category -> - // Used to know if the category is already in the db - var found = false - for (dbCategory in dbCategories) { - // If the category is already in the db, assign the id to the file's category - // and do nothing - if (category.name == dbCategory.name) { - category.id = dbCategory.id - found = true - break - } - } - // If the category isn't in the db, remove the id and insert a new category - // Store the inserted id in the category - if (!found) { - // Let the db assign the id - category.id = null - val result = databaseHelper.insertCategory(category).executeAsBlocking() - category.id = result.insertedId()?.toInt() - } - } - } - - /** - * Restores the categories a manga is in. - * - * @param manga the manga whose categories have to be restored. - * @param categories the categories to restore. - */ - internal fun restoreCategoriesForManga(manga: Manga, categories: List) { - val dbCategories = databaseHelper.getCategories().executeAsBlocking() - val mangaCategoriesToUpdate = ArrayList(categories.size) - for (backupCategoryStr in categories) { - for (dbCategory in dbCategories) { - if (backupCategoryStr == dbCategory.name) { - mangaCategoriesToUpdate.add(MangaCategory.create(manga, dbCategory)) - break - } - } - } - - // Update database - if (mangaCategoriesToUpdate.isNotEmpty()) { - databaseHelper.deleteOldMangasCategories(listOf(manga)).executeAsBlocking() - databaseHelper.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking() - } - } - - /** - * Restore history from Json - * - * @param history list containing history to be restored - */ - internal fun restoreHistoryForManga(history: List) { - // List containing history to be updated - val historyToBeUpdated = ArrayList(history.size) - for ((url, lastRead) in history) { - val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking() - // Check if history already in database and update - if (dbHistory != null) { - dbHistory.apply { - last_read = max(lastRead, dbHistory.last_read) - } - historyToBeUpdated.add(dbHistory) - } else { - // If not in database create - databaseHelper.getChapter(url).executeAsBlocking()?.let { - val historyToAdd = History.create(it).apply { - last_read = lastRead - } - historyToBeUpdated.add(historyToAdd) - } - } - } - databaseHelper.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking() - } - - /** - * Restores the sync of a manga. - * - * @param manga the manga whose sync have to be restored. - * @param tracks the track list to restore. - */ - internal fun restoreTrackForManga(manga: Manga, tracks: List) { - // Get tracks from database - val dbTracks = databaseHelper.getTracks(manga).executeAsBlocking() - val trackToUpdate = ArrayList(tracks.size) - - tracks.forEach { track -> - // Fix foreign keys with the current manga id - track.manga_id = manga.id!! - - val service = trackManager.getService(track.sync_id) - if (service != null && service.isLogged) { - var isInDatabase = false - for (dbTrack in dbTracks) { - if (track.sync_id == dbTrack.sync_id) { - // The sync is already in the db, only update its fields - if (track.media_id != dbTrack.media_id) { - dbTrack.media_id = track.media_id - } - if (track.library_id != dbTrack.library_id) { - dbTrack.library_id = track.library_id - } - dbTrack.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read) - isInDatabase = true - trackToUpdate.add(dbTrack) - break - } - } - if (!isInDatabase) { - // Insert new sync. Let the db assign the id - track.id = null - trackToUpdate.add(track) - } - } - } - // Update database - if (trackToUpdate.isNotEmpty()) { - databaseHelper.insertTracks(trackToUpdate).executeAsBlocking() - } - } - - /** - * Restore the chapters for manga if chapters already in database - * - * @param manga manga of chapters - * @param chapters list containing chapters that get restored - * @return boolean answering if chapter fetch is not needed - */ - internal fun restoreChaptersForManga(manga: Manga, chapters: List): Boolean { - val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking() - - // Return if fetch is needed - if (dbChapters.isEmpty() || dbChapters.size < chapters.size) { - return false - } - - for (chapter in chapters) { - val pos = dbChapters.indexOf(chapter) - if (pos != -1) { - val dbChapter = dbChapters[pos] - chapter.id = dbChapter.id - chapter.copyFrom(dbChapter) - break - } - - chapter.manga_id = manga.id - } - - // Filter the chapters that couldn't be found. - updateChapters(chapters.filter { it.id != null }) - - return true - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt deleted file mode 100644 index 4ce5be2541..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt +++ /dev/null @@ -1,184 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy - -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.legacy.models.Backup -import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory -import eu.kanade.tachiyomi.data.backup.legacy.models.MangaObject -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.source.Source -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.decodeFromJsonElement -import kotlinx.serialization.json.decodeFromStream -import kotlinx.serialization.json.intOrNull -import kotlinx.serialization.json.jsonPrimitive -import java.util.Date - -class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore(context, notifier) { - - override suspend fun performRestore(uri: Uri): Boolean { - // Read the json and create a Json Object, - // cannot use the backupManager json deserializer one because its not initialized yet - val backupObject = Json.decodeFromStream( - context.contentResolver.openInputStream(uri)!!, - ) - - // Get parser version - val version = backupObject["version"]?.jsonPrimitive?.intOrNull ?: 1 - - // Initialize manager - backupManager = LegacyBackupManager(context, version) - - // Decode the json object to a Backup object - val backup = backupManager.parser.decodeFromJsonElement(backupObject) - - restoreAmount = backup.mangas.size + 1 // +1 for categories - - // Restore categories - backup.categories?.let { restoreCategories(it) } - - // Store source mapping for error messages - sourceMapping = LegacyBackupRestoreValidator.getSourceMapping(backup.extensions ?: emptyList()) - - // Restore individual manga - backup.mangas.forEach { - if (job?.isActive != true) { - return false - } - - restoreManga(it) - } - - return true - } - - private fun restoreCategories(categoriesJson: List) { - db.inTransaction { - backupManager.restoreCategories(categoriesJson) - } - - restoreProgress += 1 - showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) - } - - private suspend fun restoreManga(mangaJson: MangaObject) { - val manga = mangaJson.manga - val chapters = mangaJson.chapters ?: emptyList() - val categories = mangaJson.categories ?: emptyList() - val history = mangaJson.history ?: emptyList() - val tracks = mangaJson.track ?: emptyList() - - val source = backupManager.sourceManager.get(manga.source) - val sourceName = sourceMapping[manga.source] ?: manga.source.toString() - - try { - if (source != null) { - restoreMangaData(manga, source, chapters, categories, history, tracks) - } else { - errors.add(Date() to "${manga.title} [$sourceName]: ${context.getString(R.string.source_not_found_name, sourceName)}") - } - } catch (e: Exception) { - errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}") - } - - restoreProgress += 1 - showRestoreProgress(restoreProgress, restoreAmount, manga.title) - } - - /** - * Returns a manga restore observable - * - * @param manga manga data from json - * @param source source to get manga data from - * @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, - source: Source, - chapters: List, - categories: List, - history: List, - tracks: List, - ) { - val dbManga = backupManager.getMangaFromDatabase(manga) - - db.inTransaction { - if (dbManga == null) { - // Manga not in database - restoreMangaFetch(source, manga, chapters, categories, history, tracks) - } else { // Manga in database - // Copy information from manga already in database - backupManager.restoreMangaNoFetch(manga, dbManga) - // Fetch rest of manga information - restoreMangaNoFetch(source, manga, chapters, categories, history, tracks) - } - } - } - - /** - * Fetches manga information. - * - * @param manga manga that needs updating - * @param chapters chapters of manga that needs updating - * @param categories categories that need updating - */ - private suspend fun restoreMangaFetch( - source: Source, - manga: Manga, - chapters: List, - categories: List, - history: List, - tracks: List, - ) { - try { - val fetchedManga = backupManager.fetchManga(source, manga) - fetchedManga.id ?: return - - updateChapters(source, fetchedManga, chapters) - - restoreExtraForManga(fetchedManga, categories, history, tracks) - - updateTracking(fetchedManga, tracks) - } catch (e: Exception) { - errors.add(Date() to "${manga.title} - ${e.message}") - } - } - - private suspend fun restoreMangaNoFetch( - source: Source, - backupManga: Manga, - chapters: List, - categories: List, - history: List, - tracks: List, - ) { - if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { - updateChapters(source, backupManga, chapters) - } - - restoreExtraForManga(backupManga, categories, history, tracks) - - updateTracking(backupManga, tracks) - } - - private fun restoreExtraForManga(manga: Manga, categories: List, history: List, tracks: List) { - // Restore categories - backupManager.restoreCategoriesForManga(manga, categories) - - // Restore history - backupManager.restoreHistoryForManga(history) - - // Restore tracking - backupManager.restoreTrackForManga(manga, tracks) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt deleted file mode 100644 index 0f14108170..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt +++ /dev/null @@ -1,66 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy - -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.ValidatorParseException -import eu.kanade.tachiyomi.data.backup.legacy.models.Backup -import kotlinx.serialization.json.decodeFromStream - -class LegacyBackupRestoreValidator : AbstractBackupRestoreValidator() { - - /** - * Checks for critical backup file data. - * - * @throws Exception if version or manga cannot be found. - * @return List of missing sources or missing trackers. - */ - override fun validate(context: Context, uri: Uri): Results { - val backupManager = LegacyBackupManager(context) - - val backup = try { - backupManager.parser.decodeFromStream( - context.contentResolver.openInputStream(uri)!!, - ) - } catch (e: Exception) { - throw ValidatorParseException(e) - } - - if (backup.version == null) { - throw Exception(context.getString(R.string.invalid_backup_file_missing_data)) - } - - if (backup.mangas.isEmpty()) { - throw Exception(context.getString(R.string.invalid_backup_file_missing_manga)) - } - - val sources = getSourceMapping(backup.extensions ?: emptyList()) - val missingSources = sources - .filter { sourceManager.get(it.key) == null } - .values - .sorted() - - val trackers = backup.mangas - .filterNot { it.track.isNullOrEmpty() } - .flatMap { it.track ?: emptyList() } - .map { it.sync_id } - .distinct() - val missingTrackers = trackers - .mapNotNull { trackManager.getService(it) } - .filter { !it.isLogged } - .map { context.getString(it.nameRes()) } - .sorted() - - return Results(missingSources, missingTrackers) - } - - companion object { - fun getSourceMapping(extensionsMapping: List): Map { - return extensionsMapping.associate { - val items = it.split(":") - items[0].toLong() to items[1] - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt deleted file mode 100644 index ba965cfa32..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt +++ /dev/null @@ -1,37 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.models - -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.Track -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - -@Serializable -data class Backup( - val version: Int? = null, - var mangas: MutableList = mutableListOf(), - var categories: List<@Contextual Category>? = null, - var extensions: List? = null, -) { - companion object { - const val CURRENT_VERSION = 2 - - fun getDefaultFilename(): String { - val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date()) - return "tachiyomi_$date.json" - } - } -} - -@Serializable -data class MangaObject( - var manga: @Contextual Manga, - var chapters: List<@Contextual Chapter>? = null, - var categories: List? = null, - var track: List<@Contextual Track>? = null, - var history: List<@Contextual DHistory>? = null, -) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/DHistory.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/DHistory.kt deleted file mode 100644 index 9a0ea06609..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/DHistory.kt +++ /dev/null @@ -1,3 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.models - -data class DHistory(val url: String, val lastRead: Long) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt deleted file mode 100644 index ab6861bb23..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt +++ /dev/null @@ -1,49 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.CategoryImpl -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonEncoder -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonArray -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive - -/** - * JSON Serializer used to write / read [CategoryImpl] to / from json - */ -open class CategoryBaseSerializer : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Category") - - override fun serialize(encoder: Encoder, value: T) { - encoder as JsonEncoder - encoder.encodeJsonElement( - buildJsonArray { - add(value.name) - add(value.order) - }, - ) - } - - @Suppress("UNCHECKED_CAST") - override fun deserialize(decoder: Decoder): T { - // make a category impl and cast as T so that the serializer accepts it - return CategoryImpl().apply { - decoder as JsonDecoder - val array = decoder.decodeJsonElement().jsonArray - name = array[0].jsonPrimitive.content - order = array[1].jsonPrimitive.int - } as T - } -} - -// Allow for serialization of a category and category impl -object CategoryTypeSerializer : CategoryBaseSerializer() - -object CategoryImplTypeSerializer : CategoryBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt deleted file mode 100644 index a1ae4a6bf0..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt +++ /dev/null @@ -1,66 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.ChapterImpl -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonEncoder -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.intOrNull -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put - -/** - * JSON Serializer used to write / read [ChapterImpl] to / from json - */ -open class ChapterBaseSerializer : KSerializer { - - override val descriptor = buildClassSerialDescriptor("Chapter") - - override fun serialize(encoder: Encoder, value: T) { - encoder as JsonEncoder - encoder.encodeJsonElement( - buildJsonObject { - put(URL, value.url) - if (value.read) { - put(READ, 1) - } - if (value.bookmark) { - put(BOOKMARK, 1) - } - if (value.last_page_read != 0) { - put(LAST_READ, value.last_page_read) - } - }, - ) - } - - @Suppress("UNCHECKED_CAST") - override fun deserialize(decoder: Decoder): T { - // make a chapter impl and cast as T so that the serializer accepts it - return ChapterImpl().apply { - decoder as JsonDecoder - val jsonObject = decoder.decodeJsonElement().jsonObject - url = jsonObject[URL]!!.jsonPrimitive.content - read = jsonObject[READ]?.jsonPrimitive?.intOrNull == 1 - bookmark = jsonObject[BOOKMARK]?.jsonPrimitive?.intOrNull == 1 - last_page_read = jsonObject[LAST_READ]?.jsonPrimitive?.intOrNull ?: last_page_read - } as T - } - - companion object { - private const val URL = "u" - private const val READ = "r" - private const val BOOKMARK = "b" - private const val LAST_READ = "l" - } -} - -// Allow for serialization of a chapter and chapter impl -object ChapterTypeSerializer : ChapterBaseSerializer() - -object ChapterImplTypeSerializer : ChapterBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt deleted file mode 100644 index 7cbc7a4524..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt +++ /dev/null @@ -1,41 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonEncoder -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonArray -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.long - -/** - * JSON Serializer used to write / read [DHistory] to / from json - */ -object HistoryTypeSerializer : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("History") - - override fun serialize(encoder: Encoder, value: DHistory) { - encoder as JsonEncoder - encoder.encodeJsonElement( - buildJsonArray { - add(value.url) - add(value.lastRead) - }, - ) - } - - override fun deserialize(decoder: Decoder): DHistory { - decoder as JsonDecoder - val array = decoder.decodeJsonElement().jsonArray - return DHistory( - url = array[0].jsonPrimitive.content, - lastRead = array[1].jsonPrimitive.long, - ) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt deleted file mode 100644 index bec833ab9f..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt +++ /dev/null @@ -1,56 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaImpl -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonEncoder -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonArray -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.long - -/** - * JSON Serializer used to write / read [MangaImpl] to / from json - */ -open class MangaBaseSerializer : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Manga") - - override fun serialize(encoder: Encoder, value: T) { - encoder as JsonEncoder - encoder.encodeJsonElement( - buildJsonArray { - add(value.url) - add(value.title) - add(value.source) - add(value.viewer_flags) - add(value.chapter_flags) - }, - ) - } - - @Suppress("UNCHECKED_CAST") - override fun deserialize(decoder: Decoder): T { - // make a manga impl and cast as T so that the serializer accepts it - return MangaImpl().apply { - decoder as JsonDecoder - val array = decoder.decodeJsonElement().jsonArray - url = array[0].jsonPrimitive.content - title = array[1].jsonPrimitive.content - source = array[2].jsonPrimitive.long - viewer_flags = array[3].jsonPrimitive.int - chapter_flags = array[4].jsonPrimitive.int - } as T - } -} - -// Allow for serialization of a manga and manga impl -object MangaTypeSerializer : MangaBaseSerializer() - -object MangaImplTypeSerializer : MangaBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt deleted file mode 100644 index 1d2ced7ebe..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt +++ /dev/null @@ -1,68 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.TrackImpl -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonEncoder -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.float -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.long -import kotlinx.serialization.json.put - -/** - * JSON Serializer used to write / read [TrackImpl] to / from json - */ -open class TrackBaseSerializer : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Track") - - override fun serialize(encoder: Encoder, value: T) { - encoder as JsonEncoder - encoder.encodeJsonElement( - buildJsonObject { - put(TITLE, value.title) - put(SYNC, value.sync_id) - put(MEDIA, value.media_id) - put(LIBRARY, value.library_id) - put(LAST_READ, value.last_chapter_read) - put(TRACKING_URL, value.tracking_url) - }, - ) - } - - @Suppress("UNCHECKED_CAST") - override fun deserialize(decoder: Decoder): T { - // make a track impl and cast as T so that the serializer accepts it - return TrackImpl().apply { - decoder as JsonDecoder - val jsonObject = decoder.decodeJsonElement().jsonObject - title = jsonObject[TITLE]!!.jsonPrimitive.content - sync_id = jsonObject[SYNC]!!.jsonPrimitive.int - media_id = jsonObject[MEDIA]!!.jsonPrimitive.long - library_id = jsonObject[LIBRARY]!!.jsonPrimitive.long - last_chapter_read = jsonObject[LAST_READ]!!.jsonPrimitive.float - tracking_url = jsonObject[TRACKING_URL]!!.jsonPrimitive.content - } as T - } - - companion object { - private const val SYNC = "s" - private const val MEDIA = "r" - private const val LIBRARY = "ml" - private const val TITLE = "t" - private const val LAST_READ = "l" - private const val TRACKING_URL = "u" - } -} - -// Allow for serialization of a track and track impl -object TrackTypeSerializer : TrackBaseSerializer() - -object TrackImplTypeSerializer : TrackBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt index 90c38d5375..05ee195f33 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt @@ -29,12 +29,4 @@ object TrackTable { const val COL_START_DATE = "start_date" const val COL_FINISH_DATE = "finish_date" - - val insertFromTempTable: String - get() = - """ - |INSERT INTO $TABLE($COL_ID,$COL_MANGA_ID,$COL_SYNC_ID,$COL_MEDIA_ID,$COL_LIBRARY_ID,$COL_TITLE,$COL_LAST_CHAPTER_READ,$COL_TOTAL_CHAPTERS,$COL_STATUS,$COL_SCORE,$COL_TRACKING_URL,$COL_START_DATE,$COL_FINISH_DATE) - |SELECT $COL_ID,$COL_MANGA_ID,$COL_SYNC_ID,$COL_MEDIA_ID,$COL_LIBRARY_ID,$COL_TITLE,$COL_LAST_CHAPTER_READ,$COL_TOTAL_CHAPTERS,$COL_STATUS,$COL_SCORE,$COL_TRACKING_URL,$COL_START_DATE,$COL_FINISH_DATE - |FROM ${TABLE}_tmp - """.trimMargin() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt index c5b79e647a..5ce5ad4e9e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt @@ -22,10 +22,8 @@ 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.BackupRestoreService -import eu.kanade.tachiyomi.data.backup.ValidatorParseException import eu.kanade.tachiyomi.data.backup.full.FullBackupRestoreValidator import eu.kanade.tachiyomi.data.backup.full.models.BackupFull -import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupRestoreValidator import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe import eu.kanade.tachiyomi.util.preference.bindTo @@ -272,19 +270,9 @@ class SettingsBackupController : SettingsController() { val uri: Uri = args.getParcelable(KEY_URI)!! return try { - var type = BackupConst.BACKUP_TYPE_FULL - val results = try { - FullBackupRestoreValidator().validate(activity, uri) - } catch (_: ValidatorParseException) { - type = BackupConst.BACKUP_TYPE_LEGACY - LegacyBackupRestoreValidator().validate(activity, uri) - } + val results = FullBackupRestoreValidator().validate(activity, uri) - var message = if (type == BackupConst.BACKUP_TYPE_FULL) { - activity.getString(R.string.backup_restore_content_full) - } else { - activity.getString(R.string.backup_restore_content) - } + var message = activity.getString(R.string.backup_restore_content_full) if (results.missingSources.isNotEmpty()) { message += "\n\n${activity.getString(R.string.backup_restore_missing_sources)}\n${results.missingSources.joinToString("\n") { "- $it" }}" } @@ -296,7 +284,7 @@ class SettingsBackupController : SettingsController() { .setTitle(R.string.pref_restore_backup) .setMessage(message) .setPositiveButton(R.string.action_restore) { _, _ -> - BackupRestoreService.start(activity, uri, type) + BackupRestoreService.start(activity, uri) } .create() } catch (e: Exception) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1994404e22..cea7e6da16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -436,16 +436,13 @@ Automatic backups Backup frequency Maximum backups - Source not found: %1$s Not logged in: %1$s Error: empty URI Backup created Invalid backup file - File is missing data. Backup does not contain any manga. Missing sources: Trackers not logged into: - Restore uses sources to fetch data, carrier costs may apply.\n\nMake sure you have installed all necessary extensions and are logged in to sources and tracking services before restoring. Data from the backup file will be restored.\n\nYou will need to install any missing extensions and log in to tracking services afterwards to use them. Restore completed %02d min, %02d sec