Move backup models to domain module
This commit is contained in:
parent
1a559124eb
commit
5908bd1930
26 changed files with 155 additions and 151 deletions
|
@ -31,7 +31,7 @@ import eu.kanade.presentation.components.WarningBanner
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreator
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.collections.immutable.PersistentSet
|
import kotlinx.collections.immutable.PersistentSet
|
||||||
|
@ -123,7 +123,7 @@ class CreateBackupScreen : Screen() {
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!BackupCreateJob.isManualJobRunning(context)) {
|
if (!BackupCreateJob.isManualJobRunning(context)) {
|
||||||
try {
|
try {
|
||||||
chooseBackupDir.launch(Backup.getFilename())
|
chooseBackupDir.launch(BackupCreator.getFilename())
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
context.toast(MR.strings.file_picker_error)
|
context.toast(MR.strings.file_picker_error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ class RestoreBackupScreen : Screen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val results = try {
|
val results = try {
|
||||||
BackupFileValidator().validate(context, it)
|
BackupFileValidator(context).validate(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
model.setError(InvalidRestore(it, e.message.toString()))
|
model.setError(InvalidRestore(it, e.message.toString()))
|
||||||
return@rememberLauncherForActivityResult
|
return@rememberLauncherForActivityResult
|
||||||
|
|
|
@ -17,10 +17,10 @@ import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
||||||
|
import tachiyomi.domain.backup.model.Backup
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
package eu.kanade.tachiyomi.util
|
package eu.kanade.tachiyomi.data.backup
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreator
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.gzip
|
import okio.gzip
|
||||||
import okio.source
|
import okio.source
|
||||||
|
import tachiyomi.domain.backup.model.Backup
|
||||||
|
import tachiyomi.domain.backup.model.BackupSerializer
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class BackupDecoder(
|
||||||
|
private val context: Context,
|
||||||
|
private val parser: ProtoBuf = Injekt.get(),
|
||||||
|
) {
|
||||||
|
|
||||||
object BackupUtil {
|
|
||||||
/**
|
/**
|
||||||
* Decode a potentially-gzipped backup.
|
* Decode a potentially-gzipped backup.
|
||||||
*/
|
*/
|
||||||
fun decodeBackup(context: Context, uri: Uri): Backup {
|
fun decode(uri: Uri): Backup {
|
||||||
val backupCreator = BackupCreator(context)
|
|
||||||
|
|
||||||
val backupStringSource = context.contentResolver.openInputStream(uri)!!.source().buffer()
|
val backupStringSource = context.contentResolver.openInputStream(uri)!!.source().buffer()
|
||||||
|
|
||||||
val peeked = backupStringSource.peek()
|
val peeked = backupStringSource.peek()
|
||||||
|
@ -27,6 +31,6 @@ object BackupUtil {
|
||||||
backupStringSource
|
backupStringSource
|
||||||
}.use { it.readByteArray() }
|
}.use { it.readByteArray() }
|
||||||
|
|
||||||
return backupCreator.parser.decodeFromByteArray(BackupSerializer, backupString)
|
return parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
import eu.kanade.tachiyomi.util.BackupUtil
|
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
@ -11,6 +10,8 @@ import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class BackupFileValidator(
|
class BackupFileValidator(
|
||||||
|
private val context: Context,
|
||||||
|
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val trackerManager: TrackerManager = Injekt.get(),
|
private val trackerManager: TrackerManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
@ -21,9 +22,9 @@ class BackupFileValidator(
|
||||||
* @throws Exception if manga cannot be found.
|
* @throws Exception if manga cannot be found.
|
||||||
* @return List of missing sources or missing trackers.
|
* @return List of missing sources or missing trackers.
|
||||||
*/
|
*/
|
||||||
fun validate(context: Context, uri: Uri): Results {
|
fun validate(uri: Uri): Results {
|
||||||
val backup = try {
|
val backup = try {
|
||||||
BackupUtil.decodeBackup(context, uri)
|
BackupDecoder(context).decode(uri)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw IllegalStateException(e)
|
throw IllegalStateException(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.storage.service.StorageManager
|
import tachiyomi.domain.storage.service.StorageManager
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.time.Instant
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) :
|
class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
|
@ -49,13 +48,10 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
|
||||||
setForegroundSafely()
|
setForegroundSafely()
|
||||||
|
|
||||||
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
|
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
|
||||||
val backupPreferences = Injekt.get<BackupPreferences>()
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val location = BackupCreator(context).createBackup(uri, flags, isAutoBackup)
|
val location = BackupCreator(context, isAutoBackup).backup(uri, flags)
|
||||||
if (isAutoBackup) {
|
if (!isAutoBackup) {
|
||||||
backupPreferences.lastAutoBackupTimestamp().set(Instant.now().toEpochMilli())
|
|
||||||
} else {
|
|
||||||
notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!)
|
notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!)
|
||||||
}
|
}
|
||||||
Result.success()
|
Result.success()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup.create
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_APP_PREFS
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_APP_PREFS
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CATEGORY
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CATEGORY
|
||||||
|
@ -11,13 +12,6 @@ import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator
|
||||||
import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator
|
import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator
|
||||||
import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator
|
import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator
|
||||||
import eu.kanade.tachiyomi.data.backup.create.creators.SourcesBackupCreator
|
import eu.kanade.tachiyomi.data.backup.create.creators.SourcesBackupCreator
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|
||||||
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 kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
|
@ -25,31 +19,40 @@ import okio.gzip
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
|
import tachiyomi.domain.backup.model.Backup
|
||||||
|
import tachiyomi.domain.backup.model.BackupCategory
|
||||||
|
import tachiyomi.domain.backup.model.BackupManga
|
||||||
|
import tachiyomi.domain.backup.model.BackupPreference
|
||||||
|
import tachiyomi.domain.backup.model.BackupSerializer
|
||||||
|
import tachiyomi.domain.backup.model.BackupSource
|
||||||
|
import tachiyomi.domain.backup.model.BackupSourcePreferences
|
||||||
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class BackupCreator(
|
class BackupCreator(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val isAutoBackup: Boolean,
|
||||||
|
|
||||||
|
private val parser: ProtoBuf = Injekt.get(),
|
||||||
|
private val getFavorites: GetFavorites = Injekt.get(),
|
||||||
|
private val backupPreferences: BackupPreferences = Injekt.get(),
|
||||||
|
|
||||||
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
|
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
|
||||||
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
|
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
|
||||||
private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(),
|
private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(),
|
||||||
private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
|
private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
|
||||||
private val getFavorites: GetFavorites = Injekt.get(),
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
internal val parser = ProtoBuf
|
suspend fun backup(uri: Uri, flags: Int): String {
|
||||||
|
|
||||||
/**
|
|
||||||
* Create backup file.
|
|
||||||
*
|
|
||||||
* @param uri path of Uri
|
|
||||||
* @param isAutoBackup backup called from scheduled backup job
|
|
||||||
*/
|
|
||||||
suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
|
|
||||||
var file: UniFile? = null
|
var file: UniFile? = null
|
||||||
try {
|
try {
|
||||||
file = (
|
file = (
|
||||||
|
@ -58,14 +61,14 @@ class BackupCreator(
|
||||||
val dir = UniFile.fromUri(context, uri)
|
val dir = UniFile.fromUri(context, uri)
|
||||||
|
|
||||||
// Delete older backups
|
// Delete older backups
|
||||||
dir?.listFiles { _, filename -> Backup.filenameRegex.matches(filename) }
|
dir?.listFiles { _, filename -> FILENAME_REGEX.matches(filename) }
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.sortedByDescending { it.name }
|
.sortedByDescending { it.name }
|
||||||
.drop(MAX_AUTO_BACKUPS - 1)
|
.drop(MAX_AUTO_BACKUPS - 1)
|
||||||
.forEach { it.delete() }
|
.forEach { it.delete() }
|
||||||
|
|
||||||
// Create new file to place backup
|
// Create new file to place backup
|
||||||
dir?.createFile(Backup.getFilename())
|
dir?.createFile(BackupCreator.getFilename())
|
||||||
} else {
|
} else {
|
||||||
UniFile.fromUri(context, uri)
|
UniFile.fromUri(context, uri)
|
||||||
}
|
}
|
||||||
|
@ -90,14 +93,22 @@ class BackupCreator(
|
||||||
throw IllegalStateException(context.stringResource(MR.strings.empty_backup_error))
|
throw IllegalStateException(context.stringResource(MR.strings.empty_backup_error))
|
||||||
}
|
}
|
||||||
|
|
||||||
file.openOutputStream().also {
|
file.openOutputStream()
|
||||||
// Force overwrite old file
|
.also {
|
||||||
(it as? FileOutputStream)?.channel?.truncate(0)
|
// Force overwrite old file
|
||||||
}.sink().gzip().buffer().use { it.write(byteArray) }
|
(it as? FileOutputStream)?.channel?.truncate(0)
|
||||||
|
}
|
||||||
|
.sink().gzip().buffer().use {
|
||||||
|
it.write(byteArray)
|
||||||
|
}
|
||||||
val fileUri = file.uri
|
val fileUri = file.uri
|
||||||
|
|
||||||
// Make sure it's a valid backup file
|
// Make sure it's a valid backup file
|
||||||
BackupFileValidator().validate(context, fileUri)
|
BackupFileValidator(context).validate(fileUri)
|
||||||
|
|
||||||
|
if (isAutoBackup) {
|
||||||
|
backupPreferences.lastAutoBackupTimestamp().set(Instant.now().toEpochMilli())
|
||||||
|
}
|
||||||
|
|
||||||
return fileUri.toString()
|
return fileUri.toString()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -132,6 +143,14 @@ class BackupCreator(
|
||||||
|
|
||||||
return preferenceBackupCreator.backupSourcePreferences()
|
return preferenceBackupCreator.backupSourcePreferences()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private val MAX_AUTO_BACKUPS: Int = 4
|
companion object {
|
||||||
|
private const val MAX_AUTO_BACKUPS: Int = 4
|
||||||
|
private val FILENAME_REGEX = """${BuildConfig.APPLICATION_ID}_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}.tachibk""".toRegex()
|
||||||
|
|
||||||
|
fun getFilename(): String {
|
||||||
|
val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.ENGLISH).format(Date())
|
||||||
|
return "${BuildConfig.APPLICATION_ID}_$date.tachibk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.create.creators
|
package eu.kanade.tachiyomi.data.backup.create.creators
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
import tachiyomi.domain.backup.model.BackupCategory
|
||||||
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
|
import tachiyomi.domain.backup.model.backupCategoryMapper
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.create.creators
|
package eu.kanade.tachiyomi.data.backup.create.creators
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
|
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
import tachiyomi.domain.backup.model.BackupChapter
|
||||||
|
import tachiyomi.domain.backup.model.BackupHistory
|
||||||
|
import tachiyomi.domain.backup.model.BackupManga
|
||||||
|
import tachiyomi.domain.backup.model.backupChapterMapper
|
||||||
|
import tachiyomi.domain.backup.model.backupTrackMapper
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.history.interactor.GetHistory
|
import tachiyomi.domain.history.interactor.GetHistory
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
@ -27,7 +28,7 @@ class MangaBackupCreator(
|
||||||
|
|
||||||
private suspend fun backupManga(manga: Manga, options: Int): BackupManga {
|
private suspend fun backupManga(manga: Manga, options: Int): BackupManga {
|
||||||
// Entry for this manga
|
// Entry for this manga
|
||||||
val mangaObject = BackupManga.copyFrom(manga)
|
val mangaObject = manga.toBackupManga()
|
||||||
|
|
||||||
// Check if user wants chapter information in backup
|
// Check if user wants chapter information in backup
|
||||||
if (options and BackupCreateFlags.BACKUP_CHAPTER == BackupCreateFlags.BACKUP_CHAPTER) {
|
if (options and BackupCreateFlags.BACKUP_CHAPTER == BackupCreateFlags.BACKUP_CHAPTER) {
|
||||||
|
@ -77,3 +78,24 @@ class MangaBackupCreator(
|
||||||
return mangaObject
|
return mangaObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Manga.toBackupManga() =
|
||||||
|
BackupManga(
|
||||||
|
url = this.url,
|
||||||
|
title = this.title,
|
||||||
|
artist = this.artist,
|
||||||
|
author = this.author,
|
||||||
|
description = this.description,
|
||||||
|
genre = this.genre.orEmpty(),
|
||||||
|
status = this.status.toInt(),
|
||||||
|
thumbnailUrl = this.thumbnailUrl,
|
||||||
|
favorite = this.favorite,
|
||||||
|
source = this.source,
|
||||||
|
dateAdded = this.dateAdded,
|
||||||
|
viewer = (this.viewerFlags.toInt() and ReadingMode.MASK),
|
||||||
|
viewer_flags = this.viewerFlags.toInt(),
|
||||||
|
chapterFlags = this.chapterFlags.toInt(),
|
||||||
|
updateStrategy = this.updateStrategy,
|
||||||
|
lastModifiedAt = this.lastModifiedAt,
|
||||||
|
favoriteModifiedAt = this.favoriteModifiedAt,
|
||||||
|
)
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.create.creators
|
package eu.kanade.tachiyomi.data.backup.create.creators
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
|
||||||
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.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.preferenceKey
|
import eu.kanade.tachiyomi.source.preferenceKey
|
||||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.preference.Preference
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.preference.PreferenceStore
|
||||||
|
import tachiyomi.domain.backup.model.BackupPreference
|
||||||
|
import tachiyomi.domain.backup.model.BackupSourcePreferences
|
||||||
|
import tachiyomi.domain.backup.model.BooleanPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.FloatPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.IntPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.LongPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.StringPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.StringSetPreferenceValue
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.create.creators
|
package eu.kanade.tachiyomi.data.backup.create.creators
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import tachiyomi.domain.backup.model.BackupSource
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -16,7 +17,13 @@ class SourcesBackupCreator(
|
||||||
.map(Manga::source)
|
.map(Manga::source)
|
||||||
.distinct()
|
.distinct()
|
||||||
.map(sourceManager::getOrStub)
|
.map(sourceManager::getOrStub)
|
||||||
.map(BackupSource::copyFrom)
|
.map { it.toBackupSource() }
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Source.toBackupSource() =
|
||||||
|
BackupSource(
|
||||||
|
name = this.name,
|
||||||
|
sourceId = this.id,
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializer
|
|
||||||
|
|
||||||
@Serializer(forClass = Backup::class)
|
|
||||||
object BackupSerializer
|
|
|
@ -2,21 +2,21 @@ package eu.kanade.tachiyomi.data.backup.restore
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import eu.kanade.tachiyomi.data.backup.BackupDecoder
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.CategoriesRestorer
|
import eu.kanade.tachiyomi.data.backup.restore.restorers.CategoriesRestorer
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaRestorer
|
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaRestorer
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceRestorer
|
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceRestorer
|
||||||
import eu.kanade.tachiyomi.util.BackupUtil
|
|
||||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
|
import tachiyomi.domain.backup.model.BackupCategory
|
||||||
|
import tachiyomi.domain.backup.model.BackupManga
|
||||||
|
import tachiyomi.domain.backup.model.BackupPreference
|
||||||
|
import tachiyomi.domain.backup.model.BackupSourcePreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -61,7 +61,7 @@ class BackupRestorer(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun restoreFromFile(uri: Uri, options: RestoreOptions) {
|
private suspend fun restoreFromFile(uri: Uri, options: RestoreOptions) {
|
||||||
val backup = BackupUtil.decodeBackup(context, uri)
|
val backup = BackupDecoder(context).decode(uri)
|
||||||
|
|
||||||
// Store source mapping for error messages
|
// Store source mapping for error messages
|
||||||
val backupMaps = backup.backupSources + backup.backupBrokenSources.map { it.toBackupSource() }
|
val backupMaps = backup.backupSources + backup.backupBrokenSources.map { it.toBackupSource() }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
import tachiyomi.domain.backup.model.BackupCategory
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
||||||
|
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupTracking
|
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.data.UpdateStrategyColumnAdapter
|
import tachiyomi.data.UpdateStrategyColumnAdapter
|
||||||
|
import tachiyomi.domain.backup.model.BackupCategory
|
||||||
|
import tachiyomi.domain.backup.model.BackupChapter
|
||||||
|
import tachiyomi.domain.backup.model.BackupHistory
|
||||||
|
import tachiyomi.domain.backup.model.BackupManga
|
||||||
|
import tachiyomi.domain.backup.model.BackupTracking
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
|
|
@ -2,18 +2,18 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
|
||||||
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.data.library.LibraryUpdateJob
|
||||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||||
import tachiyomi.core.preference.AndroidPreferenceStore
|
import tachiyomi.core.preference.AndroidPreferenceStore
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.preference.PreferenceStore
|
||||||
|
import tachiyomi.domain.backup.model.BackupPreference
|
||||||
|
import tachiyomi.domain.backup.model.BackupSourcePreferences
|
||||||
|
import tachiyomi.domain.backup.model.BooleanPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.FloatPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.IntPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.LongPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.StringPreferenceValue
|
||||||
|
import tachiyomi.domain.backup.model.StringSetPreferenceValue
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
||||||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
|
@ -106,6 +107,9 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
xmlVersion = XmlVersion.XML10
|
xmlVersion = XmlVersion.XML10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addSingletonFactory<ProtoBuf> {
|
||||||
|
ProtoBuf
|
||||||
|
}
|
||||||
|
|
||||||
addSingletonFactory { ChapterCache(app) }
|
addSingletonFactory { ChapterCache(app) }
|
||||||
addSingletonFactory { CoverCache(app) }
|
addSingletonFactory { CoverCache(app) }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -18,6 +19,7 @@ dependencies {
|
||||||
|
|
||||||
implementation(platform(kotlinx.coroutines.bom))
|
implementation(platform(kotlinx.coroutines.bom))
|
||||||
implementation(kotlinx.bundles.coroutines)
|
implementation(kotlinx.bundles.coroutines)
|
||||||
|
implementation(kotlinx.bundles.serialization)
|
||||||
|
|
||||||
implementation(libs.unifile)
|
implementation(libs.unifile)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Date
|
@Serializer(forClass = Backup::class)
|
||||||
import java.util.Locale
|
object BackupSerializer
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Backup(
|
data class Backup(
|
||||||
|
@ -15,14 +15,4 @@ data class Backup(
|
||||||
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
||||||
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
|
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
|
||||||
@ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(),
|
@ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(),
|
||||||
) {
|
)
|
||||||
|
|
||||||
companion object {
|
|
||||||
val filenameRegex = """${BuildConfig.APPLICATION_ID}_\d+-\d+-\d+_\d+-\d+.tachibk""".toRegex()
|
|
||||||
|
|
||||||
fun getFilename(): String {
|
|
||||||
val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date())
|
|
||||||
return "${BuildConfig.APPLICATION_ID}_$date.tachibk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,7 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
@ -60,28 +59,4 @@ data class BackupManga(
|
||||||
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
|
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun copyFrom(manga: Manga): BackupManga {
|
|
||||||
return BackupManga(
|
|
||||||
url = manga.url,
|
|
||||||
title = manga.title,
|
|
||||||
artist = manga.artist,
|
|
||||||
author = manga.author,
|
|
||||||
description = manga.description,
|
|
||||||
genre = manga.genre.orEmpty(),
|
|
||||||
status = manga.status.toInt(),
|
|
||||||
thumbnailUrl = manga.thumbnailUrl,
|
|
||||||
favorite = manga.favorite,
|
|
||||||
source = manga.source,
|
|
||||||
dateAdded = manga.dateAdded,
|
|
||||||
viewer = (manga.viewerFlags.toInt() and ReadingMode.MASK),
|
|
||||||
viewer_flags = manga.viewerFlags.toInt(),
|
|
||||||
chapterFlags = manga.chapterFlags.toInt(),
|
|
||||||
updateStrategy = manga.updateStrategy,
|
|
||||||
lastModifiedAt = manga.lastModifiedAt,
|
|
||||||
favoriteModifiedAt = manga.favoriteModifiedAt,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
|
|
||||||
|
@ -8,16 +7,7 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
data class BackupSource(
|
data class BackupSource(
|
||||||
@ProtoNumber(1) var name: String = "",
|
@ProtoNumber(1) var name: String = "",
|
||||||
@ProtoNumber(2) var sourceId: Long,
|
@ProtoNumber(2) var sourceId: Long,
|
||||||
) {
|
)
|
||||||
companion object {
|
|
||||||
fun copyFrom(source: Source): BackupSource {
|
|
||||||
return BackupSource(
|
|
||||||
name = source.name,
|
|
||||||
sourceId = source.id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class BrokenBackupSource(
|
data class BrokenBackupSource(
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.models
|
package tachiyomi.domain.backup.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
Reference in a new issue