Add a setting to control the new timeout

This commit is contained in:
skoruppa 2026-03-09 20:05:46 +01:00
parent 0faaae406c
commit 68860f147b
9 changed files with 58 additions and 3 deletions

View file

@ -184,6 +184,7 @@ data class PlayerSettings(
val streamAutoPlayRegex: String = "",
val streamAutoPlayNextEpisodeEnabled: Boolean = false,
val streamAutoPlayPreferBingeGroupForNextEpisode: Boolean = true,
val streamAutoPlayTimeoutSeconds: Int = 3,
val nextEpisodeThresholdMode: NextEpisodeThresholdMode = NextEpisodeThresholdMode.PERCENTAGE,
val nextEpisodeThresholdPercent: Float = 99f,
val nextEpisodeThresholdMinutesBeforeEnd: Float = 2f,
@ -289,6 +290,7 @@ class PlayerSettingsDataStore @Inject constructor(
private val streamAutoPlayRegexKey = stringPreferencesKey("stream_auto_play_regex")
private val streamAutoPlayNextEpisodeEnabledKey = booleanPreferencesKey("stream_auto_play_next_episode_enabled")
private val streamAutoPlayPreferBingeGroupForNextEpisodeKey = booleanPreferencesKey("stream_auto_play_prefer_bingegroup_next_episode")
private val streamAutoPlayTimeoutSecondsKey = intPreferencesKey("stream_auto_play_timeout_seconds")
private val nextEpisodeThresholdModeKey = stringPreferencesKey("next_episode_threshold_mode")
private val nextEpisodeThresholdPercentLegacyKey = intPreferencesKey("next_episode_threshold_percent")
private val nextEpisodeThresholdMinutesBeforeEndLegacyKey = intPreferencesKey("next_episode_threshold_minutes_before_end")
@ -437,6 +439,7 @@ class PlayerSettingsDataStore @Inject constructor(
streamAutoPlayNextEpisodeEnabled = prefs[streamAutoPlayNextEpisodeEnabledKey] ?: false,
streamAutoPlayPreferBingeGroupForNextEpisode =
prefs[streamAutoPlayPreferBingeGroupForNextEpisodeKey] ?: true,
streamAutoPlayTimeoutSeconds = (prefs[streamAutoPlayTimeoutSecondsKey] ?: 3).coerceIn(0, 10),
nextEpisodeThresholdMode = prefs[nextEpisodeThresholdModeKey]?.let {
runCatching { NextEpisodeThresholdMode.valueOf(it) }.getOrDefault(NextEpisodeThresholdMode.PERCENTAGE)
} ?: NextEpisodeThresholdMode.PERCENTAGE,
@ -641,6 +644,12 @@ class PlayerSettingsDataStore @Inject constructor(
}
}
suspend fun setStreamAutoPlayTimeoutSeconds(seconds: Int) {
store().edit { prefs ->
prefs[streamAutoPlayTimeoutSecondsKey] = seconds.coerceIn(0, 10)
}
}
suspend fun setNextEpisodeThresholdMode(mode: NextEpisodeThresholdMode) {
store().edit { prefs ->
prefs[nextEpisodeThresholdModeKey] = mode.name

View file

@ -822,7 +822,10 @@ internal fun PlayerRuntimeController.playNextEpisode() {
}
}
delay(5_000L)
val timeoutMs = playerSettings.streamAutoPlayTimeoutSeconds * 1_000L
if (timeoutMs > 0L) {
delay(timeoutMs)
}
timeoutElapsed = true
if (!autoSelectTriggered && lastSuccessData != null) {
autoSelectTriggered = true

View file

@ -28,6 +28,7 @@ import androidx.compose.material.icons.filled.Extension
import androidx.compose.material.icons.filled.Language
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.SkipNext
import androidx.compose.material.icons.filled.Timer
import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Tune
import androidx.compose.runtime.Composable
@ -81,6 +82,7 @@ internal fun LazyListScope.autoPlaySettingsItems(
onSetStreamAutoPlayPreferBingeGroupForNextEpisode: (Boolean) -> Unit,
onSetNextEpisodeThresholdPercent: (Float) -> Unit,
onSetNextEpisodeThresholdMinutesBeforeEnd: (Float) -> Unit,
onSetStreamAutoPlayTimeoutSeconds: (Int) -> Unit,
onSetReuseLastLinkEnabled: (Boolean) -> Unit,
onItemFocused: () -> Unit = {}
) {
@ -122,6 +124,29 @@ internal fun LazyListScope.autoPlaySettingsItems(
)
}
if (playerSettings.streamAutoPlayMode != StreamAutoPlayMode.MANUAL) {
item(key = "autoplay_stream_timeout") {
val timeoutSec = playerSettings.streamAutoPlayTimeoutSeconds
val valueText = if (timeoutSec == 0) {
stringResource(R.string.autoplay_timeout_instant)
} else {
"${timeoutSec}s"
}
SliderSettingsItem(
icon = Icons.Default.Timer,
title = stringResource(R.string.autoplay_timeout_title),
subtitle = stringResource(R.string.autoplay_timeout_sub),
value = timeoutSec,
valueText = valueText,
minValue = 0,
maxValue = 10,
step = 1,
onValueChange = { onSetStreamAutoPlayTimeoutSeconds(it) },
onFocused = onItemFocused
)
}
}
item(key = "autoplay_next_episode") {
ToggleSettingsItem(
icon = Icons.Default.SkipNext,

View file

@ -231,6 +231,9 @@ fun PlaybackSettingsContent(
onSetNextEpisodeThresholdMinutesBeforeEnd = { minutes ->
coroutineScope.launch { viewModel.setNextEpisodeThresholdMinutesBeforeEnd(minutes) }
},
onSetStreamAutoPlayTimeoutSeconds = { seconds ->
coroutineScope.launch { viewModel.setStreamAutoPlayTimeoutSeconds(seconds) }
},
onSetReuseLastLinkEnabled = { enabled -> coroutineScope.launch { viewModel.setStreamReuseLastLinkEnabled(enabled) } },
onSetLoadingOverlayEnabled = { enabled -> coroutineScope.launch { viewModel.setLoadingOverlayEnabled(enabled) } },
onSetPauseOverlayEnabled = { enabled -> coroutineScope.launch { viewModel.setPauseOverlayEnabled(enabled) } },

View file

@ -112,6 +112,7 @@ internal fun PlaybackSettingsSections(
onSetStreamAutoPlayPreferBingeGroupForNextEpisode: (Boolean) -> Unit,
onSetNextEpisodeThresholdPercent: (Float) -> Unit,
onSetNextEpisodeThresholdMinutesBeforeEnd: (Float) -> Unit,
onSetStreamAutoPlayTimeoutSeconds: (Int) -> Unit,
onSetReuseLastLinkEnabled: (Boolean) -> Unit,
onSetLoadingOverlayEnabled: (Boolean) -> Unit,
onSetPauseOverlayEnabled: (Boolean) -> Unit,
@ -315,6 +316,7 @@ internal fun PlaybackSettingsSections(
onSetStreamAutoPlayPreferBingeGroupForNextEpisode = onSetStreamAutoPlayPreferBingeGroupForNextEpisode,
onSetNextEpisodeThresholdPercent = onSetNextEpisodeThresholdPercent,
onSetNextEpisodeThresholdMinutesBeforeEnd = onSetNextEpisodeThresholdMinutesBeforeEnd,
onSetStreamAutoPlayTimeoutSeconds = onSetStreamAutoPlayTimeoutSeconds,
onSetReuseLastLinkEnabled = onSetReuseLastLinkEnabled,
onItemFocused = { focusedSection = PlaybackSection.STREAM_SELECTION }
)

View file

@ -237,6 +237,10 @@ class PlaybackSettingsViewModel @Inject constructor(
playerSettingsDataStore.setStreamAutoPlayPreferBingeGroupForNextEpisode(enabled)
}
suspend fun setStreamAutoPlayTimeoutSeconds(seconds: Int) {
playerSettingsDataStore.setStreamAutoPlayTimeoutSeconds(seconds)
}
suspend fun setNextEpisodeThresholdMode(mode: NextEpisodeThresholdMode) {
playerSettingsDataStore.setNextEpisodeThresholdMode(mode)
}

View file

@ -399,8 +399,11 @@ class StreamScreenViewModel @Inject constructor(
}
}
// After 5s: if streams arrived, auto-select now; if not, wait for first result from inner job
delay(5_000L)
// After timeout: if streams arrived, auto-select now; if not, wait for first result from inner job
val timeoutMs = playerSettings.streamAutoPlayTimeoutSeconds * 1_000L
if (timeoutMs > 0L) {
delay(timeoutMs)
}
timeoutElapsed = true
if (!autoSelectTriggered && lastSuccessData != null) {
autoSelectTriggered = true

View file

@ -282,6 +282,9 @@
<string name="autoplay_scope_addons">Tylko zainstalowane dodatki</string>
<string name="autoplay_scope_plugins">Tylko włączone wtyczki</string>
<string name="autoplay_scope">Zakres źródeł auto-play</string>
<string name="autoplay_timeout_title">Limit czasu wyboru strumienia</string>
<string name="autoplay_timeout_sub">Czas oczekiwania na dodatki przed wyborem. 0 = pierwszy wynik wygrywa.</string>
<string name="autoplay_timeout_instant">Natychmiast</string>
<string name="autoplay_allowed_addons">Dozwolone dodatki</string>
<string name="autoplay_all_addons">Wszystkie zainstalowane dodatki</string>
<string name="autoplay_allowed_plugins">Dozwolone wtyczki</string>

View file

@ -351,6 +351,9 @@
<string name="autoplay_scope_addons">Installed addons only</string>
<string name="autoplay_scope_plugins">Enabled plugins only</string>
<string name="autoplay_scope">Auto-play Source Scope</string>
<string name="autoplay_timeout_title">Stream Selection Timeout</string>
<string name="autoplay_timeout_sub">Wait time for addons before selecting. 0 = first result wins.</string>
<string name="autoplay_timeout_instant">Instant</string>
<string name="autoplay_allowed_addons">Allowed Addons</string>
<string name="autoplay_all_addons">All installed addons</string>
<string name="autoplay_allowed_plugins">Allowed Plugins</string>