mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Add Backup and Restore of Extension Repos (#1057)
* Backup/Restore Extension Repos * Refactor * Moving to Under App Settings * Sort by URL, Check existing by SHA and Error Logging Untested. Currently in a lecture and can't test if the changes really work. * Changes to logic * Don't ask me what's happening here * Renaming Variables * Fixing restoreAmount & changes to logic Co-Authored-By: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
		| @@ -6,11 +6,13 @@ import com.hippo.unifile.UniFile | ||||
| import eu.kanade.tachiyomi.BuildConfig | ||||
| import eu.kanade.tachiyomi.data.backup.BackupFileValidator | ||||
| import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator | ||||
| import eu.kanade.tachiyomi.data.backup.create.creators.ExtensionRepoBackupCreator | ||||
| 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.BackupExtensionRepos | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupManga | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupPreference | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupSource | ||||
| @@ -45,6 +47,7 @@ class BackupCreator( | ||||
|     private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(), | ||||
|     private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(), | ||||
|     private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(), | ||||
|     private val extensionRepoBackupCreator: ExtensionRepoBackupCreator = ExtensionRepoBackupCreator(), | ||||
|     private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(), | ||||
| ) { | ||||
|  | ||||
| @@ -78,6 +81,7 @@ class BackupCreator( | ||||
|                 backupCategories = backupCategories(options), | ||||
|                 backupSources = backupSources(backupManga), | ||||
|                 backupPreferences = backupAppPreferences(options), | ||||
|                 backupExtensionRepo = backupExtensionRepos(options), | ||||
|                 backupSourcePreferences = backupSourcePreferences(options), | ||||
|             ) | ||||
|  | ||||
| @@ -133,6 +137,12 @@ class BackupCreator( | ||||
|         return preferenceBackupCreator.createApp(includePrivatePreferences = options.privateSettings) | ||||
|     } | ||||
|  | ||||
|     private suspend fun backupExtensionRepos(options: BackupOptions): List<BackupExtensionRepos> { | ||||
|         if (!options.extensionRepoSettings) return emptyList() | ||||
|  | ||||
|         return extensionRepoBackupCreator() | ||||
|     } | ||||
|  | ||||
|     private fun backupSourcePreferences(options: BackupOptions): List<BackupSourcePreferences> { | ||||
|         if (!options.sourceSettings) return emptyList() | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ data class BackupOptions( | ||||
|     val tracking: Boolean = true, | ||||
|     val history: Boolean = true, | ||||
|     val appSettings: Boolean = true, | ||||
|     val extensionRepoSettings: Boolean = true, | ||||
|     val sourceSettings: Boolean = true, | ||||
|     val privateSettings: Boolean = false, | ||||
| ) { | ||||
| @@ -22,11 +23,12 @@ data class BackupOptions( | ||||
|         tracking, | ||||
|         history, | ||||
|         appSettings, | ||||
|         extensionRepoSettings, | ||||
|         sourceSettings, | ||||
|         privateSettings, | ||||
|     ) | ||||
|  | ||||
|     fun canCreate() = libraryEntries || categories || appSettings || sourceSettings | ||||
|     fun canCreate() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings | ||||
|  | ||||
|     companion object { | ||||
|         val libraryOptions = persistentListOf( | ||||
| @@ -66,6 +68,11 @@ data class BackupOptions( | ||||
|                 getter = BackupOptions::appSettings, | ||||
|                 setter = { options, enabled -> options.copy(appSettings = enabled) }, | ||||
|             ), | ||||
|             Entry( | ||||
|                 label = MR.strings.extensionRepo_settings, | ||||
|                 getter = BackupOptions::extensionRepoSettings, | ||||
|                 setter = { options, enabled -> options.copy(extensionRepoSettings = enabled) }, | ||||
|             ), | ||||
|             Entry( | ||||
|                 label = MR.strings.source_settings, | ||||
|                 getter = BackupOptions::sourceSettings, | ||||
| @@ -86,8 +93,9 @@ data class BackupOptions( | ||||
|             tracking = array[3], | ||||
|             history = array[4], | ||||
|             appSettings = array[5], | ||||
|             sourceSettings = array[6], | ||||
|             privateSettings = array[7], | ||||
|             extensionRepoSettings = array[6], | ||||
|             sourceSettings = array[7], | ||||
|             privateSettings = array[8], | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,17 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.create.creators | ||||
|  | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupExtensionRepos | ||||
| import eu.kanade.tachiyomi.data.backup.models.backupExtensionReposMapper | ||||
| import mihon.domain.extensionrepo.interactor.GetExtensionRepo | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class ExtensionRepoBackupCreator( | ||||
|     private val getExtensionRepos: GetExtensionRepo = Injekt.get(), | ||||
| ) { | ||||
|  | ||||
|     suspend operator fun invoke(): List<BackupExtensionRepos> { | ||||
|         return getExtensionRepos.getAll() | ||||
|             .map(backupExtensionReposMapper) | ||||
|     } | ||||
| } | ||||
| @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup.models | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.protobuf.ProtoNumber | ||||
|  | ||||
| @Suppress("MagicNumber") | ||||
| @Serializable | ||||
| data class Backup( | ||||
|     @ProtoNumber(1) val backupManga: List<BackupManga>, | ||||
| @@ -11,4 +12,5 @@ data class Backup( | ||||
|     @ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(), | ||||
|     @ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(), | ||||
|     @ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(), | ||||
|     @ProtoNumber(106) var backupExtensionRepo: List<BackupExtensionRepos> = emptyList(), | ||||
| ) | ||||
|   | ||||
| @@ -0,0 +1,25 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
|  | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.protobuf.ProtoNumber | ||||
| import mihon.domain.extensionrepo.model.ExtensionRepo | ||||
|  | ||||
| @Suppress("MagicNumber") | ||||
| @Serializable | ||||
| class BackupExtensionRepos( | ||||
|     @ProtoNumber(1) var baseUrl: String, | ||||
|     @ProtoNumber(2) var name: String, | ||||
|     @ProtoNumber(3) var shortName: String?, | ||||
|     @ProtoNumber(4) var website: String, | ||||
|     @ProtoNumber(5) var signingKeyFingerprint: String, | ||||
| ) | ||||
|  | ||||
| val backupExtensionReposMapper = { repo: ExtensionRepo -> | ||||
|     BackupExtensionRepos( | ||||
|         baseUrl = repo.baseUrl, | ||||
|         name = repo.name, | ||||
|         shortName = repo.shortName, | ||||
|         website = repo.website, | ||||
|         signingKeyFingerprint = repo.signingKeyFingerprint, | ||||
|     ) | ||||
| } | ||||
| @@ -5,10 +5,12 @@ import android.net.Uri | ||||
| import eu.kanade.tachiyomi.data.backup.BackupDecoder | ||||
| import eu.kanade.tachiyomi.data.backup.BackupNotifier | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupCategory | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupExtensionRepos | ||||
| 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.ExtensionRepoRestorer | ||||
| import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaRestorer | ||||
| import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceRestorer | ||||
| import eu.kanade.tachiyomi.util.system.createFileInCacheDir | ||||
| @@ -30,6 +32,7 @@ class BackupRestorer( | ||||
|  | ||||
|     private val categoriesRestorer: CategoriesRestorer = CategoriesRestorer(), | ||||
|     private val preferenceRestorer: PreferenceRestorer = PreferenceRestorer(context), | ||||
|     private val extensionRepoRestorer: ExtensionRepoRestorer = ExtensionRepoRestorer(), | ||||
|     private val mangaRestorer: MangaRestorer = MangaRestorer(), | ||||
| ) { | ||||
|  | ||||
| @@ -76,6 +79,9 @@ class BackupRestorer( | ||||
|         if (options.appSettings) { | ||||
|             restoreAmount += 1 | ||||
|         } | ||||
|         if (options.extensionRepoSettings) { | ||||
|             restoreAmount += backup.backupExtensionRepo.size | ||||
|         } | ||||
|         if (options.sourceSettings) { | ||||
|             restoreAmount += 1 | ||||
|         } | ||||
| @@ -93,6 +99,9 @@ class BackupRestorer( | ||||
|             if (options.libraryEntries) { | ||||
|                 restoreManga(backup.backupManga, if (options.categories) backup.backupCategories else emptyList()) | ||||
|             } | ||||
|             if (options.extensionRepoSettings) { | ||||
|                 restoreExtensionRepos(backup.backupExtensionRepo) | ||||
|             } | ||||
|  | ||||
|             // TODO: optionally trigger online library + tracker update | ||||
|         } | ||||
| @@ -157,6 +166,29 @@ class BackupRestorer( | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     private fun CoroutineScope.restoreExtensionRepos( | ||||
|         backupExtensionRepo: List<BackupExtensionRepos> | ||||
|     ) = launch { | ||||
|         backupExtensionRepo | ||||
|             .forEach { | ||||
|                 ensureActive() | ||||
|  | ||||
|                 try { | ||||
|                     extensionRepoRestorer(it) | ||||
|                 } catch (e: Exception) { | ||||
|                     errors.add(Date() to "Error Adding Repo: ${it.name} : ${e.message}") | ||||
|                 } | ||||
|  | ||||
|                 restoreProgress += 1 | ||||
|                 notifier.showRestoreProgress( | ||||
|                     context.stringResource(MR.strings.extensionRepo_settings), | ||||
|                     restoreProgress, | ||||
|                     restoreAmount, | ||||
|                     isSync, | ||||
|                 ) | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     private fun writeErrorLog(): File { | ||||
|         try { | ||||
|             if (errors.isNotEmpty()) { | ||||
|   | ||||
| @@ -8,17 +8,19 @@ data class RestoreOptions( | ||||
|     val libraryEntries: Boolean = true, | ||||
|     val categories: Boolean = true, | ||||
|     val appSettings: Boolean = true, | ||||
|     val sourceSettings: Boolean = true, | ||||
|     val extensionRepoSettings: Boolean = true, | ||||
|     val sourceSettings: Boolean = true | ||||
| ) { | ||||
|  | ||||
|     fun asBooleanArray() = booleanArrayOf( | ||||
|         libraryEntries, | ||||
|         categories, | ||||
|         appSettings, | ||||
|         sourceSettings, | ||||
|         extensionRepoSettings, | ||||
|         sourceSettings | ||||
|     ) | ||||
|  | ||||
|     fun canRestore() = libraryEntries || categories || appSettings || sourceSettings | ||||
|     fun canRestore() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings | ||||
|  | ||||
|     companion object { | ||||
|         val options = persistentListOf( | ||||
| @@ -37,6 +39,11 @@ data class RestoreOptions( | ||||
|                 getter = RestoreOptions::appSettings, | ||||
|                 setter = { options, enabled -> options.copy(appSettings = enabled) }, | ||||
|             ), | ||||
|             Entry( | ||||
|                 label = MR.strings.extensionRepo_settings, | ||||
|                 getter = RestoreOptions::extensionRepoSettings, | ||||
|                 setter = { options, enabled -> options.copy(extensionRepoSettings = enabled) }, | ||||
|             ), | ||||
|             Entry( | ||||
|                 label = MR.strings.source_settings, | ||||
|                 getter = RestoreOptions::sourceSettings, | ||||
| @@ -48,7 +55,8 @@ data class RestoreOptions( | ||||
|             libraryEntries = array[0], | ||||
|             categories = array[1], | ||||
|             appSettings = array[2], | ||||
|             sourceSettings = array[3], | ||||
|             extensionRepoSettings = array[3], | ||||
|             sourceSettings = array[4], | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,40 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.restore.restorers | ||||
|  | ||||
| import eu.kanade.tachiyomi.data.backup.models.BackupExtensionRepos | ||||
| import mihon.domain.extensionrepo.interactor.GetExtensionRepo | ||||
| import tachiyomi.data.DatabaseHandler | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class ExtensionRepoRestorer( | ||||
|     private val handler: DatabaseHandler = Injekt.get(), | ||||
|     private val getExtensionRepos: GetExtensionRepo = Injekt.get() | ||||
| ) { | ||||
|  | ||||
|     suspend operator fun invoke( | ||||
|         backupRepo: BackupExtensionRepos, | ||||
|     ) { | ||||
|         val dbRepos = getExtensionRepos.getAll() | ||||
|         val existingReposBySHA = dbRepos.associateBy { it.signingKeyFingerprint } | ||||
|         val existingReposByUrl = dbRepos.associateBy { it.baseUrl } | ||||
|  | ||||
|         val urlExists = existingReposByUrl[backupRepo.baseUrl] | ||||
|         val shaExists = existingReposBySHA[backupRepo.signingKeyFingerprint] | ||||
|  | ||||
|         if (urlExists != null && urlExists.signingKeyFingerprint != backupRepo.signingKeyFingerprint) { | ||||
|             error("Already Exists with different signing key fingerprint") | ||||
|         } else if (shaExists != null) { | ||||
|             error("${shaExists.name} has the same signing key fingerprint") | ||||
|         } else { | ||||
|             handler.await { | ||||
|                 extension_reposQueries.insert( | ||||
|                     backupRepo.baseUrl, | ||||
|                     backupRepo.name, | ||||
|                     backupRepo.shortName, | ||||
|                     backupRepo.website, | ||||
|                     backupRepo.signingKeyFingerprint | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user