mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-11 14:54:33 -05:00
[skip ci] Formatting
This commit is contained in:
@@ -5,7 +5,9 @@ package suwayomi.tachidesk.manga.impl.backup.models
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import java.io.Serializable
|
||||
|
||||
interface Chapter : SChapter, Serializable {
|
||||
interface Chapter :
|
||||
SChapter,
|
||||
Serializable {
|
||||
var id: Long?
|
||||
|
||||
var manga_id: Long?
|
||||
|
||||
@@ -36,7 +36,5 @@ class ChapterImpl : Chapter {
|
||||
return id == chapter.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode() + id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = url.hashCode() + id.hashCode()
|
||||
}
|
||||
|
||||
@@ -41,13 +41,9 @@ interface Manga : SManga {
|
||||
setChapterFlags(order, CHAPTER_SORT_MASK)
|
||||
}
|
||||
|
||||
fun sortDescending(): Boolean {
|
||||
return chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||
}
|
||||
fun sortDescending(): Boolean = chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||
|
||||
fun getGenres(): List<String>? {
|
||||
return genre?.split(", ")?.map { it.trim() }
|
||||
}
|
||||
fun getGenres(): List<String>? = genre?.split(", ")?.map { it.trim() }
|
||||
|
||||
private fun setChapterFlags(
|
||||
flag: Int,
|
||||
|
||||
@@ -63,7 +63,5 @@ open class MangaImpl : Manga {
|
||||
return id == manga.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode() + id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = url.hashCode() + id.hashCode()
|
||||
}
|
||||
|
||||
@@ -93,10 +93,15 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
val (hour, minute) = serverConfig.backupTime.value.split(":").map { it.toInt() }
|
||||
val (hour, minute) =
|
||||
serverConfig.backupTime.value
|
||||
.split(":")
|
||||
.map { it.toInt() }
|
||||
val backupHour = hour.coerceAtLeast(0).coerceAtMost(23)
|
||||
val backupMinute = minute.coerceAtLeast(0).coerceAtMost(59)
|
||||
val backupInterval = serverConfig.backupInterval.value.days.coerceAtLeast(1.days)
|
||||
val backupInterval =
|
||||
serverConfig.backupInterval.value.days
|
||||
.coerceAtLeast(1.days)
|
||||
|
||||
// trigger last backup in case the server wasn't running on the scheduled time
|
||||
val lastAutomatedBackup = preferences.getLong(LAST_AUTOMATED_BACKUP_KEY, 0)
|
||||
@@ -161,7 +166,10 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
|
||||
val lastAccessTime = file.lastModified()
|
||||
val isTTLReached =
|
||||
System.currentTimeMillis() - lastAccessTime >= serverConfig.backupTTL.value.days.coerceAtLeast(1.days).inWholeMilliseconds
|
||||
System.currentTimeMillis() - lastAccessTime >=
|
||||
serverConfig.backupTTL.value.days
|
||||
.coerceAtLeast(1.days)
|
||||
.inWholeMilliseconds
|
||||
if (isTTLReached) {
|
||||
file.delete()
|
||||
}
|
||||
@@ -185,7 +193,11 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
|
||||
|
||||
val byteStream = ByteArrayOutputStream()
|
||||
byteStream.sink().gzip().buffer().use { it.write(byteArray) }
|
||||
byteStream
|
||||
.sink()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.write(byteArray) }
|
||||
|
||||
return byteStream.toByteArray().inputStream()
|
||||
}
|
||||
@@ -193,8 +205,8 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
private fun backupManga(
|
||||
databaseManga: Query,
|
||||
flags: BackupFlags,
|
||||
): List<BackupManga> {
|
||||
return databaseManga.map { mangaRow ->
|
||||
): List<BackupManga> =
|
||||
databaseManga.map { mangaRow ->
|
||||
val backupManga =
|
||||
BackupManga(
|
||||
source = mangaRow[MangaTable.sourceReference],
|
||||
@@ -216,7 +228,8 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
if (flags.includeChapters) {
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.manga eq mangaId }
|
||||
ChapterTable
|
||||
.select { ChapterTable.manga eq mangaId }
|
||||
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
||||
.map {
|
||||
ChapterTable.toDataClass(it)
|
||||
@@ -277,22 +290,23 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
|
||||
backupManga
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupCategories(): List<BackupCategory> {
|
||||
return CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun backupCategories(): List<BackupCategory> =
|
||||
CategoryTable
|
||||
.selectAll()
|
||||
.orderBy(CategoryTable.order to SortOrder.ASC)
|
||||
.map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
)
|
||||
}
|
||||
|
||||
private fun backupExtensionInfo(mangas: Query): List<BackupSource> {
|
||||
return mangas
|
||||
private fun backupExtensionInfo(mangas: Query): List<BackupSource> =
|
||||
mangas
|
||||
.asSequence()
|
||||
.map { it[MangaTable.sourceReference] }
|
||||
.distinct()
|
||||
@@ -302,7 +316,5 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
sourceRow?.get(SourceTable.name) ?: "",
|
||||
it,
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
@@ -74,18 +74,22 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
|
||||
data object Failure : BackupRestoreState()
|
||||
|
||||
data class RestoringCategories(val totalManga: Int) : BackupRestoreState()
|
||||
data class RestoringCategories(
|
||||
val totalManga: Int,
|
||||
) : BackupRestoreState()
|
||||
|
||||
data class RestoringManga(val current: Int, val totalManga: Int, val title: String) : BackupRestoreState()
|
||||
data class RestoringManga(
|
||||
val current: Int,
|
||||
val totalManga: Int,
|
||||
val title: String,
|
||||
) : BackupRestoreState()
|
||||
}
|
||||
|
||||
private val backupRestoreIdToState = mutableMapOf<String, BackupRestoreState>()
|
||||
|
||||
val notifyFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST)
|
||||
|
||||
fun getRestoreState(id: String): BackupRestoreState? {
|
||||
return backupRestoreIdToState[id]
|
||||
}
|
||||
fun getRestoreState(id: String): BackupRestoreState? = backupRestoreIdToState[id]
|
||||
|
||||
private fun updateRestoreState(
|
||||
id: String,
|
||||
@@ -131,8 +135,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
suspend fun restoreLegacy(
|
||||
sourceStream: InputStream,
|
||||
restoreId: String = "legacy",
|
||||
): ValidationResult {
|
||||
return backupMutex.withLock {
|
||||
): ValidationResult =
|
||||
backupMutex.withLock {
|
||||
try {
|
||||
logger.info { "restore($restoreId): restoring..." }
|
||||
performRestore(restoreId, sourceStream)
|
||||
@@ -151,13 +155,17 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
cleanupRestoreState(restoreId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performRestore(
|
||||
id: String,
|
||||
sourceStream: InputStream,
|
||||
): ValidationResult {
|
||||
val backupString = sourceStream.source().gzip().buffer().use { it.readByteArray() }
|
||||
val backupString =
|
||||
sourceStream
|
||||
.source()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.readByteArray() }
|
||||
val backup = parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
|
||||
val validationResult = validate(backup)
|
||||
@@ -174,7 +182,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
transaction {
|
||||
backup.backupCategories.associate {
|
||||
val dbCategory =
|
||||
CategoryTable.select { CategoryTable.name eq it.name }
|
||||
CategoryTable
|
||||
.select { CategoryTable.name eq it.name }
|
||||
.firstOrNull()
|
||||
val categoryId =
|
||||
dbCategory?.let { categoryResultRow ->
|
||||
@@ -265,7 +274,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
) {
|
||||
val dbManga =
|
||||
transaction {
|
||||
MangaTable.select { (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }
|
||||
MangaTable
|
||||
.select { (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
@@ -274,26 +284,27 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
transaction {
|
||||
// insert manga to database
|
||||
val mangaId =
|
||||
MangaTable.insertAndGetId {
|
||||
it[url] = manga.url
|
||||
it[title] = manga.title
|
||||
MangaTable
|
||||
.insertAndGetId {
|
||||
it[url] = manga.url
|
||||
it[title] = manga.title
|
||||
|
||||
it[artist] = manga.artist
|
||||
it[author] = manga.author
|
||||
it[description] = manga.description
|
||||
it[genre] = manga.genre
|
||||
it[status] = manga.status
|
||||
it[thumbnail_url] = manga.thumbnail_url
|
||||
it[updateStrategy] = manga.update_strategy.name
|
||||
it[artist] = manga.artist
|
||||
it[author] = manga.author
|
||||
it[description] = manga.description
|
||||
it[genre] = manga.genre
|
||||
it[status] = manga.status
|
||||
it[thumbnail_url] = manga.thumbnail_url
|
||||
it[updateStrategy] = manga.update_strategy.name
|
||||
|
||||
it[sourceReference] = manga.source
|
||||
it[sourceReference] = manga.source
|
||||
|
||||
it[initialized] = manga.description != null
|
||||
it[initialized] = manga.description != null
|
||||
|
||||
it[inLibrary] = manga.favorite
|
||||
it[inLibrary] = manga.favorite
|
||||
|
||||
it[inLibraryAt] = TimeUnit.MILLISECONDS.toSeconds(manga.date_added)
|
||||
}.value
|
||||
it[inLibraryAt] = TimeUnit.MILLISECONDS.toSeconds(manga.date_added)
|
||||
}.value
|
||||
|
||||
// delete thumbnail in case cached data still exists
|
||||
clearThumbnail(mangaId)
|
||||
@@ -394,34 +405,36 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
}
|
||||
|
||||
val dbTrackRecordsByTrackerId =
|
||||
Tracker.getTrackRecordsByMangaId(mangaId)
|
||||
Tracker
|
||||
.getTrackRecordsByMangaId(mangaId)
|
||||
.mapNotNull { it.record?.toTrack() }
|
||||
.associateBy { it.sync_id }
|
||||
|
||||
val (existingTracks, newTracks) =
|
||||
tracks.mapNotNull { backupTrack ->
|
||||
val track = backupTrack.toTrack(mangaId)
|
||||
tracks
|
||||
.mapNotNull { backupTrack ->
|
||||
val track = backupTrack.toTrack(mangaId)
|
||||
|
||||
val isUnsupportedTracker = TrackerManager.getTracker(track.sync_id) == null
|
||||
if (isUnsupportedTracker) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
val isUnsupportedTracker = TrackerManager.getTracker(track.sync_id) == null
|
||||
if (isUnsupportedTracker) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val dbTrack =
|
||||
dbTrackRecordsByTrackerId[backupTrack.syncId]
|
||||
?: // new track
|
||||
return@mapNotNull track
|
||||
val dbTrack =
|
||||
dbTrackRecordsByTrackerId[backupTrack.syncId]
|
||||
?: // new track
|
||||
return@mapNotNull track
|
||||
|
||||
if (track.toTrackRecordDataClass().forComparison() == dbTrack.toTrackRecordDataClass().forComparison()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
if (track.toTrackRecordDataClass().forComparison() == dbTrack.toTrackRecordDataClass().forComparison()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
dbTrack.also {
|
||||
it.media_id = track.media_id
|
||||
it.library_id = track.library_id
|
||||
it.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read)
|
||||
}
|
||||
}.partition { (it.id ?: -1) > 0 }
|
||||
dbTrack.also {
|
||||
it.media_id = track.media_id
|
||||
it.library_id = track.library_id
|
||||
it.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read)
|
||||
}
|
||||
}.partition { (it.id ?: -1) > 0 }
|
||||
|
||||
existingTracks.forEach(Tracker::updateTrackRecord)
|
||||
newTracks.forEach(Tracker::insertTrackRecord)
|
||||
|
||||
@@ -64,7 +64,12 @@ object ProtoBackupValidator {
|
||||
}
|
||||
|
||||
fun validate(sourceStream: InputStream): ValidationResult {
|
||||
val backupString = sourceStream.source().gzip().buffer().use { it.readByteArray() }
|
||||
val backupString =
|
||||
sourceStream
|
||||
.source()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.readByteArray() }
|
||||
val backup = ProtoBackupImport.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
|
||||
return validate(backup)
|
||||
|
||||
@@ -14,10 +14,9 @@ data class Backup(
|
||||
@ProtoNumber(100) var brokenBackupSources: List<BrokenBackupSource> = emptyList(),
|
||||
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
||||
) {
|
||||
fun getSourceMap(): Map<Long, String> {
|
||||
return (brokenBackupSources.map { BackupSource(it.name, it.sourceId) } + backupSources)
|
||||
fun getSourceMap(): Map<Long, String> =
|
||||
(brokenBackupSources.map { BackupSource(it.name, it.sourceId) } + backupSources)
|
||||
.associate { it.sourceId to it.name }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getBasename(name: String = ""): String {
|
||||
|
||||
@@ -21,8 +21,8 @@ data class BackupChapter(
|
||||
@ProtoNumber(9) var chapterNumber: Float = 0F,
|
||||
@ProtoNumber(10) var sourceOrder: Int = 0,
|
||||
) {
|
||||
fun toChapterImpl(): ChapterImpl {
|
||||
return ChapterImpl().apply {
|
||||
fun toChapterImpl(): ChapterImpl =
|
||||
ChapterImpl().apply {
|
||||
url = this@BackupChapter.url
|
||||
name = this@BackupChapter.name
|
||||
chapter_number = this@BackupChapter.chapterNumber
|
||||
@@ -34,5 +34,4 @@ data class BackupChapter(
|
||||
date_upload = this@BackupChapter.dateUpload
|
||||
source_order = this@BackupChapter.sourceOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ data class BackupManga(
|
||||
@ProtoNumber(104) var history: List<BackupHistory> = emptyList(),
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
) {
|
||||
fun getMangaImpl(): MangaImpl {
|
||||
return MangaImpl().apply {
|
||||
fun getMangaImpl(): MangaImpl =
|
||||
MangaImpl().apply {
|
||||
url = this@BackupManga.url
|
||||
title = this@BackupManga.title
|
||||
artist = this@BackupManga.artist
|
||||
@@ -56,23 +56,20 @@ data class BackupManga(
|
||||
chapter_flags = this@BackupManga.chapterFlags
|
||||
update_strategy = this@BackupManga.updateStrategy
|
||||
}
|
||||
}
|
||||
|
||||
fun getChaptersImpl(): List<ChapterImpl> {
|
||||
return chapters.map {
|
||||
fun getChaptersImpl(): List<ChapterImpl> =
|
||||
chapters.map {
|
||||
it.toChapterImpl()
|
||||
}
|
||||
}
|
||||
|
||||
fun getTrackingImpl(): List<TrackImpl> {
|
||||
return tracking.map {
|
||||
fun getTrackingImpl(): List<TrackImpl> =
|
||||
tracking.map {
|
||||
it.getTrackingImpl()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun copyFrom(manga: Manga): BackupManga {
|
||||
return BackupManga(
|
||||
fun copyFrom(manga: Manga): BackupManga =
|
||||
BackupManga(
|
||||
url = manga.url,
|
||||
title = manga.title,
|
||||
artist = manga.artist,
|
||||
@@ -88,6 +85,5 @@ data class BackupManga(
|
||||
viewer_flags = manga.viewer_flags,
|
||||
chapterFlags = manga.chapter_flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@ data class BackupSource(
|
||||
@ProtoNumber(2) var sourceId: Long,
|
||||
) {
|
||||
companion object {
|
||||
fun copyFrom(source: Source): BackupSource {
|
||||
return BackupSource(
|
||||
fun copyFrom(source: Source): BackupSource =
|
||||
BackupSource(
|
||||
name = source.name,
|
||||
sourceId = source.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ data class BackupTracking(
|
||||
@ProtoNumber(11) var finishedReadingDate: Long = 0,
|
||||
@ProtoNumber(100) var mediaId: Long = 0,
|
||||
) {
|
||||
fun getTrackingImpl(): TrackImpl {
|
||||
return TrackImpl().apply {
|
||||
fun getTrackingImpl(): TrackImpl =
|
||||
TrackImpl().apply {
|
||||
sync_id = this@BackupTracking.syncId
|
||||
media_id =
|
||||
if (this@BackupTracking.mediaIdInt != 0) {
|
||||
@@ -48,11 +48,10 @@ data class BackupTracking(
|
||||
finished_reading_date = this@BackupTracking.finishedReadingDate
|
||||
tracking_url = this@BackupTracking.trackingUrl
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun copyFrom(track: Track): BackupTracking {
|
||||
return BackupTracking(
|
||||
fun copyFrom(track: Track): BackupTracking =
|
||||
BackupTracking(
|
||||
syncId = track.sync_id,
|
||||
mediaId = track.media_id,
|
||||
// forced not null so its compatible with 1.x backup system
|
||||
@@ -67,6 +66,5 @@ data class BackupTracking(
|
||||
finishedReadingDate = track.finished_reading_date,
|
||||
trackingUrl = track.tracking_url,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user