Show playback debug info

This commit is contained in:
Damontecres 2025-10-04 19:33:21 -04:00
parent 9cc2fc40d3
commit 64e881f51c
No known key found for this signature in database
9 changed files with 177 additions and 28 deletions

View file

@ -187,18 +187,17 @@ sealed interface AppPreference<T> {
summaryOff = R.string.disabled,
)
// val PlaybackDebugInfo =
// AppSwitchPreference(
// title = R.string.playback_debug_info,
// prefKey = R.string.pref_key_show_playback_debug_info,
// defaultValue = false,
// getter = { it.playbackPreferences.showDebugInfo },
// setter = { prefs, value ->
// prefs.updatePlaybackPreferences { showDebugInfo = value }
// },
// summaryOn = R.string.show,
// summaryOff = R.string.hide,
// )
val PlaybackDebugInfo =
AppSwitchPreference(
title = R.string.playback_debug_info,
defaultValue = false,
getter = { it.playbackPreferences.showDebugInfo },
setter = { prefs, value ->
prefs.updatePlaybackPreferences { showDebugInfo = value }
},
summaryOn = R.string.show,
summaryOff = R.string.hide,
)
// val AutoCheckForUpdates =
// AppSwitchPreference(
@ -246,6 +245,7 @@ val basicPreferences =
AppPreference.RewatchNextUp,
AppPreference.PlayThemeMusic,
AppPreference.OssLicenseInfo,
AppPreference.PlaybackDebugInfo,
),
),
)

View file

@ -25,6 +25,7 @@ class AppPreferencesSerializer
AppPreference.SkipBack.defaultValue.seconds.inWholeMilliseconds
controllerTimeoutMs = AppPreference.ControllerTimeout.defaultValue
seekBarSteps = AppPreference.SeekBarSteps.defaultValue.toInt()
showDebugInfo = AppPreference.PlaybackDebugInfo.defaultValue
}.build()
homePagePreferences =
HomePagePreferences

View file

@ -70,6 +70,7 @@ fun PlaybackContent(
modifier: Modifier = Modifier,
viewModel: PlaybackViewModel = hiltViewModel(),
) {
val prefs = preferences.appPreferences.playbackPreferences
val context = LocalContext.current
val scope = rememberCoroutineScope()
LaunchedEffect(destination.itemId) {
@ -88,6 +89,7 @@ fun PlaybackContent(
val currentPlayback by viewModel.currentPlayback.observeAsState(null)
var cues by remember { mutableStateOf<List<Cue>>(listOf()) }
var showDebugInfo by remember { mutableStateOf(prefs.showDebugInfo) }
// TODO move to viewmodel?
val cueListener =
@ -246,7 +248,9 @@ fun PlaybackContent(
contentScale = it.scale
}
PlaybackAction.ShowDebug -> TODO()
PlaybackAction.ShowDebug -> {
showDebugInfo = !showDebugInfo
}
PlaybackAction.ShowPlaylist -> TODO()
PlaybackAction.ShowVideoFilterDialog -> TODO()
is PlaybackAction.ToggleAudio -> {
@ -259,12 +263,11 @@ fun PlaybackContent(
}
},
onSeekBarChange = seekBarState::onValueChange,
showDebugInfo = false,
showDebugInfo = showDebugInfo,
scale = contentScale,
playbackSpeed = playbackSpeed,
moreButtonOptions = MoreButtonOptions(mapOf()),
subtitleIndex = currentPlayback?.subtitleIndex,
audioIndex = currentPlayback?.audioIndex,
currentPlayback = currentPlayback,
audioStreams = audioStreams,
trickplayInfo = trickplay,
trickplayUrlFor = viewModel::getTrickplayUrl,

View file

@ -289,7 +289,7 @@ fun LeftPlaybackButtons(
val options =
buildList {
addAll(moreButtonOptions.options.keys)
add(if (showDebugInfo) "Hide transcode info" else "Show transcode info")
add(if (showDebugInfo) "Hide debug info" else "Show debug info")
}
BottomDialog(
choices = options,

View file

@ -2,6 +2,7 @@ package com.github.damontecres.dolphin.ui.playback
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement
@ -35,9 +36,11 @@ import androidx.media3.common.Player
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.data.model.Chapter
import com.github.damontecres.dolphin.ui.AppColors
import com.github.damontecres.dolphin.ui.cards.ChapterCard
import com.github.damontecres.dolphin.ui.ifElse
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
import com.github.damontecres.dolphin.ui.letNotEmpty
import org.jellyfin.sdk.model.api.TrickplayInfo
import kotlin.time.Duration
@ -60,8 +63,7 @@ fun PlaybackOverlay(
scale: ContentScale,
playbackSpeed: Float,
moreButtonOptions: MoreButtonOptions,
subtitleIndex: Int?,
audioIndex: Int?,
currentPlayback: CurrentPlayback?,
audioStreams: List<AudioStream>,
modifier: Modifier = Modifier,
subtitle: String? = null,
@ -168,8 +170,8 @@ fun PlaybackOverlay(
seekEnabled = seekEnabled,
seekBarInteractionSource = seekBarInteractionSource,
moreButtonOptions = moreButtonOptions,
subtitleIndex = subtitleIndex,
audioIndex = audioIndex,
subtitleIndex = currentPlayback?.subtitleIndex,
audioIndex = currentPlayback?.audioIndex,
audioStreams = audioStreams,
playbackSpeed = playbackSpeed,
scale = scale,
@ -276,5 +278,22 @@ fun PlaybackOverlay(
}
}
}
AnimatedVisibility(
showDebugInfo && controllerViewState.controlsVisible,
modifier =
Modifier
.align(Alignment.TopStart),
) {
currentPlayback?.tracks?.letNotEmpty {
PlaybackTrackInfo(
trackSupport = it,
modifier =
Modifier
.align(Alignment.TopStart)
.padding(16.dp)
.background(AppColors.TransparentBlack50),
)
}
}
}
}

View file

@ -0,0 +1,113 @@
package com.github.damontecres.dolphin.ui.playback
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Icon
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.util.TrackSupport
@Composable
fun PlaybackTrackInfo(
trackSupport: List<TrackSupport>,
modifier: Modifier = Modifier,
) {
val selectedWeight = .5f
val weights = listOf(.25f, .4f, .5f, 1f, 1f)
val textStyle =
MaterialTheme.typography.bodySmall.copy(color = MaterialTheme.colorScheme.onBackground)
LazyColumn(
modifier = modifier,
) {
item {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier,
) {
val texts =
listOf(
"ID",
"Type",
"Codec",
"Supported",
"Labels",
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.weight(selectedWeight),
) {
Text(
text = "Selected",
style = textStyle,
)
}
texts.forEachIndexed { index, text ->
Box(
contentAlignment = Alignment.CenterStart,
modifier = Modifier.weight(weights[index]),
) {
Text(
text = text,
style = textStyle,
)
}
}
}
}
items(trackSupport) { track ->
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier,
) {
val texts =
listOf(
track.id ?: "",
track.type.name,
track.codecs ?: "",
track.supported.name,
track.labels.joinToString(", "),
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.weight(selectedWeight),
) {
if (track.selected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.size(12.dp),
)
} else {
Text(
text = "-",
style = textStyle,
)
}
}
texts.forEachIndexed { index, text ->
Box(
contentAlignment = Alignment.CenterStart,
modifier = Modifier.weight(weights[index]),
) {
Text(
text = text,
style = textStyle,
)
}
}
}
}
}
}

