mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-25 10:37:51 +02:00
Warn about missing sources before restoring backup
This commit is contained in:
@ -110,6 +110,11 @@ class BackupRestoreService : Service() {
|
||||
*/
|
||||
private var restoreAmount = 0
|
||||
|
||||
/**
|
||||
* Mapping of source ID to source name from backup data
|
||||
*/
|
||||
private var sourceMapping: Map<Long, String> = emptyMap()
|
||||
|
||||
/**
|
||||
* List containing errors
|
||||
*/
|
||||
@ -212,6 +217,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) {
|
||||
@ -259,9 +267,20 @@ class BackupRestoreService : Service() {
|
||||
)
|
||||
|
||||
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
|
||||
@ -272,6 +291,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
|
||||
@ -279,13 +299,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