mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Various fixes to screensaver, subtitle order, & playlist modifications (#1104)
## Description Several small tweaks: - Always put screensaver logo on left instead of random - Disable playback speed option if audio is being passed through - When opening playback settings such as speed or scale, focus on the current value instead of top of the list; doesn't apply to subtitles so it's easy to toggle them off - If the user has a preferred subtitle language, show tracks in that language first/top - Add toast message after creating or adding to a playlist ### Related issues Playback speed audio pass through: #164 ### Testing Emulator & shield ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
23e5225c19
commit
9057dceef6
10 changed files with 107 additions and 26 deletions
|
|
@ -135,7 +135,6 @@ fun AppScreensaverContent(
|
||||||
)
|
)
|
||||||
|
|
||||||
var logoError by remember(currentItem) { mutableStateOf(false) }
|
var logoError by remember(currentItem) { mutableStateOf(false) }
|
||||||
val alignment = listOf(Alignment.BottomStart, Alignment.BottomEnd).random()
|
|
||||||
if (!logoError) {
|
if (!logoError) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model =
|
model =
|
||||||
|
|
@ -150,7 +149,7 @@ fun AppScreensaverContent(
|
||||||
},
|
},
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.align(alignment)
|
.align(Alignment.BottomStart)
|
||||||
.size(width = 240.dp, height = 120.dp)
|
.size(width = 240.dp, height = 120.dp)
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
)
|
)
|
||||||
|
|
@ -158,7 +157,7 @@ fun AppScreensaverContent(
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.align(alignment)
|
.align(Alignment.BottomStart)
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.fillMaxWidth(.5f)
|
.fillMaxWidth(.5f)
|
||||||
.fillMaxHeight(.3f),
|
.fillMaxHeight(.3f),
|
||||||
|
|
@ -169,7 +168,7 @@ fun AppScreensaverContent(
|
||||||
style = MaterialTheme.typography.displaySmall,
|
style = MaterialTheme.typography.displaySmall,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.align(alignment),
|
modifier = Modifier.align(Alignment.BottomStart),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -632,6 +632,7 @@ fun chooseStream(
|
||||||
streams: List<MediaStream>,
|
streams: List<MediaStream>,
|
||||||
currentIndex: Int?,
|
currentIndex: Int?,
|
||||||
type: MediaStreamType,
|
type: MediaStreamType,
|
||||||
|
preferredSubtitleLanguage: String?,
|
||||||
onClick: (Int) -> Unit,
|
onClick: (Int) -> Unit,
|
||||||
): DialogParams =
|
): DialogParams =
|
||||||
DialogParams(
|
DialogParams(
|
||||||
|
|
@ -669,22 +670,32 @@ fun chooseStream(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
addAll(
|
addAll(
|
||||||
streams.filter { it.type == type }.mapIndexed { index, stream ->
|
streams
|
||||||
val simpleStream = SimpleMediaStream.from(context, stream, true)
|
.filter { it.type == type }
|
||||||
DialogItem(
|
.let {
|
||||||
selected = currentIndex == stream.index,
|
if (type == MediaStreamType.SUBTITLE && preferredSubtitleLanguage.isNotNullOrBlank()) {
|
||||||
leadingContent = {
|
it.sortedByDescending { it.language != null && it.language == preferredSubtitleLanguage }
|
||||||
SelectedLeadingContent(currentIndex == stream.index)
|
} else {
|
||||||
},
|
it
|
||||||
headlineContent = {
|
}
|
||||||
Text(text = simpleStream.streamTitle ?: simpleStream.displayTitle)
|
}.mapIndexed { index, stream ->
|
||||||
},
|
val simpleStream = SimpleMediaStream.from(context, stream, true)
|
||||||
supportingContent = {
|
DialogItem(
|
||||||
if (simpleStream.streamTitle != null) Text(text = simpleStream.displayTitle)
|
selected = currentIndex == stream.index,
|
||||||
},
|
leadingContent = {
|
||||||
onClick = { onClick.invoke(stream.index) },
|
SelectedLeadingContent(currentIndex == stream.index)
|
||||||
)
|
},
|
||||||
},
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = simpleStream.streamTitle ?: simpleStream.displayTitle,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
if (simpleStream.streamTitle != null) Text(text = simpleStream.displayTitle)
|
||||||
|
},
|
||||||
|
onClick = { onClick.invoke(stream.index) },
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import android.widget.Toast
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.services.PlaylistCreator
|
import com.github.damontecres.wholphin.services.PlaylistCreator
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
|
|
@ -14,6 +15,7 @@ import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import org.jellyfin.sdk.model.api.MediaType
|
import org.jellyfin.sdk.model.api.MediaType
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -43,7 +45,13 @@ class AddPlaylistViewModel
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launchIO(ExceptionHandler(autoToast = true)) {
|
viewModelScope.launchIO(ExceptionHandler(autoToast = true)) {
|
||||||
playlistCreator.addToServerPlaylist(playlistId, itemId)
|
try {
|
||||||
|
playlistCreator.addToServerPlaylist(playlistId, itemId)
|
||||||
|
showToast(context, context.getString(R.string.success), Toast.LENGTH_SHORT)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error adding %s to playlist %s", itemId, playlistId)
|
||||||
|
showToast(context, "Error: ${ex.localizedMessage}", Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +63,8 @@ class AddPlaylistViewModel
|
||||||
val playlistId = playlistCreator.createServerPlaylist(playlistName, listOf(itemId))
|
val playlistId = playlistCreator.createServerPlaylist(playlistName, listOf(itemId))
|
||||||
if (playlistId == null) {
|
if (playlistId == null) {
|
||||||
showToast(context, "Error creating playlist", Toast.LENGTH_LONG)
|
showToast(context, "Error creating playlist", Toast.LENGTH_LONG)
|
||||||
|
} else {
|
||||||
|
showToast(context, context.getString(R.string.success), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,13 @@ fun EpisodeDetails(
|
||||||
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
|
val preferredSubtitleLanguage =
|
||||||
|
viewModel.serverRepository.currentUserDto
|
||||||
|
.observeAsState()
|
||||||
|
.value
|
||||||
|
?.configuration
|
||||||
|
?.subtitleLanguagePreference
|
||||||
|
|
||||||
val moreActions =
|
val moreActions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
navigateTo = viewModel::navigateTo,
|
navigateTo = viewModel::navigateTo,
|
||||||
|
|
@ -200,6 +207,7 @@ fun EpisodeDetails(
|
||||||
type,
|
type,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
preferredSubtitleLanguage = preferredSubtitleLanguage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,13 @@ fun MovieDetails(
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
|
|
||||||
|
val preferredSubtitleLanguage =
|
||||||
|
viewModel.serverRepository.currentUserDto
|
||||||
|
.observeAsState()
|
||||||
|
.value
|
||||||
|
?.configuration
|
||||||
|
?.subtitleLanguagePreference
|
||||||
|
|
||||||
val moreActions =
|
val moreActions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
navigateTo = viewModel::navigateTo,
|
navigateTo = viewModel::navigateTo,
|
||||||
|
|
@ -246,6 +253,7 @@ fun MovieDetails(
|
||||||
type,
|
type,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
preferredSubtitleLanguage = preferredSubtitleLanguage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,13 @@ fun SeriesOverview(
|
||||||
}
|
}
|
||||||
val chosenStreams by viewModel.chosenStreams.observeAsState(null)
|
val chosenStreams by viewModel.chosenStreams.observeAsState(null)
|
||||||
|
|
||||||
|
val preferredSubtitleLanguage =
|
||||||
|
viewModel.serverRepository.currentUserDto
|
||||||
|
.observeAsState()
|
||||||
|
.value
|
||||||
|
?.configuration
|
||||||
|
?.subtitleLanguagePreference
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> {
|
is LoadingState.Error -> {
|
||||||
ErrorMessage(state, modifier)
|
ErrorMessage(state, modifier)
|
||||||
|
|
@ -252,6 +259,7 @@ fun SeriesOverview(
|
||||||
type,
|
type,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
preferredSubtitleLanguage = preferredSubtitleLanguage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import androidx.annotation.OptIn
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.focusGroup
|
import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -24,7 +23,6 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -37,7 +35,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
|
@ -64,6 +61,7 @@ import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.components.Button
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
|
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
|
||||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
|
import com.github.damontecres.wholphin.ui.indexOfFirstOrNull
|
||||||
import com.github.damontecres.wholphin.ui.seekBack
|
import com.github.damontecres.wholphin.ui.seekBack
|
||||||
import com.github.damontecres.wholphin.ui.seekForward
|
import com.github.damontecres.wholphin.ui.seekForward
|
||||||
import com.github.damontecres.wholphin.ui.skipStringRes
|
import com.github.damontecres.wholphin.ui.skipStringRes
|
||||||
|
|
@ -470,6 +468,14 @@ fun <T> BottomDialog(
|
||||||
gravity: Int,
|
gravity: Int,
|
||||||
currentChoice: BottomDialogItem<T>? = null,
|
currentChoice: BottomDialogItem<T>? = null,
|
||||||
) {
|
) {
|
||||||
|
val focusRequesters = remember(choices.size) { List(choices.size) { FocusRequester() } }
|
||||||
|
if (currentChoice != null) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
choices.indexOfFirstOrNull { it == currentChoice }?.let {
|
||||||
|
focusRequesters.getOrNull(it)?.tryRequestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO enforcing a width ends up ignore the gravity
|
// TODO enforcing a width ends up ignore the gravity
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
|
@ -504,6 +510,7 @@ fun <T> BottomDialog(
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
ListItem(
|
ListItem(
|
||||||
selected = choice == currentChoice,
|
selected = choice == currentChoice,
|
||||||
|
enabled = choice.enabled,
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onSelectChoice(index, choice)
|
onSelectChoice(index, choice)
|
||||||
|
|
@ -524,6 +531,7 @@ fun <T> BottomDialog(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
|
modifier = Modifier.focusRequester(focusRequesters[index]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -539,6 +547,7 @@ data class BottomDialogItem<T>(
|
||||||
val data: T,
|
val data: T,
|
||||||
val headline: String,
|
val headline: String,
|
||||||
val supporting: String?,
|
val supporting: String?,
|
||||||
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@PreviewTvSpec
|
@PreviewTvSpec
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,12 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -32,6 +35,8 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.TrackIndex
|
import com.github.damontecres.wholphin.data.model.TrackIndex
|
||||||
import com.github.damontecres.wholphin.ui.AppColors
|
import com.github.damontecres.wholphin.ui.AppColors
|
||||||
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
|
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
|
||||||
|
import com.github.damontecres.wholphin.ui.indexOfFirstOrNull
|
||||||
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
enum class PlaybackDialogType {
|
enum class PlaybackDialogType {
|
||||||
|
|
@ -54,6 +59,7 @@ data class PlaybackSettings(
|
||||||
val contentScale: ContentScale,
|
val contentScale: ContentScale,
|
||||||
val subtitleDelay: Duration,
|
val subtitleDelay: Duration,
|
||||||
val hasSubtitleDownloadPermission: Boolean,
|
val hasSubtitleDownloadPermission: Boolean,
|
||||||
|
val playbackSpeedEnabled: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -137,6 +143,7 @@ fun PlaybackDialog(
|
||||||
data = PlaybackDialogType.PLAYBACK_SPEED,
|
data = PlaybackDialogType.PLAYBACK_SPEED,
|
||||||
headline = stringResource(R.string.playback_speed),
|
headline = stringResource(R.string.playback_speed),
|
||||||
supporting = settings.playbackSpeed.toString(),
|
supporting = settings.playbackSpeed.toString(),
|
||||||
|
enabled = settings.playbackSpeedEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (enableVideoScale) {
|
if (enableVideoScale) {
|
||||||
|
|
@ -395,6 +402,14 @@ fun StreamChoiceBottomDialog(
|
||||||
gravity: Int,
|
gravity: Int,
|
||||||
currentChoice: Int? = null,
|
currentChoice: Int? = null,
|
||||||
) {
|
) {
|
||||||
|
val focusRequesters = remember(choices.size) { List(choices.size) { FocusRequester() } }
|
||||||
|
if (currentChoice != null) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
choices.indexOfFirstOrNull { it.index == currentChoice }?.let {
|
||||||
|
focusRequesters.getOrNull(it)?.tryRequestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO enforcing a width ends up ignore the gravity
|
// TODO enforcing a width ends up ignore the gravity
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
|
@ -445,6 +460,7 @@ fun StreamChoiceBottomDialog(
|
||||||
if (choice.streamTitle != null) Text(choice.displayTitle)
|
if (choice.streamTitle != null) Text(choice.displayTitle)
|
||||||
},
|
},
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
|
modifier = Modifier.focusRequester(focusRequesters[index]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -596,6 +596,9 @@ fun PlaybackPageContent(
|
||||||
subtitleDelay = subtitleDelay,
|
subtitleDelay = subtitleDelay,
|
||||||
hasSubtitleDownloadPermission =
|
hasSubtitleDownloadPermission =
|
||||||
remember(userDto) { userDto?.policy?.let { it.isAdministrator || it.enableSubtitleManagement } == true },
|
remember(userDto) { userDto?.policy?.let { it.isAdministrator || it.enableSubtitleManagement } == true },
|
||||||
|
// TODO Passing through audio prevents changing playback speed
|
||||||
|
// See https://github.com/damontecres/Wholphin/issues/164
|
||||||
|
playbackSpeedEnabled = playerBackend == PlayerBackend.MPV || currentPlayback?.audioDecoder != null,
|
||||||
),
|
),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
playbackDialog = null
|
playbackDialog = null
|
||||||
|
|
|
||||||
|
|
@ -446,11 +446,20 @@ class PlaybackViewModel
|
||||||
|
|
||||||
// Create the correct player for the media
|
// Create the correct player for the media
|
||||||
createPlayer(videoStream?.hdr == true, videoStream?.is4k == true)
|
createPlayer(videoStream?.hdr == true, videoStream?.is4k == true)
|
||||||
|
val subtitleLanguagePreference =
|
||||||
|
serverRepository.currentUserDto.value
|
||||||
|
?.configuration
|
||||||
|
?.subtitleLanguagePreference
|
||||||
val subtitleStreams =
|
val subtitleStreams =
|
||||||
mediaSource.mediaStreams
|
mediaSource.mediaStreams
|
||||||
?.filter { it.type == MediaStreamType.SUBTITLE }
|
?.filter { it.type == MediaStreamType.SUBTITLE }
|
||||||
?.map {
|
.let {
|
||||||
|
if (subtitleLanguagePreference.isNotNullOrBlank()) {
|
||||||
|
it?.sortedByDescending { it.language != null && subtitleLanguagePreference == it.language }
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}?.map {
|
||||||
SimpleMediaStream.from(context, it, true)
|
SimpleMediaStream.from(context, it, true)
|
||||||
}.orEmpty()
|
}.orEmpty()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue