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.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<PlayerItem>,
resultRecipient: ResultRecipient<VideoPlayerTrackSelectorDialogDestination, Int>,
resultRecipient: ResultRecipient<VideoPlayerTrackSelectorDialogDestination, VideoPlayerTrackSelectorDialogResult>,
) {
val viewModel = hiltViewModel<PlayerActivityViewModel>()
@ -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<Track> {
private fun getTracks(player: Player, type: Int): Array<Track> {
val tracks = arrayListOf<Track>()
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<Track> {
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
}

View file

@ -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<Track>,
resultNavigator: ResultBackNavigator<Int>,
trackType: TrackType,
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 {
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,

View file

@ -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"),