fix: Force transcoding of subtitles with nativePlayer + external (#592)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2025-11-08 16:38:14 +01:00 committed by GitHub
parent 2594a8463f
commit 0e4514af84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 43 deletions

View file

@ -34,29 +34,21 @@ fun SubtitlePicker(
val subTitles by VideoPlayerObject.subtitleTracks.collectAsState(emptyList()) val subTitles by VideoPlayerObject.subtitleTracks.collectAsState(emptyList())
val internalSubTracks by VideoPlayerObject.exoSubTracks.collectAsState(emptyList()) val internalSubTracks by VideoPlayerObject.exoSubTracks.collectAsState(emptyList())
if (internalSubTracks.isEmpty()) return if (subTitles.isEmpty()) return
val focusOffTrack = remember { FocusRequester() } val focusRequesters = remember(subTitles) {
subTitles.associateWith { FocusRequester() }
val focusRequesters = remember(internalSubTracks) {
internalSubTracks.associateWith { FocusRequester() }
} }
val listState = rememberLazyListState() val listState = rememberLazyListState()
LaunchedEffect(selectedIndex, subTitles, internalSubTracks) { LaunchedEffect(selectedIndex, subTitles) {
val serverSubIndex = subTitles.indexOfFirst { it.index == selectedIndex.toLong() } val selectedSubIndex = subTitles.indexOfFirst { it.index == selectedIndex.toLong() }
if (serverSubIndex <= 0) { if (selectedSubIndex in subTitles.indices) {
focusOffTrack.requestFocus() listState.scrollToItem(selectedSubIndex)
return@LaunchedEffect focusRequesters[subTitles[selectedSubIndex]]?.requestFocus()
} }
val internalIndex = serverSubIndex - 1
val lazyColumnIndex = internalIndex + 1
listState.scrollToItem(lazyColumnIndex)
focusRequesters[internalSubTracks[internalIndex]]?.requestFocus()
} }
CustomModalBottomSheet( CustomModalBottomSheet(
@ -69,45 +61,45 @@ fun SubtitlePicker(
.wrapContentWidth() .wrapContentWidth()
.padding(horizontal = 8.dp, vertical = 16.dp), .padding(horizontal = 8.dp, vertical = 16.dp),
) { ) {
subTitles.forEachIndexed { index, serverSub ->
val isOffTrack = index == 0
val selected = serverSub.index == selectedIndex.toLong()
item { item {
val selectedOff = -1 == selectedIndex
TrackButton( TrackButton(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.focusRequester(focusOffTrack), .focusRequester(focusRequesters[serverSub]!!),
onClick = { onClick = {
if (isOffTrack) {
VideoPlayerObject.setSubtitleTrackIndex(-1) VideoPlayerObject.setSubtitleTrackIndex(-1)
player.clearSubtitleTrack() player.clearSubtitleTrack()
}, } else {
selected = selectedOff val internalTrackIndex = index - 1
) {
Translate(Localized::off) { val internalSubTrack =
Text(it) internalSubTracks.elementAtOrNull(internalTrackIndex)
if (internalSubTrack != null) {
VideoPlayerObject.setSubtitleTrackIndex(serverSub.index.toInt())
player.setInternalSubtitleTrack(internalSubTrack)
} }
} }
}
internalSubTracks.forEachIndexed { index, subtitle ->
val serverSub = subTitles.elementAtOrNull(index + 1)
val selected = serverSub?.index == selectedIndex.toLong()
item {
TrackButton(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequesters[subtitle]!!),
onClick = {
serverSub?.index?.let {
VideoPlayerObject.setSubtitleTrackIndex(it.toInt())
}
player.setInternalSubtitleTrack(subtitle)
}, },
selected = selected, selected = selected,
) { ) {
if (isOffTrack) {
Translate(Localized::off) {
Text(it)
}
} else {
Text( Text(
text = serverSub?.name ?: "", text = serverSub.name,
) )
} }
} }
} }
} }
} }
}
} }

View file

@ -22,6 +22,7 @@ import 'package:fladder/models/playback/direct_playback_model.dart';
import 'package:fladder/models/playback/offline_playback_model.dart'; import 'package:fladder/models/playback/offline_playback_model.dart';
import 'package:fladder/models/playback/playback_options_dialogue.dart'; import 'package:fladder/models/playback/playback_options_dialogue.dart';
import 'package:fladder/models/playback/transcode_playback_model.dart'; import 'package:fladder/models/playback/transcode_playback_model.dart';
import 'package:fladder/models/settings/video_player_settings.dart';
import 'package:fladder/models/syncing/sync_item.dart'; import 'package:fladder/models/syncing/sync_item.dart';
import 'package:fladder/models/video_stream_model.dart'; import 'package:fladder/models/video_stream_model.dart';
import 'package:fladder/profiles/default_profile.dart'; import 'package:fladder/profiles/default_profile.dart';
@ -277,12 +278,18 @@ class PlaybackModelHelper {
oldModel?.mediaStreams?.currentAudioStream, oldModel?.mediaStreams?.currentAudioStream,
newStreamModel?.audioStreams, newStreamModel?.audioStreams,
newStreamModel?.defaultAudioStreamIndex); newStreamModel?.defaultAudioStreamIndex);
final subStreamIndex = selectSubStream( final subStreamIndex = selectSubStream(
ref.read(userProvider.select((value) => value?.userConfiguration?.rememberSubtitleSelections ?? true)), ref.read(userProvider.select((value) => value?.userConfiguration?.rememberSubtitleSelections ?? true)),
oldModel?.mediaStreams?.currentSubStream, oldModel?.mediaStreams?.currentSubStream,
newStreamModel?.subStreams, newStreamModel?.subStreams,
newStreamModel?.defaultSubStreamIndex); newStreamModel?.defaultSubStreamIndex);
//Native player does not allow for loading external subtitles with transcoding
final isNativePlayer =
ref.read(videoPlayerSettingsProvider.select((value) => value.wantedPlayer == PlayerOptions.nativePlayer));
final isExternalSub = newStreamModel?.currentSubStream?.isExternal == true;
final Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost( final Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
itemId: item.id, itemId: item.id,
body: PlaybackInfoDto( body: PlaybackInfoDto(
@ -295,6 +302,7 @@ class PlaybackModelHelper {
userId: userId, userId: userId,
enableDirectPlay: type != PlaybackType.transcode, enableDirectPlay: type != PlaybackType.transcode,
enableDirectStream: type != PlaybackType.transcode, enableDirectStream: type != PlaybackType.transcode,
alwaysBurnInSubtitleWhenTranscoding: isNativePlayer && isExternalSub,
maxStreamingBitrate: qualityOptions.enabledFirst.keys.firstOrNull?.bitRate, maxStreamingBitrate: qualityOptions.enabledFirst.keys.firstOrNull?.bitRate,
mediaSourceId: newStreamModel?.currentVersionStream?.id, mediaSourceId: newStreamModel?.currentVersionStream?.id,
), ),