Move backup models to domain module

This commit is contained in:
arkon 2023-12-24 17:38:01 -05:00
parent 1a559124eb
commit 5908bd1930
26 changed files with 155 additions and 151 deletions

View file

@ -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)
} }

View file

@ -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

View file

@ -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

View file

@ -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)
} }
} }

View file

@ -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)
} }

View file

@ -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()

View file

@ -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"
}
}
}

View file

@ -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

View file

@ -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,
)

View file

@ -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

View file

@ -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,
)

View file

@ -1,6 +0,0 @@
package eu.kanade.tachiyomi.data.backup.models
import kotlinx.serialization.Serializer
@Serializer(forClass = Backup::class)
object BackupSerializer

View file

@ -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() }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) }

View file

@ -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)

View file

@ -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"
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,
)
}
}
} }

View file

@ -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

View file

@ -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(

View file

@ -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