mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-11 23:04:33 -05:00
Implement SyncYomi (#1813)
* Implement SyncYomi
* Add ability to select what to sync
* Properly fix default category bug
* Add periodic sync
* Add PostgreSQL support
* Deschedule previous task
* Check if SyncYomi is enabled in syncData function
* Don't allow multiple syncs at the same time
* Convert SyncYomiSyncService to object
* Make startSync non-suspend
* Return a result from startSync
* Sync before library update
* Improvements
* Use NetworkHelper client
* Lint
* Use measureTime
* Database improvements
- Move entire sync operation into a single transaction
- Stop loading all manga to memory
* Revert "Database improvements"
This reverts commit bee8d214c3.
* Actual database improvements
* Remove runBlocking
* Remove title check
* Update updateNonFavorites function
* Update timeout code
* Improve PostgreSQL query
* Create lastSyncState variable
* Create lastSyncStatus query
* Convert lastSyncState to StateFlow
* Create lastSyncStatusChange subscription
* Replace backupRestoreStatus with backupRestoreId
* Add startDate and endDate
* Add logs for sync start and end
* Handle all errors in syncData
* Change category restore function to match Mihon's behavior
* Fix comment
* Remove duplicate BackupMangaHandler.backup call
* Remove duplicated log
* Rename subscription to syncStatusChanged
* Use same flags for restoring
* Update syncInterval config to use DurationSetting
* Update sync scheduling logic
* Reorder conditions to reduce database calls
* Prevent deleted ghost chapters from reappearing during sync
jobobby04/TachiyomiSY#1575
* Improve sync merging categories
jobobby04/TachiyomiSY#1559
* Make columns not null
* Improve H2 triggers
* Add documentation
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import suwayomi.tachidesk.global.impl.sync.SyncManager
|
||||
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
||||
import suwayomi.tachidesk.graphql.types.StartSyncResult
|
||||
|
||||
class SyncMutation {
|
||||
data class StartSyncInput(
|
||||
val clientMutationId: String? = null,
|
||||
)
|
||||
|
||||
data class StartSyncPayload(
|
||||
val clientMutationId: String? = null,
|
||||
val result: StartSyncResult,
|
||||
)
|
||||
|
||||
@RequireAuth
|
||||
fun startSync(input: StartSyncInput): StartSyncPayload {
|
||||
val (clientMutationId) = input
|
||||
|
||||
val result = SyncManager.startSync()
|
||||
|
||||
return StartSyncPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
result = result,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package suwayomi.tachidesk.graphql.queries
|
||||
|
||||
import suwayomi.tachidesk.global.impl.sync.SyncManager
|
||||
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
||||
import suwayomi.tachidesk.graphql.types.SyncStatus
|
||||
import suwayomi.tachidesk.graphql.types.toStatus
|
||||
|
||||
class SyncQuery {
|
||||
@RequireAuth
|
||||
fun lastSyncStatus(): SyncStatus? = SyncManager.lastSyncState.value?.toStatus()
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import suwayomi.tachidesk.graphql.mutations.MangaMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.MetaMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.SettingsMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.SourceMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.SyncMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.TrackMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.UpdateMutation
|
||||
import suwayomi.tachidesk.graphql.mutations.UserMutation
|
||||
@@ -41,6 +42,7 @@ import suwayomi.tachidesk.graphql.queries.MangaQuery
|
||||
import suwayomi.tachidesk.graphql.queries.MetaQuery
|
||||
import suwayomi.tachidesk.graphql.queries.SettingsQuery
|
||||
import suwayomi.tachidesk.graphql.queries.SourceQuery
|
||||
import suwayomi.tachidesk.graphql.queries.SyncQuery
|
||||
import suwayomi.tachidesk.graphql.queries.TrackQuery
|
||||
import suwayomi.tachidesk.graphql.queries.UpdateQuery
|
||||
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||
@@ -50,6 +52,7 @@ import suwayomi.tachidesk.graphql.server.primitives.GraphQLLongAsString
|
||||
import suwayomi.tachidesk.graphql.server.primitives.GraphQLUpload
|
||||
import suwayomi.tachidesk.graphql.subscriptions.DownloadSubscription
|
||||
import suwayomi.tachidesk.graphql.subscriptions.InfoSubscription
|
||||
import suwayomi.tachidesk.graphql.subscriptions.SyncSubscription
|
||||
import suwayomi.tachidesk.graphql.subscriptions.UpdateSubscription
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
@@ -98,6 +101,7 @@ val schema =
|
||||
TopLevelObject(MetaQuery()),
|
||||
TopLevelObject(SettingsQuery()),
|
||||
TopLevelObject(SourceQuery()),
|
||||
TopLevelObject(SyncQuery()),
|
||||
TopLevelObject(TrackQuery()),
|
||||
TopLevelObject(UpdateQuery()),
|
||||
),
|
||||
@@ -114,6 +118,7 @@ val schema =
|
||||
TopLevelObject(MangaMutation()),
|
||||
TopLevelObject(MetaMutation()),
|
||||
TopLevelObject(SettingsMutation()),
|
||||
TopLevelObject(SyncMutation()),
|
||||
TopLevelObject(SourceMutation()),
|
||||
TopLevelObject(TrackMutation()),
|
||||
TopLevelObject(UpdateMutation()),
|
||||
@@ -123,6 +128,7 @@ val schema =
|
||||
listOf(
|
||||
TopLevelObject(DownloadSubscription()),
|
||||
TopLevelObject(InfoSubscription()),
|
||||
TopLevelObject(SyncSubscription()),
|
||||
TopLevelObject(UpdateSubscription()),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package suwayomi.tachidesk.graphql.subscriptions
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import suwayomi.tachidesk.global.impl.sync.SyncManager
|
||||
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
||||
import suwayomi.tachidesk.graphql.types.SyncStatus
|
||||
import suwayomi.tachidesk.graphql.types.toStatus
|
||||
|
||||
class SyncSubscription {
|
||||
@RequireAuth
|
||||
fun syncStatusChanged(): Flow<SyncStatus> =
|
||||
SyncManager.lastSyncState
|
||||
.filterNotNull()
|
||||
.map { it.toStatus() }
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package suwayomi.tachidesk.graphql.types
|
||||
|
||||
import suwayomi.tachidesk.global.impl.sync.SyncManager
|
||||
|
||||
enum class StartSyncResult {
|
||||
SUCCESS,
|
||||
SYNC_IN_PROGRESS,
|
||||
SYNC_DISABLED,
|
||||
}
|
||||
|
||||
enum class SyncState {
|
||||
STARTED,
|
||||
CREATING_BACKUP,
|
||||
DOWNLOADING,
|
||||
MERGING,
|
||||
UPLOADING,
|
||||
RESTORING,
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
data class SyncStatus(
|
||||
val state: SyncState,
|
||||
val startDate: Long,
|
||||
val endDate: Long? = null,
|
||||
val backupRestoreId: String? = null,
|
||||
val errorMessage: String? = null,
|
||||
)
|
||||
|
||||
fun SyncManager.SyncState.toStatus(): SyncStatus =
|
||||
when (this) {
|
||||
is SyncManager.SyncState.Started -> {
|
||||
SyncStatus(
|
||||
state = SyncState.STARTED,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.CreatingBackup -> {
|
||||
SyncStatus(
|
||||
state = SyncState.CREATING_BACKUP,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Downloading -> {
|
||||
SyncStatus(
|
||||
state = SyncState.DOWNLOADING,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Merging -> {
|
||||
SyncStatus(
|
||||
state = SyncState.MERGING,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Uploading -> {
|
||||
SyncStatus(
|
||||
state = SyncState.UPLOADING,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Restoring -> {
|
||||
SyncStatus(
|
||||
state = SyncState.RESTORING,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
backupRestoreId = restoreId,
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Success -> {
|
||||
SyncStatus(
|
||||
state = SyncState.SUCCESS,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
endDate = endDate.toEpochMilliseconds(),
|
||||
)
|
||||
}
|
||||
|
||||
is SyncManager.SyncState.Error -> {
|
||||
SyncStatus(
|
||||
state = SyncState.ERROR,
|
||||
startDate = startDate.toEpochMilliseconds(),
|
||||
endDate = endDate.toEpochMilliseconds(),
|
||||
errorMessage = message,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user