parent
92f1cf7eee
commit
a6ebb89b78
3 changed files with 73 additions and 20 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in a new issue