mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Warn about missing sources before restoring backup
(cherry picked from commit a00d11701f)
			
			
This commit is contained in:
		| @@ -118,6 +118,11 @@ class BackupRestoreService : Service() { | ||||
|  | ||||
|     private var totalAmount = 0 | ||||
|  | ||||
|     /** | ||||
|      * Mapping of source ID to source name from backup data | ||||
|      */ | ||||
|     private var sourceMapping: Map<Long, String> = emptyMap() | ||||
|  | ||||
|     /** | ||||
|      * List containing errors | ||||
|      */ | ||||
| @@ -235,6 +240,9 @@ class BackupRestoreService : Service() { | ||||
|         // Restore categories | ||||
|         restoreCategories(json.get(CATEGORIES)) | ||||
|  | ||||
|         // Store source mapping for error messages | ||||
|         sourceMapping = BackupRestoreValidator.getSourceMapping(json) | ||||
|  | ||||
|         // Restore individual manga | ||||
|         mangasJson.forEach { | ||||
|             if (job?.isActive != true) { | ||||
| @@ -286,9 +294,20 @@ class BackupRestoreService : Service() { | ||||
|         // <-- EXH | ||||
|  | ||||
|         try { | ||||
|             restoreMangaData(manga, chapters, categories, history, tracks) | ||||
|             val source = backupManager.sourceManager.get(manga.source) | ||||
|             if (source != null) { | ||||
|                 restoreMangaData(manga, source, chapters, categories, history, tracks) | ||||
|             } else { | ||||
|                 val message = if (manga.source in sourceMapping) { | ||||
|                     getString(R.string.source_not_found_name, sourceMapping[manga.source]) | ||||
|                 } else { | ||||
|                     getString(R.string.source_not_found) | ||||
|                 } | ||||
|  | ||||
|                 errors.add(Date() to "${manga.title} - $message") | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             errors.add(Date() to "${manga.title} - ${getString(R.string.source_not_found)}") | ||||
|             errors.add(Date() to "${manga.title} - ${e.message}") | ||||
|         } | ||||
|  | ||||
|         restoreProgress += 1 | ||||
| @@ -299,6 +318,7 @@ class BackupRestoreService : Service() { | ||||
|      * 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 | ||||
| @@ -306,13 +326,12 @@ class BackupRestoreService : Service() { | ||||
|      */ | ||||
|     private fun restoreMangaData( | ||||
|         manga: Manga, | ||||
|         source: Source, | ||||
|         chapters: List<Chapter>, | ||||
|         categories: List<String>, | ||||
|         history: List<DHistory>, | ||||
|         tracks: List<Track> | ||||
|     ) { | ||||
|         // Get source | ||||
|         val source = backupManager.sourceManager.getOrStub(manga.source) | ||||
|         val dbManga = backupManager.getMangaFromDatabase(manga) | ||||
|  | ||||
|         db.inTransaction { | ||||
|   | ||||
| @@ -0,0 +1,46 @@ | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
|  | ||||
| import android.content.Context | ||||
| import android.net.Uri | ||||
| import com.google.gson.JsonObject | ||||
| import com.google.gson.JsonParser | ||||
| import com.google.gson.stream.JsonReader | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
|  | ||||
| object BackupRestoreValidator { | ||||
|  | ||||
|     /** | ||||
|      * Checks for critical backup file data. | ||||
|      * | ||||
|      * @throws Exception if version or manga cannot be found. | ||||
|      * @return List of required sources. | ||||
|      */ | ||||
|     fun validate(context: Context, uri: Uri): Map<Long, String> { | ||||
|         val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader()) | ||||
|         val json = JsonParser.parseReader(reader).asJsonObject | ||||
|  | ||||
|         val version = json.get(Backup.VERSION) | ||||
|         val mangasJson = json.get(Backup.MANGAS) | ||||
|         if (version == null || mangasJson == null) { | ||||
|             throw Exception(context.getString(R.string.invalid_backup_file_missing_data)) | ||||
|         } | ||||
|  | ||||
|         if (mangasJson.asJsonArray.size() == 0) { | ||||
|             throw Exception(context.getString(R.string.invalid_backup_file_missing_manga)) | ||||
|         } | ||||
|  | ||||
|         return getSourceMapping(json) | ||||
|     } | ||||
|  | ||||
|     fun getSourceMapping(json: JsonObject): Map<Long, String> { | ||||
|         val extensionsMapping = json.get(Backup.EXTENSIONS) ?: return emptyMap() | ||||
|  | ||||
|         return extensionsMapping.asJsonArray | ||||
|             .map { | ||||
|                 val items = it.asString.split(":") | ||||
|                 items[0].toLong() to items[1] | ||||
|             } | ||||
|             .toMap() | ||||
|     } | ||||
| } | ||||
| @@ -16,9 +16,11 @@ import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.BackupCreateService | ||||
| import eu.kanade.tachiyomi.data.backup.BackupCreatorJob | ||||
| import eu.kanade.tachiyomi.data.backup.BackupRestoreService | ||||
| import eu.kanade.tachiyomi.data.backup.BackupRestoreValidator | ||||
| import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| import eu.kanade.tachiyomi.data.preference.asImmediateFlow | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe | ||||
| import eu.kanade.tachiyomi.util.preference.defaultValue | ||||
| @@ -34,6 +36,8 @@ import eu.kanade.tachiyomi.util.system.getFilePicker | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class SettingsBackupController : SettingsController() { | ||||
|  | ||||
| @@ -247,15 +251,36 @@ class SettingsBackupController : SettingsController() { | ||||
|         ) | ||||
|  | ||||
|         override fun onCreateDialog(savedViewState: Bundle?): Dialog { | ||||
|             return MaterialDialog(activity!!) | ||||
|                 .title(R.string.pref_restore_backup) | ||||
|                 .message(R.string.backup_restore_content) | ||||
|                 .positiveButton(R.string.action_restore) { | ||||
|                     val context = applicationContext | ||||
|                     if (context != null) { | ||||
|                         BackupRestoreService.start(context, args.getParcelable(KEY_URI)!!) | ||||
|             val activity = activity!! | ||||
|             val uri: Uri = args.getParcelable(KEY_URI)!! | ||||
|  | ||||
|             return try { | ||||
|                 var message = activity.getString(R.string.backup_restore_content) | ||||
|  | ||||
|                 val sources = BackupRestoreValidator.validate(activity, uri) | ||||
|                 if (sources.isNotEmpty()) { | ||||
|                     val sourceManager = Injekt.get<SourceManager>() | ||||
|                     val missingSources = sources | ||||
|                         .filter { sourceManager.get(it.key) == null } | ||||
|                         .values | ||||
|                         .sorted() | ||||
|                     if (missingSources.isNotEmpty()) { | ||||
|                         message += "\n\n${activity.getString(R.string.backup_restore_missing_sources)}\n${missingSources.joinToString("\n") { "- $it" }}" | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 MaterialDialog(activity) | ||||
|                     .title(R.string.pref_restore_backup) | ||||
|                     .message(text = message) | ||||
|                     .positiveButton(R.string.action_restore) { | ||||
|                         BackupRestoreService.start(activity, uri) | ||||
|                     } | ||||
|             } catch (e: Exception) { | ||||
|                 MaterialDialog(activity) | ||||
|                     .title(R.string.invalid_backup_file) | ||||
|                     .message(text = e.message) | ||||
|                     .positiveButton(android.R.string.cancel) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private companion object { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user