From 545880b6121f8c1e526aec0f4466644bcbca7a49 Mon Sep 17 00:00:00 2001 From: HankFreeman1 Date: Wed, 4 Mar 2026 09:25:19 -0500 Subject: [PATCH] Add the ability to repeat a single video in a playlist --- .../stashapp/playback/PlaybackFragment.kt | 17 ++++++++++++++++ .../components/playback/PlaybackControls.kt | 20 +++++++++++++++++++ .../ui/components/playback/PlaybackOverlay.kt | 3 +++ .../playback/PlaybackPageContent.kt | 8 ++++++++ .../main/res/drawable/baseline_repeat_24.xml | 10 ++++++++++ .../res/drawable/baseline_repeat_one_24.xml | 10 ++++++++++ 6 files changed, 68 insertions(+) create mode 100644 app/src/main/res/drawable/baseline_repeat_24.xml create mode 100644 app/src/main/res/drawable/baseline_repeat_one_24.xml diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt index 8f59de7a..f63f8fdc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt @@ -517,6 +517,23 @@ abstract class PlaybackFragment( val fragment = this@PlaybackFragment as PlaylistFragment<*, *, *> fragment.showPlaylist() } + + val repeatLabel = + if (player?.repeatMode == Player.REPEAT_MODE_ONE) { + "Stop Repeating Video" + } else { + "Repeat Current Video" + } + add(repeatLabel) + callbacks[size - 1] = { + player?.let { p -> + if (p.repeatMode == Player.REPEAT_MODE_ONE) { + p.repeatMode = Player.REPEAT_MODE_OFF + } else { + p.repeatMode = Player.REPEAT_MODE_ONE + } + } + } } if (optionsButtonOptions.dataType == DataType.SCENE && diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt index e7b9c4c8..9919a164 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt @@ -106,6 +106,8 @@ sealed interface PlaybackAction { data class Scale( val scale: ContentScale, ) : PlaybackAction + + data object ToggleRepeatOne : PlaybackAction } @OptIn(UnstableApi::class) @@ -132,6 +134,8 @@ fun PlaybackControls( scale: ContentScale, seekBarIntervals: Int, modifier: Modifier = Modifier, + isPlaylist: Boolean = false, + repeatOneEnabled: Boolean = false, initialFocusRequester: FocusRequester = remember { FocusRequester() }, seekBarInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { @@ -197,6 +201,9 @@ fun PlaybackControls( previousEnabled = previousEnabled, nextEnabled = nextEnabled, modifier = Modifier, + isPlaylist = isPlaylist, + repeatOneEnabled = repeatOneEnabled, + onToggleRepeatOne = { onPlaybackActionClick(PlaybackAction.ToggleRepeatOne) }, ) RightPlaybackButtons( modifier = Modifier, @@ -504,6 +511,9 @@ fun PlaybackButtons( previousEnabled: Boolean, nextEnabled: Boolean, modifier: Modifier = Modifier, + isPlaylist: Boolean = false, + repeatOneEnabled: Boolean = false, + onToggleRepeatOne: () -> Unit = {}, ) { Row( modifier = modifier.focusGroup(), @@ -552,6 +562,16 @@ fun PlaybackButtons( enabled = nextEnabled, onControllerInteraction = onControllerInteraction, ) + if (isPlaylist) { + PlaybackButton( + iconRes = if (repeatOneEnabled) R.drawable.baseline_repeat_one_24 else R.drawable.baseline_repeat_24, + onClick = { + onControllerInteraction.invoke() + onToggleRepeatOne() + }, + onControllerInteraction = onControllerInteraction, + ) + } } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt index 836c902d..e7921700 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt @@ -164,6 +164,7 @@ fun PlaybackOverlay( audioDecoder: String?, spriteData: List, modifier: Modifier = Modifier, + repeatOneEnabled: Boolean = false, seekPreviewPlaceholder: Painter? = null, seekBarInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { @@ -331,6 +332,8 @@ fun PlaybackOverlay( scale = scale, seekBarIntervals = uiConfig.preferences.playbackPreferences.seekBarSteps, sfwMode = uiConfig.sfwMode, + isPlaylist = playlistInfo != null, + repeatOneEnabled = repeatOneEnabled, ) } if (markers.isNotEmpty()) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt index 32d63fd1..187b9d76 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt @@ -504,6 +504,7 @@ fun PlaybackPageContent( var showPlaylist by remember { mutableStateOf(false) } var contentScale by remember { mutableStateOf(ContentScale.Fit) } var playbackSpeed by remember { mutableFloatStateOf(1.0f) } + var repeatOneEnabled by remember { mutableStateOf(false) } LaunchedEffect(playbackSpeed) { player.setPlaybackSpeed(playbackSpeed) } val presentationState = rememberPresentationState(player) @@ -930,6 +931,12 @@ fun PlaybackPageContent( PlaybackAction.ShowSceneDetails -> { showSceneDetails = true } + + PlaybackAction.ToggleRepeatOne -> { + repeatOneEnabled = !repeatOneEnabled + player.repeatMode = + if (repeatOneEnabled) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF + } } }, onSeekBarChange = seekBarState::onValueChange, @@ -971,6 +978,7 @@ fun PlaybackPageContent( videoDecoder = videoDecoder, audioDecoder = audioDecoder, spriteData = spriteImageLoaded, + repeatOneEnabled = repeatOneEnabled, ) } } diff --git a/app/src/main/res/drawable/baseline_repeat_24.xml b/app/src/main/res/drawable/baseline_repeat_24.xml new file mode 100644 index 00000000..5f598a30 --- /dev/null +++ b/app/src/main/res/drawable/baseline_repeat_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/baseline_repeat_one_24.xml b/app/src/main/res/drawable/baseline_repeat_one_24.xml new file mode 100644 index 00000000..5b697d4d --- /dev/null +++ b/app/src/main/res/drawable/baseline_repeat_one_24.xml @@ -0,0 +1,10 @@ + + +