diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt index 53e3faab..9c77c517 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt @@ -40,6 +40,7 @@ import dev.jdtech.jellyfin.core.R import dev.jdtech.jellyfin.destinations.VideoPlayerTrackSelectorDialogDestination import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.Track +import dev.jdtech.jellyfin.mpv.TrackType import dev.jdtech.jellyfin.ui.components.player.VideoPlayerControlsLayout import dev.jdtech.jellyfin.ui.components.player.VideoPlayerMediaButton import dev.jdtech.jellyfin.ui.components.player.VideoPlayerMediaTitle @@ -47,6 +48,7 @@ import dev.jdtech.jellyfin.ui.components.player.VideoPlayerOverlay import dev.jdtech.jellyfin.ui.components.player.VideoPlayerSeeker import dev.jdtech.jellyfin.ui.components.player.VideoPlayerState import dev.jdtech.jellyfin.ui.components.player.rememberVideoPlayerState +import dev.jdtech.jellyfin.ui.dialogs.VideoPlayerTrackSelectorDialogResult import dev.jdtech.jellyfin.ui.theme.spacings import dev.jdtech.jellyfin.utils.handleDPadKeyEvents import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel @@ -59,7 +61,7 @@ import kotlin.time.Duration.Companion.milliseconds fun PlayerScreen( navigator: DestinationsNavigator, items: ArrayList, - resultRecipient: ResultRecipient, + resultRecipient: ResultRecipient, ) { val viewModel = hiltViewModel() @@ -120,12 +122,26 @@ fun PlayerScreen( when (result) { is NavResult.Canceled -> Unit is NavResult.Value -> { - viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters - .buildUpon() - .setOverrideForType( - TrackSelectionOverride(viewModel.player.currentTracks.groups[result.value].mediaTrackGroup, 0), - ) - .build() + val type = when (result.value.trackType) { + TrackType.VIDEO -> C.TRACK_TYPE_VIDEO + TrackType.AUDIO -> C.TRACK_TYPE_AUDIO + TrackType.SUBTITLE -> C.TRACK_TYPE_TEXT + } + if (result.value.index == -1) { + viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters + .buildUpon() + .clearOverridesOfType(type) + .setTrackTypeDisabled(type, true) + .build() + } else { + viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters + .buildUpon() + .setOverrideForType( + TrackSelectionOverride(viewModel.player.currentTracks.groups[result.value.index].mediaTrackGroup, 0), + ) + .setTrackTypeDisabled(type, false) + .build() + } } } } @@ -238,7 +254,7 @@ fun VideoPlayerControls( isPlaying = isPlaying, onClick = { val tracks = getTracks(player, C.TRACK_TYPE_AUDIO) - navigator.navigate(VideoPlayerTrackSelectorDialogDestination(tracks)) + navigator.navigate(VideoPlayerTrackSelectorDialogDestination(TrackType.AUDIO, tracks)) }, ) VideoPlayerMediaButton( @@ -247,7 +263,7 @@ fun VideoPlayerControls( isPlaying = isPlaying, onClick = { val tracks = getTracks(player, C.TRACK_TYPE_TEXT) - navigator.navigate(VideoPlayerTrackSelectorDialogDestination(tracks)) + navigator.navigate(VideoPlayerTrackSelectorDialogDestination(TrackType.SUBTITLE, tracks)) }, ) } @@ -270,7 +286,7 @@ private fun Modifier.dPadEvents( ) @androidx.annotation.OptIn(UnstableApi::class) -private fun getTracks(player: Player, type: Int): ArrayList { +private fun getTracks(player: Player, type: Int): Array { val tracks = arrayListOf() for (groupIndex in 0 until player.currentTracks.groups.count()) { val group = player.currentTracks.groups[groupIndex] @@ -283,11 +299,20 @@ private fun getTracks(player: Player, type: Int): ArrayList { language = Locale(format.language.toString()).displayLanguage, codec = format.codecs, selected = group.isSelected, - supported = group.isSupported + supported = group.isSupported, ) tracks.add(track) } } - return tracks + + val noneTrack = Track( + id = -1, + label = null, + language = null, + codec = null, + selected = !tracks.any { it.selected }, + supported = true, + ) + return arrayOf(noneTrack) + tracks } diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/dialogs/VideoPlayerTrackSelectorDialog.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/dialogs/VideoPlayerTrackSelectorDialog.kt index 5187ec1a..6a8bd9f5 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/dialogs/VideoPlayerTrackSelectorDialog.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/dialogs/VideoPlayerTrackSelectorDialog.kt @@ -1,5 +1,6 @@ package dev.jdtech.jellyfin.ui.dialogs +import android.os.Parcelable import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -14,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.tv.foundation.lazy.list.TvLazyColumn @@ -30,22 +32,38 @@ import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.EmptyResultBackNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator import dev.jdtech.jellyfin.models.Track +import dev.jdtech.jellyfin.mpv.TrackType import dev.jdtech.jellyfin.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.spacings +import kotlinx.parcelize.Parcelize +import dev.jdtech.jellyfin.core.R as CoreR +import dev.jdtech.jellyfin.player.video.R as PlayerVideoR + +@Parcelize +data class VideoPlayerTrackSelectorDialogResult( + val trackType: TrackType, + val index: Int, +) : Parcelable @OptIn(ExperimentalTvMaterial3Api::class) @Destination(style = BaseDialogStyle::class) @Composable fun VideoPlayerTrackSelectorDialog( - tracks: ArrayList, - resultNavigator: ResultBackNavigator, + trackType: TrackType, + tracks: Array, + resultNavigator: ResultBackNavigator, ) { + val dialogTitle = when (trackType) { + TrackType.AUDIO -> PlayerVideoR.string.select_audio_track + TrackType.SUBTITLE -> PlayerVideoR.string.select_subtile_track + else -> CoreR.string.unknown_error + } Surface { Column( modifier = Modifier.padding(MaterialTheme.spacings.medium), ) { Text( - text = "Select track", + text = stringResource(id = dialogTitle), style = MaterialTheme.typography.headlineMedium, ) Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium)) @@ -56,7 +74,7 @@ fun VideoPlayerTrackSelectorDialog( items(tracks) { track -> Surface( onClick = { - resultNavigator.navigateBack(result = track.id) + resultNavigator.navigateBack(result = VideoPlayerTrackSelectorDialogResult(trackType, track.id)) }, enabled = track.supported, shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)), @@ -86,7 +104,13 @@ fun VideoPlayerTrackSelectorDialog( enabled = true, ) Spacer(modifier = Modifier.width(MaterialTheme.spacings.medium)) - Text(text = listOf(track.label, track.language, track.codec).mapNotNull { it }.joinToString(" - "), style = MaterialTheme.typography.bodyLarge) + Text( + text = listOf(track.label, track.language, track.codec) + .mapNotNull { it } + .joinToString(" - ") + .ifEmpty { stringResource(id = PlayerVideoR.string.none) }, + style = MaterialTheme.typography.bodyLarge, + ) } } } @@ -100,14 +124,15 @@ fun VideoPlayerTrackSelectorDialog( private fun VideoPlayerTrackSelectorDialogPreview() { FindroidTheme { VideoPlayerTrackSelectorDialog( - tracks = arrayListOf( + trackType = TrackType.AUDIO, + tracks = arrayOf( Track( id = 0, label = null, language = "English", codec = "flac", selected = true, - supported = true + supported = true, ), Track( id = 0, diff --git a/player/video/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.kt b/player/video/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.kt index b658161c..4a88121a 100644 --- a/player/video/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.kt +++ b/player/video/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.kt @@ -1,8 +1,11 @@ package dev.jdtech.jellyfin.mpv +import android.os.Parcelable import androidx.media3.common.C +import kotlinx.parcelize.Parcelize -enum class TrackType(val type: String) { +@Parcelize +enum class TrackType(val type: String) : Parcelable { VIDEO("video"), AUDIO("audio"), SUBTITLE("sub"),