Warn about missing sources before restoring backup

This commit is contained in:
arkon 2020-05-16 11:18:47 -04:00
parent 1cf74a5396
commit a00d11701f
5 changed files with 108 additions and 14 deletions

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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 {

View file

@ -96,7 +96,7 @@
android:background="@drawable/list_item_selector"
android:gravity="center_vertical"
android:padding="16dp"
android:text="@string/ext_preferences"
android:text="@string/label_settings"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -209,7 +209,6 @@
<string name="ext_unofficial">Unofficial</string>
<string name="ext_untrusted">Untrusted</string>
<string name="ext_uninstall">Uninstall</string>
<string name="ext_preferences">Preferences</string>
<string name="ext_available">Available</string>
<string name="untrusted_extension">Untrusted extension</string>
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any login credentials stored in Tachiyomi or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
@ -327,14 +326,19 @@
<string name="pref_backup_interval">Backup frequency</string>
<string name="pref_backup_slots">Maximum backups</string>
<string name="source_not_found">Source not found</string>
<string name="source_not_found_name">Source not found: %1$s</string>
<string name="backup_created">Backup created</string>
<string name="invalid_backup_file">Invalid backup file</string>
<string name="invalid_backup_file_missing_data">File is missing data.</string>
<string name="invalid_backup_file_missing_manga">Backup does not contain any manga.</string>
<string name="backup_restore_missing_sources">Missing sources:</string>
<string name="backup_restore_content">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.</string>
<string name="restore_completed">Restore completed</string>
<string name="restore_duration">%02d min, %02d sec</string>
<plurals name="restore_completed_message">
<item quantity="one">Done in %1$s with %2$s error</item>
<item quantity="other">Done in %1$s with %2$s errors</item>
</plurals>
<string name="backup_restore_content">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.</string>
<string name="backup_in_progress">Backup is already in progress</string>
<string name="backup_choice">What do you want to backup?</string>
<string name="creating_backup">Creating backup</string>