Improve playback, preroll and TV experience
This commit is contained in:
@@ -97,5 +97,25 @@ class CastMetadataTest {
|
||||
assertEquals("portrait-tag", item.cast.single().primaryImageTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decodes person biography and life dates`() {
|
||||
val person = Json { ignoreUnknownKeys = true }.decodeFromString<BaseItem>(
|
||||
"""
|
||||
{
|
||||
"Id":"person-1",
|
||||
"Name":"Alex Actor",
|
||||
"Type":"Person",
|
||||
"Overview":"A stage and screen performer.",
|
||||
"PremiereDate":"1940-01-02T00:00:00.0000000Z",
|
||||
"EndDate":"2020-03-04T00:00:00.0000000Z"
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
assertEquals("A stage and screen performer.", person.overview)
|
||||
assertEquals("1940-01-02T00:00:00.0000000Z", person.premiereDate)
|
||||
assertEquals("2020-03-04T00:00:00.0000000Z", person.endDate)
|
||||
}
|
||||
|
||||
private fun actor(id: String) = EmbyPerson(id = id, name = id, type = "Actor")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.ponzischeme89.memby.data
|
||||
import com.ponzischeme89.memby.data.playback.DevicePlaybackCapabilities
|
||||
import com.ponzischeme89.memby.data.playback.VideoDecoderCapabilities
|
||||
import com.ponzischeme89.memby.data.playback.gatewayCapabilityTokens
|
||||
import com.ponzischeme89.memby.data.model.DeviceProfile
|
||||
import com.ponzischeme89.memby.data.model.h264TranscodeFallback
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
@@ -45,4 +48,14 @@ class DevicePlaybackCapabilitiesTest {
|
||||
assertTrue("video_h264_decode" in tokens)
|
||||
assertTrue("video_hevc_decode" !in tokens)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decoderFallbackCannotReturnHevcStreamCopy() {
|
||||
val fallback = DeviceProfile.embyAndroidTv(supportsHevc = true)
|
||||
.h264TranscodeFallback()
|
||||
|
||||
assertTrue(fallback.directPlayProfiles.isEmpty())
|
||||
assertEquals(listOf("h264"), fallback.transcodingProfiles.map { it.videoCodec })
|
||||
assertTrue(fallback.codecProfiles.none { it.codec.equals("hevc", ignoreCase = true) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ponzischeme89.memby.ui
|
||||
|
||||
import com.ponzischeme89.memby.data.launchResumePositionMs
|
||||
import com.ponzischeme89.memby.data.model.BaseItem
|
||||
import com.ponzischeme89.memby.data.model.UserItemData
|
||||
import com.ponzischeme89.memby.ui.detail.primaryActionLabel
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ContinueWatchingResumeTest {
|
||||
@Test
|
||||
fun `detail metadata keeps the continue watching playhead and resume label`() {
|
||||
val card = BaseItem(
|
||||
id = "episode",
|
||||
name = "The Episode",
|
||||
type = "Episode",
|
||||
indexNumber = 3,
|
||||
parentIndexNumber = 1,
|
||||
userData = UserItemData(playbackPositionTicks = 36_000_000L),
|
||||
)
|
||||
val detailsWithoutUserData = card.copy(
|
||||
overview = "The richer record fetched after focus.",
|
||||
userData = null,
|
||||
)
|
||||
|
||||
val focused = focusedItemWithMetadata(card, detailsWithoutUserData)
|
||||
|
||||
assertEquals(36_000_000L, focused.userData?.playbackPositionTicks)
|
||||
assertEquals("Resume S01E03", primaryActionLabel(focused))
|
||||
assertEquals("The richer record fetched after focus.", focused.overview)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pressed card resume point outranks a prefetched zero`() {
|
||||
assertEquals(
|
||||
36_000L,
|
||||
launchResumePositionMs(resolvedPositionMs = 0L, requestedPositionMs = 36_000L),
|
||||
)
|
||||
assertEquals(
|
||||
42_000L,
|
||||
launchResumePositionMs(resolvedPositionMs = 42_000L, requestedPositionMs = 0L),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,13 @@ import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
class AlertsFormatTest {
|
||||
@Test
|
||||
fun `dismissed alert hands focus to a stable neighbour`() {
|
||||
assertEquals(1, alertFocusIndexAfterRemoval(removedIndex = 1, remainingCount = 3))
|
||||
assertEquals(2, alertFocusIndexAfterRemoval(removedIndex = 3, remainingCount = 3))
|
||||
assertNull(alertFocusIndexAfterRemoval(removedIndex = 0, remainingCount = 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no alerts wears no badge`() {
|
||||
assertNull(alertBadgeLabel(0))
|
||||
|
||||
@@ -43,7 +43,11 @@ class CastPanelScreenshotTest {
|
||||
title = "The Lives of Others",
|
||||
loaded = true,
|
||||
members = listOf(
|
||||
CastMember("Ulrich Mühe", "Hauptmann Gerd Wiesler"),
|
||||
CastMember(
|
||||
"Ulrich Mühe",
|
||||
"Hauptmann Gerd Wiesler",
|
||||
deathDate = "2007-07-22T00:00:00.0000000Z",
|
||||
),
|
||||
CastMember("Martina Gedeck", "Christa-Maria Sieland"),
|
||||
CastMember("Sebastian Koch", "Georg Dreyman"),
|
||||
CastMember("Ulrich Tukur", "Oberstleutnant Anton Grubitz"),
|
||||
@@ -94,6 +98,33 @@ class CastPanelScreenshotTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `selected actor shows biography and filmography`() {
|
||||
capture(
|
||||
name = "cast-panel-person-profile",
|
||||
state = CastPanelState(
|
||||
title = "The Lives of Others",
|
||||
loaded = true,
|
||||
members = listOf(CastMember("Ulrich Mühe", id = "person-1")),
|
||||
selectedPerson = CastPersonPanel(
|
||||
id = "person-1",
|
||||
name = "Ulrich Mühe",
|
||||
role = "Hauptmann Gerd Wiesler",
|
||||
birthDate = "1953-06-20T00:00:00.0000000Z",
|
||||
deathDate = "2007-07-22T00:00:00.0000000Z",
|
||||
overview = "A celebrated German actor known for quiet, exacting performances on stage and screen. His portrayal of Gerd Wiesler earned international acclaim.",
|
||||
filmography = listOf(
|
||||
FilmographyCredit("The Lives of Others", 2006),
|
||||
FilmographyCredit("Funny Games", 1997),
|
||||
FilmographyCredit("The Castle", 1997),
|
||||
FilmographyCredit("Benny's Video", 1992),
|
||||
),
|
||||
loaded = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/** Still fetching. The panel opens instantly and says so rather than showing nothing. */
|
||||
@Test
|
||||
fun `still loading`() {
|
||||
@@ -118,6 +149,10 @@ class CastPanelScreenshotTest {
|
||||
assertEquals("C", castInitials("Cher"))
|
||||
assertEquals("AA", castInitials(" amy adams "))
|
||||
assertEquals("", castInitials(" "))
|
||||
assertEquals(
|
||||
"20 June 1953 – 22 July 2007",
|
||||
personLifeDates("1953-06-20T00:00:00Z", "2007-07-22T00:00:00Z"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun capture(name: String, state: CastPanelState, focused: Int? = null) {
|
||||
|
||||
@@ -53,4 +53,13 @@ class PlaybackRecoveryTest {
|
||||
assertEquals(3_000L, automaticRetryDelayMs(2))
|
||||
assertNull(automaticRetryDelayMs(3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prolongedMidProgrammeRebufferGetsOneCompatibilityFallback() {
|
||||
assertTrue(shouldRecoverProlongedRebuffer(true, false, false, false))
|
||||
assertFalse(shouldRecoverProlongedRebuffer(false, false, false, false))
|
||||
assertFalse(shouldRecoverProlongedRebuffer(true, true, false, false))
|
||||
assertFalse(shouldRecoverProlongedRebuffer(true, false, true, false))
|
||||
assertFalse(shouldRecoverProlongedRebuffer(true, false, false, true))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ponzischeme89.memby.ui.player
|
||||
|
||||
import androidx.media3.common.C
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
@@ -40,4 +42,11 @@ class PrerollSequenceTest {
|
||||
assertFalse(prerollCountdownAdvances(lifecycleStarted = false, playbackReady = true))
|
||||
assertFalse(prerollCountdownAdvances(lifecycleStarted = true, playbackReady = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `packaged clip duration replaces the configured fallback`() {
|
||||
assertEquals(4_133L, effectivePrerollDurationMs(4_133L, 7_000L))
|
||||
assertEquals(7_000L, effectivePrerollDurationMs(C.TIME_UNSET, 7_000L))
|
||||
assertEquals(7_000L, effectivePrerollDurationMs(0L, 7_000L))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user