|  |  |  | @@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.data.library.LibraryUpdateJob | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.source.model.copyFrom | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.source.sourcePreferences | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.util.BackupUtil | 
		
	
		
			
				|  |  |  |  | import eu.kanade.tachiyomi.util.system.createFileInCacheDir | 
		
	
	
		
			
				
					
					|  |  |  | @@ -27,7 +26,6 @@ import tachiyomi.core.preference.AndroidPreferenceStore | 
		
	
		
			
				|  |  |  |  | import tachiyomi.core.preference.PreferenceStore | 
		
	
		
			
				|  |  |  |  | import tachiyomi.data.DatabaseHandler | 
		
	
		
			
				|  |  |  |  | import tachiyomi.data.Manga_sync | 
		
	
		
			
				|  |  |  |  | import tachiyomi.data.Mangas | 
		
	
		
			
				|  |  |  |  | import tachiyomi.data.UpdateStrategyColumnAdapter | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.category.interactor.GetCategories | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId | 
		
	
	
		
			
				
					
					|  |  |  | @@ -35,6 +33,8 @@ import tachiyomi.domain.chapter.model.Chapter | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.history.model.HistoryUpdate | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.library.service.LibraryPreferences | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.manga.interactor.FetchInterval | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.manga.interactor.GetManga | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.manga.model.Manga | 
		
	
		
			
				|  |  |  |  | import tachiyomi.domain.track.model.Track | 
		
	
		
			
				|  |  |  |  | import tachiyomi.i18n.MR | 
		
	
	
		
			
				
					
					|  |  |  | @@ -50,23 +50,25 @@ import kotlin.math.max | 
		
	
		
			
				|  |  |  |  | class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |     private val context: Context, | 
		
	
		
			
				|  |  |  |  |     private val notifier: BackupNotifier, | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private val handler: DatabaseHandler = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val getCategories: GetCategories = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val getManga: GetManga = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val updateManga: UpdateManga = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val fetchInterval: FetchInterval = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private val preferenceStore: PreferenceStore = Injekt.get(), | 
		
	
		
			
				|  |  |  |  |     private val libraryPreferences: LibraryPreferences = Injekt.get(), | 
		
	
		
			
				|  |  |  |  | ) { | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private val handler: DatabaseHandler = Injekt.get() | 
		
	
		
			
				|  |  |  |  |     private val updateManga: UpdateManga = Injekt.get() | 
		
	
		
			
				|  |  |  |  |     private val getCategories: GetCategories = Injekt.get() | 
		
	
		
			
				|  |  |  |  |     private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get() | 
		
	
		
			
				|  |  |  |  |     private val fetchInterval: FetchInterval = Injekt.get() | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private val preferenceStore: PreferenceStore = Injekt.get() | 
		
	
		
			
				|  |  |  |  |     private val libraryPreferences: LibraryPreferences = Injekt.get() | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private var now = ZonedDateTime.now() | 
		
	
		
			
				|  |  |  |  |     private var currentFetchWindow = fetchInterval.getWindow(now) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private var restoreAmount = 0 | 
		
	
		
			
				|  |  |  |  |     private var restoreProgress = 0 | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private var now = ZonedDateTime.now() | 
		
	
		
			
				|  |  |  |  |     private var currentFetchWindow = fetchInterval.getWindow(now) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Mapping of source ID to source name from backup data | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
	
		
			
				
					
					|  |  |  | @@ -76,27 +78,22 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     suspend fun syncFromBackup(uri: Uri, sync: Boolean) { | 
		
	
		
			
				|  |  |  |  |         val startTime = System.currentTimeMillis() | 
		
	
		
			
				|  |  |  |  |         restoreProgress = 0 | 
		
	
		
			
				|  |  |  |  |         errors.clear() | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         performRestore(uri, sync) | 
		
	
		
			
				|  |  |  |  |         prepareState() | 
		
	
		
			
				|  |  |  |  |         restoreFromFile(uri, sync) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         val endTime = System.currentTimeMillis() | 
		
	
		
			
				|  |  |  |  |         val time = endTime - startTime | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         val logFile = writeErrorLog() | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if (sync) { | 
		
	
		
			
				|  |  |  |  |             notifier.showRestoreComplete( | 
		
	
		
			
				|  |  |  |  |                 time, | 
		
	
		
			
				|  |  |  |  |                 errors.size, | 
		
	
		
			
				|  |  |  |  |                 logFile.parent, | 
		
	
		
			
				|  |  |  |  |                 logFile.name, | 
		
	
		
			
				|  |  |  |  |                 contentTitle = context.stringResource(MR.strings.library_sync_complete), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreComplete( | 
		
	
		
			
				|  |  |  |  |             time, | 
		
	
		
			
				|  |  |  |  |             errors.size, | 
		
	
		
			
				|  |  |  |  |             logFile.parent, | 
		
	
		
			
				|  |  |  |  |             logFile.name, | 
		
	
		
			
				|  |  |  |  |             sync, | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private fun writeErrorLog(): File { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -118,7 +115,12 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         return File("") | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun performRestore(uri: Uri, sync: Boolean) { | 
		
	
		
			
				|  |  |  |  |     private fun prepareState() { | 
		
	
		
			
				|  |  |  |  |         now = ZonedDateTime.now() | 
		
	
		
			
				|  |  |  |  |         currentFetchWindow = fetchInterval.getWindow(now) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreFromFile(uri: Uri, sync: Boolean) { | 
		
	
		
			
				|  |  |  |  |         val backup = BackupUtil.decodeBackup(context, uri) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         restoreAmount = backup.backupManga.size + 3 // +3 for categories, app prefs, source prefs | 
		
	
	
		
			
				
					
					|  |  |  | @@ -126,8 +128,6 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         // Store source mapping for error messages | 
		
	
		
			
				|  |  |  |  |         val backupMaps = backup.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources | 
		
	
		
			
				|  |  |  |  |         sourceMapping = backupMaps.associate { it.sourceId to it.name } | 
		
	
		
			
				|  |  |  |  |         now = ZonedDateTime.now() | 
		
	
		
			
				|  |  |  |  |         currentFetchWindow = fetchInterval.getWindow(now) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         coroutineScope { | 
		
	
		
			
				|  |  |  |  |             ensureActive() | 
		
	
	
		
			
				
					
					|  |  |  | @@ -139,16 +139,27 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |             ensureActive() | 
		
	
		
			
				|  |  |  |  |             restoreSourcePreferences(backup.backupSourcePreferences) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             // Restore individual manga | 
		
	
		
			
				|  |  |  |  |             backup.backupManga.forEach { | 
		
	
		
			
				|  |  |  |  |                 ensureActive() | 
		
	
		
			
				|  |  |  |  |                 restoreManga(it, backup.backupCategories, sync) | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             backup.backupManga.sortByNew() | 
		
	
		
			
				|  |  |  |  |                 .forEach { | 
		
	
		
			
				|  |  |  |  |                     ensureActive() | 
		
	
		
			
				|  |  |  |  |                     restoreManga(it, backup.backupCategories, sync) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             // TODO: optionally trigger online library + tracker update | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun List<BackupManga>.sortByNew(): List<BackupManga> { | 
		
	
		
			
				|  |  |  |  |         val urlsBySource = handler.awaitList { mangasQueries.getAllMangaSourceAndUrl() } | 
		
	
		
			
				|  |  |  |  |             .groupBy({ it.source }, { it.url }) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         return this | 
		
	
		
			
				|  |  |  |  |             .sortedWith( | 
		
	
		
			
				|  |  |  |  |                 compareBy<BackupManga> { it.url in urlsBySource[it.source].orEmpty() } | 
		
	
		
			
				|  |  |  |  |                     .then(compareByDescending { it.lastModifiedAt }), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreCategories(backupCategories: List<BackupCategory>) { | 
		
	
		
			
				|  |  |  |  |         if (backupCategories.isNotEmpty()) { | 
		
	
		
			
				|  |  |  |  |             val dbCategories = getCategories.await() | 
		
	
	
		
			
				
					
					|  |  |  | @@ -170,75 +181,72 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         restoreProgress += 1 | 
		
	
		
			
				|  |  |  |  |         showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.categories), | 
		
	
		
			
				|  |  |  |  |             restoreProgress, | 
		
	
		
			
				|  |  |  |  |             restoreAmount, | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.categories), | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.restoring_backup), | 
		
	
		
			
				|  |  |  |  |             false, | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, sync: Boolean) { | 
		
	
		
			
				|  |  |  |  |         val manga = backupManga.getMangaImpl() | 
		
	
		
			
				|  |  |  |  |         val chapters = backupManga.getChaptersImpl() | 
		
	
		
			
				|  |  |  |  |         val categories = backupManga.categories.map { it.toInt() } | 
		
	
		
			
				|  |  |  |  |         val history = | 
		
	
		
			
				|  |  |  |  |             backupManga.brokenHistory.map { BackupHistory(it.url, it.lastRead, it.readDuration) } + backupManga.history | 
		
	
		
			
				|  |  |  |  |         val tracks = backupManga.getTrackingImpl() | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreManga( | 
		
	
		
			
				|  |  |  |  |         backupManga: BackupManga, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |         sync: Boolean, | 
		
	
		
			
				|  |  |  |  |     ) { | 
		
	
		
			
				|  |  |  |  |         try { | 
		
	
		
			
				|  |  |  |  |             val dbManga = getMangaFromDatabase(manga.url, manga.source) | 
		
	
		
			
				|  |  |  |  |             val dbManga = findExistingManga(backupManga) | 
		
	
		
			
				|  |  |  |  |             val manga = backupManga.getMangaImpl() | 
		
	
		
			
				|  |  |  |  |             val restoredManga = if (dbManga == null) { | 
		
	
		
			
				|  |  |  |  |                 // Manga not in database | 
		
	
		
			
				|  |  |  |  |                 restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories) | 
		
	
		
			
				|  |  |  |  |                 restoreNewManga(manga) | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 // Manga in database | 
		
	
		
			
				|  |  |  |  |                 // Copy information from manga already in database | 
		
	
		
			
				|  |  |  |  |                 val updatedManga = restoreExistingManga(manga, dbManga) | 
		
	
		
			
				|  |  |  |  |                 // Fetch rest of manga information | 
		
	
		
			
				|  |  |  |  |                 restoreNewManga(updatedManga, chapters, categories, history, tracks, backupCategories) | 
		
	
		
			
				|  |  |  |  |                 restoreExistingManga(manga, dbManga) | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             updateManga.awaitUpdateFetchInterval(restoredManga, now, currentFetchWindow) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             restoreMangaDetails( | 
		
	
		
			
				|  |  |  |  |                 manga = restoredManga, | 
		
	
		
			
				|  |  |  |  |                 chapters = backupManga.getChaptersImpl(), | 
		
	
		
			
				|  |  |  |  |                 categories = backupManga.categories, | 
		
	
		
			
				|  |  |  |  |                 backupCategories = backupCategories, | 
		
	
		
			
				|  |  |  |  |                 history = backupManga.brokenHistory.map { BackupHistory(it.url, it.lastRead, it.readDuration) } + | 
		
	
		
			
				|  |  |  |  |                     backupManga.history, | 
		
	
		
			
				|  |  |  |  |                 tracks = backupManga.getTrackingImpl(), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |         } catch (e: Exception) { | 
		
	
		
			
				|  |  |  |  |             val sourceName = sourceMapping[manga.source] ?: manga.source.toString() | 
		
	
		
			
				|  |  |  |  |             errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}") | 
		
	
		
			
				|  |  |  |  |             val sourceName = sourceMapping[backupManga.source] ?: backupManga.source.toString() | 
		
	
		
			
				|  |  |  |  |             errors.add(Date() to "${backupManga.title} [$sourceName]: ${e.message}") | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         restoreProgress += 1 | 
		
	
		
			
				|  |  |  |  |         if (sync) { | 
		
	
		
			
				|  |  |  |  |             showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |                 restoreProgress, | 
		
	
		
			
				|  |  |  |  |                 restoreAmount, | 
		
	
		
			
				|  |  |  |  |                 manga.title, | 
		
	
		
			
				|  |  |  |  |                 context.stringResource(MR.strings.syncing_library), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreProgress(backupManga.title, restoreProgress, restoreAmount, sync) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun findExistingManga(backupManga: BackupManga): Manga? { | 
		
	
		
			
				|  |  |  |  |         return getMangaByUrlAndSourceId.await(backupManga.url, backupManga.source) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreExistingManga(manga: Manga, dbManga: Manga): Manga { | 
		
	
		
			
				|  |  |  |  |         return if (manga.lastModifiedAt > dbManga.lastModifiedAt) { | 
		
	
		
			
				|  |  |  |  |             updateManga(dbManga.copyFrom(manga).copy(id = dbManga.id)) | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |                 restoreProgress, | 
		
	
		
			
				|  |  |  |  |                 restoreAmount, | 
		
	
		
			
				|  |  |  |  |                 manga.title, | 
		
	
		
			
				|  |  |  |  |                 context.stringResource(MR.strings.restoring_backup), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |             updateManga(manga.copyFrom(dbManga).copy(id = dbManga.id)) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Returns manga | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
		
			
				|  |  |  |  |      * @return [Manga], null if not found | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun getMangaFromDatabase(url: String, source: Long): Mangas? { | 
		
	
		
			
				|  |  |  |  |         return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, source) } | 
		
	
		
			
				|  |  |  |  |     private fun Manga.copyFrom(newer: Manga): Manga { | 
		
	
		
			
				|  |  |  |  |         return this.copy( | 
		
	
		
			
				|  |  |  |  |             favorite = this.favorite || newer.favorite, | 
		
	
		
			
				|  |  |  |  |             author = newer.author, | 
		
	
		
			
				|  |  |  |  |             artist = newer.artist, | 
		
	
		
			
				|  |  |  |  |             description = newer.description, | 
		
	
		
			
				|  |  |  |  |             genre = newer.genre, | 
		
	
		
			
				|  |  |  |  |             thumbnailUrl = newer.thumbnailUrl, | 
		
	
		
			
				|  |  |  |  |             status = newer.status, | 
		
	
		
			
				|  |  |  |  |             initialized = this.initialized || newer.initialized, | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas): Manga { | 
		
	
		
			
				|  |  |  |  |         var updatedManga = manga.copy(id = dbManga._id) | 
		
	
		
			
				|  |  |  |  |         updatedManga = updatedManga.copyFrom(dbManga) | 
		
	
		
			
				|  |  |  |  |         updateManga(updatedManga) | 
		
	
		
			
				|  |  |  |  |         return updatedManga | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun updateManga(manga: Manga): Long { | 
		
	
		
			
				|  |  |  |  |     private suspend fun updateManga(manga: Manga): Manga { | 
		
	
		
			
				|  |  |  |  |         handler.await(true) { | 
		
	
		
			
				|  |  |  |  |             mangasQueries.update( | 
		
	
		
			
				|  |  |  |  |                 source = manga.source, | 
		
	
	
		
			
				
					
					|  |  |  | @@ -263,28 +271,16 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |                 updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode), | 
		
	
		
			
				|  |  |  |  |             ) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return manga.id | 
		
	
		
			
				|  |  |  |  |         return manga | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Fetches manga information | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
		
			
				|  |  |  |  |      * @param manga manga that needs updating | 
		
	
		
			
				|  |  |  |  |      * @param chapters chapters of manga that needs updating | 
		
	
		
			
				|  |  |  |  |      * @param categories categories that need updating | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreExistingManga( | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreNewManga( | 
		
	
		
			
				|  |  |  |  |         manga: Manga, | 
		
	
		
			
				|  |  |  |  |         chapters: List<Chapter>, | 
		
	
		
			
				|  |  |  |  |         categories: List<Int>, | 
		
	
		
			
				|  |  |  |  |         history: List<BackupHistory>, | 
		
	
		
			
				|  |  |  |  |         tracks: List<Track>, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |     ): Manga { | 
		
	
		
			
				|  |  |  |  |         val fetchedManga = restoreNewManga(manga) | 
		
	
		
			
				|  |  |  |  |         restoreChapters(fetchedManga, chapters) | 
		
	
		
			
				|  |  |  |  |         restoreExtras(fetchedManga, categories, history, tracks, backupCategories) | 
		
	
		
			
				|  |  |  |  |         return fetchedManga | 
		
	
		
			
				|  |  |  |  |         return manga.copy( | 
		
	
		
			
				|  |  |  |  |             initialized = manga.description != null, | 
		
	
		
			
				|  |  |  |  |             id = insertManga(manga), | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreChapters(manga: Manga, chapters: List<Chapter>) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -318,13 +314,10 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         val (existingChapters, newChapters) = processed.partition { it.id > 0 } | 
		
	
		
			
				|  |  |  |  |         updateKnownChapters(existingChapters) | 
		
	
		
			
				|  |  |  |  |         insertChapters(newChapters) | 
		
	
		
			
				|  |  |  |  |         updateKnownChapters(existingChapters) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Inserts list of chapters | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun insertChapters(chapters: List<Chapter>) { | 
		
	
		
			
				|  |  |  |  |         handler.await(true) { | 
		
	
		
			
				|  |  |  |  |             chapters.forEach { chapter -> | 
		
	
	
		
			
				
					
					|  |  |  | @@ -345,9 +338,6 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Updates a list of chapters with known database ids | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun updateKnownChapters(chapters: List<Chapter>) { | 
		
	
		
			
				|  |  |  |  |         handler.await(true) { | 
		
	
		
			
				|  |  |  |  |             chapters.forEach { chapter -> | 
		
	
	
		
			
				
					
					|  |  |  | @@ -369,19 +359,6 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Fetches manga information | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
		
			
				|  |  |  |  |      * @param manga manga that needs updating | 
		
	
		
			
				|  |  |  |  |      * @return Updated manga info. | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreNewManga(manga: Manga): Manga { | 
		
	
		
			
				|  |  |  |  |         return manga.copy( | 
		
	
		
			
				|  |  |  |  |             initialized = manga.description != null, | 
		
	
		
			
				|  |  |  |  |             id = insertManga(manga), | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Inserts manga and returns id | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
	
		
			
				
					
					|  |  |  | @@ -414,29 +391,20 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreNewManga( | 
		
	
		
			
				|  |  |  |  |         backupManga: Manga, | 
		
	
		
			
				|  |  |  |  |         chapters: List<Chapter>, | 
		
	
		
			
				|  |  |  |  |         categories: List<Int>, | 
		
	
		
			
				|  |  |  |  |         history: List<BackupHistory>, | 
		
	
		
			
				|  |  |  |  |         tracks: List<Track>, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |     ): Manga { | 
		
	
		
			
				|  |  |  |  |         restoreChapters(backupManga, chapters) | 
		
	
		
			
				|  |  |  |  |         restoreExtras(backupManga, categories, history, tracks, backupCategories) | 
		
	
		
			
				|  |  |  |  |         return backupManga | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreExtras( | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreMangaDetails( | 
		
	
		
			
				|  |  |  |  |         manga: Manga, | 
		
	
		
			
				|  |  |  |  |         categories: List<Int>, | 
		
	
		
			
				|  |  |  |  |         chapters: List<Chapter>, | 
		
	
		
			
				|  |  |  |  |         categories: List<Long>, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |         history: List<BackupHistory>, | 
		
	
		
			
				|  |  |  |  |         tracks: List<Track>, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |     ) { | 
		
	
		
			
				|  |  |  |  |     ): Manga { | 
		
	
		
			
				|  |  |  |  |         restoreChapters(manga, chapters) | 
		
	
		
			
				|  |  |  |  |         restoreCategories(manga, categories, backupCategories) | 
		
	
		
			
				|  |  |  |  |         restoreHistory(history) | 
		
	
		
			
				|  |  |  |  |         restoreTracking(manga, tracks) | 
		
	
		
			
				|  |  |  |  |         updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow) | 
		
	
		
			
				|  |  |  |  |         return manga | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
	
		
			
				
					
					|  |  |  | @@ -445,23 +413,24 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |      * @param manga the manga whose categories have to be restored. | 
		
	
		
			
				|  |  |  |  |      * @param categories the categories to restore. | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) { | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreCategories( | 
		
	
		
			
				|  |  |  |  |         manga: Manga, | 
		
	
		
			
				|  |  |  |  |         categories: List<Long>, | 
		
	
		
			
				|  |  |  |  |         backupCategories: List<BackupCategory>, | 
		
	
		
			
				|  |  |  |  |     ) { | 
		
	
		
			
				|  |  |  |  |         val dbCategories = getCategories.await() | 
		
	
		
			
				|  |  |  |  |         val mangaCategoriesToUpdate = mutableListOf<Pair<Long, Long>>() | 
		
	
		
			
				|  |  |  |  |         val dbCategoriesByName = dbCategories.associateBy { it.name } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         categories.forEach { backupCategoryOrder -> | 
		
	
		
			
				|  |  |  |  |             backupCategories.firstOrNull { | 
		
	
		
			
				|  |  |  |  |                 it.order == backupCategoryOrder.toLong() | 
		
	
		
			
				|  |  |  |  |             }?.let { backupCategory -> | 
		
	
		
			
				|  |  |  |  |                 dbCategories.firstOrNull { dbCategory -> | 
		
	
		
			
				|  |  |  |  |                     dbCategory.name == backupCategory.name | 
		
	
		
			
				|  |  |  |  |                 }?.let { dbCategory -> | 
		
	
		
			
				|  |  |  |  |                     mangaCategoriesToUpdate.add(Pair(manga.id, dbCategory.id)) | 
		
	
		
			
				|  |  |  |  |         val backupCategoriesByOrder = backupCategories.associateBy { it.order } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         val mangaCategoriesToUpdate = categories.mapNotNull { backupCategoryOrder -> | 
		
	
		
			
				|  |  |  |  |             backupCategoriesByOrder[backupCategoryOrder]?.let { backupCategory -> | 
		
	
		
			
				|  |  |  |  |                 dbCategoriesByName[backupCategory.name]?.let { dbCategory -> | 
		
	
		
			
				|  |  |  |  |                     Pair(manga.id, dbCategory.id) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Update database | 
		
	
		
			
				|  |  |  |  |         if (mangaCategoriesToUpdate.isNotEmpty()) { | 
		
	
		
			
				|  |  |  |  |             handler.await(true) { | 
		
	
		
			
				|  |  |  |  |                 mangas_categoriesQueries.deleteMangaCategoryByMangaId(manga.id) | 
		
	
	
		
			
				
					
					|  |  |  | @@ -472,11 +441,6 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Restore history from Json | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
		
			
				|  |  |  |  |      * @param history list containing history to be restored | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreHistory(history: List<BackupHistory>) { | 
		
	
		
			
				|  |  |  |  |         // List containing history to be updated | 
		
	
		
			
				|  |  |  |  |         val toUpdate = mutableListOf<HistoryUpdate>() | 
		
	
	
		
			
				
					
					|  |  |  | @@ -496,7 +460,7 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |                     ), | 
		
	
		
			
				|  |  |  |  |                 ) | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 // If not in database create | 
		
	
		
			
				|  |  |  |  |                 // If not in database, create | 
		
	
		
			
				|  |  |  |  |                 handler | 
		
	
		
			
				|  |  |  |  |                     .awaitOneOrNull { chaptersQueries.getChapterByUrl(url) } | 
		
	
		
			
				|  |  |  |  |                     ?.let { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -521,12 +485,6 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /** | 
		
	
		
			
				|  |  |  |  |      * Restores the sync of a manga. | 
		
	
		
			
				|  |  |  |  |      * | 
		
	
		
			
				|  |  |  |  |      * @param manga the manga whose sync have to be restored. | 
		
	
		
			
				|  |  |  |  |      * @param tracks the track list to restore. | 
		
	
		
			
				|  |  |  |  |      */ | 
		
	
		
			
				|  |  |  |  |     private suspend fun restoreTracking(manga: Manga, tracks: List<Track>) { | 
		
	
		
			
				|  |  |  |  |         // Get tracks from database | 
		
	
		
			
				|  |  |  |  |         val dbTracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id) } | 
		
	
	
		
			
				
					
					|  |  |  | @@ -611,11 +569,11 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         BackupCreateJob.setupTask(context) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         restoreProgress += 1 | 
		
	
		
			
				|  |  |  |  |         showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.app_settings), | 
		
	
		
			
				|  |  |  |  |             restoreProgress, | 
		
	
		
			
				|  |  |  |  |             restoreAmount, | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.app_settings), | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.restoring_backup), | 
		
	
		
			
				|  |  |  |  |             false, | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -626,11 +584,11 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         restoreProgress += 1 | 
		
	
		
			
				|  |  |  |  |         showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreProgress( | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.source_settings), | 
		
	
		
			
				|  |  |  |  |             restoreProgress, | 
		
	
		
			
				|  |  |  |  |             restoreAmount, | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.source_settings), | 
		
	
		
			
				|  |  |  |  |             context.stringResource(MR.strings.restoring_backup), | 
		
	
		
			
				|  |  |  |  |             false, | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -674,8 +632,4 @@ class BackupRestorer( | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private fun showRestoreProgress(progress: Int, amount: Int, title: String, contentTitle: String) { | 
		
	
		
			
				|  |  |  |  |         notifier.showRestoreProgress(title, contentTitle, progress, amount) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					
					| 
							
							
							
						 |  |  |   |