Reset subtitle delay when the new track has none saved #73
2 changed files with 80 additions and 1 deletions
|
|
@ -1669,6 +1669,9 @@ class PlaybackViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the enabled subtitle track's saved delay, or zero if no delay is saved.
|
||||||
|
*/
|
||||||
suspend fun loadSubtitleDelay() {
|
suspend fun loadSubtitleDelay() {
|
||||||
state.value.currentPlayback?.let {
|
state.value.currentPlayback?.let {
|
||||||
if (it.subtitleIndexEnabled) {
|
if (it.subtitleIndexEnabled) {
|
||||||
|
|
@ -1681,7 +1684,9 @@ class PlaybackViewModel
|
||||||
it.subtitleIndex,
|
it.subtitleIndex,
|
||||||
it.item.id,
|
it.item.id,
|
||||||
)
|
)
|
||||||
updateCurrentPlayback { it?.copy(subtitleDelay = result.delayMs.milliseconds) }
|
}
|
||||||
|
updateCurrentPlayback {
|
||||||
|
it?.copy(subtitleDelay = result?.delayMs?.milliseconds ?: Duration.ZERO)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@ package com.github.damontecres.wholphin.ui.playback
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
import androidx.media3.common.TrackSelectionParameters
|
||||||
import com.github.damontecres.wholphin.data.ItemPlaybackDao
|
import com.github.damontecres.wholphin.data.ItemPlaybackDao
|
||||||
import com.github.damontecres.wholphin.data.ItemPlaybackRepository
|
import com.github.damontecres.wholphin.data.ItemPlaybackRepository
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.ItemTrackModification
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||||
import com.github.damontecres.wholphin.data.model.Playlist
|
import com.github.damontecres.wholphin.data.model.Playlist
|
||||||
import com.github.damontecres.wholphin.data.model.PlaylistItem
|
import com.github.damontecres.wholphin.data.model.PlaylistItem
|
||||||
import com.github.damontecres.wholphin.data.model.SeriesTrackChoiceType
|
import com.github.damontecres.wholphin.data.model.SeriesTrackChoiceType
|
||||||
import com.github.damontecres.wholphin.data.model.ServerUserConfig
|
import com.github.damontecres.wholphin.data.model.ServerUserConfig
|
||||||
|
import com.github.damontecres.wholphin.data.model.TrackIndex
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
|
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.ShowNextUpWhen
|
import com.github.damontecres.wholphin.preferences.ShowNextUpWhen
|
||||||
|
|
@ -82,6 +85,7 @@ import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
@ -292,6 +296,76 @@ class PlaybackViewModelTests {
|
||||||
return viewModel
|
return viewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Subtitle track switches load saved delays or zero and ignore disabled subtitles`() =
|
||||||
|
runTest(testDispatcher) {
|
||||||
|
setupPreferences { cinemaMode = false }
|
||||||
|
val tracks =
|
||||||
|
TestTracks
|
||||||
|
.Builder()
|
||||||
|
.addVideo()
|
||||||
|
.addAudio()
|
||||||
|
.addSubtitle(2)
|
||||||
|
val source = tracks.buildMediaSourceInfo()
|
||||||
|
val subtitles = source.mediaStreams!!.filter { it.type == MediaStreamType.SUBTITLE }
|
||||||
|
val trackA = subtitles[0].index
|
||||||
|
val trackB = subtitles[1].index
|
||||||
|
coEvery { mockUserLibraryApi.getItem(movie.id) } returns successResponse(movie)
|
||||||
|
coEvery { mockMediaInfoApi.getPostedPlaybackInfo(any(), any()) } returns
|
||||||
|
successResponse(
|
||||||
|
PlaybackInfoResponse(
|
||||||
|
mediaSources = listOf(source),
|
||||||
|
playSessionId = playSessionId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
coEvery { mockStreamChoiceService.chooseSource(any(), any()) } returns source
|
||||||
|
coEvery {
|
||||||
|
mockStreamChoiceService.chooseAudioStream(any(), any(), any(), any(), any())
|
||||||
|
} returns
|
||||||
|
StreamChoiceResult(
|
||||||
|
source.mediaStreams!!.first { it.type == MediaStreamType.AUDIO },
|
||||||
|
StreamChoiceReason.Unknown,
|
||||||
|
)
|
||||||
|
coEvery {
|
||||||
|
mockStreamChoiceService.chooseSubtitleStream(any(), any(), any(), any(), any(), any())
|
||||||
|
} returns StreamChoiceResult(subtitles[0], StreamChoiceReason.Unknown)
|
||||||
|
coEvery {
|
||||||
|
mockStreamChoiceService.resolveSubtitleIndex(any(), any(), any(), any(), any())
|
||||||
|
} answers { arg<Int>(3) }
|
||||||
|
every { mockPlayer.currentTracks } returns tracks.buildForExoPlayer().toTracks()
|
||||||
|
every { mockPlayer.trackSelectionParameters } returns TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT
|
||||||
|
coEvery { mockItemPlaybackRepository.getTrackModifications(movie.id, trackA) } returns
|
||||||
|
ItemTrackModification(user.rowId, movie.id, trackA, 2000L)
|
||||||
|
|
||||||
|
val cases =
|
||||||
|
listOf(
|
||||||
|
Triple(trackB, null, Duration.ZERO),
|
||||||
|
Triple(trackB, -750L, (-750).milliseconds),
|
||||||
|
Triple(TrackIndex.DISABLED, null, 2.seconds),
|
||||||
|
)
|
||||||
|
cases.forEach { (targetIndex, savedDelayMs, expectedDelay) ->
|
||||||
|
coEvery { mockItemPlaybackRepository.getTrackModifications(movie.id, trackB) } returns
|
||||||
|
savedDelayMs?.let {
|
||||||
|
ItemTrackModification(user.rowId, movie.id, trackB, it)
|
||||||
|
}
|
||||||
|
val viewModel = createViewModel(Destination.Playback(movie.id, 0L))
|
||||||
|
val initialPlayback = viewModel.state.value.currentPlayback
|
||||||
|
Assert.assertEquals(trackA, initialPlayback?.subtitleIndex)
|
||||||
|
Assert.assertEquals(2.seconds, initialPlayback?.subtitleDelay)
|
||||||
|
|
||||||
|
viewModel.changeSubtitleStream(targetIndex).join()
|
||||||
|
testScheduler.advanceUntilIdle()
|
||||||
|
|
||||||
|
val playback = viewModel.state.value.currentPlayback
|
||||||
|
Assert.assertEquals(targetIndex, playback?.subtitleIndex)
|
||||||
|
Assert.assertEquals(expectedDelay, playback?.subtitleDelay)
|
||||||
|
}
|
||||||
|
coVerify(exactly = 0) {
|
||||||
|
mockItemPlaybackRepository.getTrackModifications(movie.id, TrackIndex.DISABLED)
|
||||||
|
}
|
||||||
|
verify(exactly = cases.size) { mockPlayer.setMediaItem(any(), any<Long>()) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Direct play includes the opened live stream ID`() =
|
fun `Direct play includes the opened live stream ID`() =
|
||||||
runTest(testDispatcher) {
|
runTest(testDispatcher) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue