Make backup restoring logic more sequential

This commit is contained in:
arkon 2021-02-12 12:19:12 -05:00
parent 41d7cee020
commit aded11e599
5 changed files with 60 additions and 62 deletions

View file

@ -37,9 +37,9 @@ abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val co
protected val errors = mutableListOf<Pair<Date, String>>() protected val errors = mutableListOf<Pair<Date, String>>()
abstract fun performRestore(uri: Uri): Boolean abstract suspend fun performRestore(uri: Uri): Boolean
fun restoreBackup(uri: Uri): Boolean { suspend fun restoreBackup(uri: Uri): Boolean {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
restoreProgress = 0 restoreProgress = 0
errors.clear() errors.clear()

View file

@ -14,7 +14,10 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.acquireWakeLock import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.isServiceRunning
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -68,12 +71,14 @@ class BackupRestoreService : Service() {
*/ */
private lateinit var wakeLock: PowerManager.WakeLock private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var ioScope: CoroutineScope
private var backupRestore: AbstractBackupRestore<*>? = null private var backupRestore: AbstractBackupRestore<*>? = null
private lateinit var notifier: BackupNotifier private lateinit var notifier: BackupNotifier
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
notifier = BackupNotifier(this) notifier = BackupNotifier(this)
wakeLock = acquireWakeLock(javaClass.name) wakeLock = acquireWakeLock(javaClass.name)
@ -92,6 +97,7 @@ class BackupRestoreService : Service() {
private fun destroyJob() { private fun destroyJob() {
backupRestore?.job?.cancel() backupRestore?.job?.cancel()
ioScope?.cancel()
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
wakeLock.release() wakeLock.release()
} }
@ -122,6 +128,7 @@ class BackupRestoreService : Service() {
BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier, online) BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier, online)
else -> LegacyBackupRestore(this, notifier) else -> LegacyBackupRestore(this, notifier)
} }
val handler = CoroutineExceptionHandler { _, exception -> val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception) Timber.e(exception)
backupRestore?.writeErrorLog() backupRestore?.writeErrorLog()
@ -129,14 +136,15 @@ class BackupRestoreService : Service() {
notifier.showRestoreError(exception.message) notifier.showRestoreError(exception.message)
stopSelf(startId) stopSelf(startId)
} }
backupRestore?.job = GlobalScope.launch(handler) { val job = ioScope.launch(handler) {
if (backupRestore?.restoreBackup(uri) == false) { if (backupRestore?.restoreBackup(uri) == false) {
notifier.showRestoreError(getString(R.string.restoring_backup_canceled)) notifier.showRestoreError(getString(R.string.restoring_backup_canceled))
} }
} }
backupRestore?.job?.invokeOnCompletion { job.invokeOnCompletion {
stopSelf(startId) stopSelf(startId)
} }
backupRestore?.job = job
return START_NOT_STICKY return START_NOT_STICKY
} }

View file

@ -13,7 +13,6 @@ 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.Source
import eu.kanade.tachiyomi.util.lang.launchIO
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
import okio.source import okio.source
@ -21,7 +20,7 @@ import java.util.Date
class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) { class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) {
override fun performRestore(uri: Uri): Boolean { override suspend fun performRestore(uri: Uri): Boolean {
backupManager = FullBackupManager(context) backupManager = FullBackupManager(context)
val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() } val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() }
@ -58,7 +57,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 fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, online: Boolean) { private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, online: Boolean) {
val manga = backupManga.getMangaImpl() val manga = backupManga.getMangaImpl()
val chapters = backupManga.getChaptersImpl() val chapters = backupManga.getChaptersImpl()
val categories = backupManga.categories val categories = backupManga.categories
@ -92,7 +91,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
* @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 fun restoreMangaData( private suspend fun restoreMangaData(
manga: Manga, manga: Manga,
source: Source?, source: Source?,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -124,7 +123,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 fun restoreMangaFetch( private suspend fun restoreMangaFetch(
source: Source?, source: Source?,
manga: Manga, manga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -134,27 +133,25 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
launchIO { try {
try { val fetchedManga = backupManager.restoreMangaFetch(source, manga, online)
val fetchedManga = backupManager.restoreMangaFetch(source, manga, online) fetchedManga.id ?: return
fetchedManga.id ?: (return@launchIO)
if (online && source != null) { if (online && source != null) {
updateChapters(source, fetchedManga, chapters) updateChapters(source, fetchedManga, chapters)
} else { } else {
backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters) backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters)
}
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories)
updateTracking(fetchedManga, tracks)
} catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}")
} }
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories)
updateTracking(fetchedManga, tracks)
} catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}")
} }
} }
private fun restoreMangaNoFetch( private suspend fun restoreMangaNoFetch(
source: Source?, source: Source?,
backupManga: Manga, backupManga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -164,19 +161,17 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
launchIO { if (online && source != null) {
if (online && source != null) { if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { updateChapters(source, backupManga, chapters)
updateChapters(source, backupManga, chapters)
}
} else {
backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
} }
} else {
restoreExtraForManga(backupManga, categories, history, tracks, backupCategories) backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
updateTracking(backupManga, tracks)
} }
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

@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.launchIO
import java.util.Date import java.util.Date
class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) { class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) {
override fun performRestore(uri: Uri): Boolean { override suspend fun performRestore(uri: Uri): Boolean {
val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader()) val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader())
val json = JsonParser.parseReader(reader).asJsonObject val json = JsonParser.parseReader(reader).asJsonObject
@ -63,7 +62,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
} }
private fun restoreManga(mangaJson: JsonObject) { private suspend fun restoreManga(mangaJson: JsonObject) {
val manga = backupManager.parser.fromJson<MangaImpl>( val manga = backupManager.parser.fromJson<MangaImpl>(
mangaJson.get( mangaJson.get(
Backup.MANGA Backup.MANGA
@ -113,7 +112,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
* @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 fun restoreMangaData( private suspend fun restoreMangaData(
manga: Manga, manga: Manga,
source: Source, source: Source,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -143,7 +142,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
* @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 fun restoreMangaFetch( private suspend fun restoreMangaFetch(
source: Source, source: Source,
manga: Manga, manga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -151,23 +150,21 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
history: List<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
launchIO { try {
try { val fetchedManga = backupManager.fetchManga(source, manga)
val fetchedManga = backupManager.fetchManga(source, manga) fetchedManga.id ?: return
fetchedManga.id ?: (return@launchIO)
updateChapters(source, fetchedManga, chapters) updateChapters(source, fetchedManga, chapters)
restoreExtraForManga(fetchedManga, categories, history, tracks) restoreExtraForManga(fetchedManga, categories, history, tracks)
updateTracking(fetchedManga, tracks) 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 fun restoreMangaNoFetch( private suspend fun restoreMangaNoFetch(
source: Source, source: Source,
backupManga: Manga, backupManga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -175,15 +172,13 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
history: List<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
launchIO { if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { updateChapters(source, backupManga, chapters)
updateChapters(source, backupManga, chapters)
}
restoreExtraForManga(backupManga, categories, history, tracks)
updateTracking(backupManga, tracks)
} }
restoreExtraForManga(backupManga, categories, history, tracks)
updateTracking(backupManga, tracks)
} }
private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) { private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) {

View file

@ -158,8 +158,8 @@ class LibraryUpdateService(
* lock. * lock.
*/ */
override fun onDestroy() { override fun onDestroy() {
ioScope?.cancel()
updateJob?.cancel() updateJob?.cancel()
ioScope?.cancel()
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
wakeLock.release() wakeLock.release()
} }