mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-03 18:16:15 +00:00
feat: Implement sync payload encoding and decoding for various settings
This commit is contained in:
parent
26b7edee2a
commit
9de7f8a34d
17 changed files with 737 additions and 473 deletions
|
|
@ -2,7 +2,14 @@ package com.nuvio.app.features.mdblist
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
actual object MdbListSettingsStorage {
|
||||
private const val preferencesName = "nuvio_mdblist_settings"
|
||||
|
|
@ -15,6 +22,17 @@ actual object MdbListSettingsStorage {
|
|||
private const val useTraktKey = "mdblist_use_trakt"
|
||||
private const val useLetterboxdKey = "mdblist_use_letterboxd"
|
||||
private const val useAudienceKey = "mdblist_use_audience"
|
||||
private val syncKeys = listOf(
|
||||
enabledKey,
|
||||
apiKey,
|
||||
useImdbKey,
|
||||
useTmdbKey,
|
||||
useTomatoesKey,
|
||||
useMetacriticKey,
|
||||
useTraktKey,
|
||||
useLetterboxdKey,
|
||||
useAudienceKey,
|
||||
)
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -96,4 +114,32 @@ actual object MdbListSettingsStorage {
|
|||
?.putBoolean(ProfileScopedKey.of(key), enabled)
|
||||
?.apply()
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
loadApiKey()?.let { put(apiKey, encodeSyncString(it)) }
|
||||
loadUseImdb()?.let { put(useImdbKey, encodeSyncBoolean(it)) }
|
||||
loadUseTmdb()?.let { put(useTmdbKey, encodeSyncBoolean(it)) }
|
||||
loadUseTomatoes()?.let { put(useTomatoesKey, encodeSyncBoolean(it)) }
|
||||
loadUseMetacritic()?.let { put(useMetacriticKey, encodeSyncBoolean(it)) }
|
||||
loadUseTrakt()?.let { put(useTraktKey, encodeSyncBoolean(it)) }
|
||||
loadUseLetterboxd()?.let { put(useLetterboxdKey, encodeSyncBoolean(it)) }
|
||||
loadUseAudience()?.let { put(useAudienceKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
preferences?.edit()?.apply {
|
||||
syncKeys.forEach { remove(ProfileScopedKey.of(it)) }
|
||||
}?.apply()
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
payload.decodeSyncString(apiKey)?.let(::saveApiKey)
|
||||
payload.decodeSyncBoolean(useImdbKey)?.let(::saveUseImdb)
|
||||
payload.decodeSyncBoolean(useTmdbKey)?.let(::saveUseTmdb)
|
||||
payload.decodeSyncBoolean(useTomatoesKey)?.let(::saveUseTomatoes)
|
||||
payload.decodeSyncBoolean(useMetacriticKey)?.let(::saveUseMetacritic)
|
||||
payload.decodeSyncBoolean(useTraktKey)?.let(::saveUseTrakt)
|
||||
payload.decodeSyncBoolean(useLetterboxdKey)?.let(::saveUseLetterboxd)
|
||||
payload.decodeSyncBoolean(useAudienceKey)?.let(::saveUseAudience)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,20 @@ package com.nuvio.app.features.player
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncFloat
|
||||
import com.nuvio.app.core.sync.decodeSyncInt
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.decodeSyncStringSet
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncFloat
|
||||
import com.nuvio.app.core.sync.encodeSyncInt
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncStringSet
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
actual object PlayerSettingsStorage {
|
||||
private const val preferencesName = "nuvio_player_settings"
|
||||
|
|
@ -36,6 +49,38 @@ actual object PlayerSettingsStorage {
|
|||
private const val nextEpisodeThresholdMinutesBeforeEndKey = "next_episode_threshold_minutes_before_end_v2"
|
||||
private const val useLibassKey = "use_libass"
|
||||
private const val libassRenderTypeKey = "libass_render_type"
|
||||
private val syncKeys = listOf(
|
||||
showLoadingOverlayKey,
|
||||
preferredAudioLanguageKey,
|
||||
secondaryPreferredAudioLanguageKey,
|
||||
preferredSubtitleLanguageKey,
|
||||
secondaryPreferredSubtitleLanguageKey,
|
||||
subtitleTextColorKey,
|
||||
subtitleOutlineEnabledKey,
|
||||
subtitleFontSizeSpKey,
|
||||
subtitleBottomOffsetKey,
|
||||
streamReuseLastLinkEnabledKey,
|
||||
streamReuseLastLinkCacheHoursKey,
|
||||
decoderPriorityKey,
|
||||
mapDV7ToHevcKey,
|
||||
tunnelingEnabledKey,
|
||||
streamAutoPlayModeKey,
|
||||
streamAutoPlaySourceKey,
|
||||
streamAutoPlaySelectedAddonsKey,
|
||||
streamAutoPlaySelectedPluginsKey,
|
||||
streamAutoPlayRegexKey,
|
||||
streamAutoPlayTimeoutSecondsKey,
|
||||
skipIntroEnabledKey,
|
||||
animeSkipEnabledKey,
|
||||
animeSkipClientIdKey,
|
||||
streamAutoPlayNextEpisodeEnabledKey,
|
||||
streamAutoPlayPreferBingeGroupKey,
|
||||
nextEpisodeThresholdModeKey,
|
||||
nextEpisodeThresholdPercentKey,
|
||||
nextEpisodeThresholdMinutesBeforeEndKey,
|
||||
useLibassKey,
|
||||
libassRenderTypeKey,
|
||||
)
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -489,4 +534,74 @@ actual object PlayerSettingsStorage {
|
|||
?.putString(ProfileScopedKey.of(libassRenderTypeKey), renderType)
|
||||
?.apply()
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadShowLoadingOverlay()?.let { put(showLoadingOverlayKey, encodeSyncBoolean(it)) }
|
||||
loadPreferredAudioLanguage()?.let { put(preferredAudioLanguageKey, encodeSyncString(it)) }
|
||||
loadSecondaryPreferredAudioLanguage()?.let { put(secondaryPreferredAudioLanguageKey, encodeSyncString(it)) }
|
||||
loadPreferredSubtitleLanguage()?.let { put(preferredSubtitleLanguageKey, encodeSyncString(it)) }
|
||||
loadSecondaryPreferredSubtitleLanguage()?.let { put(secondaryPreferredSubtitleLanguageKey, encodeSyncString(it)) }
|
||||
loadSubtitleTextColor()?.let { put(subtitleTextColorKey, encodeSyncString(it)) }
|
||||
loadSubtitleOutlineEnabled()?.let { put(subtitleOutlineEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadSubtitleFontSizeSp()?.let { put(subtitleFontSizeSpKey, encodeSyncInt(it)) }
|
||||
loadSubtitleBottomOffset()?.let { put(subtitleBottomOffsetKey, encodeSyncInt(it)) }
|
||||
loadStreamReuseLastLinkEnabled()?.let { put(streamReuseLastLinkEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamReuseLastLinkCacheHours()?.let { put(streamReuseLastLinkCacheHoursKey, encodeSyncInt(it)) }
|
||||
loadDecoderPriority()?.let { put(decoderPriorityKey, encodeSyncInt(it)) }
|
||||
loadMapDV7ToHevc()?.let { put(mapDV7ToHevcKey, encodeSyncBoolean(it)) }
|
||||
loadTunnelingEnabled()?.let { put(tunnelingEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayMode()?.let { put(streamAutoPlayModeKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlaySource()?.let { put(streamAutoPlaySourceKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlaySelectedAddons()?.let { put(streamAutoPlaySelectedAddonsKey, encodeSyncStringSet(it)) }
|
||||
loadStreamAutoPlaySelectedPlugins()?.let { put(streamAutoPlaySelectedPluginsKey, encodeSyncStringSet(it)) }
|
||||
loadStreamAutoPlayRegex()?.let { put(streamAutoPlayRegexKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlayTimeoutSeconds()?.let { put(streamAutoPlayTimeoutSecondsKey, encodeSyncInt(it)) }
|
||||
loadSkipIntroEnabled()?.let { put(skipIntroEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipEnabled()?.let { put(animeSkipEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipClientId()?.let { put(animeSkipClientIdKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlayNextEpisodeEnabled()?.let { put(streamAutoPlayNextEpisodeEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayPreferBingeGroup()?.let { put(streamAutoPlayPreferBingeGroupKey, encodeSyncBoolean(it)) }
|
||||
loadNextEpisodeThresholdMode()?.let { put(nextEpisodeThresholdModeKey, encodeSyncString(it)) }
|
||||
loadNextEpisodeThresholdPercent()?.let { put(nextEpisodeThresholdPercentKey, encodeSyncFloat(it)) }
|
||||
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
|
||||
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
|
||||
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
preferences?.edit()?.apply {
|
||||
syncKeys.forEach { remove(ProfileScopedKey.of(it)) }
|
||||
}?.apply()
|
||||
|
||||
payload.decodeSyncBoolean(showLoadingOverlayKey)?.let(::saveShowLoadingOverlay)
|
||||
payload.decodeSyncString(preferredAudioLanguageKey)?.let(::savePreferredAudioLanguage)
|
||||
payload.decodeSyncString(secondaryPreferredAudioLanguageKey)?.let(::saveSecondaryPreferredAudioLanguage)
|
||||
payload.decodeSyncString(preferredSubtitleLanguageKey)?.let(::savePreferredSubtitleLanguage)
|
||||
payload.decodeSyncString(secondaryPreferredSubtitleLanguageKey)?.let(::saveSecondaryPreferredSubtitleLanguage)
|
||||
payload.decodeSyncString(subtitleTextColorKey)?.let(::saveSubtitleTextColor)
|
||||
payload.decodeSyncBoolean(subtitleOutlineEnabledKey)?.let(::saveSubtitleOutlineEnabled)
|
||||
payload.decodeSyncInt(subtitleFontSizeSpKey)?.let(::saveSubtitleFontSizeSp)
|
||||
payload.decodeSyncInt(subtitleBottomOffsetKey)?.let(::saveSubtitleBottomOffset)
|
||||
payload.decodeSyncBoolean(streamReuseLastLinkEnabledKey)?.let(::saveStreamReuseLastLinkEnabled)
|
||||
payload.decodeSyncInt(streamReuseLastLinkCacheHoursKey)?.let(::saveStreamReuseLastLinkCacheHours)
|
||||
payload.decodeSyncInt(decoderPriorityKey)?.let(::saveDecoderPriority)
|
||||
payload.decodeSyncBoolean(mapDV7ToHevcKey)?.let(::saveMapDV7ToHevc)
|
||||
payload.decodeSyncBoolean(tunnelingEnabledKey)?.let(::saveTunnelingEnabled)
|
||||
payload.decodeSyncString(streamAutoPlayModeKey)?.let(::saveStreamAutoPlayMode)
|
||||
payload.decodeSyncString(streamAutoPlaySourceKey)?.let(::saveStreamAutoPlaySource)
|
||||
payload.decodeSyncStringSet(streamAutoPlaySelectedAddonsKey)?.let(::saveStreamAutoPlaySelectedAddons)
|
||||
payload.decodeSyncStringSet(streamAutoPlaySelectedPluginsKey)?.let(::saveStreamAutoPlaySelectedPlugins)
|
||||
payload.decodeSyncString(streamAutoPlayRegexKey)?.let(::saveStreamAutoPlayRegex)
|
||||
payload.decodeSyncInt(streamAutoPlayTimeoutSecondsKey)?.let(::saveStreamAutoPlayTimeoutSeconds)
|
||||
payload.decodeSyncBoolean(skipIntroEnabledKey)?.let(::saveSkipIntroEnabled)
|
||||
payload.decodeSyncBoolean(animeSkipEnabledKey)?.let(::saveAnimeSkipEnabled)
|
||||
payload.decodeSyncString(animeSkipClientIdKey)?.let(::saveAnimeSkipClientId)
|
||||
payload.decodeSyncBoolean(streamAutoPlayNextEpisodeEnabledKey)?.let(::saveStreamAutoPlayNextEpisodeEnabled)
|
||||
payload.decodeSyncBoolean(streamAutoPlayPreferBingeGroupKey)?.let(::saveStreamAutoPlayPreferBingeGroup)
|
||||
payload.decodeSyncString(nextEpisodeThresholdModeKey)?.let(::saveNextEpisodeThresholdMode)
|
||||
payload.decodeSyncFloat(nextEpisodeThresholdPercentKey)?.let(::saveNextEpisodeThresholdPercent)
|
||||
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
|
||||
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
|
||||
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,20 @@ package com.nuvio.app.features.settings
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
actual object ThemeSettingsStorage {
|
||||
private const val preferencesName = "nuvio_theme_settings"
|
||||
private const val selectedThemeKey = "selected_theme"
|
||||
private const val amoledEnabledKey = "amoled_enabled"
|
||||
private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey)
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -37,4 +45,18 @@ actual object ThemeSettingsStorage {
|
|||
?.putBoolean(ProfileScopedKey.of(amoledEnabledKey), enabled)
|
||||
?.apply()
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadSelectedTheme()?.let { put(selectedThemeKey, encodeSyncString(it)) }
|
||||
loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
preferences?.edit()?.apply {
|
||||
syncKeys.forEach { remove(ProfileScopedKey.of(it)) }
|
||||
}?.apply()
|
||||
|
||||
payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme)
|
||||
payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,14 @@ package com.nuvio.app.features.tmdb
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
actual object TmdbSettingsStorage {
|
||||
private const val preferencesName = "nuvio_tmdb_settings"
|
||||
|
|
@ -20,6 +27,22 @@ actual object TmdbSettingsStorage {
|
|||
private const val useSeasonPostersKey = "tmdb_use_season_posters"
|
||||
private const val useMoreLikeThisKey = "tmdb_use_more_like_this"
|
||||
private const val useCollectionsKey = "tmdb_use_collections"
|
||||
private val syncKeys = listOf(
|
||||
enabledKey,
|
||||
apiKeyKey,
|
||||
languageKey,
|
||||
useTrailersKey,
|
||||
useArtworkKey,
|
||||
useBasicInfoKey,
|
||||
useDetailsKey,
|
||||
useCreditsKey,
|
||||
useProductionsKey,
|
||||
useNetworksKey,
|
||||
useEpisodesKey,
|
||||
useSeasonPostersKey,
|
||||
useMoreLikeThisKey,
|
||||
useCollectionsKey,
|
||||
)
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -135,4 +158,42 @@ actual object TmdbSettingsStorage {
|
|||
?.putBoolean(ProfileScopedKey.of(key), enabled)
|
||||
?.apply()
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
loadApiKey()?.let { put(apiKeyKey, encodeSyncString(it)) }
|
||||
loadLanguage()?.let { put(languageKey, encodeSyncString(it)) }
|
||||
loadUseTrailers()?.let { put(useTrailersKey, encodeSyncBoolean(it)) }
|
||||
loadUseArtwork()?.let { put(useArtworkKey, encodeSyncBoolean(it)) }
|
||||
loadUseBasicInfo()?.let { put(useBasicInfoKey, encodeSyncBoolean(it)) }
|
||||
loadUseDetails()?.let { put(useDetailsKey, encodeSyncBoolean(it)) }
|
||||
loadUseCredits()?.let { put(useCreditsKey, encodeSyncBoolean(it)) }
|
||||
loadUseProductions()?.let { put(useProductionsKey, encodeSyncBoolean(it)) }
|
||||
loadUseNetworks()?.let { put(useNetworksKey, encodeSyncBoolean(it)) }
|
||||
loadUseEpisodes()?.let { put(useEpisodesKey, encodeSyncBoolean(it)) }
|
||||
loadUseSeasonPosters()?.let { put(useSeasonPostersKey, encodeSyncBoolean(it)) }
|
||||
loadUseMoreLikeThis()?.let { put(useMoreLikeThisKey, encodeSyncBoolean(it)) }
|
||||
loadUseCollections()?.let { put(useCollectionsKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
preferences?.edit()?.apply {
|
||||
syncKeys.forEach { remove(ProfileScopedKey.of(it)) }
|
||||
}?.apply()
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
payload.decodeSyncString(apiKeyKey)?.let(::saveApiKey)
|
||||
payload.decodeSyncString(languageKey)?.let(::saveLanguage)
|
||||
payload.decodeSyncBoolean(useTrailersKey)?.let(::saveUseTrailers)
|
||||
payload.decodeSyncBoolean(useArtworkKey)?.let(::saveUseArtwork)
|
||||
payload.decodeSyncBoolean(useBasicInfoKey)?.let(::saveUseBasicInfo)
|
||||
payload.decodeSyncBoolean(useDetailsKey)?.let(::saveUseDetails)
|
||||
payload.decodeSyncBoolean(useCreditsKey)?.let(::saveUseCredits)
|
||||
payload.decodeSyncBoolean(useProductionsKey)?.let(::saveUseProductions)
|
||||
payload.decodeSyncBoolean(useNetworksKey)?.let(::saveUseNetworks)
|
||||
payload.decodeSyncBoolean(useEpisodesKey)?.let(::saveUseEpisodes)
|
||||
payload.decodeSyncBoolean(useSeasonPostersKey)?.let(::saveUseSeasonPosters)
|
||||
payload.decodeSyncBoolean(useMoreLikeThisKey)?.let(::saveUseMoreLikeThis)
|
||||
payload.decodeSyncBoolean(useCollectionsKey)?.let(::saveUseCollections)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@ package com.nuvio.app.features.trakt
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
internal actual object TraktCommentsStorage {
|
||||
private const val preferencesName = "nuvio_trakt_comments"
|
||||
private const val enabledKey = "comments_enabled"
|
||||
private val syncKeys = listOf(enabledKey)
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -26,4 +32,16 @@ internal actual object TraktCommentsStorage {
|
|||
?.putBoolean(ProfileScopedKey.of(enabledKey), enabled)
|
||||
?.apply()
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
preferences?.edit()?.apply {
|
||||
syncKeys.forEach { remove(ProfileScopedKey.of(it)) }
|
||||
}?.apply()
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,26 @@
|
|||
package com.nuvio.app.core.sync
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.nuvio.app.core.auth.AuthRepository
|
||||
import com.nuvio.app.core.auth.AuthState
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.ui.AppTheme
|
||||
import com.nuvio.app.features.details.MetaScreenSectionItem
|
||||
import com.nuvio.app.features.details.MetaScreenSectionKey
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsStorage
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
import com.nuvio.app.features.mdblist.MdbListMetadataService
|
||||
import com.nuvio.app.features.mdblist.MdbListSettingsStorage
|
||||
import com.nuvio.app.features.mdblist.MdbListSettingsRepository
|
||||
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepository
|
||||
import com.nuvio.app.features.player.PlayerSettingsStorage
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
import com.nuvio.app.features.player.SubtitleStyleState
|
||||
import com.nuvio.app.features.player.subtitleColorFromStorage
|
||||
import com.nuvio.app.features.player.toStorageHexString
|
||||
import com.nuvio.app.features.player.skip.NextEpisodeThresholdMode
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.features.settings.ThemeSettingsStorage
|
||||
import com.nuvio.app.features.settings.ThemeSettingsRepository
|
||||
import com.nuvio.app.features.streams.StreamAutoPlayMode
|
||||
import com.nuvio.app.features.streams.StreamAutoPlaySource
|
||||
import com.nuvio.app.features.tmdb.TmdbSettingsStorage
|
||||
import com.nuvio.app.features.tmdb.TmdbSettingsRepository
|
||||
import com.nuvio.app.features.trakt.TraktCommentsStorage
|
||||
import com.nuvio.app.features.trakt.TraktCommentsSettings
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesStorage
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
import kotlin.concurrent.Volatile
|
||||
|
|
@ -103,23 +100,23 @@ object ProfileSettingsSync {
|
|||
return@withLock false
|
||||
}
|
||||
|
||||
val remoteBlob = runCatching {
|
||||
json.decodeFromJsonElement(ProfileSettingsBlob.serializer(), remoteJson)
|
||||
}.getOrElse { error ->
|
||||
log.e(error) { "pull(profileId=$profileId) — failed to decode remote settings blob" }
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
val remoteSignature = buildSignature(remoteBlob)
|
||||
if (remoteSignature == localSignature) {
|
||||
log.d { "pull(profileId=$profileId) — remote matches local" }
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
isApplyingRemoteBlob = true
|
||||
try {
|
||||
val remoteBlob = runCatching {
|
||||
json.decodeFromJsonElement(MobileProfileSettingsBlob.serializer(), remoteJson)
|
||||
}.getOrElse { error ->
|
||||
log.e(error) { "pull(profileId=$profileId) — failed to decode remote settings blob" }
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
val remoteSignature = buildSignature(remoteBlob)
|
||||
if (remoteSignature == localSignature) {
|
||||
log.d { "pull(profileId=$profileId) — remote matches local" }
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
applyRemoteBlob(remoteBlob)
|
||||
skipNextPushSignature = remoteSignature
|
||||
skipNextPushSignature = currentObservedStateSignature()
|
||||
} finally {
|
||||
isApplyingRemoteBlob = false
|
||||
}
|
||||
|
|
@ -149,21 +146,19 @@ object ProfileSettingsSync {
|
|||
@OptIn(FlowPreview::class)
|
||||
private fun observeLocalChangesAndPush() {
|
||||
val signatureFlows = listOf(
|
||||
ThemeSettingsRepository.selectedTheme.map { "theme=${it.name}" },
|
||||
ThemeSettingsRepository.amoledEnabled.map { "amoled=$it" },
|
||||
PlayerSettingsRepository.uiState.map { buildPlayerSignature(it) },
|
||||
TmdbSettingsRepository.uiState.map { buildTmdbSignature(it) },
|
||||
MdbListSettingsRepository.uiState.map { buildMdbListSignature(it) },
|
||||
MetaScreenSettingsRepository.uiState.map { buildMetaScreenSignature(it) },
|
||||
ContinueWatchingPreferencesRepository.uiState.map { buildContinueWatchingSignature(it) },
|
||||
TraktCommentsSettings.enabled.map { "trakt_comments=$it" },
|
||||
EpisodeReleaseNotificationsRepository.uiState.map { "episode_release_alerts=${it.isEnabled}" },
|
||||
ThemeSettingsRepository.selectedTheme.map { "theme" },
|
||||
ThemeSettingsRepository.amoledEnabled.map { "amoled" },
|
||||
PlayerSettingsRepository.uiState.map { "player" },
|
||||
TmdbSettingsRepository.uiState.map { "tmdb" },
|
||||
MdbListSettingsRepository.uiState.map { "mdblist" },
|
||||
MetaScreenSettingsRepository.uiState.map { "meta" },
|
||||
ContinueWatchingPreferencesRepository.uiState.map { "continue_watching" },
|
||||
TraktCommentsSettings.enabled.map { "trakt_comments" },
|
||||
EpisodeReleaseNotificationsRepository.uiState.map { "episode_release_alerts" },
|
||||
)
|
||||
|
||||
observeJob = scope.launch {
|
||||
combine(signatureFlows) { parts ->
|
||||
parts.joinToString(separator = "||")
|
||||
}
|
||||
combine(signatureFlows) { currentObservedStateSignature() }
|
||||
.drop(1)
|
||||
.distinctUntilChanged()
|
||||
.debounce(PUSH_DEBOUNCE_MS)
|
||||
|
|
@ -180,243 +175,58 @@ object ProfileSettingsSync {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun pushToRemoteLocked(profileId: Int, blob: ProfileSettingsBlob) {
|
||||
private suspend fun pushToRemoteLocked(profileId: Int, blob: MobileProfileSettingsBlob) {
|
||||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_platform", MOBILE_SYNC_PLATFORM)
|
||||
put("p_settings_json", json.encodeToJsonElement(ProfileSettingsBlob.serializer(), blob))
|
||||
put("p_settings_json", json.encodeToJsonElement(MobileProfileSettingsBlob.serializer(), blob))
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_profile_settings_blob", params)
|
||||
log.d { "pushToRemoteLocked(profileId=$profileId) — success" }
|
||||
}
|
||||
|
||||
private fun exportSettingsBlob(): ProfileSettingsBlob {
|
||||
private fun exportSettingsBlob(): MobileProfileSettingsBlob {
|
||||
ensureRepositoriesLoaded()
|
||||
|
||||
val themePayload = ThemeSettingsPayload(
|
||||
selectedTheme = ThemeSettingsRepository.selectedTheme.value.name,
|
||||
amoledEnabled = ThemeSettingsRepository.amoledEnabled.value,
|
||||
)
|
||||
|
||||
val playerState = PlayerSettingsRepository.uiState.value
|
||||
val playerPayload = PlayerSettingsPayload(
|
||||
showLoadingOverlay = playerState.showLoadingOverlay,
|
||||
preferredAudioLanguage = playerState.preferredAudioLanguage,
|
||||
secondaryPreferredAudioLanguage = playerState.secondaryPreferredAudioLanguage,
|
||||
preferredSubtitleLanguage = playerState.preferredSubtitleLanguage,
|
||||
secondaryPreferredSubtitleLanguage = playerState.secondaryPreferredSubtitleLanguage,
|
||||
subtitleStyle = SubtitleStylePayload(
|
||||
textColorHex = playerState.subtitleStyle.textColor.toStorageHexString(),
|
||||
outlineEnabled = playerState.subtitleStyle.outlineEnabled,
|
||||
fontSizeSp = playerState.subtitleStyle.fontSizeSp,
|
||||
bottomOffset = playerState.subtitleStyle.bottomOffset,
|
||||
),
|
||||
streamReuseLastLinkEnabled = playerState.streamReuseLastLinkEnabled,
|
||||
streamReuseLastLinkCacheHours = playerState.streamReuseLastLinkCacheHours,
|
||||
decoderPriority = playerState.decoderPriority,
|
||||
mapDV7ToHevc = playerState.mapDV7ToHevc,
|
||||
tunnelingEnabled = playerState.tunnelingEnabled,
|
||||
streamAutoPlayMode = playerState.streamAutoPlayMode.name,
|
||||
streamAutoPlaySource = playerState.streamAutoPlaySource.name,
|
||||
streamAutoPlaySelectedAddons = playerState.streamAutoPlaySelectedAddons.sorted(),
|
||||
streamAutoPlaySelectedPlugins = playerState.streamAutoPlaySelectedPlugins.sorted(),
|
||||
streamAutoPlayRegex = playerState.streamAutoPlayRegex,
|
||||
streamAutoPlayTimeoutSeconds = playerState.streamAutoPlayTimeoutSeconds,
|
||||
skipIntroEnabled = playerState.skipIntroEnabled,
|
||||
animeSkipEnabled = playerState.animeSkipEnabled,
|
||||
animeSkipClientId = playerState.animeSkipClientId,
|
||||
streamAutoPlayNextEpisodeEnabled = playerState.streamAutoPlayNextEpisodeEnabled,
|
||||
streamAutoPlayPreferBingeGroup = playerState.streamAutoPlayPreferBingeGroup,
|
||||
nextEpisodeThresholdMode = playerState.nextEpisodeThresholdMode.name,
|
||||
nextEpisodeThresholdPercent = playerState.nextEpisodeThresholdPercent,
|
||||
nextEpisodeThresholdMinutesBeforeEnd = playerState.nextEpisodeThresholdMinutesBeforeEnd,
|
||||
useLibass = playerState.useLibass,
|
||||
libassRenderType = playerState.libassRenderType,
|
||||
)
|
||||
|
||||
val tmdbState = TmdbSettingsRepository.snapshot()
|
||||
val tmdbPayload = TmdbSettingsPayload(
|
||||
enabled = tmdbState.enabled,
|
||||
apiKey = tmdbState.apiKey,
|
||||
language = tmdbState.language,
|
||||
useTrailers = tmdbState.useTrailers,
|
||||
useArtwork = tmdbState.useArtwork,
|
||||
useBasicInfo = tmdbState.useBasicInfo,
|
||||
useDetails = tmdbState.useDetails,
|
||||
useCredits = tmdbState.useCredits,
|
||||
useProductions = tmdbState.useProductions,
|
||||
useNetworks = tmdbState.useNetworks,
|
||||
useEpisodes = tmdbState.useEpisodes,
|
||||
useSeasonPosters = tmdbState.useSeasonPosters,
|
||||
useMoreLikeThis = tmdbState.useMoreLikeThis,
|
||||
useCollections = tmdbState.useCollections,
|
||||
)
|
||||
|
||||
val mdbListState = MdbListSettingsRepository.snapshot()
|
||||
val mdbListPayload = MdbListSettingsPayload(
|
||||
enabled = mdbListState.enabled,
|
||||
apiKey = mdbListState.apiKey,
|
||||
useImdb = mdbListState.useImdb,
|
||||
useTmdb = mdbListState.useTmdb,
|
||||
useTomatoes = mdbListState.useTomatoes,
|
||||
useMetacritic = mdbListState.useMetacritic,
|
||||
useTrakt = mdbListState.useTrakt,
|
||||
useLetterboxd = mdbListState.useLetterboxd,
|
||||
useAudience = mdbListState.useAudience,
|
||||
)
|
||||
|
||||
val metaState = MetaScreenSettingsRepository.uiState.value
|
||||
val metaPayload = MetaScreenSettingsPayload(
|
||||
items = metaState.items
|
||||
.sortedBy { it.order }
|
||||
.map { item ->
|
||||
MetaScreenItemPayload(
|
||||
key = item.key.name,
|
||||
enabled = item.enabled,
|
||||
order = item.order,
|
||||
tabGroup = item.tabGroup,
|
||||
)
|
||||
},
|
||||
cinematicBackground = metaState.cinematicBackground,
|
||||
tabLayout = metaState.tabLayout,
|
||||
)
|
||||
|
||||
val continueWatchingState = ContinueWatchingPreferencesRepository.uiState.value
|
||||
val continueWatchingPayload = ContinueWatchingSettingsPayload(
|
||||
isVisible = continueWatchingState.isVisible,
|
||||
style = continueWatchingState.style.name,
|
||||
upNextFromFurthestEpisode = continueWatchingState.upNextFromFurthestEpisode,
|
||||
dismissedNextUpKeys = continueWatchingState.dismissedNextUpKeys.sorted(),
|
||||
)
|
||||
|
||||
val traktCommentsPayload = TraktCommentsPayload(
|
||||
enabled = TraktCommentsSettings.enabled.value,
|
||||
)
|
||||
|
||||
val notificationsPayload = EpisodeReleaseNotificationsPayload(
|
||||
alertsEnabled = EpisodeReleaseNotificationsRepository.uiState.value.isEnabled,
|
||||
)
|
||||
|
||||
return ProfileSettingsBlob(
|
||||
features = ProfileSettingsFeatures(
|
||||
theme = themePayload,
|
||||
player = playerPayload,
|
||||
tmdb = tmdbPayload,
|
||||
mdbList = mdbListPayload,
|
||||
metaScreen = metaPayload,
|
||||
continueWatching = continueWatchingPayload,
|
||||
traktComments = traktCommentsPayload,
|
||||
episodeReleaseNotifications = notificationsPayload,
|
||||
return MobileProfileSettingsBlob(
|
||||
features = MobileProfileSettingsFeatures(
|
||||
themeSettings = ThemeSettingsStorage.exportToSyncPayload(),
|
||||
playerSettings = PlayerSettingsStorage.exportToSyncPayload(),
|
||||
tmdbSettings = TmdbSettingsStorage.exportToSyncPayload(),
|
||||
mdbListSettings = MdbListSettingsStorage.exportToSyncPayload(),
|
||||
metaScreenSettingsPayload = MetaScreenSettingsStorage.loadPayload().orEmpty().trim(),
|
||||
continueWatchingSettingsPayload = ContinueWatchingPreferencesStorage.loadPayload().orEmpty().trim(),
|
||||
traktCommentsSettings = TraktCommentsStorage.exportToSyncPayload(),
|
||||
notificationsSettings = NotificationsSettingsPayload(
|
||||
episodeReleaseAlertsEnabled = EpisodeReleaseNotificationsRepository.uiState.value.isEnabled,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyRemoteBlob(blob: ProfileSettingsBlob) {
|
||||
val theme = blob.features.theme
|
||||
ThemeSettingsRepository.setTheme(
|
||||
runCatching { AppTheme.valueOf(theme.selectedTheme) }.getOrDefault(AppTheme.WHITE),
|
||||
)
|
||||
ThemeSettingsRepository.setAmoled(theme.amoledEnabled)
|
||||
private fun applyRemoteBlob(blob: MobileProfileSettingsBlob) {
|
||||
ThemeSettingsStorage.replaceFromSyncPayload(blob.features.themeSettings)
|
||||
ThemeSettingsRepository.onProfileChanged()
|
||||
|
||||
val player = blob.features.player
|
||||
PlayerSettingsRepository.setShowLoadingOverlay(player.showLoadingOverlay)
|
||||
PlayerSettingsRepository.setPreferredAudioLanguage(player.preferredAudioLanguage)
|
||||
PlayerSettingsRepository.setSecondaryPreferredAudioLanguage(player.secondaryPreferredAudioLanguage)
|
||||
PlayerSettingsRepository.setPreferredSubtitleLanguage(player.preferredSubtitleLanguage)
|
||||
PlayerSettingsRepository.setSecondaryPreferredSubtitleLanguage(player.secondaryPreferredSubtitleLanguage)
|
||||
PlayerSettingsRepository.setSubtitleStyle(
|
||||
SubtitleStyleState(
|
||||
textColor = subtitleColorFromStorage(player.subtitleStyle.textColorHex) ?: Color.White,
|
||||
outlineEnabled = player.subtitleStyle.outlineEnabled,
|
||||
fontSizeSp = player.subtitleStyle.fontSizeSp,
|
||||
bottomOffset = player.subtitleStyle.bottomOffset,
|
||||
),
|
||||
)
|
||||
PlayerSettingsRepository.setStreamReuseLastLinkEnabled(player.streamReuseLastLinkEnabled)
|
||||
PlayerSettingsRepository.setStreamReuseLastLinkCacheHours(player.streamReuseLastLinkCacheHours)
|
||||
PlayerSettingsRepository.setDecoderPriority(player.decoderPriority)
|
||||
PlayerSettingsRepository.setMapDV7ToHevc(player.mapDV7ToHevc)
|
||||
PlayerSettingsRepository.setTunnelingEnabled(player.tunnelingEnabled)
|
||||
PlayerSettingsRepository.setStreamAutoPlayMode(
|
||||
runCatching { StreamAutoPlayMode.valueOf(player.streamAutoPlayMode) }.getOrDefault(StreamAutoPlayMode.MANUAL),
|
||||
)
|
||||
PlayerSettingsRepository.setStreamAutoPlaySource(
|
||||
runCatching { StreamAutoPlaySource.valueOf(player.streamAutoPlaySource) }
|
||||
.getOrDefault(StreamAutoPlaySource.ALL_SOURCES),
|
||||
)
|
||||
PlayerSettingsRepository.setStreamAutoPlaySelectedAddons(player.streamAutoPlaySelectedAddons.toSet())
|
||||
PlayerSettingsRepository.setStreamAutoPlaySelectedPlugins(player.streamAutoPlaySelectedPlugins.toSet())
|
||||
PlayerSettingsRepository.setStreamAutoPlayRegex(player.streamAutoPlayRegex)
|
||||
PlayerSettingsRepository.setStreamAutoPlayTimeoutSeconds(player.streamAutoPlayTimeoutSeconds)
|
||||
PlayerSettingsRepository.setSkipIntroEnabled(player.skipIntroEnabled)
|
||||
PlayerSettingsRepository.setAnimeSkipEnabled(player.animeSkipEnabled)
|
||||
PlayerSettingsRepository.setAnimeSkipClientId(player.animeSkipClientId)
|
||||
PlayerSettingsRepository.setStreamAutoPlayNextEpisodeEnabled(player.streamAutoPlayNextEpisodeEnabled)
|
||||
PlayerSettingsRepository.setStreamAutoPlayPreferBingeGroup(player.streamAutoPlayPreferBingeGroup)
|
||||
PlayerSettingsRepository.setNextEpisodeThresholdMode(
|
||||
runCatching { NextEpisodeThresholdMode.valueOf(player.nextEpisodeThresholdMode) }
|
||||
.getOrDefault(NextEpisodeThresholdMode.PERCENTAGE),
|
||||
)
|
||||
PlayerSettingsRepository.setNextEpisodeThresholdPercent(player.nextEpisodeThresholdPercent)
|
||||
PlayerSettingsRepository.setNextEpisodeThresholdMinutesBeforeEnd(player.nextEpisodeThresholdMinutesBeforeEnd)
|
||||
PlayerSettingsRepository.setUseLibass(player.useLibass)
|
||||
PlayerSettingsRepository.setLibassRenderType(player.libassRenderType)
|
||||
PlayerSettingsStorage.replaceFromSyncPayload(blob.features.playerSettings)
|
||||
PlayerSettingsRepository.onProfileChanged()
|
||||
|
||||
val tmdb = blob.features.tmdb
|
||||
TmdbSettingsRepository.setApiKey(tmdb.apiKey)
|
||||
TmdbSettingsRepository.setLanguage(tmdb.language)
|
||||
TmdbSettingsRepository.setUseTrailers(tmdb.useTrailers)
|
||||
TmdbSettingsRepository.setUseArtwork(tmdb.useArtwork)
|
||||
TmdbSettingsRepository.setUseBasicInfo(tmdb.useBasicInfo)
|
||||
TmdbSettingsRepository.setUseDetails(tmdb.useDetails)
|
||||
TmdbSettingsRepository.setUseCredits(tmdb.useCredits)
|
||||
TmdbSettingsRepository.setUseProductions(tmdb.useProductions)
|
||||
TmdbSettingsRepository.setUseNetworks(tmdb.useNetworks)
|
||||
TmdbSettingsRepository.setUseEpisodes(tmdb.useEpisodes)
|
||||
TmdbSettingsRepository.setUseSeasonPosters(tmdb.useSeasonPosters)
|
||||
TmdbSettingsRepository.setUseMoreLikeThis(tmdb.useMoreLikeThis)
|
||||
TmdbSettingsRepository.setUseCollections(tmdb.useCollections)
|
||||
TmdbSettingsRepository.setEnabled(tmdb.enabled)
|
||||
TmdbSettingsStorage.replaceFromSyncPayload(blob.features.tmdbSettings)
|
||||
TmdbSettingsRepository.onProfileChanged()
|
||||
|
||||
val mdbList = blob.features.mdbList
|
||||
MdbListSettingsRepository.setApiKey(mdbList.apiKey)
|
||||
MdbListSettingsRepository.setProviderEnabled("imdb", mdbList.useImdb)
|
||||
MdbListSettingsRepository.setProviderEnabled("tmdb", mdbList.useTmdb)
|
||||
MdbListSettingsRepository.setProviderEnabled("tomatoes", mdbList.useTomatoes)
|
||||
MdbListSettingsRepository.setProviderEnabled("metacritic", mdbList.useMetacritic)
|
||||
MdbListSettingsRepository.setProviderEnabled("trakt", mdbList.useTrakt)
|
||||
MdbListSettingsRepository.setProviderEnabled("letterboxd", mdbList.useLetterboxd)
|
||||
MdbListSettingsRepository.setProviderEnabled("audience", mdbList.useAudience)
|
||||
MdbListSettingsRepository.setEnabled(mdbList.enabled)
|
||||
MdbListSettingsStorage.replaceFromSyncPayload(blob.features.mdbListSettings)
|
||||
MdbListMetadataService.clearCache()
|
||||
MdbListSettingsRepository.onProfileChanged()
|
||||
|
||||
val metaScreen = blob.features.metaScreen
|
||||
MetaScreenSettingsRepository.applyFromSync(
|
||||
items = metaScreen.items.map { item ->
|
||||
MetaScreenSectionItem(
|
||||
key = runCatching { MetaScreenSectionKey.valueOf(item.key) }
|
||||
.getOrDefault(MetaScreenSectionKey.ACTIONS),
|
||||
title = "",
|
||||
description = "",
|
||||
enabled = item.enabled,
|
||||
order = item.order,
|
||||
tabGroup = item.tabGroup,
|
||||
)
|
||||
},
|
||||
cinematicBackground = metaScreen.cinematicBackground,
|
||||
tabLayout = metaScreen.tabLayout,
|
||||
)
|
||||
MetaScreenSettingsStorage.savePayload(blob.features.metaScreenSettingsPayload)
|
||||
MetaScreenSettingsRepository.onProfileChanged()
|
||||
|
||||
val continueWatching = blob.features.continueWatching
|
||||
ContinueWatchingPreferencesRepository.applyFromSync(
|
||||
isVisible = continueWatching.isVisible,
|
||||
style = runCatching { ContinueWatchingSectionStyle.valueOf(continueWatching.style) }
|
||||
.getOrDefault(ContinueWatchingSectionStyle.Wide),
|
||||
upNextFromFurthestEpisode = continueWatching.upNextFromFurthestEpisode,
|
||||
dismissedNextUpKeys = continueWatching.dismissedNextUpKeys.toSet(),
|
||||
)
|
||||
ContinueWatchingPreferencesStorage.savePayload(blob.features.continueWatchingSettingsPayload)
|
||||
ContinueWatchingPreferencesRepository.onProfileChanged()
|
||||
|
||||
TraktCommentsSettings.setEnabled(blob.features.traktComments.enabled)
|
||||
EpisodeReleaseNotificationsRepository.applyFromSyncEnabled(blob.features.episodeReleaseNotifications.alertsEnabled)
|
||||
TraktCommentsStorage.replaceFromSyncPayload(blob.features.traktCommentsSettings)
|
||||
TraktCommentsSettings.onProfileChanged()
|
||||
|
||||
EpisodeReleaseNotificationsRepository.applyFromSyncEnabled(blob.features.notificationsSettings.episodeReleaseAlertsEnabled)
|
||||
}
|
||||
|
||||
private fun ensureRepositoriesLoaded() {
|
||||
|
|
@ -430,232 +240,51 @@ object ProfileSettingsSync {
|
|||
EpisodeReleaseNotificationsRepository.ensureLoaded()
|
||||
}
|
||||
|
||||
private fun buildSignature(blob: ProfileSettingsBlob): String =
|
||||
json.encodeToString(ProfileSettingsBlob.serializer(), blob)
|
||||
private fun buildSignature(blob: MobileProfileSettingsBlob): String =
|
||||
json.encodeToString(MobileProfileSettingsBlob.serializer(), blob)
|
||||
|
||||
private fun defaultSignature(): String =
|
||||
buildSignature(ProfileSettingsBlob())
|
||||
buildSignature(MobileProfileSettingsBlob())
|
||||
|
||||
private fun buildPlayerSignature(state: com.nuvio.app.features.player.PlayerSettingsUiState): String =
|
||||
json.encodeToString(PlayerSettingsPayload.serializer(), PlayerSettingsPayload(
|
||||
showLoadingOverlay = state.showLoadingOverlay,
|
||||
preferredAudioLanguage = state.preferredAudioLanguage,
|
||||
secondaryPreferredAudioLanguage = state.secondaryPreferredAudioLanguage,
|
||||
preferredSubtitleLanguage = state.preferredSubtitleLanguage,
|
||||
secondaryPreferredSubtitleLanguage = state.secondaryPreferredSubtitleLanguage,
|
||||
subtitleStyle = SubtitleStylePayload(
|
||||
textColorHex = state.subtitleStyle.textColor.toStorageHexString(),
|
||||
outlineEnabled = state.subtitleStyle.outlineEnabled,
|
||||
fontSizeSp = state.subtitleStyle.fontSizeSp,
|
||||
bottomOffset = state.subtitleStyle.bottomOffset,
|
||||
),
|
||||
streamReuseLastLinkEnabled = state.streamReuseLastLinkEnabled,
|
||||
streamReuseLastLinkCacheHours = state.streamReuseLastLinkCacheHours,
|
||||
decoderPriority = state.decoderPriority,
|
||||
mapDV7ToHevc = state.mapDV7ToHevc,
|
||||
tunnelingEnabled = state.tunnelingEnabled,
|
||||
streamAutoPlayMode = state.streamAutoPlayMode.name,
|
||||
streamAutoPlaySource = state.streamAutoPlaySource.name,
|
||||
streamAutoPlaySelectedAddons = state.streamAutoPlaySelectedAddons.sorted(),
|
||||
streamAutoPlaySelectedPlugins = state.streamAutoPlaySelectedPlugins.sorted(),
|
||||
streamAutoPlayRegex = state.streamAutoPlayRegex,
|
||||
streamAutoPlayTimeoutSeconds = state.streamAutoPlayTimeoutSeconds,
|
||||
skipIntroEnabled = state.skipIntroEnabled,
|
||||
animeSkipEnabled = state.animeSkipEnabled,
|
||||
animeSkipClientId = state.animeSkipClientId,
|
||||
streamAutoPlayNextEpisodeEnabled = state.streamAutoPlayNextEpisodeEnabled,
|
||||
streamAutoPlayPreferBingeGroup = state.streamAutoPlayPreferBingeGroup,
|
||||
nextEpisodeThresholdMode = state.nextEpisodeThresholdMode.name,
|
||||
nextEpisodeThresholdPercent = state.nextEpisodeThresholdPercent,
|
||||
nextEpisodeThresholdMinutesBeforeEnd = state.nextEpisodeThresholdMinutesBeforeEnd,
|
||||
useLibass = state.useLibass,
|
||||
libassRenderType = state.libassRenderType,
|
||||
))
|
||||
|
||||
private fun buildTmdbSignature(state: com.nuvio.app.features.tmdb.TmdbSettings): String =
|
||||
json.encodeToString(TmdbSettingsPayload.serializer(), TmdbSettingsPayload(
|
||||
enabled = state.enabled,
|
||||
apiKey = state.apiKey,
|
||||
language = state.language,
|
||||
useTrailers = state.useTrailers,
|
||||
useArtwork = state.useArtwork,
|
||||
useBasicInfo = state.useBasicInfo,
|
||||
useDetails = state.useDetails,
|
||||
useCredits = state.useCredits,
|
||||
useProductions = state.useProductions,
|
||||
useNetworks = state.useNetworks,
|
||||
useEpisodes = state.useEpisodes,
|
||||
useSeasonPosters = state.useSeasonPosters,
|
||||
useMoreLikeThis = state.useMoreLikeThis,
|
||||
useCollections = state.useCollections,
|
||||
))
|
||||
|
||||
private fun buildMdbListSignature(state: com.nuvio.app.features.mdblist.MdbListSettings): String =
|
||||
json.encodeToString(MdbListSettingsPayload.serializer(), MdbListSettingsPayload(
|
||||
enabled = state.enabled,
|
||||
apiKey = state.apiKey,
|
||||
useImdb = state.useImdb,
|
||||
useTmdb = state.useTmdb,
|
||||
useTomatoes = state.useTomatoes,
|
||||
useMetacritic = state.useMetacritic,
|
||||
useTrakt = state.useTrakt,
|
||||
useLetterboxd = state.useLetterboxd,
|
||||
useAudience = state.useAudience,
|
||||
))
|
||||
|
||||
private fun buildMetaScreenSignature(state: com.nuvio.app.features.details.MetaScreenSettingsUiState): String =
|
||||
json.encodeToString(MetaScreenSettingsPayload.serializer(), MetaScreenSettingsPayload(
|
||||
items = state.items.sortedBy { it.order }.map { item ->
|
||||
MetaScreenItemPayload(
|
||||
key = item.key.name,
|
||||
enabled = item.enabled,
|
||||
order = item.order,
|
||||
tabGroup = item.tabGroup,
|
||||
)
|
||||
},
|
||||
cinematicBackground = state.cinematicBackground,
|
||||
tabLayout = state.tabLayout,
|
||||
))
|
||||
|
||||
private fun buildContinueWatchingSignature(state: com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesUiState): String =
|
||||
json.encodeToString(ContinueWatchingSettingsPayload.serializer(), ContinueWatchingSettingsPayload(
|
||||
isVisible = state.isVisible,
|
||||
style = state.style.name,
|
||||
upNextFromFurthestEpisode = state.upNextFromFurthestEpisode,
|
||||
dismissedNextUpKeys = state.dismissedNextUpKeys.sorted(),
|
||||
))
|
||||
private fun currentObservedStateSignature(): String = listOf(
|
||||
"theme=${ThemeSettingsRepository.selectedTheme.value.name}",
|
||||
"amoled=${ThemeSettingsRepository.amoledEnabled.value}",
|
||||
"player=${PlayerSettingsRepository.uiState.value}",
|
||||
"tmdb=${TmdbSettingsRepository.uiState.value}",
|
||||
"mdblist=${MdbListSettingsRepository.uiState.value}",
|
||||
"meta=${MetaScreenSettingsRepository.uiState.value}",
|
||||
"continue=${ContinueWatchingPreferencesRepository.uiState.value}",
|
||||
"trakt_comments=${TraktCommentsSettings.enabled.value}",
|
||||
"episode_release_alerts=${EpisodeReleaseNotificationsRepository.uiState.value.isEnabled}",
|
||||
).joinToString(separator = "||")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class MobileProfileSettingsBlob(
|
||||
val version: Int = 2,
|
||||
val features: MobileProfileSettingsFeatures = MobileProfileSettingsFeatures(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MobileProfileSettingsFeatures(
|
||||
@SerialName("theme_settings") val themeSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("player_settings") val playerSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("tmdb_settings") val tmdbSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("mdblist_settings") val mdbListSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("meta_screen_settings_payload") val metaScreenSettingsPayload: String = "",
|
||||
@SerialName("continue_watching_settings_payload") val continueWatchingSettingsPayload: String = "",
|
||||
@SerialName("trakt_comments_settings") val traktCommentsSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("notifications_settings") val notificationsSettings: NotificationsSettingsPayload = NotificationsSettingsPayload(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class NotificationsSettingsPayload(
|
||||
@SerialName("episode_release_alerts_enabled") val episodeReleaseAlertsEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class SettingsBlobResponse(
|
||||
@SerialName("profile_id") val profileId: Int = 0,
|
||||
@SerialName("settings_json") val settingsJson: JsonObject? = null,
|
||||
@SerialName("updated_at") val updatedAt: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class ProfileSettingsBlob(
|
||||
val version: Int = 1,
|
||||
val features: ProfileSettingsFeatures = ProfileSettingsFeatures(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class ProfileSettingsFeatures(
|
||||
val theme: ThemeSettingsPayload = ThemeSettingsPayload(),
|
||||
val player: PlayerSettingsPayload = PlayerSettingsPayload(),
|
||||
val tmdb: TmdbSettingsPayload = TmdbSettingsPayload(),
|
||||
@SerialName("mdb_list") val mdbList: MdbListSettingsPayload = MdbListSettingsPayload(),
|
||||
@SerialName("meta_screen") val metaScreen: MetaScreenSettingsPayload = MetaScreenSettingsPayload(),
|
||||
@SerialName("continue_watching") val continueWatching: ContinueWatchingSettingsPayload = ContinueWatchingSettingsPayload(),
|
||||
@SerialName("trakt_comments") val traktComments: TraktCommentsPayload = TraktCommentsPayload(),
|
||||
@SerialName("episode_release_notifications") val episodeReleaseNotifications: EpisodeReleaseNotificationsPayload = EpisodeReleaseNotificationsPayload(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class ThemeSettingsPayload(
|
||||
val selectedTheme: String = AppTheme.WHITE.name,
|
||||
val amoledEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class SubtitleStylePayload(
|
||||
val textColorHex: String = Color.White.toStorageHexString(),
|
||||
val outlineEnabled: Boolean = false,
|
||||
val fontSizeSp: Int = 18,
|
||||
val bottomOffset: Int = 20,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class PlayerSettingsPayload(
|
||||
val showLoadingOverlay: Boolean = true,
|
||||
val preferredAudioLanguage: String = "device",
|
||||
val secondaryPreferredAudioLanguage: String? = null,
|
||||
val preferredSubtitleLanguage: String = "none",
|
||||
val secondaryPreferredSubtitleLanguage: String? = null,
|
||||
val subtitleStyle: SubtitleStylePayload = SubtitleStylePayload(),
|
||||
val streamReuseLastLinkEnabled: Boolean = false,
|
||||
val streamReuseLastLinkCacheHours: Int = 24,
|
||||
val decoderPriority: Int = 1,
|
||||
val mapDV7ToHevc: Boolean = false,
|
||||
val tunnelingEnabled: Boolean = false,
|
||||
val streamAutoPlayMode: String = StreamAutoPlayMode.MANUAL.name,
|
||||
val streamAutoPlaySource: String = StreamAutoPlaySource.ALL_SOURCES.name,
|
||||
val streamAutoPlaySelectedAddons: List<String> = emptyList(),
|
||||
val streamAutoPlaySelectedPlugins: List<String> = emptyList(),
|
||||
val streamAutoPlayRegex: String = "",
|
||||
val streamAutoPlayTimeoutSeconds: Int = 3,
|
||||
val skipIntroEnabled: Boolean = true,
|
||||
val animeSkipEnabled: Boolean = false,
|
||||
val animeSkipClientId: String = "",
|
||||
val streamAutoPlayNextEpisodeEnabled: Boolean = false,
|
||||
val streamAutoPlayPreferBingeGroup: Boolean = true,
|
||||
val nextEpisodeThresholdMode: String = NextEpisodeThresholdMode.PERCENTAGE.name,
|
||||
val nextEpisodeThresholdPercent: Float = 99f,
|
||||
val nextEpisodeThresholdMinutesBeforeEnd: Float = 2f,
|
||||
val useLibass: Boolean = false,
|
||||
val libassRenderType: String = "CUES",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbSettingsPayload(
|
||||
val enabled: Boolean = false,
|
||||
val apiKey: String = "",
|
||||
val language: String = "en",
|
||||
val useTrailers: Boolean = true,
|
||||
val useArtwork: Boolean = true,
|
||||
val useBasicInfo: Boolean = true,
|
||||
val useDetails: Boolean = true,
|
||||
val useCredits: Boolean = true,
|
||||
val useProductions: Boolean = true,
|
||||
val useNetworks: Boolean = true,
|
||||
val useEpisodes: Boolean = true,
|
||||
val useSeasonPosters: Boolean = true,
|
||||
val useMoreLikeThis: Boolean = true,
|
||||
val useCollections: Boolean = true,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MdbListSettingsPayload(
|
||||
val enabled: Boolean = false,
|
||||
val apiKey: String = "",
|
||||
val useImdb: Boolean = true,
|
||||
val useTmdb: Boolean = true,
|
||||
val useTomatoes: Boolean = true,
|
||||
val useMetacritic: Boolean = true,
|
||||
val useTrakt: Boolean = true,
|
||||
val useLetterboxd: Boolean = true,
|
||||
val useAudience: Boolean = true,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MetaScreenItemPayload(
|
||||
val key: String,
|
||||
val enabled: Boolean = true,
|
||||
val order: Int = 0,
|
||||
val tabGroup: Int? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MetaScreenSettingsPayload(
|
||||
val items: List<MetaScreenItemPayload> = emptyList(),
|
||||
val cinematicBackground: Boolean = false,
|
||||
val tabLayout: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class ContinueWatchingSettingsPayload(
|
||||
val isVisible: Boolean = true,
|
||||
val style: String = ContinueWatchingSectionStyle.Wide.name,
|
||||
val upNextFromFurthestEpisode: Boolean = true,
|
||||
val dismissedNextUpKeys: List<String> = emptyList(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TraktCommentsPayload(
|
||||
val enabled: Boolean = true,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class EpisodeReleaseNotificationsPayload(
|
||||
val alertsEnabled: Boolean = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
package com.nuvio.app.core.sync
|
||||
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.booleanOrNull
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.floatOrNull
|
||||
import kotlinx.serialization.json.intOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
private const val TYPE_KEY = "type"
|
||||
private const val VALUE_KEY = "value"
|
||||
private const val TYPE_STRING = "string"
|
||||
private const val TYPE_BOOLEAN = "boolean"
|
||||
private const val TYPE_INT = "int"
|
||||
private const val TYPE_FLOAT = "float"
|
||||
private const val TYPE_STRING_SET = "string_set"
|
||||
|
||||
internal fun encodeSyncString(value: String): JsonObject = buildJsonObject {
|
||||
put(TYPE_KEY, TYPE_STRING)
|
||||
put(VALUE_KEY, value)
|
||||
}
|
||||
|
||||
internal fun encodeSyncBoolean(value: Boolean): JsonObject = buildJsonObject {
|
||||
put(TYPE_KEY, TYPE_BOOLEAN)
|
||||
put(VALUE_KEY, value)
|
||||
}
|
||||
|
||||
internal fun encodeSyncInt(value: Int): JsonObject = buildJsonObject {
|
||||
put(TYPE_KEY, TYPE_INT)
|
||||
put(VALUE_KEY, value)
|
||||
}
|
||||
|
||||
internal fun encodeSyncFloat(value: Float): JsonObject = buildJsonObject {
|
||||
put(TYPE_KEY, TYPE_FLOAT)
|
||||
put(VALUE_KEY, value)
|
||||
}
|
||||
|
||||
internal fun encodeSyncStringSet(values: Set<String>): JsonObject = buildJsonObject {
|
||||
put(TYPE_KEY, TYPE_STRING_SET)
|
||||
put(VALUE_KEY, JsonArray(values.sorted().map(::JsonPrimitive)))
|
||||
}
|
||||
|
||||
internal fun JsonObject.decodeSyncString(key: String): String? =
|
||||
get(key)
|
||||
?.jsonObject
|
||||
?.takeIf { it[TYPE_KEY]?.jsonPrimitive?.contentOrNull == TYPE_STRING }
|
||||
?.get(VALUE_KEY)
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
|
||||
internal fun JsonObject.decodeSyncBoolean(key: String): Boolean? =
|
||||
get(key)
|
||||
?.jsonObject
|
||||
?.takeIf { it[TYPE_KEY]?.jsonPrimitive?.contentOrNull == TYPE_BOOLEAN }
|
||||
?.get(VALUE_KEY)
|
||||
?.jsonPrimitive
|
||||
?.booleanOrNull
|
||||
|
||||
internal fun JsonObject.decodeSyncInt(key: String): Int? =
|
||||
get(key)
|
||||
?.jsonObject
|
||||
?.takeIf { it[TYPE_KEY]?.jsonPrimitive?.contentOrNull == TYPE_INT }
|
||||
?.get(VALUE_KEY)
|
||||
?.jsonPrimitive
|
||||
?.intOrNull
|
||||
|
||||
internal fun JsonObject.decodeSyncFloat(key: String): Float? =
|
||||
get(key)
|
||||
?.jsonObject
|
||||
?.takeIf { it[TYPE_KEY]?.jsonPrimitive?.contentOrNull == TYPE_FLOAT }
|
||||
?.get(VALUE_KEY)
|
||||
?.jsonPrimitive
|
||||
?.floatOrNull
|
||||
|
||||
internal fun JsonObject.decodeSyncStringSet(key: String): Set<String>? =
|
||||
get(key)
|
||||
?.jsonObject
|
||||
?.takeIf { it[TYPE_KEY]?.jsonPrimitive?.contentOrNull == TYPE_STRING_SET }
|
||||
?.get(VALUE_KEY)
|
||||
?.jsonArray
|
||||
?.mapNotNull { it.jsonPrimitive.contentOrNull?.trim() }
|
||||
?.filter(String::isNotBlank)
|
||||
?.toSet()
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.nuvio.app.features.mdblist
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
internal expect object MdbListSettingsStorage {
|
||||
fun loadEnabled(): Boolean?
|
||||
fun saveEnabled(enabled: Boolean)
|
||||
|
|
@ -19,4 +21,6 @@ internal expect object MdbListSettingsStorage {
|
|||
fun saveUseLetterboxd(enabled: Boolean)
|
||||
fun loadUseAudience(): Boolean?
|
||||
fun saveUseAudience(enabled: Boolean)
|
||||
fun exportToSyncPayload(): JsonObject
|
||||
fun replaceFromSyncPayload(payload: JsonObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.nuvio.app.features.player
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
internal expect object PlayerSettingsStorage {
|
||||
fun loadShowLoadingOverlay(): Boolean?
|
||||
fun saveShowLoadingOverlay(enabled: Boolean)
|
||||
|
|
@ -61,4 +63,6 @@ internal expect object PlayerSettingsStorage {
|
|||
fun saveUseLibass(enabled: Boolean)
|
||||
fun loadLibassRenderType(): String?
|
||||
fun saveLibassRenderType(renderType: String)
|
||||
fun exportToSyncPayload(): JsonObject
|
||||
fun replaceFromSyncPayload(payload: JsonObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.nuvio.app.features.settings
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
internal expect object ThemeSettingsStorage {
|
||||
fun loadSelectedTheme(): String?
|
||||
fun saveSelectedTheme(themeName: String)
|
||||
fun loadAmoledEnabled(): Boolean?
|
||||
fun saveAmoledEnabled(enabled: Boolean)
|
||||
fun exportToSyncPayload(): JsonObject
|
||||
fun replaceFromSyncPayload(payload: JsonObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.nuvio.app.features.tmdb
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
internal expect object TmdbSettingsStorage {
|
||||
fun loadEnabled(): Boolean?
|
||||
fun saveEnabled(enabled: Boolean)
|
||||
|
|
@ -29,4 +31,6 @@ internal expect object TmdbSettingsStorage {
|
|||
fun saveUseMoreLikeThis(enabled: Boolean)
|
||||
fun loadUseCollections(): Boolean?
|
||||
fun saveUseCollections(enabled: Boolean)
|
||||
fun exportToSyncPayload(): JsonObject
|
||||
fun replaceFromSyncPayload(payload: JsonObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.nuvio.app.features.trakt
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
internal expect object TraktCommentsStorage {
|
||||
fun loadEnabled(): Boolean?
|
||||
fun saveEnabled(enabled: Boolean)
|
||||
fun exportToSyncPayload(): JsonObject
|
||||
fun replaceFromSyncPayload(payload: JsonObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package com.nuvio.app.features.mdblist
|
||||
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object MdbListSettingsStorage {
|
||||
|
|
@ -13,6 +20,17 @@ actual object MdbListSettingsStorage {
|
|||
private const val useTraktKey = "mdblist_use_trakt"
|
||||
private const val useLetterboxdKey = "mdblist_use_letterboxd"
|
||||
private const val useAudienceKey = "mdblist_use_audience"
|
||||
private val syncKeys = listOf(
|
||||
enabledKey,
|
||||
apiKey,
|
||||
useImdbKey,
|
||||
useTmdbKey,
|
||||
useTomatoesKey,
|
||||
useMetacriticKey,
|
||||
useTraktKey,
|
||||
useLetterboxdKey,
|
||||
useAudienceKey,
|
||||
)
|
||||
|
||||
actual fun loadEnabled(): Boolean? = loadBoolean(enabledKey)
|
||||
|
||||
|
|
@ -82,4 +100,32 @@ actual object MdbListSettingsStorage {
|
|||
private fun saveBoolean(key: String, enabled: Boolean) {
|
||||
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
loadApiKey()?.let { put(apiKey, encodeSyncString(it)) }
|
||||
loadUseImdb()?.let { put(useImdbKey, encodeSyncBoolean(it)) }
|
||||
loadUseTmdb()?.let { put(useTmdbKey, encodeSyncBoolean(it)) }
|
||||
loadUseTomatoes()?.let { put(useTomatoesKey, encodeSyncBoolean(it)) }
|
||||
loadUseMetacritic()?.let { put(useMetacriticKey, encodeSyncBoolean(it)) }
|
||||
loadUseTrakt()?.let { put(useTraktKey, encodeSyncBoolean(it)) }
|
||||
loadUseLetterboxd()?.let { put(useLetterboxdKey, encodeSyncBoolean(it)) }
|
||||
loadUseAudience()?.let { put(useAudienceKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { key ->
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
payload.decodeSyncString(apiKey)?.let(::saveApiKey)
|
||||
payload.decodeSyncBoolean(useImdbKey)?.let(::saveUseImdb)
|
||||
payload.decodeSyncBoolean(useTmdbKey)?.let(::saveUseTmdb)
|
||||
payload.decodeSyncBoolean(useTomatoesKey)?.let(::saveUseTomatoes)
|
||||
payload.decodeSyncBoolean(useMetacriticKey)?.let(::saveUseMetacritic)
|
||||
payload.decodeSyncBoolean(useTraktKey)?.let(::saveUseTrakt)
|
||||
payload.decodeSyncBoolean(useLetterboxdKey)?.let(::saveUseLetterboxd)
|
||||
payload.decodeSyncBoolean(useAudienceKey)?.let(::saveUseAudience)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
package com.nuvio.app.features.player
|
||||
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncFloat
|
||||
import com.nuvio.app.core.sync.decodeSyncInt
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.decodeSyncStringSet
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncFloat
|
||||
import com.nuvio.app.core.sync.encodeSyncInt
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncStringSet
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object PlayerSettingsStorage {
|
||||
|
|
@ -32,6 +45,40 @@ actual object PlayerSettingsStorage {
|
|||
private const val nextEpisodeThresholdModeKey = "next_episode_threshold_mode"
|
||||
private const val nextEpisodeThresholdPercentKey = "next_episode_threshold_percent_v2"
|
||||
private const val nextEpisodeThresholdMinutesBeforeEndKey = "next_episode_threshold_minutes_before_end_v2"
|
||||
private const val useLibassKey = "use_libass"
|
||||
private const val libassRenderTypeKey = "libass_render_type"
|
||||
private val syncKeys = listOf(
|
||||
showLoadingOverlayKey,
|
||||
preferredAudioLanguageKey,
|
||||
secondaryPreferredAudioLanguageKey,
|
||||
preferredSubtitleLanguageKey,
|
||||
secondaryPreferredSubtitleLanguageKey,
|
||||
subtitleTextColorKey,
|
||||
subtitleOutlineEnabledKey,
|
||||
subtitleFontSizeSpKey,
|
||||
subtitleBottomOffsetKey,
|
||||
streamReuseLastLinkEnabledKey,
|
||||
streamReuseLastLinkCacheHoursKey,
|
||||
decoderPriorityKey,
|
||||
mapDV7ToHevcKey,
|
||||
tunnelingEnabledKey,
|
||||
streamAutoPlayModeKey,
|
||||
streamAutoPlaySourceKey,
|
||||
streamAutoPlaySelectedAddonsKey,
|
||||
streamAutoPlaySelectedPluginsKey,
|
||||
streamAutoPlayRegexKey,
|
||||
streamAutoPlayTimeoutSecondsKey,
|
||||
skipIntroEnabledKey,
|
||||
animeSkipEnabledKey,
|
||||
animeSkipClientIdKey,
|
||||
streamAutoPlayNextEpisodeEnabledKey,
|
||||
streamAutoPlayPreferBingeGroupKey,
|
||||
nextEpisodeThresholdModeKey,
|
||||
nextEpisodeThresholdPercentKey,
|
||||
nextEpisodeThresholdMinutesBeforeEndKey,
|
||||
useLibassKey,
|
||||
libassRenderTypeKey,
|
||||
)
|
||||
|
||||
actual fun loadShowLoadingOverlay(): Boolean? {
|
||||
val defaults = NSUserDefaults.standardUserDefaults
|
||||
|
|
@ -400,4 +447,74 @@ actual object PlayerSettingsStorage {
|
|||
actual fun loadLibassRenderType(): String? = null
|
||||
|
||||
actual fun saveLibassRenderType(renderType: String) {}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadShowLoadingOverlay()?.let { put(showLoadingOverlayKey, encodeSyncBoolean(it)) }
|
||||
loadPreferredAudioLanguage()?.let { put(preferredAudioLanguageKey, encodeSyncString(it)) }
|
||||
loadSecondaryPreferredAudioLanguage()?.let { put(secondaryPreferredAudioLanguageKey, encodeSyncString(it)) }
|
||||
loadPreferredSubtitleLanguage()?.let { put(preferredSubtitleLanguageKey, encodeSyncString(it)) }
|
||||
loadSecondaryPreferredSubtitleLanguage()?.let { put(secondaryPreferredSubtitleLanguageKey, encodeSyncString(it)) }
|
||||
loadSubtitleTextColor()?.let { put(subtitleTextColorKey, encodeSyncString(it)) }
|
||||
loadSubtitleOutlineEnabled()?.let { put(subtitleOutlineEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadSubtitleFontSizeSp()?.let { put(subtitleFontSizeSpKey, encodeSyncInt(it)) }
|
||||
loadSubtitleBottomOffset()?.let { put(subtitleBottomOffsetKey, encodeSyncInt(it)) }
|
||||
loadStreamReuseLastLinkEnabled()?.let { put(streamReuseLastLinkEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamReuseLastLinkCacheHours()?.let { put(streamReuseLastLinkCacheHoursKey, encodeSyncInt(it)) }
|
||||
loadDecoderPriority()?.let { put(decoderPriorityKey, encodeSyncInt(it)) }
|
||||
loadMapDV7ToHevc()?.let { put(mapDV7ToHevcKey, encodeSyncBoolean(it)) }
|
||||
loadTunnelingEnabled()?.let { put(tunnelingEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayMode()?.let { put(streamAutoPlayModeKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlaySource()?.let { put(streamAutoPlaySourceKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlaySelectedAddons()?.let { put(streamAutoPlaySelectedAddonsKey, encodeSyncStringSet(it)) }
|
||||
loadStreamAutoPlaySelectedPlugins()?.let { put(streamAutoPlaySelectedPluginsKey, encodeSyncStringSet(it)) }
|
||||
loadStreamAutoPlayRegex()?.let { put(streamAutoPlayRegexKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlayTimeoutSeconds()?.let { put(streamAutoPlayTimeoutSecondsKey, encodeSyncInt(it)) }
|
||||
loadSkipIntroEnabled()?.let { put(skipIntroEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipEnabled()?.let { put(animeSkipEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipClientId()?.let { put(animeSkipClientIdKey, encodeSyncString(it)) }
|
||||
loadStreamAutoPlayNextEpisodeEnabled()?.let { put(streamAutoPlayNextEpisodeEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayPreferBingeGroup()?.let { put(streamAutoPlayPreferBingeGroupKey, encodeSyncBoolean(it)) }
|
||||
loadNextEpisodeThresholdMode()?.let { put(nextEpisodeThresholdModeKey, encodeSyncString(it)) }
|
||||
loadNextEpisodeThresholdPercent()?.let { put(nextEpisodeThresholdPercentKey, encodeSyncFloat(it)) }
|
||||
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
|
||||
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
|
||||
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { key ->
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
payload.decodeSyncBoolean(showLoadingOverlayKey)?.let(::saveShowLoadingOverlay)
|
||||
payload.decodeSyncString(preferredAudioLanguageKey)?.let(::savePreferredAudioLanguage)
|
||||
payload.decodeSyncString(secondaryPreferredAudioLanguageKey)?.let(::saveSecondaryPreferredAudioLanguage)
|
||||
payload.decodeSyncString(preferredSubtitleLanguageKey)?.let(::savePreferredSubtitleLanguage)
|
||||
payload.decodeSyncString(secondaryPreferredSubtitleLanguageKey)?.let(::saveSecondaryPreferredSubtitleLanguage)
|
||||
payload.decodeSyncString(subtitleTextColorKey)?.let(::saveSubtitleTextColor)
|
||||
payload.decodeSyncBoolean(subtitleOutlineEnabledKey)?.let(::saveSubtitleOutlineEnabled)
|
||||
payload.decodeSyncInt(subtitleFontSizeSpKey)?.let(::saveSubtitleFontSizeSp)
|
||||
payload.decodeSyncInt(subtitleBottomOffsetKey)?.let(::saveSubtitleBottomOffset)
|
||||
payload.decodeSyncBoolean(streamReuseLastLinkEnabledKey)?.let(::saveStreamReuseLastLinkEnabled)
|
||||
payload.decodeSyncInt(streamReuseLastLinkCacheHoursKey)?.let(::saveStreamReuseLastLinkCacheHours)
|
||||
payload.decodeSyncInt(decoderPriorityKey)?.let(::saveDecoderPriority)
|
||||
payload.decodeSyncBoolean(mapDV7ToHevcKey)?.let(::saveMapDV7ToHevc)
|
||||
payload.decodeSyncBoolean(tunnelingEnabledKey)?.let(::saveTunnelingEnabled)
|
||||
payload.decodeSyncString(streamAutoPlayModeKey)?.let(::saveStreamAutoPlayMode)
|
||||
payload.decodeSyncString(streamAutoPlaySourceKey)?.let(::saveStreamAutoPlaySource)
|
||||
payload.decodeSyncStringSet(streamAutoPlaySelectedAddonsKey)?.let(::saveStreamAutoPlaySelectedAddons)
|
||||
payload.decodeSyncStringSet(streamAutoPlaySelectedPluginsKey)?.let(::saveStreamAutoPlaySelectedPlugins)
|
||||
payload.decodeSyncString(streamAutoPlayRegexKey)?.let(::saveStreamAutoPlayRegex)
|
||||
payload.decodeSyncInt(streamAutoPlayTimeoutSecondsKey)?.let(::saveStreamAutoPlayTimeoutSeconds)
|
||||
payload.decodeSyncBoolean(skipIntroEnabledKey)?.let(::saveSkipIntroEnabled)
|
||||
payload.decodeSyncBoolean(animeSkipEnabledKey)?.let(::saveAnimeSkipEnabled)
|
||||
payload.decodeSyncString(animeSkipClientIdKey)?.let(::saveAnimeSkipClientId)
|
||||
payload.decodeSyncBoolean(streamAutoPlayNextEpisodeEnabledKey)?.let(::saveStreamAutoPlayNextEpisodeEnabled)
|
||||
payload.decodeSyncBoolean(streamAutoPlayPreferBingeGroupKey)?.let(::saveStreamAutoPlayPreferBingeGroup)
|
||||
payload.decodeSyncString(nextEpisodeThresholdModeKey)?.let(::saveNextEpisodeThresholdMode)
|
||||
payload.decodeSyncFloat(nextEpisodeThresholdPercentKey)?.let(::saveNextEpisodeThresholdPercent)
|
||||
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
|
||||
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
|
||||
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
package com.nuvio.app.features.settings
|
||||
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object ThemeSettingsStorage {
|
||||
private const val selectedThemeKey = "selected_theme"
|
||||
private const val amoledEnabledKey = "amoled_enabled"
|
||||
private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey)
|
||||
|
||||
actual fun loadSelectedTheme(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedThemeKey))
|
||||
|
|
@ -27,4 +35,18 @@ actual object ThemeSettingsStorage {
|
|||
actual fun saveAmoledEnabled(enabled: Boolean) {
|
||||
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(amoledEnabledKey))
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadSelectedTheme()?.let { put(selectedThemeKey, encodeSyncString(it)) }
|
||||
loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { key ->
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme)
|
||||
payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package com.nuvio.app.features.tmdb
|
||||
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.decodeSyncString
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncString
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object TmdbSettingsStorage {
|
||||
|
|
@ -18,6 +25,22 @@ actual object TmdbSettingsStorage {
|
|||
private const val useSeasonPostersKey = "tmdb_use_season_posters"
|
||||
private const val useMoreLikeThisKey = "tmdb_use_more_like_this"
|
||||
private const val useCollectionsKey = "tmdb_use_collections"
|
||||
private val syncKeys = listOf(
|
||||
enabledKey,
|
||||
apiKeyKey,
|
||||
languageKey,
|
||||
useTrailersKey,
|
||||
useArtworkKey,
|
||||
useBasicInfoKey,
|
||||
useDetailsKey,
|
||||
useCreditsKey,
|
||||
useProductionsKey,
|
||||
useNetworksKey,
|
||||
useEpisodesKey,
|
||||
useSeasonPostersKey,
|
||||
useMoreLikeThisKey,
|
||||
useCollectionsKey,
|
||||
)
|
||||
|
||||
actual fun loadEnabled(): Boolean? = loadBoolean(enabledKey)
|
||||
|
||||
|
|
@ -118,4 +141,42 @@ actual object TmdbSettingsStorage {
|
|||
private fun saveBoolean(key: String, enabled: Boolean) {
|
||||
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
loadApiKey()?.let { put(apiKeyKey, encodeSyncString(it)) }
|
||||
loadLanguage()?.let { put(languageKey, encodeSyncString(it)) }
|
||||
loadUseTrailers()?.let { put(useTrailersKey, encodeSyncBoolean(it)) }
|
||||
loadUseArtwork()?.let { put(useArtworkKey, encodeSyncBoolean(it)) }
|
||||
loadUseBasicInfo()?.let { put(useBasicInfoKey, encodeSyncBoolean(it)) }
|
||||
loadUseDetails()?.let { put(useDetailsKey, encodeSyncBoolean(it)) }
|
||||
loadUseCredits()?.let { put(useCreditsKey, encodeSyncBoolean(it)) }
|
||||
loadUseProductions()?.let { put(useProductionsKey, encodeSyncBoolean(it)) }
|
||||
loadUseNetworks()?.let { put(useNetworksKey, encodeSyncBoolean(it)) }
|
||||
loadUseEpisodes()?.let { put(useEpisodesKey, encodeSyncBoolean(it)) }
|
||||
loadUseSeasonPosters()?.let { put(useSeasonPostersKey, encodeSyncBoolean(it)) }
|
||||
loadUseMoreLikeThis()?.let { put(useMoreLikeThisKey, encodeSyncBoolean(it)) }
|
||||
loadUseCollections()?.let { put(useCollectionsKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { key ->
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
payload.decodeSyncString(apiKeyKey)?.let(::saveApiKey)
|
||||
payload.decodeSyncString(languageKey)?.let(::saveLanguage)
|
||||
payload.decodeSyncBoolean(useTrailersKey)?.let(::saveUseTrailers)
|
||||
payload.decodeSyncBoolean(useArtworkKey)?.let(::saveUseArtwork)
|
||||
payload.decodeSyncBoolean(useBasicInfoKey)?.let(::saveUseBasicInfo)
|
||||
payload.decodeSyncBoolean(useDetailsKey)?.let(::saveUseDetails)
|
||||
payload.decodeSyncBoolean(useCreditsKey)?.let(::saveUseCredits)
|
||||
payload.decodeSyncBoolean(useProductionsKey)?.let(::saveUseProductions)
|
||||
payload.decodeSyncBoolean(useNetworksKey)?.let(::saveUseNetworks)
|
||||
payload.decodeSyncBoolean(useEpisodesKey)?.let(::saveUseEpisodes)
|
||||
payload.decodeSyncBoolean(useSeasonPostersKey)?.let(::saveUseSeasonPosters)
|
||||
payload.decodeSyncBoolean(useMoreLikeThisKey)?.let(::saveUseMoreLikeThis)
|
||||
payload.decodeSyncBoolean(useCollectionsKey)?.let(::saveUseCollections)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
package com.nuvio.app.features.trakt
|
||||
|
||||
import com.nuvio.app.core.sync.decodeSyncBoolean
|
||||
import com.nuvio.app.core.sync.encodeSyncBoolean
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
internal actual object TraktCommentsStorage {
|
||||
private const val enabledKey = "comments_enabled"
|
||||
private val syncKeys = listOf(enabledKey)
|
||||
|
||||
actual fun loadEnabled(): Boolean? {
|
||||
val defaults = NSUserDefaults.standardUserDefaults
|
||||
|
|
@ -19,4 +25,16 @@ internal actual object TraktCommentsStorage {
|
|||
actual fun saveEnabled(enabled: Boolean) {
|
||||
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(enabledKey))
|
||||
}
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadEnabled()?.let { put(enabledKey, encodeSyncBoolean(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { key ->
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key))
|
||||
}
|
||||
|
||||
payload.decodeSyncBoolean(enabledKey)?.let(::saveEnabled)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue