feat(tv): allow audio and subtitle tracks to be disabled

Closes #608
This commit is contained in:
Jarne Demeulemeester 2024-01-01 00:31:13 +01:00
parent 92f1cf7eee
commit a6ebb89b78
No known key found for this signature in database
GPG key ID: 1E5C6AFBD622E9F5
3 changed files with 73 additions and 20 deletions

View file

@ -40,6 +40,7 @@ import dev.jdtech.jellyfin.core.R
import dev.jdtech.jellyfin.destinations.VideoPlayerTrackSelectorDialogDestination import dev.jdtech.jellyfin.destinations.VideoPlayerTrackSelectorDialogDestination
import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.models.Track 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.VideoPlayerControlsLayout
import dev.jdtech.jellyfin.ui.components.player.VideoPlayerMediaButton import dev.jdtech.jellyfin.ui.components.player.VideoPlayerMediaButton
import dev.jdtech.jellyfin.ui.components.player.VideoPlayerMediaTitle 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.VideoPlayerSeeker
import dev.jdtech.jellyfin.ui.components.player.VideoPlayerState import dev.jdtech.jellyfin.ui.components.player.VideoPlayerState
import dev.jdtech.jellyfin.ui.components.player.rememberVideoPlayerState 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.ui.theme.spacings
import dev.jdtech.jellyfin.utils.handleDPadKeyEvents import dev.jdtech.jellyfin.utils.handleDPadKeyEvents
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
@ -59,7 +61,7 @@ import kotlin.time.Duration.Companion.milliseconds
fun PlayerScreen( fun PlayerScreen(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
items: ArrayList<PlayerItem>, items: ArrayList<PlayerItem>,
resultRecipient: ResultRecipient<VideoPlayerTrackSelectorDialogDestination, Int>, resultRecipient: ResultRecipient<VideoPlayerTrackSelectorDialogDestination, VideoPlayerTrackSelectorDialogResult>,
) { ) {
val viewModel = hiltViewModel<PlayerActivityViewModel>() val viewModel = hiltViewModel<PlayerActivityViewModel>()
@ -120,12 +122,26 @@ fun PlayerScreen(
when (result) { when (result) {
is NavResult.Canceled -> Unit is NavResult.Canceled -> Unit
is NavResult.Value -> { is NavResult.Value -> {
viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters val type = when (result.value.trackType) {
.buildUpon() TrackType.VIDEO -> C.TRACK_TYPE_VIDEO
.setOverrideForType( TrackType.AUDIO -> C.TRACK_TYPE_AUDIO
TrackSelectionOverride(viewModel.player.currentTracks.groups[result.value].mediaTrackGroup, 0), TrackType.SUBTITLE -> C.TRACK_TYPE_TEXT
) }
.build() 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, isPlaying = isPlaying,
onClick = { onClick = {
val tracks = getTracks(player, C.TRACK_TYPE_AUDIO) val tracks = getTracks(player, C.TRACK_TYPE_AUDIO)
navigator.navigate(VideoPlayerTrackSelectorDialogDestination(tracks)) navigator.navigate(VideoPlayerTrackSelectorDialogDestination(TrackType.AUDIO, tracks))
}, },
) )
VideoPlayerMediaButton( VideoPlayerMediaButton(
@ -247,7 +263,7 @@ fun VideoPlayerControls(
isPlaying = isPlaying, isPlaying = isPlaying,
onClick = { onClick = {
val tracks = getTracks(player, C.TRACK_TYPE_TEXT) 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) @androidx.annotation.OptIn(UnstableApi::class)
private fun getTracks(player: Player, type: Int): ArrayList<Track> { private fun getTracks(player: Player, type: Int): Array<Track> {
val tracks = arrayListOf<Track>() val tracks = arrayListOf<Track>()
for (groupIndex in 0 until player.currentTracks.groups.count()) { for (groupIndex in 0 until player.currentTracks.groups.count()) {
val group = player.currentTracks.groups[groupIndex] val group = player.currentTracks.groups[groupIndex]
@ -283,11 +299,20 @@ private fun getTracks(player: Player, type: Int): ArrayList<Track> {
language = Locale(format.language.toString()).displayLanguage, language = Locale(format.language.toString()).displayLanguage,
codec = format.codecs, codec = format.codecs,
selected = group.isSelected, selected = group.isSelected,
supported = group.isSupported supported = group.isSupported,
) )
tracks.add(track) 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
} }

View file

@ -1,5 +1,6 @@
package dev.jdtech.jellyfin.ui.dialogs package dev.jdtech.jellyfin.ui.dialogs
import android.os.Parcelable
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -14,6 +15,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.foundation.lazy.list.TvLazyColumn 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.EmptyResultBackNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator
import dev.jdtech.jellyfin.models.Track 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.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings 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) @OptIn(ExperimentalTvMaterial3Api::class)
@Destination(style = BaseDialogStyle::class) @Destination(style = BaseDialogStyle::class)
@Composable @Composable
fun VideoPlayerTrackSelectorDialog( fun VideoPlayerTrackSelectorDialog(
tracks: ArrayList<Track>, trackType: TrackType,
resultNavigator: ResultBackNavigator<Int>, tracks: Array<Track>,
resultNavigator: ResultBackNavigator<VideoPlayerTrackSelectorDialogResult>,
) { ) {
val dialogTitle = when (trackType) {
TrackType.AUDIO -> PlayerVideoR.string.select_audio_track
TrackType.SUBTITLE -> PlayerVideoR.string.select_subtile_track
else -> CoreR.string.unknown_error
}
Surface { Surface {
Column( Column(
modifier = Modifier.padding(MaterialTheme.spacings.medium), modifier = Modifier.padding(MaterialTheme.spacings.medium),
) { ) {
Text( Text(
text = "Select track", text = stringResource(id = dialogTitle),
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
) )
Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium)) Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium))
@ -56,7 +74,7 @@ fun VideoPlayerTrackSelectorDialog(
items(tracks) { track -> items(tracks) { track ->
Surface( Surface(
onClick = { onClick = {
resultNavigator.navigateBack(result = track.id) resultNavigator.navigateBack(result = VideoPlayerTrackSelectorDialogResult(trackType, track.id))
}, },
enabled = track.supported, enabled = track.supported,
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)), shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
@ -86,7 +104,13 @@ fun VideoPlayerTrackSelectorDialog(
enabled = true, enabled = true,
) )
Spacer(modifier = Modifier.width(MaterialTheme.spacings.medium)) 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() { private fun VideoPlayerTrackSelectorDialogPreview() {
FindroidTheme { FindroidTheme {
VideoPlayerTrackSelectorDialog( VideoPlayerTrackSelectorDialog(
tracks = arrayListOf( trackType = TrackType.AUDIO,
tracks = arrayOf(
Track( Track(
id = 0, id = 0,
label = null, label = null,
language = "English", language = "English",
codec = "flac", codec = "flac",
selected = true, selected = true,
supported = true supported = true,
), ),
Track( Track(
id = 0, id = 0,

View file

@ -1,8 +1,11 @@
package dev.jdtech.jellyfin.mpv package dev.jdtech.jellyfin.mpv
import android.os.Parcelable
import androidx.media3.common.C 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"), VIDEO("video"),
AUDIO("audio"), AUDIO("audio"),
SUBTITLE("sub"), SUBTITLE("sub"),