Persist audio & subtitle track changes for series if in different language (#405)

This only applies if the user has set a preferred audio and/or subtitle
language on the web UI!

If the user chooses an audio/subtitle track that is a different language
than their preferred, it will be remembered for that series. Playback of
future episodes of the series will pick a matching language.

The audio & subtitle selections are independent. You can make the choice
before playback or during playback.

Closes #378
This commit is contained in:
damontecres 2025-12-11 10:16:45 -05:00 committed by GitHub
parent 033bdccaa2
commit 0f4254c6ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 990 additions and 324 deletions

View file

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -26,12 +27,8 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.ProvideTextStyle
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.data.model.chooseStream
import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.preferences.AppThemeColors
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.playback.audioStreamCount
@ -40,16 +37,30 @@ import com.github.damontecres.wholphin.ui.playback.externalSubtitlesCount
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.util.languageName
import com.github.damontecres.wholphin.util.profile.Codec
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.MediaStreamType
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.MediaStream
import org.jellyfin.sdk.model.api.VideoRange
import org.jellyfin.sdk.model.api.VideoRangeType
@Composable
@NonRestartableComposable
fun VideoStreamDetails(
preferences: UserPreferences,
dto: BaseItemDto,
itemPlayback: ItemPlayback?,
chosenStreams: ChosenStreams?,
modifier: Modifier = Modifier,
) = VideoStreamDetails(
chosenStreams?.source,
chosenStreams?.videoStream,
chosenStreams?.audioStream,
chosenStreams?.subtitleStream,
modifier,
)
@Composable
fun VideoStreamDetails(
source: MediaSourceInfo?,
videoStream: MediaStream?,
audioStream: MediaStream?,
subtitleStream: MediaStream?,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
@ -57,17 +68,6 @@ fun VideoStreamDetails(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier,
) {
val source = remember(dto, itemPlayback) { chooseSource(dto, itemPlayback) }
val videoStream =
remember(dto, itemPlayback) {
chooseStream(
dto,
itemPlayback,
MediaStreamType.VIDEO,
preferences,
)
}
val video =
remember(videoStream) {
videoStream
@ -95,15 +95,6 @@ fun VideoStreamDetails(
StreamLabel(it)
}
val audioStream =
remember(dto, itemPlayback) {
chooseStream(
dto,
itemPlayback,
MediaStreamType.AUDIO,
preferences,
)
}
val audioCount = remember(source) { source?.audioStreamCount ?: 0 }
val audio =
remember(audioCount, audioStream) {
@ -124,23 +115,14 @@ fun VideoStreamDetails(
modifier = Modifier.widthIn(max = 200.dp),
)
val subtitleStream =
remember(dto, itemPlayback) {
chooseStream(
dto,
itemPlayback,
MediaStreamType.SUBTITLE,
preferences,
)
}
val subtitleCount =
remember(source) {
(source?.embeddedSubtitleCount ?: 0) + (source?.externalSubtitlesCount ?: 0)
}
var disabled by remember { mutableStateOf(false) }
val subtitle =
remember(subtitleCount, subtitleStream, itemPlayback) {
if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) {
remember(subtitleCount, subtitleStream) {
if (subtitleCount > 0 && subtitleStream == null) {
disabled = true
context.getString(R.string.disabled)
} else if (subtitleCount == 0 || subtitleStream == null) {

View file

@ -30,7 +30,6 @@ import androidx.lifecycle.compose.LifecycleStartEffect
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.components.DetailsBackdropImage
import com.github.damontecres.wholphin.ui.components.DialogParams
@ -57,6 +56,7 @@ import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.MediaType
import org.jellyfin.sdk.model.extensions.ticks
import org.jellyfin.sdk.model.serializer.toUUID
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import java.util.UUID
import kotlin.time.Duration
@ -157,7 +157,7 @@ fun EpisodeDetails(
watched = ep.data.userData?.played ?: false,
favorite = ep.data.userData?.isFavorite ?: false,
seriesId = ep.data.seriesId,
sourceId = chosenStreams?.sourceId,
sourceId = chosenStreams?.source?.id?.toUUIDOrNull(),
actions = moreActions,
onChooseVersion = {
chooseVersion =
@ -174,25 +174,26 @@ fun EpisodeDetails(
moreDialog = null
},
onChooseTracks = { type ->
chooseSource(
ep.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
ep,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
viewModel.streamChoiceService
.chooseSource(
ep.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
ep,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
},
),
)

View file

@ -58,9 +58,7 @@ fun EpisodeDetailsHeader(
EpisodeQuickDetails(dto)
VideoStreamDetails(
preferences = preferences,
dto = dto,
itemPlayback = chosenStreams?.itemPlayback,
chosenStreams = chosenStreams,
modifier = Modifier.padding(bottom = padding),
)
dto.taglines?.firstOrNull()?.let { tagline ->

View file

@ -12,7 +12,9 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.preferences.ThemeSongVolume
import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.setValueOnMain
@ -44,8 +46,10 @@ class EpisodeViewModel
private val navigationManager: NavigationManager,
val serverRepository: ServerRepository,
val itemPlaybackRepository: ItemPlaybackRepository,
val streamChoiceService: StreamChoiceService,
private val themeSongPlayer: ThemeSongPlayer,
private val favoriteWatchManager: FavoriteWatchManager,
private val userPreferencesService: UserPreferencesService,
@Assisted val itemId: UUID,
) : ViewModel() {
@AssistedFactory
@ -85,8 +89,9 @@ class EpisodeViewModel
"Error fetching movie",
),
) {
val prefs = userPreferencesService.getCurrent()
val item = fetchAndSetItem().await()
val result = itemPlaybackRepository.getSelectedTracks(item.id, item)
val result = itemPlaybackRepository.getSelectedTracks(item.id, item, prefs)
withContext(Dispatchers.Main) {
this@EpisodeViewModel.item.value = item
chosenStreams.value = result
@ -115,10 +120,12 @@ class EpisodeViewModel
sourceId: UUID,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result = itemPlaybackRepository.savePlayVersion(item.id, sourceId)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen
@ -133,6 +140,8 @@ class EpisodeViewModel
type: MediaStreamType,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result =
itemPlaybackRepository.saveTrackSelection(
item = item,
@ -142,7 +151,7 @@ class EpisodeViewModel
)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen

View file

@ -45,7 +45,6 @@ import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.RemoteTrailer
import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.data.model.aspectRatioFloat
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.ui.AspectRatios
@ -83,6 +82,7 @@ import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.MediaType
import org.jellyfin.sdk.model.extensions.ticks
import org.jellyfin.sdk.model.serializer.toUUID
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import java.util.UUID
import kotlin.time.Duration
@ -204,7 +204,7 @@ fun MovieDetails(
watched = movie.data.userData?.played ?: false,
favorite = movie.data.userData?.isFavorite ?: false,
seriesId = null,
sourceId = chosenStreams?.sourceId,
sourceId = chosenStreams?.source?.id?.toUUIDOrNull(),
actions = moreActions,
onChooseVersion = {
chooseVersion =
@ -221,25 +221,26 @@ fun MovieDetails(
moreDialog = null
},
onChooseTracks = { type ->
chooseSource(
movie.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
movie,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
viewModel.streamChoiceService
.chooseSource(
movie.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
movie,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
},
),
)

View file

@ -72,9 +72,7 @@ fun MovieDetailsHeader(
}
VideoStreamDetails(
preferences = preferences,
dto = dto,
itemPlayback = chosenStreams?.itemPlayback,
chosenStreams = chosenStreams,
modifier = Modifier.padding(bottom = padding),
)
dto.taglines?.firstOrNull()?.let { tagline ->

View file

@ -18,8 +18,10 @@ import com.github.damontecres.wholphin.services.ExtrasService
import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.PeopleFavorites
import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
@ -54,11 +56,13 @@ class MovieViewModel
private val navigationManager: NavigationManager,
val serverRepository: ServerRepository,
val itemPlaybackRepository: ItemPlaybackRepository,
val streamChoiceService: StreamChoiceService,
private val themeSongPlayer: ThemeSongPlayer,
private val favoriteWatchManager: FavoriteWatchManager,
private val peopleFavorites: PeopleFavorites,
private val trailerService: TrailerService,
private val extrasService: ExtrasService,
private val userPreferencesService: UserPreferencesService,
@Assisted val itemId: UUID,
) : ViewModel() {
@AssistedFactory
@ -104,7 +108,12 @@ class MovieViewModel
),
) {
val item = fetchAndSetItem().await()
val result = itemPlaybackRepository.getSelectedTracks(item.id, item)
val result =
itemPlaybackRepository.getSelectedTracks(
item.id,
item,
userPreferencesService.getCurrent(),
)
withContext(Dispatchers.Main) {
this@MovieViewModel.item.value = item
chosenStreams.value = result
@ -172,10 +181,12 @@ class MovieViewModel
sourceId: UUID,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result = itemPlaybackRepository.savePlayVersion(item.id, sourceId)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen
@ -190,6 +201,8 @@ class MovieViewModel
type: MediaStreamType,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result =
itemPlaybackRepository.saveTrackSelection(
item = item,
@ -199,7 +212,7 @@ class MovieViewModel
)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen

View file

@ -37,9 +37,7 @@ fun FocusedEpisodeHeader(
if (dto != null) {
VideoStreamDetails(
preferences = preferences,
dto = dto,
itemPlayback = chosenStreams?.itemPlayback,
chosenStreams = chosenStreams,
modifier = Modifier,
)
}

View file

@ -21,7 +21,6 @@ import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.map
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
import com.github.damontecres.wholphin.ui.components.DialogParams
@ -53,6 +52,7 @@ import org.jellyfin.sdk.model.api.PersonKind
import org.jellyfin.sdk.model.extensions.ticks
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import org.jellyfin.sdk.model.serializer.toUUID
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import timber.log.Timber
import java.util.UUID
import kotlin.time.Duration
@ -219,7 +219,7 @@ fun SeriesOverview(
watched = ep.data.userData?.played ?: false,
favorite = ep.data.userData?.isFavorite ?: false,
seriesId = series.id,
sourceId = chosenStreams?.sourceId,
sourceId = chosenStreams?.source?.id?.toUUIDOrNull(),
actions =
MoreDialogActions(
navigateTo = viewModel::navigateTo,
@ -257,25 +257,26 @@ fun SeriesOverview(
moreDialog = null
},
onChooseTracks = { type ->
chooseSource(
ep.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
ep,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
viewModel.streamChoiceService
.chooseSource(
ep.data,
chosenStreams?.itemPlayback,
)?.let { source ->
chooseVersion =
chooseStream(
context = context,
streams = source.mediaStreams.orEmpty(),
type = type,
onClick = { trackIndex ->
viewModel.saveTrackSelection(
ep,
chosenStreams?.itemPlayback,
trackIndex,
type,
)
},
)
}
},
),
)

View file

@ -18,8 +18,10 @@ import com.github.damontecres.wholphin.services.ExtrasService
import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.PeopleFavorites
import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.ui.detail.ItemViewModel
import com.github.damontecres.wholphin.ui.equalsNotNull
@ -70,6 +72,8 @@ class SeriesViewModel
private val peopleFavorites: PeopleFavorites,
private val trailerService: TrailerService,
private val extrasService: ExtrasService,
val streamChoiceService: StreamChoiceService,
private val userPreferencesService: UserPreferencesService,
) : ItemViewModel(api) {
private lateinit var seriesId: UUID
private lateinit var prefs: UserPreferences
@ -372,7 +376,12 @@ class SeriesViewModel
chosenStreamsJob?.cancel()
chosenStreamsJob =
viewModelScope.launchIO {
val result = itemPlaybackRepository.getSelectedTracks(itemId, item)
val result =
itemPlaybackRepository.getSelectedTracks(
itemId,
item,
userPreferencesService.getCurrent(),
)
withContext(Dispatchers.Main) {
chosenStreams.value = result
}
@ -384,10 +393,12 @@ class SeriesViewModel
sourceId: UUID,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result = itemPlaybackRepository.savePlayVersion(item.id, sourceId)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen
@ -402,6 +413,8 @@ class SeriesViewModel
type: MediaStreamType,
) {
viewModelScope.launchIO {
val prefs = userPreferencesService.getCurrent()
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
val result =
itemPlaybackRepository.saveTrackSelection(
item = item,
@ -411,7 +424,7 @@ class SeriesViewModel
)
val chosen =
result?.let {
itemPlaybackRepository.getChosenItemFromPlayback(item, result)
itemPlaybackRepository.getChosenItemFromPlayback(item, result, plc, prefs)
}
withContext(Dispatchers.Main) {
chosenStreams.value = chosen

View file

@ -29,8 +29,6 @@ import com.github.damontecres.wholphin.data.model.Chapter
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.Playlist
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.data.model.chooseStream
import com.github.damontecres.wholphin.preferences.AppPreference
import com.github.damontecres.wholphin.preferences.PlayerBackend
import com.github.damontecres.wholphin.preferences.ShowNextUpWhen
@ -43,6 +41,7 @@ import com.github.damontecres.wholphin.services.PlayerFactory
import com.github.damontecres.wholphin.services.PlaylistCreationResult
import com.github.damontecres.wholphin.services.PlaylistCreator
import com.github.damontecres.wholphin.services.RefreshRateService
import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.onMain
@ -123,6 +122,7 @@ class PlaybackViewModel
private val deviceInfo: DeviceInfo,
private val deviceProfileService: DeviceProfileService,
private val refreshRateService: RefreshRateService,
val streamChoiceService: StreamChoiceService,
) : ViewModel(),
Player.Listener,
AnalyticsListener {
@ -338,7 +338,8 @@ class PlaybackViewModel
}
}
}
val mediaSource = chooseSource(base, playbackConfig)
val mediaSource = streamChoiceService.chooseSource(base, playbackConfig)
val plc = streamChoiceService.getPlaybackLanguageChoice(base)
if (mediaSource == null) {
showToast(
@ -362,12 +363,26 @@ class PlaybackViewModel
.orEmpty()
val audioIndex =
chooseStream(base, playbackConfig, MediaStreamType.AUDIO, preferences)
?.index
streamChoiceService
.chooseStream(
mediaSource,
base.seriesId,
playbackConfig,
plc,
MediaStreamType.AUDIO,
preferences,
)?.index
val subtitleIndex =
chooseStream(base, playbackConfig, MediaStreamType.SUBTITLE, preferences)
?.index
streamChoiceService
.chooseStream(
mediaSource,
base.seriesId,
playbackConfig,
plc,
MediaStreamType.SUBTITLE,
preferences,
)?.index
Timber.d("Selected mediaSource=${mediaSource.id}, audioIndex=$audioIndex, subtitleIndex=$subtitleIndex")
@ -563,7 +578,7 @@ class PlaybackViewModel
else -> throw Exception("No supported playback method")
}
Timber.v("Playback decision: $transcodeType")
Timber.i("Playback decision for $itemId: $transcodeType")
val externalSubtitleCount = source.externalSubtitlesCount
@ -605,21 +620,6 @@ class PlaybackViewModel
liveStreamId = source.liveStreamId,
mediaSourceInfo = source,
)
val itemPlayback =
currentItemPlayback.copy(
sourceId = source.id?.toUUIDOrNull(),
audioIndex = audioIndex ?: TrackIndex.UNSPECIFIED,
subtitleIndex = subtitleIndex ?: TrackIndex.DISABLED,
)
if (userInitiated) {
viewModelScope.launchIO {
Timber.v("Saving user initiated item playback: %s", itemPlayback)
val updated = itemPlaybackRepository.saveItemPlayback(itemPlayback)
withContext(Dispatchers.Main) {
this@PlaybackViewModel.currentItemPlayback.value = updated
}
}
}
if (preferences.appPreferences.playbackPreferences.refreshRateSwitching) {
source.mediaStreams?.firstOrNull { it.type == MediaStreamType.VIDEO }?.let {
@ -638,14 +638,13 @@ class PlaybackViewModel
api = api,
player = player,
playback = playback,
itemPlayback = itemPlayback,
itemPlayback = currentItemPlayback,
)
player.addListener(activityListener)
this@PlaybackViewModel.activityListener = activityListener
loading.value = LoadingState.Success
this@PlaybackViewModel.currentPlayback.update { playback }
this@PlaybackViewModel.currentItemPlayback.value = itemPlayback
player.setMediaItem(
mediaItem,
positionMs,
@ -679,23 +678,41 @@ class PlaybackViewModel
fun changeAudioStream(index: Int) {
viewModelScope.launchIO {
Timber.d("Changing audio track to %s", index)
val itemPlayback =
itemPlaybackRepository.saveTrackSelection(
item = item,
itemPlayback = currentItemPlayback.value!!,
trackIndex = index,
type = MediaStreamType.AUDIO,
)
this@PlaybackViewModel.currentItemPlayback.setValueOnMain(itemPlayback)
changeStreams(
item,
currentItemPlayback.value!!,
itemPlayback,
index,
currentItemPlayback.value?.subtitleIndex,
itemPlayback.subtitleIndex,
onMain { player.currentPosition },
true,
)
}
}
fun changeSubtitleStream(index: Int?): Job =
fun changeSubtitleStream(index: Int): Job =
viewModelScope.launchIO {
Timber.d("Changing subtitle track to %s", index)
val itemPlayback =
itemPlaybackRepository.saveTrackSelection(
item = item,
itemPlayback = currentItemPlayback.value!!,
trackIndex = index,
type = MediaStreamType.SUBTITLE,
)
this@PlaybackViewModel.currentItemPlayback.setValueOnMain(itemPlayback)
changeStreams(
item,
currentItemPlayback.value!!,
currentItemPlayback.value?.audioIndex,
itemPlayback,
itemPlayback.audioIndex,
index,
onMain { player.currentPosition },
true,

View file

@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.data.model.chooseSource
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.setValueOnMain
@ -101,7 +100,7 @@ fun PlaybackViewModel.downloadAndSwitchSubtitles(
api.userLibraryApi.getItem(itemId = it.itemId).content,
api,
)
val mediaSource = chooseSource(item.data, it)
val mediaSource = streamChoiceService.chooseSource(item.data, it)
if (mediaSource == null) {
// This shouldn't happen, but just in case
showToast(