mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Several bug fixes & minor UI changes (#1466)
## Description ### Bug fixes - Fixes a race condition crash when configuring subtitle styling before libmpv is initialized - Save the clicked item position on grid pages (#1428 & #1462) - Fix querying the server every 3 seconds for transcode reason if the video is direct playing ### UI changes - Makes the title on grid pages slightly smaller - Makes adjustments to the song list item on now playing so that no space is used if there's no artist - Use settings icon instead of triple dots on now playing page ### Related issues See above ### Testing Emulator, nvidia shield & google streamer ## Screenshots ### Song list item Before & after <img width="199" height="119" alt="image" src="https://github.com/user-attachments/assets/7fd3636a-d1e4-4b1f-8379-8599068d6309" /> ## AI or LLM usage None
This commit is contained in:
parent
8ea6bbbfca
commit
33161d4d6d
8 changed files with 77 additions and 67 deletions
|
|
@ -239,7 +239,7 @@ fun GridTitle(
|
|||
modifier: Modifier = Modifier,
|
||||
) = Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -653,6 +653,7 @@ fun CollectionFolderView(
|
|||
},
|
||||
) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
var position by rememberInt(viewModel.position)
|
||||
|
||||
var showContextMenu by remember { mutableStateOf<ContextMenu?>(null) }
|
||||
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
||||
|
|
@ -692,9 +693,15 @@ fun CollectionFolderView(
|
|||
val gridActions =
|
||||
remember(actions) {
|
||||
GridClickActions(
|
||||
onClickItem = actions.onClickItem,
|
||||
onLongClickItem =
|
||||
actions.onLongClickItem ?: { position, item ->
|
||||
onClickItem = { index, item ->
|
||||
position = index
|
||||
actions.onClickItem.invoke(index, item)
|
||||
},
|
||||
onLongClickItem = { index, item ->
|
||||
position = index
|
||||
if (actions.onLongClickItem != null) {
|
||||
actions.onLongClickItem.invoke(index, item)
|
||||
} else {
|
||||
showContextMenu =
|
||||
ContextMenu.ForBaseItem(
|
||||
fromLongClick = true,
|
||||
|
|
@ -707,6 +714,7 @@ fun CollectionFolderView(
|
|||
canRemoveNextUp = false,
|
||||
actions = contextActions,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClickPlayAll =
|
||||
actions.onClickPlayAll ?: { shuffle ->
|
||||
|
|
@ -785,7 +793,6 @@ fun CollectionFolderView(
|
|||
var showHeader by rememberSaveable { mutableStateOf(true) }
|
||||
val gridFocusRequester = remember { FocusRequester() }
|
||||
val pager = remember(state.items) { state.items.successValue }
|
||||
var position by rememberInt(viewModel.position)
|
||||
|
||||
val focusedItem = pager?.getOrNull(position)
|
||||
if (state.viewOptions.showBackdrop) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.github.damontecres.wholphin.ui.components
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
|
@ -10,13 +9,10 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||
|
|
@ -120,13 +116,8 @@ fun ItemGrid(
|
|||
focusRequester.tryRequestFocus()
|
||||
}
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = destination.title.getString(),
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
GridTitle(destination.title.getString())
|
||||
|
||||
CardGrid(
|
||||
pager = st.data,
|
||||
onClickItem = { index: Int, item: BaseItem ->
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import com.github.damontecres.wholphin.ui.playback.ControllerViewState
|
|||
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackAction
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackButton
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackButtons
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackFaButton
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.buttonSpacing
|
||||
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
|
|
@ -74,7 +75,7 @@ fun NowPlayingButtons(
|
|||
modifier = Modifier.align(Alignment.CenterStart),
|
||||
) {
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_more_vert_96,
|
||||
iconRes = R.drawable.vector_settings,
|
||||
onClick = {
|
||||
onControllerInteraction.invoke()
|
||||
onClickMore.invoke()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.detail.music
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -21,6 +22,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.DenseListItem
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.ListItemDefaults
|
||||
|
|
@ -118,38 +120,27 @@ fun SongListItem(
|
|||
)
|
||||
}
|
||||
|
||||
if (showArtist) {
|
||||
// TODO use dense?
|
||||
ListItem(
|
||||
DenseListItem(
|
||||
selected = isPlaying,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
interactionSource = interactionSource,
|
||||
leadingContent = leadingContent,
|
||||
headlineContent = headlineContent,
|
||||
supportingContent = {
|
||||
supportingContent =
|
||||
if (showArtist && artist != null) {
|
||||
{
|
||||
Text(
|
||||
text = artist ?: "",
|
||||
text = artist,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
trailingContent = trailingContent,
|
||||
scale = ListItemDefaults.scale(1f, 1f, .95f),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
ListItem(
|
||||
selected = isPlaying,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
interactionSource = interactionSource,
|
||||
leadingContent = leadingContent,
|
||||
headlineContent = headlineContent,
|
||||
supportingContent = null,
|
||||
trailingContent = trailingContent,
|
||||
scale = ListItemDefaults.scale(1f, 1f, .95f),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
if (showMoreButton) {
|
||||
Button(
|
||||
onClick = onClickMore,
|
||||
|
|
@ -198,7 +189,7 @@ fun MusicQueueMarker(
|
|||
@Composable
|
||||
fun SongListItemPreview() {
|
||||
WholphinTheme {
|
||||
Column {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
SongListItem(
|
||||
title = "Song title",
|
||||
artist = "Artists",
|
||||
|
|
@ -232,6 +223,17 @@ fun SongListItemPreview() {
|
|||
showArtist = true,
|
||||
isPlaying = true,
|
||||
)
|
||||
SongListItem(
|
||||
title = "Song title",
|
||||
artist = null,
|
||||
indexNumber = 2,
|
||||
runtime = 2.minutes + 30.seconds,
|
||||
onClick = {},
|
||||
onLongClick = { },
|
||||
modifier = Modifier,
|
||||
showArtist = true,
|
||||
isPlaying = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ import androidx.compose.ui.graphics.graphicsLayer
|
|||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
|
|
@ -85,7 +84,6 @@ import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackOverlay
|
|||
import com.github.damontecres.wholphin.ui.playback.overlay.SkipIndicator
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.SkipSegmentButton
|
||||
import com.github.damontecres.wholphin.ui.playback.overlay.rememberSeekBarState
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.applyToMpv
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.calculateEdgeSize
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.toSubtitleStyle
|
||||
import com.github.damontecres.wholphin.ui.seasonEpisode
|
||||
|
|
@ -95,7 +93,6 @@ import com.github.damontecres.wholphin.util.LoadingState
|
|||
import com.github.damontecres.wholphin.util.Media3SubtitleOverride
|
||||
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
|
||||
import io.github.peerless2012.ass.media.widget.AssSubtitleView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
|
@ -163,24 +160,12 @@ fun PlaybackPageContent(
|
|||
|
||||
val prefs = preferences.appPreferences.playbackPreferences
|
||||
val scope = rememberCoroutineScope()
|
||||
val configuration = LocalConfiguration.current
|
||||
val density = LocalDensity.current
|
||||
val userDto by viewModel.currentUserDto.collectAsState()
|
||||
|
||||
var showDebugInfo by remember { mutableStateOf(prefs.showDebugInfo) }
|
||||
|
||||
var playbackDialog by remember { mutableStateOf<PlaybackDialogType?>(null) }
|
||||
LaunchedEffect(player) {
|
||||
if (playerBackend == PlayerBackend.MPV) {
|
||||
scope.launch(Dispatchers.IO + ExceptionHandler()) {
|
||||
// MPV can't play HDR, so always use regular settings
|
||||
preferences.appPreferences.interfacePreferences.subtitlesPreferences.applyToMpv(
|
||||
configuration,
|
||||
density,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var contentScale by remember(playerBackend) {
|
||||
mutableStateOf(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.media.MediaCodecList
|
|||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -57,6 +58,7 @@ import com.github.damontecres.wholphin.ui.launchDefault
|
|||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.onMain
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.applyToMpv
|
||||
import com.github.damontecres.wholphin.ui.seekBack
|
||||
import com.github.damontecres.wholphin.ui.seekForward
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
|
|
@ -66,6 +68,7 @@ import com.github.damontecres.wholphin.util.LoadingState
|
|||
import com.github.damontecres.wholphin.util.PlaybackItemState
|
||||
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
|
||||
import com.github.damontecres.wholphin.util.checkForSupport
|
||||
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
|
||||
import com.github.damontecres.wholphin.util.mpv.mpvDeviceProfile
|
||||
import com.github.damontecres.wholphin.util.profile.Codec
|
||||
import com.github.damontecres.wholphin.util.subtitleMimeTypes
|
||||
|
|
@ -1165,10 +1168,10 @@ class PlaybackViewModel
|
|||
private fun listenForTranscodeReason(): Job =
|
||||
viewModelScope.launchIO {
|
||||
state.map { it.currentPlayback }.collectLatest {
|
||||
if (it != null) {
|
||||
if (it != null && it.playMethod == PlayMethod.TRANSCODE && it.transcodeInfo == null) {
|
||||
try {
|
||||
var transcodeInfo = it.transcodeInfo
|
||||
while (isActive && it.playMethod == PlayMethod.TRANSCODE && transcodeInfo == null) {
|
||||
while (isActive && transcodeInfo == null) {
|
||||
delay(2.seconds)
|
||||
transcodeInfo =
|
||||
api.sessionApi
|
||||
|
|
@ -1526,6 +1529,22 @@ class PlaybackViewModel
|
|||
screensaverService.keepScreenOn(isPlaying)
|
||||
}
|
||||
|
||||
override fun onAvailableCommandsChanged(availableCommands: Player.Commands) {
|
||||
val player = this@PlaybackViewModel.player
|
||||
if (availableCommands.contains(Player.COMMAND_PREPARE) && player is MpvPlayer) {
|
||||
// MpvPlayer is initialized, so configure subtitles
|
||||
Timber.i("Applying subtitle config to MPV")
|
||||
viewModelScope.launchDefault {
|
||||
val configuration = context.resources.configuration
|
||||
val density = Density(context.resources.displayMetrics.density)
|
||||
preferences.appPreferences.interfacePreferences.subtitlesPreferences.applyToMpv(
|
||||
configuration,
|
||||
density,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBandwidthEstimate(
|
||||
eventTime: AnalyticsListener.EventTime,
|
||||
totalLoadTimeMs: Int,
|
||||
|
|
|
|||
|
|
@ -167,6 +167,11 @@ class MpvPlayer(
|
|||
// COMMAND_GET_TEXT,
|
||||
COMMAND_RELEASE,
|
||||
).build()
|
||||
notifyListeners(EVENT_AVAILABLE_COMMANDS_CHANGED) {
|
||||
onAvailableCommandsChanged(
|
||||
availableCommands,
|
||||
)
|
||||
}
|
||||
trackSelector.init(this, DefaultBandwidthMeter.getSingletonInstance(context))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue