Remove online protobuf backup restore option

This commit is contained in:
arkon 2021-03-13 18:45:22 -05:00
parent 112e233498
commit 94f5117941
5 changed files with 63 additions and 158 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

@ -26,9 +26,6 @@ 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 kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
@ -183,24 +180,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) { 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)
}
} }
} }
@ -309,29 +295,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
@ -340,47 +323,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 ->

View File

@ -12,13 +12,12 @@ 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 okio.buffer import okio.buffer
import okio.gzip 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 {
backupManager = FullBackupManager(context) backupManager = FullBackupManager(context)
@ -42,7 +41,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
@ -57,7 +56,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
} }
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
@ -68,8 +67,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, online) restoreMangaData(manga, chapters, categories, history, tracks, backupCategories)
} 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)}")
} }
@ -85,33 +84,30 @@ 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>
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, online) restoreMangaFetch(manga, chapters, categories, history, tracks, backupCategories)
} 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, online) restoreMangaNoFetch(manga, chapters, categories, history, tracks, backupCategories)
} }
} }
} }
@ -123,55 +119,37 @@ 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>,
history: List<BackupHistory>, history: List<BackupHistory>,
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>
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)
updateChapters(source, fetchedManga, chapters)
} else {
backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters)
}
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories) restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories)
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>,
history: List<BackupHistory>, history: List<BackupHistory>,
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>
online: Boolean
) { ) {
if (online && source != null) { backupManager.restoreChaptersForManga(backupManga, chapters)
if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
updateChapters(source, backupManga, chapters)
}
} else {
backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
}
restoreExtraForManga(backupManga, categories, history, tracks, backupCategories) restoreExtraForManga(backupManga, categories, history, tracks, backupCategories)
updateTracking(backupManga, tracks)
} }
private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) {

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

@ -377,9 +377,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>
@ -390,6 +387,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">