mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: Preparing sync
This commit is contained in:
parent
4521857a52
commit
ed1b2326ed
9 changed files with 278 additions and 9 deletions
|
@ -0,0 +1,72 @@
|
|||
package dev.yokai.data.sync.models
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
data class SyncTriggerOptions(
|
||||
val syncOnChapterRead: Boolean = false,
|
||||
val syncOnChapterOpen: Boolean = false,
|
||||
val syncOnAppStart: Boolean = false,
|
||||
val syncOnAppResume: Boolean = false,
|
||||
val syncOnLibraryUpdate: Boolean = false,
|
||||
) {
|
||||
fun asBooleanArray() = booleanArrayOf(
|
||||
syncOnChapterRead,
|
||||
syncOnChapterOpen,
|
||||
syncOnAppStart,
|
||||
syncOnAppResume,
|
||||
syncOnLibraryUpdate,
|
||||
)
|
||||
|
||||
fun anyEnabled() = syncOnChapterRead ||
|
||||
syncOnChapterOpen ||
|
||||
syncOnAppStart ||
|
||||
syncOnAppResume ||
|
||||
syncOnLibraryUpdate
|
||||
|
||||
companion object {
|
||||
val mainOptions = persistentListOf(
|
||||
Entry(
|
||||
label = R.string.sync_on_chapter_read,
|
||||
getter = SyncTriggerOptions::syncOnChapterRead,
|
||||
setter = { options, enabled -> options.copy(syncOnChapterRead = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.sync_on_chapter_open,
|
||||
getter = SyncTriggerOptions::syncOnChapterOpen,
|
||||
setter = { options, enabled -> options.copy(syncOnChapterOpen = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.sync_on_app_start,
|
||||
getter = SyncTriggerOptions::syncOnAppStart,
|
||||
setter = { options, enabled -> options.copy(syncOnAppStart = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.sync_on_app_resume,
|
||||
getter = SyncTriggerOptions::syncOnAppResume,
|
||||
setter = { options, enabled -> options.copy(syncOnAppResume = enabled) },
|
||||
),
|
||||
Entry(
|
||||
label = R.string.sync_on_library_update,
|
||||
getter = SyncTriggerOptions::syncOnLibraryUpdate,
|
||||
setter = { options, enabled -> options.copy(syncOnLibraryUpdate = enabled) },
|
||||
),
|
||||
)
|
||||
|
||||
fun fromBooleanArray(array: BooleanArray) = SyncTriggerOptions(
|
||||
syncOnChapterRead = array[0],
|
||||
syncOnChapterOpen = array[1],
|
||||
syncOnAppStart = array[2],
|
||||
syncOnAppResume = array[3],
|
||||
syncOnLibraryUpdate = array[4],
|
||||
)
|
||||
}
|
||||
|
||||
data class Entry(
|
||||
@StringRes val label: Int,
|
||||
val getter: (SyncTriggerOptions) -> Boolean,
|
||||
val setter: (SyncTriggerOptions, Boolean) -> SyncTriggerOptions,
|
||||
val enabled: (SyncTriggerOptions) -> Boolean = { true },
|
||||
)
|
||||
}
|
92
app/src/main/java/dev/yokai/domain/sync/SyncPreferences.kt
Normal file
92
app/src/main/java/dev/yokai/domain/sync/SyncPreferences.kt
Normal file
|
@ -0,0 +1,92 @@
|
|||
package dev.yokai.domain.sync
|
||||
|
||||
import dev.yokai.data.sync.models.SyncTriggerOptions
|
||||
import dev.yokai.domain.sync.models.SyncSettings
|
||||
import eu.kanade.tachiyomi.core.preference.Preference.Companion.appStateKey
|
||||
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
||||
import java.util.*
|
||||
|
||||
class SyncPreferences(
|
||||
private val preferenceStore: PreferenceStore,
|
||||
) {
|
||||
fun clientHost() = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
|
||||
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "")
|
||||
fun lastSyncTimestamp() = preferenceStore.getLong(appStateKey("last_sync_timestamp"), 0L)
|
||||
|
||||
fun syncInterval() = preferenceStore.getInt("sync_interval", 0)
|
||||
fun syncService() = preferenceStore.getInt("sync_service", 0)
|
||||
|
||||
fun googleDriveAccessToken() = preferenceStore.getString(
|
||||
appStateKey("google_drive_access_token"),
|
||||
"",
|
||||
)
|
||||
|
||||
fun googleDriveRefreshToken() = preferenceStore.getString(
|
||||
appStateKey("google_drive_refresh_token"),
|
||||
"",
|
||||
)
|
||||
|
||||
fun uniqueDeviceID(): String {
|
||||
val uniqueIDPreference = preferenceStore.getString(appStateKey("unique_device_id"), "")
|
||||
|
||||
// Retrieve the current value of the preference
|
||||
var uniqueID = uniqueIDPreference.get()
|
||||
if (uniqueID.isBlank()) {
|
||||
uniqueID = UUID.randomUUID().toString()
|
||||
uniqueIDPreference.set(uniqueID)
|
||||
}
|
||||
|
||||
return uniqueID
|
||||
}
|
||||
|
||||
fun isSyncEnabled(): Boolean {
|
||||
return syncService().get() != 0
|
||||
}
|
||||
|
||||
fun getSyncSettings(): SyncSettings {
|
||||
return SyncSettings(
|
||||
libraryEntries = preferenceStore.getBoolean("library_entries", true).get(),
|
||||
categories = preferenceStore.getBoolean("categories", true).get(),
|
||||
chapters = preferenceStore.getBoolean("chapters", true).get(),
|
||||
tracking = preferenceStore.getBoolean("tracking", true).get(),
|
||||
history = preferenceStore.getBoolean("history", true).get(),
|
||||
appSettings = preferenceStore.getBoolean("appSettings", true).get(),
|
||||
sourceSettings = preferenceStore.getBoolean("sourceSettings", true).get(),
|
||||
privateSettings = preferenceStore.getBoolean("privateSettings", true).get(),
|
||||
)
|
||||
}
|
||||
|
||||
fun setSyncSettings(syncSettings: SyncSettings) {
|
||||
preferenceStore.getBoolean("library_entries", true).set(syncSettings.libraryEntries)
|
||||
preferenceStore.getBoolean("categories", true).set(syncSettings.categories)
|
||||
preferenceStore.getBoolean("chapters", true).set(syncSettings.chapters)
|
||||
preferenceStore.getBoolean("tracking", true).set(syncSettings.tracking)
|
||||
preferenceStore.getBoolean("history", true).set(syncSettings.history)
|
||||
preferenceStore.getBoolean("appSettings", true).set(syncSettings.appSettings)
|
||||
preferenceStore.getBoolean("sourceSettings", true).set(syncSettings.sourceSettings)
|
||||
preferenceStore.getBoolean("privateSettings", true).set(syncSettings.privateSettings)
|
||||
}
|
||||
|
||||
fun getSyncTriggerOptions(): SyncTriggerOptions {
|
||||
return SyncTriggerOptions(
|
||||
syncOnChapterRead = preferenceStore.getBoolean("sync_on_chapter_read", false).get(),
|
||||
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
|
||||
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
|
||||
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
|
||||
syncOnLibraryUpdate = preferenceStore.getBoolean("sync_on_library_update", false).get(),
|
||||
)
|
||||
}
|
||||
|
||||
fun setSyncTriggerOptions(syncTriggerOptions: SyncTriggerOptions) {
|
||||
preferenceStore.getBoolean("sync_on_chapter_read", false)
|
||||
.set(syncTriggerOptions.syncOnChapterRead)
|
||||
preferenceStore.getBoolean("sync_on_chapter_open", false)
|
||||
.set(syncTriggerOptions.syncOnChapterOpen)
|
||||
preferenceStore.getBoolean("sync_on_app_start", false)
|
||||
.set(syncTriggerOptions.syncOnAppStart)
|
||||
preferenceStore.getBoolean("sync_on_app_resume", false)
|
||||
.set(syncTriggerOptions.syncOnAppResume)
|
||||
preferenceStore.getBoolean("sync_on_library_update", false)
|
||||
.set(syncTriggerOptions.syncOnLibraryUpdate)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package dev.yokai.domain.sync.models
|
||||
|
||||
data class SyncSettings(
|
||||
val libraryEntries: Boolean = true,
|
||||
val categories: Boolean = true,
|
||||
val chapters: Boolean = true,
|
||||
val tracking: Boolean = true,
|
||||
val history: Boolean = true,
|
||||
val appSettings: Boolean = true,
|
||||
val sourceSettings: Boolean = true,
|
||||
val privateSettings: Boolean = false,
|
||||
)
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
|
|||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_HIDE_TITLE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_INITIALIZED
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_IS_SYNCING
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_LAST_UPDATE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_SOURCE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_STATUS
|
||||
|
@ -29,9 +30,11 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_THUMBNAIL_URL
|
|||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_TITLE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_UPDATE_STRATEGY
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_URL
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_VERSION
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_VIEWER
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.TABLE
|
||||
import eu.kanade.tachiyomi.data.database.updateStrategyAdapter
|
||||
import eu.kanade.tachiyomi.util.system.toBoolean
|
||||
|
||||
class MangaTypeMapping : SQLiteTypeMapping<Manga>(
|
||||
MangaPutResolver(),
|
||||
|
@ -71,6 +74,8 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
|
|||
put(COL_DATE_ADDED, obj.date_added)
|
||||
put(COL_FILTERED_SCANLATORS, obj.filtered_scanlators)
|
||||
put(COL_UPDATE_STRATEGY, obj.update_strategy.let(updateStrategyAdapter::encode))
|
||||
put(COL_VERSION, obj.version)
|
||||
put(COL_IS_SYNCING, obj.isSyncing)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,17 +91,19 @@ interface BaseMangaGetResolver {
|
|||
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
|
||||
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
|
||||
thumbnail_url = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL))
|
||||
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1
|
||||
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)).toBoolean()
|
||||
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
|
||||
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)).toBoolean()
|
||||
viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
|
||||
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
|
||||
hide_title = cursor.getInt(cursor.getColumnIndex(COL_HIDE_TITLE)) == 1
|
||||
hide_title = cursor.getInt(cursor.getColumnIndex(COL_HIDE_TITLE)).toBoolean()
|
||||
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
|
||||
filtered_scanlators = cursor.getString(cursor.getColumnIndex(COL_FILTERED_SCANLATORS))
|
||||
update_strategy = cursor.getInt(cursor.getColumnIndex(COL_UPDATE_STRATEGY)).let(
|
||||
updateStrategyAdapter::decode,
|
||||
)
|
||||
version = cursor.getInt(cursor.getColumnIndex(COL_VERSION))
|
||||
isSyncing = cursor.getInt(cursor.getColumnIndex(COL_IS_SYNCING)).toBoolean()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@ import eu.kanade.tachiyomi.source.model.SManga
|
|||
import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
||||
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
||||
import eu.kanade.tachiyomi.util.system.toBoolean
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
|
||||
interface Manga : SManga {
|
||||
|
||||
|
@ -34,6 +35,10 @@ interface Manga : SManga {
|
|||
|
||||
var filtered_scanlators: String?
|
||||
|
||||
var version: Int
|
||||
|
||||
var isSyncing: Boolean
|
||||
|
||||
fun isBlank() = id == Long.MIN_VALUE
|
||||
fun isHidden() = status == -1
|
||||
|
||||
|
@ -359,7 +364,9 @@ interface Manga : SManga {
|
|||
chapterFlags: Long,
|
||||
dateAdded: Long?,
|
||||
filteredScanlators: String?,
|
||||
updateStrategy: Long
|
||||
updateStrategy: Long,
|
||||
version: Long,
|
||||
isSyncing: Long,
|
||||
): Manga = create(source).apply {
|
||||
this.id = id
|
||||
this.url = url
|
||||
|
@ -370,15 +377,17 @@ interface Manga : SManga {
|
|||
this.title = title
|
||||
this.status = status.toInt()
|
||||
this.thumbnail_url = thumbnailUrl
|
||||
this.favorite = favorite > 0
|
||||
this.favorite = favorite.toBoolean()
|
||||
this.last_update = lastUpdate ?: 0L
|
||||
this.initialized = initialized
|
||||
this.viewer_flags = viewerFlags.toInt()
|
||||
this.chapter_flags = chapterFlags.toInt()
|
||||
this.hide_title = hideTitle > 0
|
||||
this.hide_title = hideTitle.toBoolean()
|
||||
this.date_added = dateAdded ?: 0L
|
||||
this.filtered_scanlators = filteredScanlators
|
||||
this.update_strategy = updateStrategy.toInt().let(updateStrategyAdapter::decode)
|
||||
this.version = version.toInt()
|
||||
this.isSyncing = isSyncing.toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,4 +42,7 @@ object MangaTable {
|
|||
|
||||
const val COL_UPDATE_STRATEGY = "update_strategy"
|
||||
|
||||
const val COL_VERSION = "version"
|
||||
|
||||
const val COL_IS_SYNCING = "is_syncing"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
fun Boolean.toInt() = if (this) 1 else 0
|
||||
fun Int.toBoolean() = this == 1
|
||||
fun Number.toBoolean() = this == 1
|
||||
|
|
|
@ -847,6 +847,60 @@
|
|||
</plurals>
|
||||
<string name="not_logged_into_">Not logged into %1$s</string>
|
||||
<string name="backup_private_pref">Include sensitive settings (e.g. tracker login tokens)</string>
|
||||
<string name="label_sync">Sync</string>
|
||||
<string name="label_triggers">Triggers</string>
|
||||
<string name="pref_backup_and_sync_summary">Manual & automatic backups and sync</string>
|
||||
|
||||
<!-- Sync section -->
|
||||
<string name="syncing_library">Syncing library</string>
|
||||
<string name="library_sync_complete">Library sync complete</string>
|
||||
<string name="sync_error">Syncing library failed</string>
|
||||
<string name="sync_complete">Syncing library complete</string>
|
||||
<string name="sync_in_progress">Sync is already in progress</string>
|
||||
<string name="pref_sync_host">Host</string>
|
||||
<string name="pref_sync_host_summ">Enter the host address for synchronizing your library</string>
|
||||
<string name="pref_sync_api_key">API key</string>
|
||||
<string name="pref_sync_api_key_summ">Enter the API key to synchronize your library</string>
|
||||
<string name="pref_sync_now_group_title">Sync Actions</string>
|
||||
<string name="pref_sync_now">Sync now</string>
|
||||
<string name="pref_sync_confirmation_title">Sync confirmation</string>
|
||||
<string name="pref_sync_now_subtitle">Initiate immediate synchronization of your data</string>
|
||||
<string name="pref_sync_confirmation_message">Syncing will overwrite your local library with the remote library. Are you sure you want to continue?</string>
|
||||
<string name="pref_sync_service">Service</string>
|
||||
<string name="pref_sync_service_summ">Select the service to sync your library with</string>
|
||||
<string name="pref_sync_service_category">Sync</string>
|
||||
<string name="pref_sync_automatic_category">Automatic Synchronization</string>
|
||||
<string name="pref_sync_interval">Synchronization frequency</string>
|
||||
<string name="pref_choose_what_to_sync">Choose what to sync</string>
|
||||
<string name="success_reset_sync_timestamp">Last sync timestamp reset</string>
|
||||
<string name="syncyomi">SyncYomi</string>
|
||||
<string name="sync_completed_message">Done in %1$s</string>
|
||||
<string name="last_synchronization">Last Synchronization: %1$s</string>
|
||||
<string name="google_drive">Google Drive</string>
|
||||
<string name="pref_google_drive_sign_in">Sign in</string>
|
||||
<string name="google_drive_sign_in_success">Signed in successfully</string>
|
||||
<string name="google_drive_sign_in_failed">Sign in failed</string>
|
||||
<string name="authentication">Authentication</string>
|
||||
<string name="pref_google_drive_purge_sync_data">Clear Sync Data from Google Drive</string>
|
||||
<string name="google_drive_sync_data_purged">Sync data purged from Google Drive</string>
|
||||
<string name="google_drive_sync_data_not_found">No sync data found in Google Drive</string>
|
||||
<string name="google_drive_sync_data_purge_error">Error purging sync data from Google Drive, Try to sign in again.</string>
|
||||
<string name="google_drive_login_success">Logged in to Google Drive</string>
|
||||
<string name="google_drive_login_failed">Failed to log in to Google Drive: %s</string>
|
||||
<string name="google_drive_not_signed_in">Not signed in to Google Drive</string>
|
||||
<string name="error_uploading_sync_data">Error uploading sync data to Google Drive</string>
|
||||
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
|
||||
<string name="error_before_sync_gdrive">Error before sync: %s</string>
|
||||
<string name="pref_purge_confirmation_title">Purge confirmation</string>
|
||||
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
|
||||
<string name="pref_sync_options">Create sync triggers</string>
|
||||
<string name="pref_sync_options_summ">Can be used to set sync triggers</string>
|
||||
<string name="sync_on_chapter_read">Sync on Chapter Read</string>
|
||||
<string name="sync_on_chapter_open">Sync on Chapter Open</string>
|
||||
<string name="sync_on_app_start">Sync on App Start</string>
|
||||
<string name="sync_on_app_resume">Sync on App Resume</string>
|
||||
<string name="sync_on_library_update">Sync on Library Update</string>
|
||||
<string name="sync_library">Sync library</string>
|
||||
|
||||
<!-- Advanced section -->
|
||||
<string name="clear_chapter_cache">Clear chapter cache</string>
|
||||
|
@ -1097,6 +1151,9 @@
|
|||
|
||||
<!-- Time -->
|
||||
<string name="manual">Manual</string>
|
||||
<string name="every_30_min">Every 30 minutes</string>
|
||||
<string name="every_1_hour">Every hour</string>
|
||||
<string name="every_3_hour">Every 3 hours</string>
|
||||
<string name="every_6_hours">Every 6 hours</string>
|
||||
<string name="every_12_hours">Every 12 hours</string>
|
||||
<string name="daily">Daily</string>
|
||||
|
|
|
@ -20,12 +20,29 @@ CREATE TABLE mangas(
|
|||
chapter_flags INTEGER NOT NULL,
|
||||
date_added INTEGER AS Long,
|
||||
filtered_scanlators TEXT,
|
||||
update_strategy INTEGER NOT NULL DEFAULT 0
|
||||
update_strategy INTEGER NOT NULL DEFAULT 0,
|
||||
version INTEGER NOT NULL DEFAULT 0,
|
||||
is_syncing INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX mangas_url_index ON mangas(url);
|
||||
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
|
||||
|
||||
CREATE TRIGGER update_manga_version AFTER UPDATE ON mangas
|
||||
BEGIN
|
||||
UPDATE mangas SET version = version + 1
|
||||
WHERE _id = new._id AND new.is_syncing = 0 AND (
|
||||
new.url != old.url OR
|
||||
new.description != old.description OR
|
||||
new.favorite != old.favorite
|
||||
);
|
||||
END;
|
||||
|
||||
findAll:
|
||||
SELECT *
|
||||
FROM mangas;
|
||||
|
||||
resetIsSyncing:
|
||||
UPDATE mangas
|
||||
SET is_syncing = 0
|
||||
WHERE is_syncing = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue