mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-14 16:24:34 -05:00
Feature/backup suwayomi data (#1430)
* Export meta data * Import meta data * Add missing "opdsUseBinaryFileSize" setting to gql * Export server settings * Import server settings * Streamline server config enum handling * Use "restore amount" in backup import progress
This commit is contained in:
@@ -13,4 +13,6 @@ data class BackupFlags(
|
||||
val includeChapters: Boolean,
|
||||
val includeTracking: Boolean,
|
||||
val includeHistory: Boolean,
|
||||
val includeClientData: Boolean,
|
||||
val includeServerSettings: Boolean,
|
||||
)
|
||||
|
||||
@@ -22,6 +22,8 @@ interface Chapter :
|
||||
|
||||
var source_order: Int
|
||||
|
||||
var meta: Map<String, String>
|
||||
|
||||
val isRecognizedNumber: Boolean
|
||||
get() = chapter_number >= 0f
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class ChapterImpl : Chapter {
|
||||
|
||||
override var source_order: Int = 0
|
||||
|
||||
override var meta: Map<String, String> = emptyMap()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
||||
@@ -37,6 +37,8 @@ interface Manga : SManga {
|
||||
|
||||
var cover_last_modified: Long
|
||||
|
||||
var meta: Map<String, String>
|
||||
|
||||
fun setChapterOrder(order: Int) {
|
||||
setChapterFlags(order, CHAPTER_SORT_MASK)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ open class MangaImpl : Manga {
|
||||
|
||||
override var initialized: Boolean = false
|
||||
|
||||
override var meta: Map<String, String> = emptyMap()
|
||||
|
||||
/** Reader mode value
|
||||
* ref: https://github.com/tachiyomiorg/tachiyomi/blob/ff369010074b058bb734ce24c66508300e6e9ac6/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt#L8
|
||||
* 0 -> Default
|
||||
|
||||
@@ -24,13 +24,18 @@ import org.jetbrains.exposed.sql.Query
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.global.impl.GlobalMeta
|
||||
import suwayomi.tachidesk.manga.impl.Category
|
||||
import suwayomi.tachidesk.manga.impl.CategoryManga
|
||||
import suwayomi.tachidesk.manga.impl.Chapter
|
||||
import suwayomi.tachidesk.manga.impl.Manga
|
||||
import suwayomi.tachidesk.manga.impl.Source
|
||||
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.Backup
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupCategory
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupChapter
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupManga
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupServerSettings
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupSource
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupTracking
|
||||
import suwayomi.tachidesk.manga.impl.track.Track
|
||||
@@ -123,6 +128,8 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
includeChapters = true,
|
||||
includeTracking = true,
|
||||
includeHistory = true,
|
||||
includeClientData = true,
|
||||
includeServerSettings = true,
|
||||
),
|
||||
).use { input ->
|
||||
val automatedBackupDir = File(applicationDirs.automatedBackupRoot)
|
||||
@@ -181,8 +188,10 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
transaction {
|
||||
Backup(
|
||||
backupManga(databaseManga, flags),
|
||||
backupCategories(),
|
||||
backupExtensionInfo(databaseManga),
|
||||
backupCategories(flags),
|
||||
backupExtensionInfo(databaseManga, flags),
|
||||
backupGlobalMeta(flags),
|
||||
backupServerSettings(flags),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,6 +229,10 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
|
||||
val mangaId = mangaRow[MangaTable.id].value
|
||||
|
||||
if (flags.includeClientData) {
|
||||
backupManga.meta = Manga.getMangaMetaMap(mangaId)
|
||||
}
|
||||
|
||||
if (flags.includeChapters) {
|
||||
val chapters =
|
||||
transaction {
|
||||
@@ -231,6 +244,7 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
ChapterTable.toDataClass(it)
|
||||
}
|
||||
}
|
||||
val chapterToMeta = Chapter.getChaptersMetaMaps(chapters.map { it.id })
|
||||
|
||||
backupManga.chapters =
|
||||
chapters.map {
|
||||
@@ -245,7 +259,11 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
it.uploadDate,
|
||||
it.chapterNumber,
|
||||
chapters.size - it.index,
|
||||
)
|
||||
).apply {
|
||||
if (flags.includeClientData) {
|
||||
this.meta = chapterToMeta[it.id] ?: emptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,31 +305,135 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
backupManga
|
||||
}
|
||||
|
||||
private fun backupCategories(): List<BackupCategory> =
|
||||
CategoryTable
|
||||
.selectAll()
|
||||
.orderBy(CategoryTable.order to SortOrder.ASC)
|
||||
.map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}.filter { it.id != Category.DEFAULT_CATEGORY_ID }
|
||||
.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
)
|
||||
}
|
||||
private fun backupCategories(flags: BackupFlags): List<BackupCategory> {
|
||||
val categories =
|
||||
CategoryTable
|
||||
.selectAll()
|
||||
.orderBy(CategoryTable.order to SortOrder.ASC)
|
||||
.map { CategoryTable.toDataClass(it) }
|
||||
val categoryToMeta = Category.getCategoriesMetaMaps(categories.map { it.id })
|
||||
|
||||
private fun backupExtensionInfo(mangas: Query): List<BackupSource> =
|
||||
mangas
|
||||
.asSequence()
|
||||
.map { it[MangaTable.sourceReference] }
|
||||
.distinct()
|
||||
.map {
|
||||
val sourceRow = SourceTable.selectAll().where { SourceTable.id eq it }.firstOrNull()
|
||||
return categories.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
).apply {
|
||||
if (flags.includeClientData) {
|
||||
this.meta = categoryToMeta[it.id] ?: emptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupExtensionInfo(
|
||||
mangas: Query,
|
||||
flags: BackupFlags,
|
||||
): List<BackupSource> {
|
||||
val inLibraryMangaSourceIds =
|
||||
mangas
|
||||
.asSequence()
|
||||
.map { it[MangaTable.sourceReference] }
|
||||
.distinct()
|
||||
.toList()
|
||||
val sources = SourceTable.selectAll().where { SourceTable.id inList inLibraryMangaSourceIds }
|
||||
val sourceToMeta = Source.getSourcesMetaMaps(sources.map { it[SourceTable.id].value })
|
||||
|
||||
return inLibraryMangaSourceIds
|
||||
.map { mangaSourceId ->
|
||||
val source = sources.firstOrNull { it[SourceTable.id].value == mangaSourceId }
|
||||
BackupSource(
|
||||
sourceRow?.get(SourceTable.name) ?: "",
|
||||
it,
|
||||
)
|
||||
source?.get(SourceTable.name) ?: "",
|
||||
mangaSourceId,
|
||||
).apply {
|
||||
if (flags.includeClientData) {
|
||||
this.meta = sourceToMeta[mangaSourceId] ?: emptyMap()
|
||||
}
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
private fun backupGlobalMeta(flags: BackupFlags): Map<String, String> {
|
||||
if (!flags.includeClientData) {
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
return GlobalMeta.getMetaMap()
|
||||
}
|
||||
|
||||
private fun backupServerSettings(flags: BackupFlags): BackupServerSettings? {
|
||||
if (!flags.includeServerSettings) {
|
||||
return null
|
||||
}
|
||||
|
||||
return BackupServerSettings(
|
||||
ip = serverConfig.ip.value,
|
||||
port = serverConfig.port.value,
|
||||
// socks
|
||||
socksProxyEnabled = serverConfig.socksProxyEnabled.value,
|
||||
socksProxyVersion = serverConfig.socksProxyVersion.value,
|
||||
socksProxyHost = serverConfig.socksProxyHost.value,
|
||||
socksProxyPort = serverConfig.socksProxyPort.value,
|
||||
socksProxyUsername = serverConfig.socksProxyUsername.value,
|
||||
socksProxyPassword = serverConfig.socksProxyPassword.value,
|
||||
// webUI
|
||||
webUIFlavor = serverConfig.webUIFlavor.value,
|
||||
initialOpenInBrowserEnabled = serverConfig.initialOpenInBrowserEnabled.value,
|
||||
webUIInterface = serverConfig.webUIInterface.value,
|
||||
electronPath = serverConfig.electronPath.value,
|
||||
webUIChannel = serverConfig.webUIChannel.value,
|
||||
webUIUpdateCheckInterval = serverConfig.webUIUpdateCheckInterval.value,
|
||||
// downloader
|
||||
downloadAsCbz = serverConfig.downloadAsCbz.value,
|
||||
downloadsPath = serverConfig.downloadsPath.value,
|
||||
autoDownloadNewChapters = serverConfig.autoDownloadNewChapters.value,
|
||||
excludeEntryWithUnreadChapters = serverConfig.excludeEntryWithUnreadChapters.value,
|
||||
autoDownloadAheadLimit = 0, // deprecated
|
||||
autoDownloadNewChaptersLimit = serverConfig.autoDownloadNewChaptersLimit.value,
|
||||
autoDownloadIgnoreReUploads = serverConfig.autoDownloadIgnoreReUploads.value,
|
||||
// extension
|
||||
extensionRepos = serverConfig.extensionRepos.value,
|
||||
// requests
|
||||
maxSourcesInParallel = serverConfig.maxSourcesInParallel.value,
|
||||
// updater
|
||||
excludeUnreadChapters = serverConfig.excludeUnreadChapters.value,
|
||||
excludeNotStarted = serverConfig.excludeNotStarted.value,
|
||||
excludeCompleted = serverConfig.excludeCompleted.value,
|
||||
globalUpdateInterval = serverConfig.globalUpdateInterval.value,
|
||||
updateMangas = serverConfig.updateMangas.value,
|
||||
// Authentication
|
||||
basicAuthEnabled = serverConfig.basicAuthEnabled.value,
|
||||
basicAuthUsername = serverConfig.basicAuthUsername.value,
|
||||
basicAuthPassword = serverConfig.basicAuthPassword.value,
|
||||
// misc
|
||||
debugLogsEnabled = serverConfig.debugLogsEnabled.value,
|
||||
gqlDebugLogsEnabled = false, // deprecated
|
||||
systemTrayEnabled = serverConfig.systemTrayEnabled.value,
|
||||
maxLogFiles = serverConfig.maxLogFiles.value,
|
||||
maxLogFileSize = serverConfig.maxLogFileSize.value,
|
||||
maxLogFolderSize = serverConfig.maxLogFolderSize.value,
|
||||
// backup
|
||||
backupPath = serverConfig.backupPath.value,
|
||||
backupTime = serverConfig.backupTime.value,
|
||||
backupInterval = serverConfig.backupInterval.value,
|
||||
backupTTL = serverConfig.backupTTL.value,
|
||||
// local source
|
||||
localSourcePath = serverConfig.localSourcePath.value,
|
||||
// cloudflare bypass
|
||||
flareSolverrEnabled = serverConfig.flareSolverrEnabled.value,
|
||||
flareSolverrUrl = serverConfig.flareSolverrUrl.value,
|
||||
flareSolverrTimeout = serverConfig.flareSolverrTimeout.value,
|
||||
flareSolverrSessionName = serverConfig.flareSolverrSessionName.value,
|
||||
flareSolverrSessionTtl = serverConfig.flareSolverrSessionTtl.value,
|
||||
flareSolverrAsResponseFallback = serverConfig.flareSolverrAsResponseFallback.value,
|
||||
// opds
|
||||
opdsUseBinaryFileSizes = serverConfig.opdsUseBinaryFileSizes.value,
|
||||
opdsItemsPerPage = serverConfig.opdsItemsPerPage.value,
|
||||
opdsEnablePageReadProgress = serverConfig.opdsEnablePageReadProgress.value,
|
||||
opdsMarkAsReadOnDownload = serverConfig.opdsMarkAsReadOnDownload.value,
|
||||
opdsShowOnlyUnreadChapters = serverConfig.opdsShowOnlyUnreadChapters.value,
|
||||
opdsShowOnlyDownloadedChapters = serverConfig.opdsShowOnlyDownloadedChapters.value,
|
||||
opdsChapterSortOrder = serverConfig.opdsChapterSortOrder.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,16 @@ import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.statements.BatchUpdateStatement
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import suwayomi.tachidesk.global.impl.GlobalMeta
|
||||
import suwayomi.tachidesk.graphql.mutations.SettingsMutation
|
||||
import suwayomi.tachidesk.graphql.types.toStatus
|
||||
import suwayomi.tachidesk.manga.impl.Category
|
||||
import suwayomi.tachidesk.manga.impl.Category.modifyCategoriesMetas
|
||||
import suwayomi.tachidesk.manga.impl.CategoryManga
|
||||
import suwayomi.tachidesk.manga.impl.Chapter.modifyChaptersMetas
|
||||
import suwayomi.tachidesk.manga.impl.Manga.clearThumbnail
|
||||
import suwayomi.tachidesk.manga.impl.Manga.modifyMangasMetas
|
||||
import suwayomi.tachidesk.manga.impl.Source.modifySourceMetas
|
||||
import suwayomi.tachidesk.manga.impl.backup.models.Chapter
|
||||
import suwayomi.tachidesk.manga.impl.backup.models.Manga
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupValidator.ValidationResult
|
||||
@@ -42,6 +48,8 @@ import suwayomi.tachidesk.manga.impl.backup.proto.models.Backup
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupCategory
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupHistory
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupManga
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupServerSettings
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupSource
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupTracking
|
||||
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
|
||||
import suwayomi.tachidesk.manga.impl.track.tracker.model.toTrack
|
||||
@@ -82,6 +90,17 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
data object Failure : BackupRestoreState()
|
||||
|
||||
data class RestoringCategories(
|
||||
val current: Int,
|
||||
val totalManga: Int,
|
||||
) : BackupRestoreState()
|
||||
|
||||
data class RestoringMeta(
|
||||
val current: Int,
|
||||
val totalManga: Int,
|
||||
) : BackupRestoreState()
|
||||
|
||||
data class RestoringSettings(
|
||||
val current: Int,
|
||||
val totalManga: Int,
|
||||
) : BackupRestoreState()
|
||||
|
||||
@@ -177,12 +196,29 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
|
||||
val validationResult = validate(backup)
|
||||
|
||||
restoreAmount = backup.backupManga.size + 1 // +1 for categories
|
||||
val restoreCategories = 1
|
||||
val restoreMeta = 1
|
||||
val restoreSettings = 1
|
||||
val getRestoreAmount = { size: Int -> size + restoreCategories + restoreMeta + restoreSettings }
|
||||
restoreAmount = getRestoreAmount(backup.backupManga.size)
|
||||
|
||||
updateRestoreState(id, BackupRestoreState.RestoringCategories(backup.backupManga.size))
|
||||
updateRestoreState(id, BackupRestoreState.RestoringCategories(restoreCategories, restoreAmount))
|
||||
|
||||
val categoryMapping = restoreCategories(backup.backupCategories)
|
||||
|
||||
updateRestoreState(id, BackupRestoreState.RestoringMeta(restoreCategories + restoreMeta, restoreAmount))
|
||||
|
||||
restoreGlobalMeta(backup.meta)
|
||||
|
||||
restoreSourceMeta(backup.backupSources)
|
||||
|
||||
updateRestoreState(
|
||||
id,
|
||||
BackupRestoreState.RestoringSettings(restoreCategories + restoreMeta + restoreSettings, restoreAmount),
|
||||
)
|
||||
|
||||
restoreServerSettings(backup.serverSettings)
|
||||
|
||||
// Store source mapping for error messages
|
||||
sourceMapping = backup.getSourceMap()
|
||||
|
||||
@@ -191,8 +227,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
updateRestoreState(
|
||||
id,
|
||||
BackupRestoreState.RestoringManga(
|
||||
current = index + 1,
|
||||
totalManga = backup.backupManga.size,
|
||||
current = getRestoreAmount(index + 1),
|
||||
totalManga = restoreAmount,
|
||||
title = manga.title,
|
||||
),
|
||||
)
|
||||
@@ -225,6 +261,15 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
private fun restoreCategories(backupCategories: List<BackupCategory>): Map<Int, Int> {
|
||||
val categoryIds = Category.createCategories(backupCategories.map { it.name })
|
||||
|
||||
val metaEntryByCategoryId =
|
||||
categoryIds
|
||||
.zip(backupCategories)
|
||||
.associate { (categoryId, backupCategory) ->
|
||||
categoryId to backupCategory.meta
|
||||
}
|
||||
|
||||
modifyCategoriesMetas(metaEntryByCategoryId)
|
||||
|
||||
return backupCategories.withIndex().associate { (index, backupCategory) ->
|
||||
backupCategory.order to categoryIds[index]
|
||||
}
|
||||
@@ -318,6 +363,10 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
// delete thumbnail in case cached data still exists
|
||||
clearThumbnail(mangaId)
|
||||
|
||||
if (manga.meta.isNotEmpty()) {
|
||||
modifyMangasMetas(mapOf(mangaId to manga.meta))
|
||||
}
|
||||
|
||||
// merge chapter data
|
||||
restoreMangaChapterData(mangaId, restoreMode, chapters)
|
||||
|
||||
@@ -358,26 +407,28 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
) = dbTransaction {
|
||||
val (chaptersToInsert, chaptersToUpdateToDbChapter) = getMangaChapterToRestoreInfo(mangaId, restoreMode, chapters)
|
||||
|
||||
ChapterTable.batchInsert(chaptersToInsert) { chapter ->
|
||||
this[ChapterTable.url] = chapter.url
|
||||
this[ChapterTable.name] = chapter.name
|
||||
if (chapter.date_upload == 0L) {
|
||||
this[ChapterTable.date_upload] = chapter.date_fetch
|
||||
} else {
|
||||
this[ChapterTable.date_upload] = chapter.date_upload
|
||||
}
|
||||
this[ChapterTable.chapter_number] = chapter.chapter_number
|
||||
this[ChapterTable.scanlator] = chapter.scanlator
|
||||
val insertedChapterIds =
|
||||
ChapterTable
|
||||
.batchInsert(chaptersToInsert) { chapter ->
|
||||
this[ChapterTable.url] = chapter.url
|
||||
this[ChapterTable.name] = chapter.name
|
||||
if (chapter.date_upload == 0L) {
|
||||
this[ChapterTable.date_upload] = chapter.date_fetch
|
||||
} else {
|
||||
this[ChapterTable.date_upload] = chapter.date_upload
|
||||
}
|
||||
this[ChapterTable.chapter_number] = chapter.chapter_number
|
||||
this[ChapterTable.scanlator] = chapter.scanlator
|
||||
|
||||
this[ChapterTable.sourceOrder] = chaptersToInsert.size - chapter.source_order
|
||||
this[ChapterTable.manga] = mangaId
|
||||
this[ChapterTable.sourceOrder] = chaptersToInsert.size - chapter.source_order
|
||||
this[ChapterTable.manga] = mangaId
|
||||
|
||||
this[ChapterTable.isRead] = chapter.read
|
||||
this[ChapterTable.lastPageRead] = chapter.last_page_read.coerceAtLeast(0)
|
||||
this[ChapterTable.isBookmarked] = chapter.bookmark
|
||||
this[ChapterTable.isRead] = chapter.read
|
||||
this[ChapterTable.lastPageRead] = chapter.last_page_read.coerceAtLeast(0)
|
||||
this[ChapterTable.isBookmarked] = chapter.bookmark
|
||||
|
||||
this[ChapterTable.fetchedAt] = TimeUnit.MILLISECONDS.toSeconds(chapter.date_fetch)
|
||||
}
|
||||
this[ChapterTable.fetchedAt] = TimeUnit.MILLISECONDS.toSeconds(chapter.date_fetch)
|
||||
}.map { it[ChapterTable.id].value }
|
||||
|
||||
if (chaptersToUpdateToDbChapter.isNotEmpty()) {
|
||||
BatchUpdateStatement(ChapterTable).apply {
|
||||
@@ -391,6 +442,20 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
execute(this@dbTransaction)
|
||||
}
|
||||
}
|
||||
|
||||
val chaptersToInsertByChapterId = insertedChapterIds.zip(chaptersToInsert)
|
||||
val chapterToUpdateByChapterId =
|
||||
chaptersToUpdateToDbChapter.map { (backupChapter, dbChapter) ->
|
||||
dbChapter[ChapterTable.id].value to
|
||||
backupChapter
|
||||
}
|
||||
val metaEntryByChapterId =
|
||||
(chaptersToInsertByChapterId + chapterToUpdateByChapterId)
|
||||
.associate { (chapterId, backupChapter) ->
|
||||
chapterId to backupChapter.meta
|
||||
}
|
||||
|
||||
modifyChaptersMetas(metaEntryByChapterId)
|
||||
}
|
||||
|
||||
private fun restoreMangaCategoryData(
|
||||
@@ -440,5 +505,21 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
Tracker.insertTrackRecords(newTracks)
|
||||
}
|
||||
|
||||
private fun restoreGlobalMeta(meta: Map<String, String>) {
|
||||
GlobalMeta.modifyMetas(meta)
|
||||
}
|
||||
|
||||
private fun restoreSourceMeta(backupSources: List<BackupSource>) {
|
||||
modifySourceMetas(backupSources.associateBy { it.sourceId }.mapValues { it.value.meta })
|
||||
}
|
||||
|
||||
private fun restoreServerSettings(backupServerSettings: BackupServerSettings?) {
|
||||
if (backupServerSettings == null) {
|
||||
return
|
||||
}
|
||||
|
||||
SettingsMutation().updateSettings(backupServerSettings)
|
||||
}
|
||||
|
||||
private fun TrackRecordDataClass.forComparison() = this.copy(id = 0, mangaId = 0)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ data class Backup(
|
||||
// Bump by 100 to specify this is a 0.x value
|
||||
// @ProtoNumber(100) var brokenBackupSources: List<BrokenBackupSource> = emptyList(),
|
||||
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
||||
// suwayomi
|
||||
@ProtoNumber(9000) var meta: Map<String, String> = emptyMap(),
|
||||
@ProtoNumber(9001) var serverSettings: BackupServerSettings?,
|
||||
) {
|
||||
fun getSourceMap(): Map<Long, String> =
|
||||
backupSources
|
||||
|
||||
@@ -10,4 +10,6 @@ class BackupCategory(
|
||||
// @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x
|
||||
// Bump by 100 to specify this is a 0.x value
|
||||
@ProtoNumber(100) var flags: Int = 0,
|
||||
// suwayomi
|
||||
@ProtoNumber(9000) var meta: Map<String, String> = emptyMap(),
|
||||
)
|
||||
|
||||
@@ -20,6 +20,8 @@ data class BackupChapter(
|
||||
// chapterNumber is called number is 1.x
|
||||
@ProtoNumber(9) var chapterNumber: Float = 0F,
|
||||
@ProtoNumber(10) var sourceOrder: Int = 0,
|
||||
// suwayomi
|
||||
@ProtoNumber(9000) var meta: Map<String, String> = emptyMap(),
|
||||
) {
|
||||
fun toChapterImpl(): ChapterImpl =
|
||||
ChapterImpl().apply {
|
||||
@@ -33,5 +35,6 @@ data class BackupChapter(
|
||||
date_fetch = this@BackupChapter.dateFetch
|
||||
date_upload = this@BackupChapter.dateUpload
|
||||
source_order = this@BackupChapter.sourceOrder
|
||||
meta = this@BackupChapter.meta
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ data class BackupManga(
|
||||
@ProtoNumber(103) var viewer_flags: Int? = null,
|
||||
@ProtoNumber(104) var history: List<BackupHistory> = emptyList(),
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
// suwayomi
|
||||
@ProtoNumber(9000) var meta: Map<String, String> = emptyMap(),
|
||||
) {
|
||||
fun getMangaImpl(): MangaImpl =
|
||||
MangaImpl().apply {
|
||||
@@ -55,6 +57,7 @@ data class BackupManga(
|
||||
viewer_flags = this@BackupManga.viewer_flags ?: this@BackupManga.viewer
|
||||
chapter_flags = this@BackupManga.chapterFlags
|
||||
update_strategy = this@BackupManga.updateStrategy
|
||||
meta = this@BackupManga.meta
|
||||
}
|
||||
|
||||
fun getChaptersImpl(): List<ChapterImpl> =
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package suwayomi.tachidesk.manga.impl.backup.proto.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
import suwayomi.tachidesk.graphql.types.Settings
|
||||
import suwayomi.tachidesk.graphql.types.WebUIChannel
|
||||
import suwayomi.tachidesk.graphql.types.WebUIFlavor
|
||||
import suwayomi.tachidesk.graphql.types.WebUIInterface
|
||||
|
||||
@Serializable
|
||||
data class BackupServerSettings(
|
||||
@ProtoNumber(1) override var ip: String,
|
||||
@ProtoNumber(2) override var port: Int,
|
||||
// socks
|
||||
@ProtoNumber(3) override var socksProxyEnabled: Boolean,
|
||||
@ProtoNumber(4) override var socksProxyVersion: Int,
|
||||
@ProtoNumber(5) override var socksProxyHost: String,
|
||||
@ProtoNumber(6) override var socksProxyPort: String,
|
||||
@ProtoNumber(7) override var socksProxyUsername: String,
|
||||
@ProtoNumber(8) override var socksProxyPassword: String,
|
||||
// webUI
|
||||
@ProtoNumber(9) override var webUIFlavor: WebUIFlavor,
|
||||
@ProtoNumber(10) override var initialOpenInBrowserEnabled: Boolean,
|
||||
@ProtoNumber(11) override var webUIInterface: WebUIInterface,
|
||||
@ProtoNumber(12) override var electronPath: String,
|
||||
@ProtoNumber(13) override var webUIChannel: WebUIChannel,
|
||||
@ProtoNumber(14) override var webUIUpdateCheckInterval: Double,
|
||||
// downloader
|
||||
@ProtoNumber(15) override var downloadAsCbz: Boolean,
|
||||
@ProtoNumber(16) override var downloadsPath: String,
|
||||
@ProtoNumber(17) override var autoDownloadNewChapters: Boolean,
|
||||
@ProtoNumber(18) override var excludeEntryWithUnreadChapters: Boolean,
|
||||
@ProtoNumber(19) override var autoDownloadAheadLimit: Int,
|
||||
@ProtoNumber(20) override var autoDownloadNewChaptersLimit: Int,
|
||||
@ProtoNumber(21) override var autoDownloadIgnoreReUploads: Boolean,
|
||||
// extension
|
||||
@ProtoNumber(22) override var extensionRepos: List<String>,
|
||||
// requests
|
||||
@ProtoNumber(23) override var maxSourcesInParallel: Int,
|
||||
// updater
|
||||
@ProtoNumber(24) override var excludeUnreadChapters: Boolean,
|
||||
@ProtoNumber(25) override var excludeNotStarted: Boolean,
|
||||
@ProtoNumber(26) override var excludeCompleted: Boolean,
|
||||
@ProtoNumber(27) override var globalUpdateInterval: Double,
|
||||
@ProtoNumber(28) override var updateMangas: Boolean,
|
||||
// Authentication
|
||||
@ProtoNumber(29) override var basicAuthEnabled: Boolean,
|
||||
@ProtoNumber(30) override var basicAuthUsername: String,
|
||||
@ProtoNumber(31) override var basicAuthPassword: String,
|
||||
// misc
|
||||
@ProtoNumber(32) override var debugLogsEnabled: Boolean,
|
||||
@ProtoNumber(33) override var gqlDebugLogsEnabled: Boolean,
|
||||
@ProtoNumber(34) override var systemTrayEnabled: Boolean,
|
||||
@ProtoNumber(35) override var maxLogFiles: Int,
|
||||
@ProtoNumber(36) override var maxLogFileSize: String,
|
||||
@ProtoNumber(37) override var maxLogFolderSize: String,
|
||||
// backup
|
||||
@ProtoNumber(38) override var backupPath: String,
|
||||
@ProtoNumber(39) override var backupTime: String,
|
||||
@ProtoNumber(40) override var backupInterval: Int,
|
||||
@ProtoNumber(41) override var backupTTL: Int,
|
||||
// local source
|
||||
@ProtoNumber(42) override var localSourcePath: String,
|
||||
// cloudflare bypass
|
||||
@ProtoNumber(43) override var flareSolverrEnabled: Boolean,
|
||||
@ProtoNumber(44) override var flareSolverrUrl: String,
|
||||
@ProtoNumber(45) override var flareSolverrTimeout: Int,
|
||||
@ProtoNumber(46) override var flareSolverrSessionName: String,
|
||||
@ProtoNumber(47) override var flareSolverrSessionTtl: Int,
|
||||
@ProtoNumber(48) override var flareSolverrAsResponseFallback: Boolean,
|
||||
// opds
|
||||
@ProtoNumber(49) override var opdsUseBinaryFileSizes: Boolean,
|
||||
@ProtoNumber(50) override var opdsItemsPerPage: Int,
|
||||
@ProtoNumber(51) override var opdsEnablePageReadProgress: Boolean,
|
||||
@ProtoNumber(52) override var opdsMarkAsReadOnDownload: Boolean,
|
||||
@ProtoNumber(53) override var opdsShowOnlyUnreadChapters: Boolean,
|
||||
@ProtoNumber(54) override var opdsShowOnlyDownloadedChapters: Boolean,
|
||||
@ProtoNumber(55) override var opdsChapterSortOrder: SortOrder,
|
||||
) : Settings
|
||||
@@ -8,6 +8,8 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
||||
data class BackupSource(
|
||||
@ProtoNumber(1) var name: String = "",
|
||||
@ProtoNumber(2) var sourceId: Long,
|
||||
// suwayomi
|
||||
@ProtoNumber(9000) var meta: Map<String, String> = emptyMap(),
|
||||
) {
|
||||
companion object {
|
||||
fun copyFrom(source: Source): BackupSource =
|
||||
|
||||
Reference in New Issue
Block a user