View file

@ -19,6 +19,8 @@ import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.util.ExceptionHandler
import com.github.damontecres.dolphin.util.TrackActivityPlaybackListener
import com.github.damontecres.dolphin.util.TrackSupport
import com.github.damontecres.dolphin.util.checkForSupport
import com.github.damontecres.dolphin.util.profile.PlaybackListener
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@ -224,6 +226,7 @@ class PlaybackViewModel
allowAudioStreamCopy = true,
autoOpenLiveStream = true,
mediaSourceId = null,
alwaysBurnInSubtitleWhenTranscoding = false,
),
).content
val source = response.mediaSources.firstOrNull()
@ -251,16 +254,19 @@ class PlaybackViewModel
val decision = StreamDecision(itemId, transcodeType, mediaUrl)
Timber.v("Playback decision: $decision")
val playback =
CurrentPlayback(
itemId,
audioIndex,
subtitleIndex,
source.id?.toUUIDOrNull(),
listOf(),
)
withContext(Dispatchers.Main) {
duration.value = source.runTimeTicks?.ticks
stream.value = decision
currentPlayback.value =
CurrentPlayback(
itemId,
audioIndex,
subtitleIndex,
source.id?.toUUIDOrNull(),
)
currentPlayback.value = playback
player.setMediaItem(
decision.mediaItem,
positionMs,
@ -277,6 +283,10 @@ class PlaybackViewModel
audioIndex,
subtitleIndex,
)
currentPlayback.value =
currentPlayback.value?.copy(
tracks = checkForSupport(tracks),
)
player.removeListener(this)
}
}
@ -338,6 +348,7 @@ data class CurrentPlayback(
val audioIndex: Int?,
val subtitleIndex: Int?,
val mediaSourceId: UUID?,
val tracks: List<TrackSupport>,
)
private val Format.idAsInt: Int?

View file

@ -8,6 +8,7 @@ message PlaybackPreferences {
int64 skip_back_ms = 2;
int64 controller_timeout_ms = 3;
int32 seek_bar_steps = 4;
bool show_debug_info = 5;
}
message HomePagePreferences{

View file

@ -44,4 +44,5 @@
<string name="genres">Genres</string>
<string name="play_theme_music">Play theme music</string>
<string name="license_info">License information</string>
<string name="playback_debug_info">Show playback debug info</string>
</resources>