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