mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Move backup restoring functions from BackupManager to BackupRestorer
Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
parent
1559c317d7
commit
4e008eb8e5
4 changed files with 218 additions and 220 deletions
|
@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.data.backup
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import okio.source
|
||||
import eu.kanade.tachiyomi.util.BackupUtil
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
|
@ -24,13 +21,8 @@ class BackupFileValidator(
|
|||
* @return List of missing sources or missing trackers.
|
||||
*/
|
||||
fun validate(context: Context, uri: Uri): Results {
|
||||
val backupManager = BackupManager(context)
|
||||
|
||||
val backup = try {
|
||||
val backupString =
|
||||
context.contentResolver.openInputStream(uri)!!.source().gzip().buffer()
|
||||
.use { it.readByteArray() }
|
||||
backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
BackupUtil.decodeBackup(context, uri)
|
||||
} catch (e: Exception) {
|
||||
throw IllegalStateException(e)
|
||||
}
|
||||
|
|
|
@ -37,19 +37,13 @@ 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.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||
import eu.kanade.tachiyomi.data.preference.Preference
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.preferenceKey
|
||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
|
@ -60,7 +54,6 @@ import timber.log.Timber
|
|||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.math.max
|
||||
|
||||
class BackupManager(val context: Context) {
|
||||
|
||||
|
@ -68,7 +61,6 @@ class BackupManager(val context: Context) {
|
|||
val parser = ProtoBuf
|
||||
private val db: DatabaseHelper = Injekt.get()
|
||||
private val sourceManager: SourceManager = Injekt.get()
|
||||
private val trackManager: TrackManager = Injekt.get()
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
private val customMangaManager: CustomMangaManager = Injekt.get()
|
||||
|
||||
|
@ -265,185 +257,4 @@ class BackupManager(val context: Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreExistingManga(manga: Manga, dbManga: Manga) {
|
||||
manga.id = dbManga.id
|
||||
manga.copyFrom(dbManga)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches manga information
|
||||
*
|
||||
* @param manga manga that needs updating
|
||||
* @return Updated manga info.
|
||||
*/
|
||||
fun restoreNewManga(manga: Manga): Manga {
|
||||
return manga.also {
|
||||
it.initialized = it.description != null
|
||||
it.id = db.insertManga(it).executeAsBlocking().insertedId()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the categories from Json
|
||||
*
|
||||
* @param backupCategories list containing categories
|
||||
*/
|
||||
internal fun restoreCategories(backupCategories: List<BackupCategory>) {
|
||||
// Get categories from file and from db
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
// Iterate over them
|
||||
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
||||
// Used to know if the category is already in the db
|
||||
var found = false
|
||||
for (dbCategory in dbCategories) {
|
||||
// If the category is already in the db, assign the id to the file's category
|
||||
// and do nothing
|
||||
if (category.name == dbCategory.name) {
|
||||
category.id = dbCategory.id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// If the category isn't in the db, remove the id and insert a new category
|
||||
// Store the inserted id in the category
|
||||
if (!found) {
|
||||
// Let the db assign the id
|
||||
category.id = null
|
||||
val result = db.insertCategory(category).executeAsBlocking()
|
||||
category.id = result.insertedId()?.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the categories a manga is in.
|
||||
*
|
||||
* @param manga the manga whose categories have to be restored.
|
||||
* @param categories the categories to restore.
|
||||
*/
|
||||
internal fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
val mangaCategoriesToUpdate = ArrayList<MangaCategory>(categories.size)
|
||||
categories.forEach { backupCategoryOrder ->
|
||||
backupCategories.firstOrNull {
|
||||
it.order == backupCategoryOrder
|
||||
}?.let { backupCategory ->
|
||||
dbCategories.firstOrNull { dbCategory ->
|
||||
dbCategory.name == backupCategory.name
|
||||
}?.let { dbCategory ->
|
||||
mangaCategoriesToUpdate += MangaCategory.create(manga, dbCategory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update database
|
||||
if (mangaCategoriesToUpdate.isNotEmpty()) {
|
||||
db.deleteOldMangasCategories(listOf(manga)).executeAsBlocking()
|
||||
db.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore history from Json
|
||||
*
|
||||
* @param history list containing history to be restored
|
||||
*/
|
||||
internal fun restoreHistoryForManga(history: List<BackupHistory>) {
|
||||
// List containing history to be updated
|
||||
val historyToBeUpdated = ArrayList<History>(history.size)
|
||||
for ((url, lastRead, readDuration) in history) {
|
||||
val dbHistory = db.getHistoryByChapterUrl(url).executeAsBlocking()
|
||||
// Check if history already in database and update
|
||||
if (dbHistory != null) {
|
||||
dbHistory.apply {
|
||||
last_read = max(lastRead, dbHistory.last_read)
|
||||
time_read = max(readDuration, dbHistory.time_read)
|
||||
}
|
||||
historyToBeUpdated.add(dbHistory)
|
||||
} else {
|
||||
// If not in database create
|
||||
db.getChapter(url).executeAsBlocking()?.let {
|
||||
val historyToAdd = History.create(it).apply {
|
||||
last_read = lastRead
|
||||
time_read = readDuration
|
||||
}
|
||||
historyToBeUpdated.add(historyToAdd)
|
||||
}
|
||||
}
|
||||
}
|
||||
db.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the sync of a manga.
|
||||
*
|
||||
* @param manga the manga whose sync have to be restored.
|
||||
* @param tracks the track list to restore.
|
||||
*/
|
||||
internal fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
|
||||
// Fix foreign keys with the current manga id
|
||||
tracks.map { it.manga_id = manga.id!! }
|
||||
|
||||
// Get tracks from database
|
||||
val dbTracks = db.getTracks(manga).executeAsBlocking()
|
||||
val trackToUpdate = mutableListOf<Track>()
|
||||
|
||||
tracks.forEach { track ->
|
||||
var isInDatabase = false
|
||||
for (dbTrack in dbTracks) {
|
||||
if (track.sync_id == dbTrack.sync_id) {
|
||||
// The sync is already in the db, only update its fields
|
||||
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 (!isInDatabase) {
|
||||
// Insert new sync. Let the db assign the id
|
||||
track.id = null
|
||||
trackToUpdate.add(track)
|
||||
}
|
||||
}
|
||||
// Update database
|
||||
if (trackToUpdate.isNotEmpty()) {
|
||||
db.insertTracks(trackToUpdate).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
|
||||
chapters.forEach { chapter ->
|
||||
val dbChapter = dbChapters.find { it.url == chapter.url }
|
||||
if (dbChapter != null) {
|
||||
chapter.id = dbChapter.id
|
||||
chapter.copyFrom(dbChapter as SChapter)
|
||||
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
|
||||
}
|
||||
|
||||
val newChapters = chapters.groupBy { it.id != null }
|
||||
newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() }
|
||||
newChapters[false]?.let { db.insertChapters(it).executeAsBlocking() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.data.backup
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
@ -8,7 +7,6 @@ import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||
|
@ -19,18 +17,21 @@ import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
|||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||
import eu.kanade.tachiyomi.util.BackupUtil
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import okio.source
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -39,13 +40,14 @@ import java.io.File
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
||||
|
||||
private lateinit var backupManager: BackupManager
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
private val customMangaManager: CustomMangaManager by injectLazy()
|
||||
private val preferenceStore: PreferenceStore = Injekt.get()
|
||||
private val parser = ProtoBuf
|
||||
|
||||
private var restoreAmount = 0
|
||||
private var restoreProgress = 0
|
||||
|
@ -75,15 +77,10 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
return true
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
suspend fun performRestore(uri: Uri): Boolean {
|
||||
backupManager = BackupManager(context)
|
||||
private suspend fun performRestore(uri: Uri): Boolean {
|
||||
val backup = BackupUtil.decodeBackup(context, uri)
|
||||
|
||||
val stream = context.contentResolver.openInputStream(uri)
|
||||
val backupString = stream!!.source().gzip().buffer().use { it.readByteArray() }
|
||||
val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
|
||||
restoreAmount = backup.backupManga.size + 1 // +1 for categories
|
||||
restoreAmount = backup.backupManga.size + 3 // +3 for categories, app prefs, source prefs
|
||||
|
||||
// Restore categories
|
||||
if (backup.backupCategories.isNotEmpty()) {
|
||||
|
@ -113,7 +110,31 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
|
||||
private fun restoreCategories(backupCategories: List<BackupCategory>) {
|
||||
db.inTransaction {
|
||||
backupManager.restoreCategories(backupCategories)
|
||||
// Get categories from file and from db
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
// Iterate over them
|
||||
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
||||
// Used to know if the category is already in the db
|
||||
var found = false
|
||||
for (dbCategory in dbCategories) {
|
||||
// If the category is already in the db, assign the id to the file's category
|
||||
// and do nothing
|
||||
if (category.name == dbCategory.name) {
|
||||
category.id = dbCategory.id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// If the category isn't in the db, remove the id and insert a new category
|
||||
// Store the inserted id in the category
|
||||
if (!found) {
|
||||
// Let the db assign the id
|
||||
category.id = null
|
||||
val result = db.insertCategory(category).executeAsBlocking()
|
||||
category.id = result.insertedId()?.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restoreProgress += 1
|
||||
|
@ -137,7 +158,9 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
} else {
|
||||
// Manga in database
|
||||
// Copy information from manga already in database
|
||||
backupManager.restoreExistingManga(manga, dbManga)
|
||||
manga.id = dbManga.id
|
||||
manga.copyFrom(dbManga)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
// Fetch rest of manga information
|
||||
restoreNewManga(manga, chapters, categories, history, tracks, backupCategories, customManga)
|
||||
}
|
||||
|
@ -167,10 +190,13 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
backupCategories: List<BackupCategory>,
|
||||
customManga: CustomMangaManager.MangaJson?,
|
||||
) {
|
||||
val fetchedManga = backupManager.restoreNewManga(manga)
|
||||
val fetchedManga = manga.also {
|
||||
it.initialized = it.description != null
|
||||
it.id = db.insertManga(it).executeAsBlocking().insertedId()
|
||||
}
|
||||
fetchedManga.id ?: return
|
||||
|
||||
backupManager.restoreChapters(fetchedManga, chapters)
|
||||
restoreChapters(fetchedManga, chapters)
|
||||
restoreExtras(fetchedManga, categories, history, tracks, backupCategories, customManga)
|
||||
}
|
||||
|
||||
|
@ -183,10 +209,37 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
backupCategories: List<BackupCategory>,
|
||||
customManga: CustomMangaManager.MangaJson?,
|
||||
) {
|
||||
backupManager.restoreChapters(backupManga, chapters)
|
||||
restoreChapters(backupManga, chapters)
|
||||
restoreExtras(backupManga, categories, history, tracks, backupCategories, customManga)
|
||||
}
|
||||
|
||||
private fun restoreChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
|
||||
chapters.forEach { chapter ->
|
||||
val dbChapter = dbChapters.find { it.url == chapter.url }
|
||||
if (dbChapter != null) {
|
||||
chapter.id = dbChapter.id
|
||||
chapter.copyFrom(dbChapter as SChapter)
|
||||
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
|
||||
}
|
||||
|
||||
val newChapters = chapters.groupBy { it.id != null }
|
||||
newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() }
|
||||
newChapters[false]?.let { db.insertChapters(it).executeAsBlocking() }
|
||||
}
|
||||
|
||||
private fun restoreExtras(
|
||||
manga: Manga,
|
||||
categories: List<Int>,
|
||||
|
@ -195,15 +248,120 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
backupCategories: List<BackupCategory>,
|
||||
customManga: CustomMangaManager.MangaJson?,
|
||||
) {
|
||||
backupManager.restoreCategories(manga, categories, backupCategories)
|
||||
backupManager.restoreHistoryForManga(history)
|
||||
backupManager.restoreTrackForManga(manga, tracks)
|
||||
restoreCategories(manga, categories, backupCategories)
|
||||
restoreHistoryForManga(history)
|
||||
restoreTrackForManga(manga, tracks)
|
||||
customManga?.id = manga.id!!
|
||||
customManga?.let { customMangaManager.saveMangaInfo(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the categories a manga is in.
|
||||
*
|
||||
* @param manga the manga whose categories have to be restored.
|
||||
* @param categories the categories to restore.
|
||||
*/
|
||||
private fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
val mangaCategoriesToUpdate = ArrayList<MangaCategory>(categories.size)
|
||||
categories.forEach { backupCategoryOrder ->
|
||||
backupCategories.firstOrNull {
|
||||
it.order == backupCategoryOrder
|
||||
}?.let { backupCategory ->
|
||||
dbCategories.firstOrNull { dbCategory ->
|
||||
dbCategory.name == backupCategory.name
|
||||
}?.let { dbCategory ->
|
||||
mangaCategoriesToUpdate += MangaCategory.create(manga, dbCategory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update database
|
||||
if (mangaCategoriesToUpdate.isNotEmpty()) {
|
||||
db.deleteOldMangasCategories(listOf(manga)).executeAsBlocking()
|
||||
db.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore history from Json
|
||||
*
|
||||
* @param history list containing history to be restored
|
||||
*/
|
||||
internal fun restoreHistoryForManga(history: List<BackupHistory>) {
|
||||
// List containing history to be updated
|
||||
val historyToBeUpdated = ArrayList<History>(history.size)
|
||||
for ((url, lastRead, readDuration) in history) {
|
||||
val dbHistory = db.getHistoryByChapterUrl(url).executeAsBlocking()
|
||||
// Check if history already in database and update
|
||||
if (dbHistory != null) {
|
||||
dbHistory.apply {
|
||||
last_read = max(lastRead, dbHistory.last_read)
|
||||
time_read = max(readDuration, dbHistory.time_read)
|
||||
}
|
||||
historyToBeUpdated.add(dbHistory)
|
||||
} else {
|
||||
// If not in database create
|
||||
db.getChapter(url).executeAsBlocking()?.let {
|
||||
val historyToAdd = History.create(it).apply {
|
||||
last_read = lastRead
|
||||
time_read = readDuration
|
||||
}
|
||||
historyToBeUpdated.add(historyToAdd)
|
||||
}
|
||||
}
|
||||
}
|
||||
db.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the sync of a manga.
|
||||
*
|
||||
* @param manga the manga whose sync have to be restored.
|
||||
* @param tracks the track list to restore.
|
||||
*/
|
||||
private fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
|
||||
// Fix foreign keys with the current manga id
|
||||
tracks.map { it.manga_id = manga.id!! }
|
||||
|
||||
// Get tracks from database
|
||||
val dbTracks = db.getTracks(manga).executeAsBlocking()
|
||||
val trackToUpdate = mutableListOf<Track>()
|
||||
|
||||
tracks.forEach { track ->
|
||||
var isInDatabase = false
|
||||
for (dbTrack in dbTracks) {
|
||||
if (track.sync_id == dbTrack.sync_id) {
|
||||
// The sync is already in the db, only update its fields
|
||||
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 (!isInDatabase) {
|
||||
// Insert new sync. Let the db assign the id
|
||||
track.id = null
|
||||
trackToUpdate.add(track)
|
||||
}
|
||||
}
|
||||
// Update database
|
||||
if (trackToUpdate.isNotEmpty()) {
|
||||
db.insertTracks(trackToUpdate).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreAppPreferences(preferences: List<BackupPreference>) {
|
||||
restorePreferences(preferences, preferenceStore)
|
||||
|
||||
restoreProgress += 1
|
||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.app_settings))
|
||||
}
|
||||
|
||||
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
||||
|
@ -211,6 +369,9 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
|
|||
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey))
|
||||
restorePreferences(it.prefs, sourcePrefs)
|
||||
}
|
||||
|
||||
restoreProgress += 1
|
||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.source_settings))
|
||||
}
|
||||
|
||||
private fun restorePreferences(
|
||||
|
|
34
app/src/main/java/eu/kanade/tachiyomi/util/BackupUtil.kt
Normal file
34
app/src/main/java/eu/kanade/tachiyomi/util/BackupUtil.kt
Normal file
|
@ -0,0 +1,34 @@
|
|||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.data.backup.BackupManager
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import okio.source
|
||||
|
||||
object BackupUtil {
|
||||
/**
|
||||
* Decode a potentially-gzipped backup.
|
||||
*/
|
||||
@SuppressLint("Recycle")
|
||||
fun decodeBackup(context: Context, uri: Uri): Backup {
|
||||
val backupCreator = BackupManager(context)
|
||||
|
||||
val backupStringSource = context.contentResolver.openInputStream(uri)!!.source().buffer()
|
||||
|
||||
val peeked = backupStringSource.peek()
|
||||
peeked.require(2)
|
||||
val id1id2 = peeked.readShort()
|
||||
val backupString = if (id1id2.toInt() == 0x1f8b) { // 0x1f8b is gzip magic bytes
|
||||
backupStringSource.gzip().buffer()
|
||||
} else {
|
||||
backupStringSource
|
||||
}.use { it.readByteArray() }
|
||||
|
||||
return backupCreator.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue