Remove online protobuf backup restore option

(cherry picked from commit 94f5117941c368ee5cb300af0d89b39b5427b8ad)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt
This commit is contained in:
arkon 2021-03-13 18:45:22 -05:00 committed by Jobobby04
parent df950219f5
commit e31e71ad44
5 changed files with 65 additions and 166 deletions

View File

@ -43,12 +43,11 @@ class BackupRestoreService : Service() {
* @param context context of application * @param context context of application
* @param uri path of Uri * @param uri path of Uri
*/ */
fun start(context: Context, uri: Uri, mode: Int, online: Boolean?) { fun start(context: Context, uri: Uri, mode: Int) {
if (!isRunning(context)) { if (!isRunning(context)) {
val intent = Intent(context, BackupRestoreService::class.java).apply { val intent = Intent(context, BackupRestoreService::class.java).apply {
putExtra(BackupConst.EXTRA_URI, uri) putExtra(BackupConst.EXTRA_URI, uri)
putExtra(BackupConst.EXTRA_MODE, mode) putExtra(BackupConst.EXTRA_MODE, mode)
online?.let { putExtra(BackupConst.EXTRA_TYPE, it) }
} }
ContextCompat.startForegroundService(context, intent) ContextCompat.startForegroundService(context, intent)
} }
@ -119,13 +118,12 @@ class BackupRestoreService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.getParcelableExtra<Uri>(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY val uri = intent?.getParcelableExtra<Uri>(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY
val mode = intent.getIntExtra(BackupConst.EXTRA_MODE, BackupConst.BACKUP_TYPE_FULL) val mode = intent.getIntExtra(BackupConst.EXTRA_MODE, BackupConst.BACKUP_TYPE_FULL)
val online = intent.getBooleanExtra(BackupConst.EXTRA_TYPE, true)
// Cancel any previous job if needed. // Cancel any previous job if needed.
backupRestore?.job?.cancel() backupRestore?.job?.cancel()
backupRestore = when (mode) { backupRestore = when (mode) {
BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier, online) BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier)
else -> LegacyBackupRestore(this, notifier) else -> LegacyBackupRestore(this, notifier)
} }

View File

@ -29,11 +29,7 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track 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 eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadataAsync import exh.metadata.metadata.base.insertFlatMetadataAsync
@ -238,24 +234,13 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
/** /**
* Fetches manga information * Fetches manga information
* *
* @param source source of manga
* @param manga manga that needs updating * @param manga manga that needs updating
* @return Updated manga info. * @return Updated manga info.
*/ */
suspend fun restoreMangaFetch(source: Source?, manga: Manga, online: Boolean): Manga { fun restoreManga(manga: Manga): Manga {
return if (online && source != null /* SY --> */ && source !is MergedSource /* SY <-- */) { return manga.also {
val networkManga = source.getMangaDetails(manga.toMangaInfo()) it.initialized = it.description != null
manga.also { it.id = insertManga(it)
it.copyFrom(networkManga.toSManga())
it.favorite = manga.favorite
it.initialized = true
it.id = insertManga(manga)
}
} else {
manga.also {
it.initialized = it.description != null
it.id = insertManga(it)
}
} }
} }
@ -364,29 +349,26 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
val trackToUpdate = mutableListOf<Track>() val trackToUpdate = mutableListOf<Track>()
tracks.forEach { track -> tracks.forEach { track ->
val service = trackManager.getService(track.sync_id) var isInDatabase = false
if (service != null && service.isLogged) { for (dbTrack in dbTracks) {
var isInDatabase = false if (track.sync_id == dbTrack.sync_id) {
for (dbTrack in dbTracks) { // The sync is already in the db, only update its fields
if (track.sync_id == dbTrack.sync_id) { if (track.media_id != dbTrack.media_id) {
// The sync is already in the db, only update its fields dbTrack.media_id = track.media_id
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 (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 if (!isInDatabase) {
track.id = null // Insert new sync. Let the db assign the id
trackToUpdate.add(track) track.id = null
} trackToUpdate.add(track)
} }
} }
// Update database // Update database
@ -395,47 +377,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
} }
} }
/** internal fun restoreChaptersForManga(manga: Manga, chapters: List<Chapter>) {
* 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<Chapter>): Boolean {
val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking()
// Return if fetch is needed
if (dbChapters.isEmpty() || dbChapters.size < chapters.size) {
return false
}
chapters.forEach { chapter ->
val dbChapter = dbChapters.find { it.url == chapter.url }
if (dbChapter != null) {
chapter.id = dbChapter.id
chapter.copyFrom(dbChapter)
if (dbChapter.read && !chapter.read) {
chapter.read = dbChapter.read
chapter.last_page_read = dbChapter.last_page_read
} else if (chapter.last_page_read == 0 && dbChapter.last_page_read != 0) {
chapter.last_page_read = dbChapter.last_page_read
}
if (!chapter.bookmark && dbChapter.bookmark) {
chapter.bookmark = dbChapter.bookmark
}
}
chapter.manga_id = manga.id
}
// Filter the chapters that couldn't be found.
updateChapters(chapters.filter { it.id != null })
return true
}
internal fun restoreChaptersForMangaOffline(manga: Manga, chapters: List<Chapter>) {
val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking() val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking()
chapters.forEach { chapter -> chapters.forEach { chapter ->
@ -528,7 +470,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
} }
} }
internal suspend fun restoreFlatMetadata(manga: Manga, backupFlatMetadata: BackupFlatMetadata) { internal fun restoreFlatMetadata(manga: Manga, backupFlatMetadata: BackupFlatMetadata) {
val mangaId = manga.id ?: return val mangaId = manga.id ?: return
launchIO { launchIO {
databaseHelper.getFlatMetadataForManga(mangaId).executeOnIO().let { databaseHelper.getFlatMetadataForManga(mangaId).executeOnIO().let {

View File

@ -15,8 +15,6 @@ import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.all.MergedSource
import exh.EXHMigrations import exh.EXHMigrations
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import okio.buffer import okio.buffer
@ -24,7 +22,7 @@ import okio.gzip
import okio.source import okio.source
import java.util.Date import java.util.Date
class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) { class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<FullBackupManager>(context, notifier) {
override suspend fun performRestore(uri: Uri): Boolean { override suspend fun performRestore(uri: Uri): Boolean {
// SY --> // SY -->
@ -57,7 +55,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
return false return false
} }
restoreManga(it, backup.backupCategories, online) restoreManga(it, backup.backupCategories)
} }
return true return true
@ -81,7 +79,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
} }
// SY <-- // SY <--
private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, online: Boolean) { private fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>) {
val manga = backupManga.getMangaImpl() val manga = backupManga.getMangaImpl()
val chapters = backupManga.getChaptersImpl() val chapters = backupManga.getChaptersImpl()
val categories = backupManga.categories val categories = backupManga.categories
@ -100,8 +98,8 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
val sourceName = sourceMapping[manga.source] ?: manga.source.toString() val sourceName = sourceMapping[manga.source] ?: manga.source.toString()
try { try {
if (source != null || !online) { if (source != null) {
restoreMangaData(manga, source, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata, online) restoreMangaData(manga, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata)
} else { } else {
errors.add(Date() to "${manga.title} [$sourceName]: ${context.getString(R.string.source_not_found_name, sourceName)}") errors.add(Date() to "${manga.title} [$sourceName]: ${context.getString(R.string.source_not_found_name, sourceName)}")
} }
@ -117,35 +115,32 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
* Returns a manga restore observable * Returns a manga restore observable
* *
* @param manga manga data from json * @param manga manga data from json
* @param source source to get manga data from
* @param chapters chapters data from json * @param chapters chapters data from json
* @param categories categories data from json * @param categories categories data from json
* @param history history data from json * @param history history data from json
* @param tracks tracking data from json * @param tracks tracking data from json
*/ */
private suspend fun restoreMangaData( private fun restoreMangaData(
manga: Manga, manga: Manga,
source: Source?,
chapters: List<Chapter>, chapters: List<Chapter>,
categories: List<Int>, categories: List<Int>,
history: List<BackupHistory>, history: List<BackupHistory>,
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
mergedMangaReferences: List<BackupMergedMangaReference>, mergedMangaReferences: List<BackupMergedMangaReference>,
flatMetadata: BackupFlatMetadata?, flatMetadata: BackupFlatMetadata?
online: Boolean
) { ) {
val dbManga = backupManager.getMangaFromDatabase(manga)
db.inTransaction { db.inTransaction {
val dbManga = backupManager.getMangaFromDatabase(manga)
if (dbManga == null) { if (dbManga == null) {
// Manga not in database // Manga not in database
restoreMangaFetch(source, manga, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata, online) restoreMangaFetch(manga, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata)
} else { // Manga in database } else {
// Manga in database
// Copy information from manga already in database // Copy information from manga already in database
backupManager.restoreMangaNoFetch(manga, dbManga) backupManager.restoreMangaNoFetch(manga, dbManga)
// Fetch rest of manga information // Fetch rest of manga information
restoreMangaNoFetch(source, manga, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata, online) restoreMangaNoFetch(manga, chapters, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata)
} }
} }
} }
@ -157,8 +152,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
* @param chapters chapters of manga that needs updating * @param chapters chapters of manga that needs updating
* @param categories categories that need updating * @param categories categories that need updating
*/ */
private suspend fun restoreMangaFetch( private fun restoreMangaFetch(
source: Source?,
manga: Manga, manga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
categories: List<Int>, categories: List<Int>,
@ -166,33 +160,21 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
mergedMangaReferences: List<BackupMergedMangaReference>, mergedMangaReferences: List<BackupMergedMangaReference>,
flatMetadata: BackupFlatMetadata?, flatMetadata: BackupFlatMetadata?
online: Boolean
) { ) {
try { try {
val fetchedManga = backupManager.restoreMangaFetch(source, manga, online) val fetchedManga = backupManager.restoreManga(manga)
fetchedManga.id ?: return fetchedManga.id ?: return
if (online && source != null) { backupManager.restoreChaptersForManga(fetchedManga, chapters)
// SY -->
if (source !is MergedSource) {
updateChapters(source, fetchedManga, chapters)
}
// SY <--
} else {
backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters)
}
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata) restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata)
updateTracking(fetchedManga, tracks)
} catch (e: Exception) { } catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}") errors.add(Date() to "${manga.title} - ${e.message}")
} }
} }
private suspend fun restoreMangaNoFetch( private fun restoreMangaNoFetch(
source: Source?,
backupManga: Manga, backupManga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
categories: List<Int>, categories: List<Int>,
@ -200,23 +182,14 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
mergedMangaReferences: List<BackupMergedMangaReference>, mergedMangaReferences: List<BackupMergedMangaReference>,
flatMetadata: BackupFlatMetadata?, flatMetadata: BackupFlatMetadata?
online: Boolean
) { ) {
if (online && source != null) { backupManager.restoreChaptersForManga(backupManga, chapters)
if (/* SY --> */ source !is MergedSource && /* SY <-- */ !backupManager.restoreChaptersForManga(backupManga, chapters)) {
updateChapters(source, backupManga, chapters)
}
} else {
backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
}
restoreExtraForManga(backupManga, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata) restoreExtraForManga(backupManga, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata)
updateTracking(backupManga, tracks)
} }
private suspend fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>, mergedMangaReferences: List<BackupMergedMangaReference>, flatMetadata: BackupFlatMetadata?) { private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>, mergedMangaReferences: List<BackupMergedMangaReference>, flatMetadata: BackupFlatMetadata?) {
// Restore categories // Restore categories
backupManager.restoreCategoriesForManga(manga, categories, backupCategories) backupManager.restoreCategoriesForManga(manga, categories, backupCategories)

View File

@ -15,7 +15,6 @@ import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupConst import eu.kanade.tachiyomi.data.backup.BackupConst
@ -206,31 +205,15 @@ class SettingsBackupController : SettingsController() {
val fileName = DocumentFile.fromSingleUri(activity, uri)?.name ?: uri.toString() val fileName = DocumentFile.fromSingleUri(activity, uri)?.name ?: uri.toString()
when { when {
fileName.endsWith(".proto.gz") -> { fileName.endsWith(".proto.gz") -> {
val options = arrayOf( RestoreBackupDialog(
R.string.full_restore_offline, uri,
R.string.full_restore_online BackupConst.BACKUP_TYPE_FULL
) ).showDialog(router)
.map { activity.getString(it) }
MaterialDialog(activity)
.title(R.string.full_restore_mode)
.listItemsSingleChoice(
items = options,
initialSelection = 0
) { _, index, _ ->
RestoreBackupDialog(
uri,
BackupConst.BACKUP_TYPE_FULL,
isOnline = index != 0
).showDialog(router)
}
.positiveButton(R.string.action_restore)
.show()
} }
fileName.endsWith(".json") -> { fileName.endsWith(".json") -> {
RestoreBackupDialog( RestoreBackupDialog(
uri, uri,
BackupConst.BACKUP_TYPE_LEGACY, BackupConst.BACKUP_TYPE_LEGACY
isOnline = true
).showDialog(router) ).showDialog(router)
} }
else -> { else -> {
@ -326,11 +309,10 @@ class SettingsBackupController : SettingsController() {
} }
class RestoreBackupDialog(bundle: Bundle? = null) : DialogController(bundle) { class RestoreBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
constructor(uri: Uri, type: Int, isOnline: Boolean) : this( constructor(uri: Uri, type: Int) : this(
bundleOf( bundleOf(
KEY_URI to uri, KEY_URI to uri,
KEY_TYPE to type, KEY_TYPE to type
KEY_MODE to isOnline
) )
) )
@ -338,12 +320,19 @@ class SettingsBackupController : SettingsController() {
val activity = activity!! val activity = activity!!
val uri: Uri = args.getParcelable(KEY_URI)!! val uri: Uri = args.getParcelable(KEY_URI)!!
val type: Int = args.getInt(KEY_TYPE) val type: Int = args.getInt(KEY_TYPE)
val isOnline: Boolean = args.getBoolean(KEY_MODE, true)
return try { return try {
var message = activity.getString(R.string.backup_restore_content) var message = if (type == BackupConst.BACKUP_TYPE_FULL) {
activity.getString(R.string.backup_restore_content_full)
} else {
activity.getString(R.string.backup_restore_content)
}
val validator = if (type == BackupConst.BACKUP_TYPE_FULL) FullBackupRestoreValidator() else LegacyBackupRestoreValidator() val validator = if (type == BackupConst.BACKUP_TYPE_FULL) {
FullBackupRestoreValidator()
} else {
LegacyBackupRestoreValidator()
}
val results = validator.validate(activity, uri) val results = validator.validate(activity, uri)
if (results.missingSources.isNotEmpty()) { if (results.missingSources.isNotEmpty()) {
@ -357,7 +346,7 @@ class SettingsBackupController : SettingsController() {
.title(R.string.pref_restore_backup) .title(R.string.pref_restore_backup)
.message(text = message) .message(text = message)
.positiveButton(R.string.action_restore) { .positiveButton(R.string.action_restore) {
BackupRestoreService.start(activity, uri, type, isOnline) BackupRestoreService.start(activity, uri, type)
} }
} catch (e: Exception) { } catch (e: Exception) {
MaterialDialog(activity) MaterialDialog(activity)
@ -370,7 +359,6 @@ class SettingsBackupController : SettingsController() {
private companion object { private companion object {
const val KEY_URI = "RestoreBackupDialog.uri" const val KEY_URI = "RestoreBackupDialog.uri"
const val KEY_TYPE = "RestoreBackupDialog.type" const val KEY_TYPE = "RestoreBackupDialog.type"
const val KEY_MODE = "RestoreBackupDialog.mode"
} }
} }

View File

@ -381,9 +381,6 @@
<string name="pref_backup_service_category">Automatic backups</string> <string name="pref_backup_service_category">Automatic backups</string>
<string name="pref_backup_interval">Backup frequency</string> <string name="pref_backup_interval">Backup frequency</string>
<string name="pref_backup_slots">Maximum backups</string> <string name="pref_backup_slots">Maximum backups</string>
<string name="full_restore_mode">Network Mode</string>
<string name="full_restore_online">Restore online, much slower but gives you more updated info and chapters</string>
<string name="full_restore_offline">Restore offline, finishes quickly but contains only what your backup has</string>
<string name="source_not_found_name">Source not found: %1$s</string> <string name="source_not_found_name">Source not found: %1$s</string>
<string name="tracker_not_logged_in">Not logged in: %1$s</string> <string name="tracker_not_logged_in">Not logged in: %1$s</string>
<string name="backup_created">Backup created</string> <string name="backup_created">Backup created</string>
@ -394,6 +391,7 @@
<string name="backup_restore_missing_sources">Missing sources:</string> <string name="backup_restore_missing_sources">Missing sources:</string>
<string name="backup_restore_missing_trackers">Trackers not logged into:</string> <string name="backup_restore_missing_trackers">Trackers not logged into:</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="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_restore_content_full">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.</string>
<string name="restore_completed">Restore completed</string> <string name="restore_completed">Restore completed</string>
<string name="restore_duration">%02d min, %02d sec</string> <string name="restore_duration">%02d min, %02d sec</string>
<plurals name="restore_completed_message"> <plurals name="restore_completed_message">