mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
feat: add remote sync capability - schedule updates only when value was really changed (true -> true is not a change)
This commit is contained in:
parent
fe36b69758
commit
7145befb55
4 changed files with 69 additions and 40 deletions
|
@ -25,15 +25,17 @@ interface BackupAPI<LOGIN_DATA> {
|
|||
val result: JSONCompareResult?
|
||||
)
|
||||
|
||||
data class PreferencesSchedulerData(
|
||||
val prefs: SharedPreferences,
|
||||
data class PreferencesSchedulerData<T>(
|
||||
val syncPrefs: SharedPreferences,
|
||||
val storeKey: String,
|
||||
val isSettings: Boolean
|
||||
val oldValue: T,
|
||||
val newValue: T,
|
||||
val source: BackupUtils.RestoreSource
|
||||
)
|
||||
|
||||
data class SharedPreferencesWithListener(
|
||||
val self: SharedPreferences,
|
||||
val scheduler: Scheduler<PreferencesSchedulerData>
|
||||
val scheduler: Scheduler<PreferencesSchedulerData<*>>
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -39,16 +39,16 @@ import java.util.Date
|
|||
*
|
||||
* | State | Priority | Description
|
||||
* |---------:|:--------:|---------------------------------------------------------------------
|
||||
* | Progress | 1 | Check if data was really changed when calling backupscheduler.work then
|
||||
* | | | dont update sync meta if not needed
|
||||
* | Progress | 4 | Implement backup before user quits application
|
||||
* | Waiting | 2 | Add button to manually trigger sync
|
||||
* | Waiting | 3 | Move "https://chiff.github.io/cloudstream-sync/google-drive"
|
||||
* | Waiting | 4 | Implement backup before user quits application
|
||||
* | Waiting | 5 | Choose what should be synced and recheck `invalidKeys` in createBackupScheduler
|
||||
* | Someday | 3 | Add option to use proper OAuth through Google Services One Tap
|
||||
* | Someday | 5 | Encrypt data on Drive (low priority)
|
||||
* | Solved | 1 | Racing conditions when multiple devices in use
|
||||
* | Solved | 2 | Restoring backup should update view models
|
||||
* | Solved | 1 | Check if data was really changed when calling backupscheduler.work then
|
||||
* | | | dont update sync meta if not needed
|
||||
*/
|
||||
class GoogleDriveApi(index: Int) :
|
||||
InAppOAuth2APIManager(index),
|
||||
|
|
|
@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.json.JsonMapper
|
|||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.syncproviders.BackupAPI
|
||||
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.logHistoryChanged
|
||||
|
||||
const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
|
||||
|
||||
|
@ -106,15 +105,21 @@ object DataStore {
|
|||
try {
|
||||
val prefs = getSharedPrefs()
|
||||
if (prefs.contains(path)) {
|
||||
val oldValueExists = prefs.getString(path, null) != null
|
||||
|
||||
val editor: SharedPreferences.Editor = prefs.edit()
|
||||
editor.remove(path)
|
||||
editor.apply()
|
||||
|
||||
val success =
|
||||
backupScheduler.work(BackupAPI.PreferencesSchedulerData(prefs, path, false))
|
||||
if (success) {
|
||||
getSyncPrefs().logHistoryChanged(path, BackupUtils.RestoreSource.DATA)
|
||||
}
|
||||
backupScheduler.work(
|
||||
BackupAPI.PreferencesSchedulerData(
|
||||
prefs,
|
||||
path,
|
||||
oldValueExists,
|
||||
false,
|
||||
BackupUtils.RestoreSource.DATA
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
|
@ -132,14 +137,22 @@ object DataStore {
|
|||
fun <T> Context.setKey(path: String, value: T) {
|
||||
try {
|
||||
val prefs = getSharedPrefs()
|
||||
val oldValue = prefs.getString(path, null)
|
||||
val newValue = mapper.writeValueAsString(value)
|
||||
|
||||
val editor: SharedPreferences.Editor = prefs.edit()
|
||||
editor.putString(path, mapper.writeValueAsString(value))
|
||||
editor.putString(path, newValue)
|
||||
editor.apply()
|
||||
|
||||
val success = backupScheduler.work(BackupAPI.PreferencesSchedulerData(prefs,path, false))
|
||||
if (success) {
|
||||
getSyncPrefs().logHistoryChanged(path, BackupUtils.RestoreSource.DATA)
|
||||
}
|
||||
backupScheduler.work(
|
||||
BackupAPI.PreferencesSchedulerData(
|
||||
prefs,
|
||||
path,
|
||||
oldValue,
|
||||
newValue,
|
||||
BackupUtils.RestoreSource.DATA
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
|||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||
import com.lagradost.cloudstream3.syncproviders.BackupAPI
|
||||
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.logHistoryChanged
|
||||
import com.lagradost.cloudstream3.ui.home.HOME_BOOKMARK_VALUE_LIST
|
||||
import com.lagradost.cloudstream3.ui.player.PLAYBACK_SPEED_KEY
|
||||
import com.lagradost.cloudstream3.ui.player.RESIZE_MODE_KEY
|
||||
|
||||
|
@ -18,17 +19,26 @@ class Scheduler<INPUT>(
|
|||
companion object {
|
||||
var SCHEDULER_ID = 1
|
||||
|
||||
fun createBackupScheduler() = Scheduler<BackupAPI.PreferencesSchedulerData>(
|
||||
private val invalidSchedulerKeys = listOf(
|
||||
VideoDownloadManager.KEY_DOWNLOAD_INFO,
|
||||
PLAYBACK_SPEED_KEY,
|
||||
HOME_BOOKMARK_VALUE_LIST,
|
||||
RESIZE_MODE_KEY
|
||||
)
|
||||
|
||||
fun createBackupScheduler() = Scheduler<BackupAPI.PreferencesSchedulerData<*>>(
|
||||
BackupAPI.UPLOAD_THROTTLE.inWholeMilliseconds,
|
||||
onWork = { input ->
|
||||
if (input == null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
input.syncPrefs.logHistoryChanged(input.storeKey, input.source)
|
||||
|
||||
AccountManager.BackupApis.forEach {
|
||||
it.addToQueue(
|
||||
input.storeKey,
|
||||
input.isSettings
|
||||
input.source == BackupUtils.RestoreSource.SETTINGS
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -37,13 +47,17 @@ class Scheduler<INPUT>(
|
|||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
val invalidKeys = listOf(
|
||||
VideoDownloadManager.KEY_DOWNLOAD_INFO,
|
||||
PLAYBACK_SPEED_KEY,
|
||||
RESIZE_MODE_KEY
|
||||
)
|
||||
val hasInvalidKey = invalidSchedulerKeys.contains(input.storeKey)
|
||||
if (hasInvalidKey) {
|
||||
return@Scheduler false
|
||||
}
|
||||
|
||||
return@Scheduler !invalidKeys.contains(input.storeKey)
|
||||
val valueDidNotChange = input.oldValue == input.newValue
|
||||
if (valueDidNotChange) {
|
||||
return@Scheduler false
|
||||
}
|
||||
|
||||
return@Scheduler true
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -51,30 +65,30 @@ class Scheduler<INPUT>(
|
|||
// which means it is mostly used for settings preferences, therefore we use `isSettings: Boolean = true`, be careful
|
||||
// if you need to directly access `context.getSharedPreferences` (without using DataStore) and dont forget to turn it off
|
||||
fun SharedPreferences.attachBackupListener(
|
||||
isSettings: Boolean = true,
|
||||
source: BackupUtils.RestoreSource = BackupUtils.RestoreSource.SETTINGS,
|
||||
syncPrefs: SharedPreferences
|
||||
): BackupAPI.SharedPreferencesWithListener {
|
||||
val scheduler = createBackupScheduler()
|
||||
registerOnSharedPreferenceChangeListener { _, storeKey ->
|
||||
val success =
|
||||
scheduler.work(
|
||||
BackupAPI.PreferencesSchedulerData(
|
||||
syncPrefs,
|
||||
storeKey,
|
||||
isSettings
|
||||
)
|
||||
)
|
||||
|
||||
if (success) {
|
||||
syncPrefs.logHistoryChanged(storeKey, BackupUtils.RestoreSource.SETTINGS)
|
||||
}
|
||||
var lastValue = all
|
||||
registerOnSharedPreferenceChangeListener { sharedPreferences, storeKey ->
|
||||
scheduler.work(
|
||||
BackupAPI.PreferencesSchedulerData(
|
||||
syncPrefs,
|
||||
storeKey,
|
||||
lastValue[storeKey],
|
||||
sharedPreferences.all[storeKey],
|
||||
source
|
||||
)
|
||||
)
|
||||
lastValue = sharedPreferences.all
|
||||
}
|
||||
|
||||
return BackupAPI.SharedPreferencesWithListener(this, scheduler)
|
||||
}
|
||||
|
||||
fun SharedPreferences.attachBackupListener(syncPrefs: SharedPreferences): BackupAPI.SharedPreferencesWithListener {
|
||||
return attachBackupListener(true, syncPrefs)
|
||||
return attachBackupListener(BackupUtils.RestoreSource.SETTINGS, syncPrefs)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue