diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt index 74d31347..270f276c 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt @@ -60,6 +60,9 @@ actual object PlayerSettingsStorage { private const val streamAutoPlayRegexKey = "stream_auto_play_regex" private const val streamAutoPlayTimeoutSecondsKey = "stream_auto_play_timeout_seconds" private const val skipIntroEnabledKey = "skip_intro_enabled" + private const val autoSkipIntroEnabledKey = "auto_skip_intro_enabled" + private const val autoSkipRecapEnabledKey = "auto_skip_recap_enabled" + private const val autoSkipOutroEnabledKey = "auto_skip_outro_enabled" private const val animeSkipEnabledKey = "animeskip_enabled" private const val animeSkipClientIdKey = "animeskip_client_id" private const val introDbApiKeyKey = "introdb_api_key" @@ -129,6 +132,9 @@ actual object PlayerSettingsStorage { streamAutoPlayRegexKey, streamAutoPlayTimeoutSecondsKey, skipIntroEnabledKey, + autoSkipIntroEnabledKey, + autoSkipRecapEnabledKey, + autoSkipOutroEnabledKey, animeSkipEnabledKey, animeSkipClientIdKey, streamAutoPlayNextEpisodeEnabledKey, @@ -775,6 +781,57 @@ actual object PlayerSettingsStorage { ?.apply() } + actual fun loadAutoSkipIntroEnabled(): Boolean? = + preferences?.let { sharedPreferences -> + val key = ProfileScopedKey.of(autoSkipIntroEnabledKey) + if (sharedPreferences.contains(key)) { + sharedPreferences.getBoolean(key, false) + } else { + null + } + } + + actual fun saveAutoSkipIntroEnabled(enabled: Boolean) { + preferences + ?.edit() + ?.putBoolean(ProfileScopedKey.of(autoSkipIntroEnabledKey), enabled) + ?.apply() + } + + actual fun loadAutoSkipRecapEnabled(): Boolean? = + preferences?.let { sharedPreferences -> + val key = ProfileScopedKey.of(autoSkipRecapEnabledKey) + if (sharedPreferences.contains(key)) { + sharedPreferences.getBoolean(key, false) + } else { + null + } + } + + actual fun saveAutoSkipRecapEnabled(enabled: Boolean) { + preferences + ?.edit() + ?.putBoolean(ProfileScopedKey.of(autoSkipRecapEnabledKey), enabled) + ?.apply() + } + + actual fun loadAutoSkipOutroEnabled(): Boolean? = + preferences?.let { sharedPreferences -> + val key = ProfileScopedKey.of(autoSkipOutroEnabledKey) + if (sharedPreferences.contains(key)) { + sharedPreferences.getBoolean(key, false) + } else { + null + } + } + + actual fun saveAutoSkipOutroEnabled(enabled: Boolean) { + preferences + ?.edit() + ?.putBoolean(ProfileScopedKey.of(autoSkipOutroEnabledKey), enabled) + ?.apply() + } + actual fun loadAnimeSkipEnabled(): Boolean? = preferences?.let { sharedPreferences -> val key = ProfileScopedKey.of(animeSkipEnabledKey) @@ -1116,6 +1173,9 @@ actual object PlayerSettingsStorage { loadStreamAutoPlayRegex()?.let { put(streamAutoPlayRegexKey, encodeSyncString(it)) } loadStreamAutoPlayTimeoutSeconds()?.let { put(streamAutoPlayTimeoutSecondsKey, encodeSyncInt(it)) } loadSkipIntroEnabled()?.let { put(skipIntroEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipIntroEnabled()?.let { put(autoSkipIntroEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipRecapEnabled()?.let { put(autoSkipRecapEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipOutroEnabled()?.let { put(autoSkipOutroEnabledKey, encodeSyncBoolean(it)) } loadAnimeSkipEnabled()?.let { put(animeSkipEnabledKey, encodeSyncBoolean(it)) } loadAnimeSkipClientId()?.let { put(animeSkipClientIdKey, encodeSyncString(it)) } loadStreamAutoPlayNextEpisodeEnabled()?.let { put(streamAutoPlayNextEpisodeEnabledKey, encodeSyncBoolean(it)) } @@ -1190,6 +1250,9 @@ actual object PlayerSettingsStorage { payload.decodeSyncString(streamAutoPlayRegexKey)?.let(::saveStreamAutoPlayRegex) payload.decodeSyncInt(streamAutoPlayTimeoutSecondsKey)?.let(::saveStreamAutoPlayTimeoutSeconds) payload.decodeSyncBoolean(skipIntroEnabledKey)?.let(::saveSkipIntroEnabled) + payload.decodeSyncBoolean(autoSkipIntroEnabledKey)?.let(::saveAutoSkipIntroEnabled) + payload.decodeSyncBoolean(autoSkipRecapEnabledKey)?.let(::saveAutoSkipRecapEnabled) + payload.decodeSyncBoolean(autoSkipOutroEnabledKey)?.let(::saveAutoSkipOutroEnabled) payload.decodeSyncBoolean(animeSkipEnabledKey)?.let(::saveAnimeSkipEnabled) payload.decodeSyncString(animeSkipClientIdKey)?.let(::saveAnimeSkipClientId) payload.decodeSyncString(introDbApiKeyKey)?.let(::saveIntroDbApiKey) diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index f00ffc65..1f809570 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -1033,6 +1033,12 @@ Vertical Offset Skip Intro Use introdb.app to detect intros and recaps. + Auto-skip Intro / Opening + Skip intros and anime openings automatically. + Auto-skip Recap + Skip recap segments automatically. + Auto-skip Outro / Ending + Skip outros and anime endings automatically. Auto-play Source Scope All installed addons Auto-play only considers streams coming from your installed addons. diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt index 02df155a..49810a52 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt @@ -10,6 +10,7 @@ import com.nuvio.app.features.p2p.P2pStreamingEngine import com.nuvio.app.features.p2p.P2pStreamingState import com.nuvio.app.features.player.skip.NextEpisodeInfo import com.nuvio.app.features.player.skip.PlayerNextEpisodeRules +import com.nuvio.app.features.player.skip.SkipInterval import com.nuvio.app.features.player.skip.SkipIntroRepository import com.nuvio.app.features.streams.BingeGroupCacheRepository import com.nuvio.app.features.streams.StreamLinkCacheRepository @@ -22,6 +23,15 @@ import kotlinx.coroutines.launch import nuvio.composeapp.generated.resources.* import org.jetbrains.compose.resources.getString +/** Whether auto-skip is enabled for this segment's type (categorized by SkipIntroRepository). */ +internal fun PlayerSettingsUiState.autoSkips(interval: SkipInterval): Boolean = + when (SkipIntroRepository.segmentCategory(interval.type)) { + "opening" -> autoSkipIntroEnabled + "recap" -> autoSkipRecapEnabled + "ending" -> autoSkipOutroEnabled + else -> false + } + @Composable internal fun PlayerScreenRuntime.BindPlayerRuntimeEffects() { val currentFeedback = liveGestureFeedback ?: gestureFeedback @@ -387,6 +397,7 @@ private fun PlayerScreenRuntime.BindPlayerMetadataAndSkipEffects() { skipIntervals = emptyList() activeSkipInterval = null skipIntervalDismissed = false + autoSkippedIntervals = emptySet() showNextEpisodeCard = false nextEpisodeAutoPlayJob?.cancel() nextEpisodeAutoPlaySearching = false @@ -422,6 +433,21 @@ private fun PlayerScreenRuntime.BindPlayerMetadataAndSkipEffects() { } } + // Automatically skip the segment currently playing, once per segment. + LaunchedEffect(playbackSnapshot.positionMs, skipIntervals) { + if (!initialLoadCompleted || !initialSeekApplied || pausedOverlayVisible) return@LaunchedEffect + val positionSec = playbackSnapshot.positionMs / 1000.0 + val current = skipIntervals.firstOrNull { interval -> + positionSec >= interval.startTime && positionSec < interval.endTime - 0.5 + } ?: return@LaunchedEffect + if (current in autoSkippedIntervals) return@LaunchedEffect + if (!playerSettingsUiState.autoSkips(current)) return@LaunchedEffect + autoSkippedIntervals = autoSkippedIntervals + current + playerController?.seekTo((current.endTime * 1000).toLong()) + scheduleProgressSyncAfterSeek() + skipIntervalDismissed = true + } + LaunchedEffect(playerMetaVideos, activeSeasonNumber, activeEpisodeNumber) { if (!isSeries || playerMetaVideos.isEmpty()) { nextEpisodeInfo = null diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeState.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeState.kt index 43b4977a..f3dc11f2 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeState.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeState.kt @@ -164,6 +164,7 @@ internal class PlayerScreenRuntime( var skipIntervals by mutableStateOf>(emptyList()) var activeSkipInterval by mutableStateOf(null) var skipIntervalDismissed by mutableStateOf(false) + var autoSkippedIntervals by mutableStateOf(setOf()) var parentalWarnings by mutableStateOf>(emptyList()) var showParentalGuide by mutableStateOf(false) var parentalGuideHasShown by mutableStateOf(false) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeUi.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeUi.kt index d83c22fd..ddf352f1 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeUi.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeUi.kt @@ -380,7 +380,9 @@ private fun BoxScope.RenderPlaybackOverlays( renderedGestureFeedback = renderedGestureFeedback, initialLoadCompleted = initialLoadCompleted, pausedOverlayVisible = pausedOverlayVisible, - activeSkipInterval = activeSkipInterval, + activeSkipInterval = activeSkipInterval?.takeUnless { + playerSettingsUiState.autoSkips(it) && (it !in autoSkippedIntervals || skipIntervalDismissed) + }, skipIntervalDismissed = skipIntervalDismissed, controlsVisible = controlsVisible, onSkipInterval = { interval -> diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt index c0f4c429..90e16b1b 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt @@ -64,6 +64,9 @@ data class PlayerSettingsUiState( val streamAutoPlayRegex: String = "", val streamAutoPlayTimeoutSeconds: Int = 3, val skipIntroEnabled: Boolean = true, + val autoSkipIntroEnabled: Boolean = false, + val autoSkipRecapEnabled: Boolean = false, + val autoSkipOutroEnabled: Boolean = false, val animeSkipEnabled: Boolean = false, val animeSkipClientId: String = "", val introDbApiKey: String = "", @@ -130,6 +133,9 @@ object PlayerSettingsRepository { private var streamAutoPlayRegex = "" private var streamAutoPlayTimeoutSeconds = 3 private var skipIntroEnabled = true + private var autoSkipIntroEnabled = false + private var autoSkipRecapEnabled = false + private var autoSkipOutroEnabled = false private var animeSkipEnabled = false private var animeSkipClientId = "" private var introDbApiKey = "" @@ -201,6 +207,9 @@ object PlayerSettingsRepository { streamAutoPlayRegex = "" streamAutoPlayTimeoutSeconds = 3 skipIntroEnabled = true + autoSkipIntroEnabled = false + autoSkipRecapEnabled = false + autoSkipOutroEnabled = false animeSkipEnabled = false animeSkipClientId = "" introDbApiKey = "" @@ -324,6 +333,9 @@ object PlayerSettingsRepository { PlayerSettingsStorage.saveStreamAutoPlayTimeoutSeconds(streamAutoPlayTimeoutSeconds) } skipIntroEnabled = PlayerSettingsStorage.loadSkipIntroEnabled() ?: true + autoSkipIntroEnabled = PlayerSettingsStorage.loadAutoSkipIntroEnabled() ?: false + autoSkipRecapEnabled = PlayerSettingsStorage.loadAutoSkipRecapEnabled() ?: false + autoSkipOutroEnabled = PlayerSettingsStorage.loadAutoSkipOutroEnabled() ?: false animeSkipEnabled = PlayerSettingsStorage.loadAnimeSkipEnabled() ?: false animeSkipClientId = PlayerSettingsStorage.loadAnimeSkipClientId() ?: "" introDbApiKey = PlayerSettingsStorage.loadIntroDbApiKey() ?: "" @@ -647,6 +659,30 @@ object PlayerSettingsRepository { PlayerSettingsStorage.saveSkipIntroEnabled(enabled) } + fun setAutoSkipIntroEnabled(enabled: Boolean) { + ensureLoaded() + if (autoSkipIntroEnabled == enabled) return + autoSkipIntroEnabled = enabled + publish() + PlayerSettingsStorage.saveAutoSkipIntroEnabled(enabled) + } + + fun setAutoSkipRecapEnabled(enabled: Boolean) { + ensureLoaded() + if (autoSkipRecapEnabled == enabled) return + autoSkipRecapEnabled = enabled + publish() + PlayerSettingsStorage.saveAutoSkipRecapEnabled(enabled) + } + + fun setAutoSkipOutroEnabled(enabled: Boolean) { + ensureLoaded() + if (autoSkipOutroEnabled == enabled) return + autoSkipOutroEnabled = enabled + publish() + PlayerSettingsStorage.saveAutoSkipOutroEnabled(enabled) + } + fun setAnimeSkipEnabled(enabled: Boolean) { ensureLoaded() if (animeSkipEnabled == enabled) return @@ -942,6 +978,9 @@ object PlayerSettingsRepository { streamAutoPlayRegex = streamAutoPlayRegex, streamAutoPlayTimeoutSeconds = streamAutoPlayTimeoutSeconds, skipIntroEnabled = skipIntroEnabled, + autoSkipIntroEnabled = autoSkipIntroEnabled, + autoSkipRecapEnabled = autoSkipRecapEnabled, + autoSkipOutroEnabled = autoSkipOutroEnabled, animeSkipEnabled = animeSkipEnabled, animeSkipClientId = animeSkipClientId, introDbApiKey = introDbApiKey, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt index 312a0764..c8856bef 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt @@ -85,6 +85,12 @@ internal expect object PlayerSettingsStorage { fun saveStreamAutoPlayTimeoutSeconds(seconds: Int) fun loadSkipIntroEnabled(): Boolean? fun saveSkipIntroEnabled(enabled: Boolean) + fun loadAutoSkipIntroEnabled(): Boolean? + fun saveAutoSkipIntroEnabled(enabled: Boolean) + fun loadAutoSkipRecapEnabled(): Boolean? + fun saveAutoSkipRecapEnabled(enabled: Boolean) + fun loadAutoSkipOutroEnabled(): Boolean? + fun saveAutoSkipOutroEnabled(enabled: Boolean) fun loadAnimeSkipEnabled(): Boolean? fun saveAnimeSkipEnabled(enabled: Boolean) fun loadAnimeSkipClientId(): String? diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SkipIntroRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SkipIntroRepository.kt index d99dc978..7063b3fb 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SkipIntroRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SkipIntroRepository.kt @@ -151,7 +151,7 @@ object SkipIntroRepository { return chosen.values.toList() } - private fun segmentCategory(type: String): String? = when (type.lowercase()) { + internal fun segmentCategory(type: String): String? = when (type.lowercase()) { "intro", "op", "mixed-op" -> "opening" "outro", "ed", "mixed-ed", "credits", "ending" -> "ending" "recap" -> "recap" diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt index 3dcf5a16..7a5e2308 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt @@ -1028,6 +1028,33 @@ private fun PlaybackSettingsSection( onCheckedChange = PlayerSettingsRepository::setSkipIntroEnabled, ) SettingsGroupDivider(isTablet = isTablet) + SettingsSwitchRow( + title = stringResource(Res.string.settings_playback_auto_skip_intro), + description = stringResource(Res.string.settings_playback_auto_skip_intro_description), + checked = autoPlayPlayerSettings.autoSkipIntroEnabled, + enabled = autoPlayPlayerSettings.skipIntroEnabled, + isTablet = isTablet, + onCheckedChange = PlayerSettingsRepository::setAutoSkipIntroEnabled, + ) + SettingsGroupDivider(isTablet = isTablet) + SettingsSwitchRow( + title = stringResource(Res.string.settings_playback_auto_skip_recap), + description = stringResource(Res.string.settings_playback_auto_skip_recap_description), + checked = autoPlayPlayerSettings.autoSkipRecapEnabled, + enabled = autoPlayPlayerSettings.skipIntroEnabled, + isTablet = isTablet, + onCheckedChange = PlayerSettingsRepository::setAutoSkipRecapEnabled, + ) + SettingsGroupDivider(isTablet = isTablet) + SettingsSwitchRow( + title = stringResource(Res.string.settings_playback_auto_skip_outro), + description = stringResource(Res.string.settings_playback_auto_skip_outro_description), + checked = autoPlayPlayerSettings.autoSkipOutroEnabled, + enabled = autoPlayPlayerSettings.skipIntroEnabled, + isTablet = isTablet, + onCheckedChange = PlayerSettingsRepository::setAutoSkipOutroEnabled, + ) + SettingsGroupDivider(isTablet = isTablet) SettingsSwitchRow( title = stringResource(Res.string.settings_playback_anime_skip), description = stringResource(Res.string.settings_playback_anime_skip_description), diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt index 8f4aefce..035a6810 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt @@ -637,6 +637,9 @@ internal fun settingsSearchEntries( icon = Icons.Rounded.PlayArrow, rows = listOf( PlaybackSearchRow("skip-intro", stringResource(Res.string.settings_playback_skip_intro_outro_recap), stringResource(Res.string.settings_playback_skip_intro_outro_recap_description)), + PlaybackSearchRow("auto-skip-intro", stringResource(Res.string.settings_playback_auto_skip_intro), stringResource(Res.string.settings_playback_auto_skip_intro_description)), + PlaybackSearchRow("auto-skip-recap", stringResource(Res.string.settings_playback_auto_skip_recap), stringResource(Res.string.settings_playback_auto_skip_recap_description)), + PlaybackSearchRow("auto-skip-outro", stringResource(Res.string.settings_playback_auto_skip_outro), stringResource(Res.string.settings_playback_auto_skip_outro_description)), PlaybackSearchRow("anime-skip", stringResource(Res.string.settings_playback_anime_skip), stringResource(Res.string.settings_playback_anime_skip_description)), PlaybackSearchRow("anime-skip-client", stringResource(Res.string.settings_playback_anime_skip_client_id), stringResource(Res.string.settings_playback_anime_skip_client_id_description)), PlaybackSearchRow("intro-submit", stringResource(Res.string.settings_playback_intro_submit_enabled), stringResource(Res.string.settings_playback_intro_submit_enabled_description)), diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt index 4eeb657a..b9297674 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt @@ -58,6 +58,9 @@ actual object PlayerSettingsStorage { private const val streamAutoPlayRegexKey = "stream_auto_play_regex" private const val streamAutoPlayTimeoutSecondsKey = "stream_auto_play_timeout_seconds" private const val skipIntroEnabledKey = "skip_intro_enabled" + private const val autoSkipIntroEnabledKey = "auto_skip_intro_enabled" + private const val autoSkipRecapEnabledKey = "auto_skip_recap_enabled" + private const val autoSkipOutroEnabledKey = "auto_skip_outro_enabled" private const val animeSkipEnabledKey = "animeskip_enabled" private const val animeSkipClientIdKey = "animeskip_client_id" private const val introDbApiKeyKey = "introdb_api_key" @@ -127,6 +130,9 @@ actual object PlayerSettingsStorage { streamAutoPlayRegexKey, streamAutoPlayTimeoutSecondsKey, skipIntroEnabledKey, + autoSkipIntroEnabledKey, + autoSkipRecapEnabledKey, + autoSkipOutroEnabledKey, animeSkipEnabledKey, animeSkipClientIdKey, streamAutoPlayNextEpisodeEnabledKey, @@ -656,6 +662,48 @@ actual object PlayerSettingsStorage { NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(skipIntroEnabledKey)) } + actual fun loadAutoSkipIntroEnabled(): Boolean? { + val defaults = NSUserDefaults.standardUserDefaults + val key = ProfileScopedKey.of(autoSkipIntroEnabledKey) + return if (defaults.objectForKey(key) != null) { + defaults.boolForKey(key) + } else { + null + } + } + + actual fun saveAutoSkipIntroEnabled(enabled: Boolean) { + NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(autoSkipIntroEnabledKey)) + } + + actual fun loadAutoSkipRecapEnabled(): Boolean? { + val defaults = NSUserDefaults.standardUserDefaults + val key = ProfileScopedKey.of(autoSkipRecapEnabledKey) + return if (defaults.objectForKey(key) != null) { + defaults.boolForKey(key) + } else { + null + } + } + + actual fun saveAutoSkipRecapEnabled(enabled: Boolean) { + NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(autoSkipRecapEnabledKey)) + } + + actual fun loadAutoSkipOutroEnabled(): Boolean? { + val defaults = NSUserDefaults.standardUserDefaults + val key = ProfileScopedKey.of(autoSkipOutroEnabledKey) + return if (defaults.objectForKey(key) != null) { + defaults.boolForKey(key) + } else { + null + } + } + + actual fun saveAutoSkipOutroEnabled(enabled: Boolean) { + NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(autoSkipOutroEnabledKey)) + } + actual fun loadAnimeSkipEnabled(): Boolean? { val defaults = NSUserDefaults.standardUserDefaults val key = ProfileScopedKey.of(animeSkipEnabledKey) @@ -936,6 +984,9 @@ actual object PlayerSettingsStorage { loadStreamAutoPlayRegex()?.let { put(streamAutoPlayRegexKey, encodeSyncString(it)) } loadStreamAutoPlayTimeoutSeconds()?.let { put(streamAutoPlayTimeoutSecondsKey, encodeSyncInt(it)) } loadSkipIntroEnabled()?.let { put(skipIntroEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipIntroEnabled()?.let { put(autoSkipIntroEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipRecapEnabled()?.let { put(autoSkipRecapEnabledKey, encodeSyncBoolean(it)) } + loadAutoSkipOutroEnabled()?.let { put(autoSkipOutroEnabledKey, encodeSyncBoolean(it)) } loadAnimeSkipEnabled()?.let { put(animeSkipEnabledKey, encodeSyncBoolean(it)) } loadAnimeSkipClientId()?.let { put(animeSkipClientIdKey, encodeSyncString(it)) } loadStreamAutoPlayNextEpisodeEnabled()?.let { put(streamAutoPlayNextEpisodeEnabledKey, encodeSyncBoolean(it)) } @@ -1009,6 +1060,9 @@ actual object PlayerSettingsStorage { payload.decodeSyncString(streamAutoPlayRegexKey)?.let(::saveStreamAutoPlayRegex) payload.decodeSyncInt(streamAutoPlayTimeoutSecondsKey)?.let(::saveStreamAutoPlayTimeoutSeconds) payload.decodeSyncBoolean(skipIntroEnabledKey)?.let(::saveSkipIntroEnabled) + payload.decodeSyncBoolean(autoSkipIntroEnabledKey)?.let(::saveAutoSkipIntroEnabled) + payload.decodeSyncBoolean(autoSkipRecapEnabledKey)?.let(::saveAutoSkipRecapEnabled) + payload.decodeSyncBoolean(autoSkipOutroEnabledKey)?.let(::saveAutoSkipOutroEnabled) payload.decodeSyncBoolean(animeSkipEnabledKey)?.let(::saveAnimeSkipEnabled) payload.decodeSyncString(animeSkipClientIdKey)?.let(::saveAnimeSkipClientId) payload.decodeSyncString(introDbApiKeyKey)?.let(::saveIntroDbApiKey)