mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
feat: add remote sync capability - update scheduling logic
This commit is contained in:
parent
d50fbe566c
commit
5ec443916b
15 changed files with 148 additions and 55 deletions
|
@ -1,36 +1,103 @@
|
||||||
package com.lagradost.cloudstream3.syncproviders
|
package com.lagradost.cloudstream3.syncproviders
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
import com.lagradost.cloudstream3.mvvm.launchSafe
|
import com.lagradost.cloudstream3.mvvm.launchSafe
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
interface BackupAPI<LOGIN_DATA> {
|
interface BackupAPI<LOGIN_DATA> {
|
||||||
private companion object {
|
companion object {
|
||||||
val DEBOUNCE_TIME_MS = 15.seconds
|
val UPLOAD_THROTTLE = 10.seconds
|
||||||
|
val DOWNLOAD_THROTTLE = 60.seconds
|
||||||
|
|
||||||
|
fun createBackupScheduler() = Scheduler<Pair<String, Boolean>>(
|
||||||
|
UPLOAD_THROTTLE.inWholeMilliseconds
|
||||||
|
) { input ->
|
||||||
|
if (input == null) {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountManager.BackupApis.forEach { it.addToQueue(input.first, input.second) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SharedPreferences.attachListener(isSettings: Boolean = true): Pair<SharedPreferences, Scheduler<Pair<String, Boolean>>> {
|
||||||
|
val scheduler = createBackupScheduler()
|
||||||
|
registerOnSharedPreferenceChangeListener { _, key ->
|
||||||
|
scheduler.work(Pair(key, isSettings))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(
|
||||||
|
this,
|
||||||
|
scheduler
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadSyncData()
|
fun downloadSyncData()
|
||||||
fun uploadSyncData()
|
fun uploadSyncData()
|
||||||
fun shouldUpdate(): Boolean
|
fun shouldUpdate(changedKey: String, isSettings: Boolean): Boolean
|
||||||
|
|
||||||
fun Context.mergeBackup(incomingData: String)
|
fun Context.mergeBackup(incomingData: String)
|
||||||
fun Context.createBackup(loginData: LOGIN_DATA)
|
fun Context.createBackup(loginData: LOGIN_DATA)
|
||||||
|
|
||||||
var uploadJob: Job?
|
var uploadJob: Job?
|
||||||
fun addToQueue() {
|
fun addToQueue(changedKey: String, isSettings: Boolean) {
|
||||||
if (!shouldUpdate()) {
|
if (!shouldUpdate(changedKey, isSettings)) {
|
||||||
|
Log.d("SYNC_API", "upload not required, data is same")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadJob?.cancel()
|
if (uploadJob != null && uploadJob!!.isActive) {
|
||||||
|
Log.d("SYNC_API", "upload is canceled, scheduling new")
|
||||||
|
uploadJob?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should ensure job will before app is closed
|
||||||
uploadJob = CoroutineScope(Dispatchers.IO).launchSafe {
|
uploadJob = CoroutineScope(Dispatchers.IO).launchSafe {
|
||||||
delay(DEBOUNCE_TIME_MS)
|
Log.d("SYNC_API", "upload is running now")
|
||||||
uploadSyncData()
|
uploadSyncData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
class Scheduler<INPUT>(
|
||||||
|
private val throttleTimeMs: Long,
|
||||||
|
private val onWork: (INPUT?) -> Unit
|
||||||
|
) {
|
||||||
|
private companion object {
|
||||||
|
var SCHEDULER_ID = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private val id = SCHEDULER_ID++
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
private var runnable: Runnable? = null
|
||||||
|
|
||||||
|
fun work(input: INPUT? = null) {
|
||||||
|
Log.d("SYNC_API", "[$id] wants to schedule")
|
||||||
|
throttle(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
runnable?.let {
|
||||||
|
handler.removeCallbacks(it)
|
||||||
|
runnable = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun throttle(input: INPUT?) {
|
||||||
|
stop()
|
||||||
|
|
||||||
|
runnable = Runnable {
|
||||||
|
Log.d("SYNC_API", "[$id] schedule success")
|
||||||
|
onWork(input)
|
||||||
|
}
|
||||||
|
handler.postDelayed(runnable!!, throttleTimeMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.syncproviders.providers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
@ -19,7 +20,6 @@ import com.google.api.services.drive.model.File
|
||||||
import com.lagradost.cloudstream3.AcraApplication
|
import com.lagradost.cloudstream3.AcraApplication
|
||||||
import com.lagradost.cloudstream3.CommonActivity
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.launchSafe
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.BackupAPI
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.InAppOAuth2API
|
import com.lagradost.cloudstream3.syncproviders.InAppOAuth2API
|
||||||
|
@ -29,11 +29,9 @@ import com.lagradost.cloudstream3.utils.BackupUtils
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.getBackup
|
import com.lagradost.cloudstream3.utils.BackupUtils.getBackup
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.restore
|
import com.lagradost.cloudstream3.utils.BackupUtils.restore
|
||||||
import com.lagradost.cloudstream3.utils.DataStore
|
import com.lagradost.cloudstream3.utils.DataStore
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -68,8 +66,6 @@ class GoogleDriveApi(index: Int) :
|
||||||
var tempAuthFlow: AuthorizationCodeFlow? = null
|
var tempAuthFlow: AuthorizationCodeFlow? = null
|
||||||
var lastBackupJson: String? = null
|
var lastBackupJson: String? = null
|
||||||
|
|
||||||
var continuousDownloadJob: Job? = null
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// OAuth2API implementation
|
// OAuth2API implementation
|
||||||
|
@ -104,7 +100,7 @@ class GoogleDriveApi(index: Int) :
|
||||||
)
|
)
|
||||||
|
|
||||||
storeValue(K.TOKEN, googleTokenResponse)
|
storeValue(K.TOKEN, googleTokenResponse)
|
||||||
startContinuousDownload()
|
runDownloader()
|
||||||
|
|
||||||
tempAuthFlow = null
|
tempAuthFlow = null
|
||||||
return true
|
return true
|
||||||
|
@ -118,22 +114,7 @@ class GoogleDriveApi(index: Int) :
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
startContinuousDownload()
|
runDownloader()
|
||||||
}
|
|
||||||
|
|
||||||
private fun startContinuousDownload() {
|
|
||||||
continuousDownloadJob?.cancel()
|
|
||||||
continuousDownloadJob = CoroutineScope(Dispatchers.IO).launchSafe {
|
|
||||||
if (uploadJob?.isActive == true) {
|
|
||||||
uploadJob!!.invokeOnCompletion {
|
|
||||||
startContinuousDownload()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
downloadSyncData()
|
|
||||||
delay(1000 * 60)
|
|
||||||
startContinuousDownload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loginInfo(): AuthAPI.LoginInfo? {
|
override fun loginInfo(): AuthAPI.LoginInfo? {
|
||||||
|
@ -192,13 +173,11 @@ class GoogleDriveApi(index: Int) :
|
||||||
removeKey(it)
|
removeKey(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
restore(
|
restore(
|
||||||
newData,
|
newData,
|
||||||
restoreSettings = true,
|
restoreSettings = true,
|
||||||
restoreDataStore = true
|
restoreDataStore = true
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🤮
|
// 🤮
|
||||||
|
@ -227,6 +206,7 @@ class GoogleDriveApi(index: Int) :
|
||||||
val drive = getDriveService()!!
|
val drive = getDriveService()!!
|
||||||
|
|
||||||
val fileName = loginData.fileName
|
val fileName = loginData.fileName
|
||||||
|
val syncFileId = loginData.syncFileId
|
||||||
val ioFile = java.io.File(AcraApplication.context?.cacheDir, fileName)
|
val ioFile = java.io.File(AcraApplication.context?.cacheDir, fileName)
|
||||||
lastBackupJson = getBackup().toJson()
|
lastBackupJson = getBackup().toJson()
|
||||||
ioFile.writeText(lastBackupJson!!)
|
ioFile.writeText(lastBackupJson!!)
|
||||||
|
@ -250,7 +230,10 @@ class GoogleDriveApi(index: Int) :
|
||||||
loginData.syncFileId = file.id
|
loginData.syncFileId = file.id
|
||||||
}
|
}
|
||||||
|
|
||||||
storeValue(K.LOGIN_DATA, loginData)
|
// in case we had to create new file
|
||||||
|
if (syncFileId != loginData.syncFileId) {
|
||||||
|
storeValue(K.LOGIN_DATA, loginData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun downloadSyncData() {
|
override fun downloadSyncData() {
|
||||||
|
@ -273,11 +256,13 @@ class GoogleDriveApi(index: Int) :
|
||||||
try {
|
try {
|
||||||
val inputStream: InputStream = existingFile.executeMediaAsInputStream()
|
val inputStream: InputStream = existingFile.executeMediaAsInputStream()
|
||||||
val content: String = inputStream.bufferedReader().use { it.readText() }
|
val content: String = inputStream.bufferedReader().use { it.readText() }
|
||||||
|
Log.d("SYNC_API", "downloadSyncData merging")
|
||||||
ctx.mergeBackup(content)
|
ctx.mergeBackup(content)
|
||||||
return
|
return
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Log.d("SYNC_API", "downloadSyncData file not exists")
|
||||||
uploadSyncData()
|
uploadSyncData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,12 +295,14 @@ class GoogleDriveApi(index: Int) :
|
||||||
override fun uploadSyncData() {
|
override fun uploadSyncData() {
|
||||||
val ctx = AcraApplication.context ?: return
|
val ctx = AcraApplication.context ?: return
|
||||||
val loginData = getLatestLoginData() ?: return
|
val loginData = getLatestLoginData() ?: return
|
||||||
|
Log.d("SYNC_API", "uploadSyncData createBackup")
|
||||||
ctx.createBackup(loginData)
|
ctx.createBackup(loginData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldUpdate(): Boolean {
|
override fun shouldUpdate(changedKey: String, isSettings: Boolean): Boolean {
|
||||||
val ctx = AcraApplication.context ?: return false
|
val ctx = AcraApplication.context ?: return false
|
||||||
|
|
||||||
|
// would be smarter to check properties, but its called once in UPLOAD_THROTTLE seconds
|
||||||
val newBackup = ctx.getBackup().toJson()
|
val newBackup = ctx.getBackup().toJson()
|
||||||
return lastBackupJson != newBackup
|
return lastBackupJson != newBackup
|
||||||
}
|
}
|
||||||
|
@ -335,6 +322,25 @@ class GoogleDriveApi(index: Int) :
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// Internal
|
// Internal
|
||||||
|
private val continuousDownloader = BackupAPI.Scheduler<Unit>(
|
||||||
|
BackupAPI.DOWNLOAD_THROTTLE.inWholeMilliseconds
|
||||||
|
) {
|
||||||
|
if (uploadJob?.isActive == true) {
|
||||||
|
uploadJob!!.invokeOnCompletion {
|
||||||
|
Log.d("SYNC_API", "upload is running, reschedule download")
|
||||||
|
runDownloader()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("SYNC_API", "downloadSyncData will run")
|
||||||
|
downloadSyncData()
|
||||||
|
runDownloader()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runDownloader() {
|
||||||
|
continuousDownloader.work()
|
||||||
|
}
|
||||||
|
|
||||||
private fun getCredentialsFromStore(): Credential? {
|
private fun getCredentialsFromStore(): Credential? {
|
||||||
val LOGIN_DATA = getLatestLoginData()
|
val LOGIN_DATA = getLatestLoginData()
|
||||||
val TOKEN = getValue<TokenResponse>(K.TOKEN)
|
val TOKEN = getValue<TokenResponse>(K.TOKEN)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.mvvm.*
|
import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subtitleProviders
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subtitleProviders
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioTrackLanguage
|
import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioTrackLanguage
|
||||||
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
|
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
|
||||||
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
|
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
|
||||||
|
@ -664,7 +665,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceDialog.subtitles_click_settings?.setOnClickListener {
|
sourceDialog.subtitles_click_settings?.setOnClickListener {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx).attachListener().first
|
||||||
|
|
||||||
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
|
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
|
||||||
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
|
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.network.initClient
|
import com.lagradost.cloudstream3.network.initClient
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
@ -137,20 +138,26 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
// Stores the real URI using download_path_key
|
// Stores the real URI using download_path_key
|
||||||
// Important that the URI is stored instead of filepath due to permissions.
|
// Important that the URI is stored instead of filepath due to permissions.
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.edit().putString(getString(R.string.download_path_key), uri.toString()).apply()
|
.attachListener().first
|
||||||
|
.edit()
|
||||||
|
.putString(getString(R.string.download_path_key), uri.toString())
|
||||||
|
.apply()
|
||||||
|
|
||||||
// From URI -> File path
|
// From URI -> File path
|
||||||
// File path here is purely for cosmetic purposes in settings
|
// File path here is purely for cosmetic purposes in settings
|
||||||
(file.filePath ?: uri.toString()).let {
|
(file.filePath ?: uri.toString()).let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.edit().putString(getString(R.string.download_path_pref), it).apply()
|
.attachListener().first
|
||||||
|
.edit()
|
||||||
|
.putString(getString(R.string.download_path_pref), it)
|
||||||
|
.apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settins_general, rootKey)
|
setPreferencesFromResource(R.xml.settins_general, rootKey)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
|
||||||
|
|
||||||
fun getCurrent(): MutableList<CustomSite> {
|
fun getCurrent(): MutableList<CustomSite> {
|
||||||
return getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
|
return getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
@ -27,7 +28,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settings_player, rootKey)
|
setPreferencesFromResource(R.xml.settings_player, rootKey)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
|
||||||
|
|
||||||
getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener {
|
getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener {
|
||||||
val prefNames = resources.getStringArray(R.array.video_buffer_length_names)
|
val prefNames = resources.getStringArray(R.array.video_buffer_length_names)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
@ -30,7 +31,7 @@ class SettingsProviders : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settings_providers, rootKey)
|
setPreferencesFromResource(R.xml.settings_providers, rootKey)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
|
||||||
|
|
||||||
getPref(R.string.display_sub_key)?.setOnPreferenceClickListener {
|
getPref(R.string.display_sub_key)?.setOnPreferenceClickListener {
|
||||||
activity?.getApiDubstatusSettings()?.let { current ->
|
activity?.getApiDubstatusSettings()?.let { current ->
|
||||||
|
|
|
@ -8,6 +8,7 @@ import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.SearchQuality
|
import com.lagradost.cloudstream3.SearchQuality
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
@ -28,7 +29,7 @@ class SettingsUI : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settins_ui, rootKey)
|
setPreferencesFromResource(R.xml.settins_ui, rootKey)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
|
||||||
|
|
||||||
getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener {
|
getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener {
|
||||||
val prefNames = resources.getStringArray(R.array.poster_ui_options)
|
val prefNames = resources.getStringArray(R.array.poster_ui_options)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
|
@ -126,7 +127,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPref(R.string.apk_installer_key)?.setOnPreferenceClickListener {
|
getPref(R.string.apk_installer_key)?.setOnPreferenceClickListener {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context).attachListener().first
|
||||||
|
|
||||||
val prefNames = resources.getStringArray(R.array.apk_installer_pref)
|
val prefNames = resources.getStringArray(R.array.apk_installer_pref)
|
||||||
val prefValues = resources.getIntArray(R.array.apk_installer_values)
|
val prefValues = resources.getIntArray(R.array.apk_installer_values)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
||||||
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
|
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
|
@ -42,7 +43,7 @@ class SetupFragmentLanguage : Fragment() {
|
||||||
normalSafeApiCall {
|
normalSafeApiCall {
|
||||||
with(context) {
|
with(context) {
|
||||||
if (this == null) return@normalSafeApiCall
|
if (this == null) return@normalSafeApiCall
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
|
|
||||||
val arrayAdapter =
|
val arrayAdapter =
|
||||||
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
import kotlinx.android.synthetic.main.fragment_setup_layout.*
|
import kotlinx.android.synthetic.main.fragment_setup_layout.*
|
||||||
import kotlinx.android.synthetic.main.fragment_setup_media.listview1
|
import kotlinx.android.synthetic.main.fragment_setup_media.listview1
|
||||||
|
@ -33,7 +34,7 @@ class SetupFragmentLayout : Fragment() {
|
||||||
|
|
||||||
with(context) {
|
with(context) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
|
|
||||||
val prefNames = resources.getStringArray(R.array.app_layout)
|
val prefNames = resources.getStringArray(R.array.app_layout)
|
||||||
val prefValues = resources.getIntArray(R.array.app_layout_values)
|
val prefValues = resources.getIntArray(R.array.app_layout_values)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||||
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
|
@ -32,7 +33,7 @@ class SetupFragmentMedia : Fragment() {
|
||||||
|
|
||||||
with(context) {
|
with(context) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
|
|
||||||
val arrayAdapter =
|
val arrayAdapter =
|
||||||
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.APIHolder
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||||
import com.lagradost.cloudstream3.AllLanguagesName
|
import com.lagradost.cloudstream3.AllLanguagesName
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
import kotlinx.android.synthetic.main.fragment_setup_media.*
|
import kotlinx.android.synthetic.main.fragment_setup_media.*
|
||||||
|
@ -33,7 +34,7 @@ class SetupFragmentProviderLanguage : Fragment() {
|
||||||
|
|
||||||
with(context) {
|
with(context) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
|
|
||||||
val arrayAdapter =
|
val arrayAdapter =
|
||||||
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
|
||||||
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
|
@ -447,6 +448,7 @@ class SubtitlesFragment : Fragment() {
|
||||||
subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b ->
|
subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b ->
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
PreferenceManager.getDefaultSharedPreferences(ctx)
|
PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
|
.attachListener().first
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean(getString(R.string.filter_sub_lang_key), b)
|
.putBoolean(getString(R.string.filter_sub_lang_key), b)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
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.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI
|
||||||
|
|
||||||
const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
|
const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ object DataStore {
|
||||||
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
|
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val backupScheduler = BackupAPI.createBackupScheduler()
|
||||||
|
|
||||||
private fun getPreferences(context: Context): SharedPreferences {
|
private fun getPreferences(context: Context): SharedPreferences {
|
||||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
|
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||||
}
|
}
|
||||||
|
@ -83,8 +85,7 @@ object DataStore {
|
||||||
val editor: SharedPreferences.Editor = prefs.edit()
|
val editor: SharedPreferences.Editor = prefs.edit()
|
||||||
editor.remove(path)
|
editor.remove(path)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
backupScheduler.work(Pair(path, false))
|
||||||
AccountManager.BackupApis.forEach { it.addToQueue() }
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
|
@ -104,8 +105,7 @@ object DataStore {
|
||||||
val editor: SharedPreferences.Editor = getSharedPrefs().edit()
|
val editor: SharedPreferences.Editor = getSharedPrefs().edit()
|
||||||
editor.putString(path, mapper.writeValueAsString(value))
|
editor.putString(path, mapper.writeValueAsString(value))
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
backupScheduler.work(Pair(path, false))
|
||||||
AccountManager.BackupApis.forEach { it.addToQueue() }
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ object DataStore {
|
||||||
setKey(getFolderName(folder, path), value)
|
setKey(getFolderName(folder, path), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified T : Any> String.toKotlinObject(): T {
|
inline fun <reified T : Any> String.toKotlinObject(): T {
|
||||||
return mapper.readValue(this, T::class.java)
|
return mapper.readValue(this, T::class.java)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import okio.buffer
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -73,7 +74,7 @@ class InAppUpdater {
|
||||||
|
|
||||||
private suspend fun Activity.getAppUpdate(): Update {
|
private suspend fun Activity.getAppUpdate(): Update {
|
||||||
return try {
|
return try {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
if (settingsManager.getBoolean(
|
if (settingsManager.getBoolean(
|
||||||
getString(R.string.prerelease_update_key),
|
getString(R.string.prerelease_update_key),
|
||||||
resources.getBoolean(R.bool.is_prerelease)
|
resources.getBoolean(R.bool.is_prerelease)
|
||||||
|
@ -254,7 +255,7 @@ class InAppUpdater {
|
||||||
* @param checkAutoUpdate if the update check was launched automatically
|
* @param checkAutoUpdate if the update check was launched automatically
|
||||||
**/
|
**/
|
||||||
suspend fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean {
|
suspend fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
|
||||||
|
|
||||||
if (!checkAutoUpdate || settingsManager.getBoolean(
|
if (!checkAutoUpdate || settingsManager.getBoolean(
|
||||||
getString(R.string.auto_update_key),
|
getString(R.string.auto_update_key),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue