From 0a4ab7a5476830e2bb9143aba8621e477a2ef4e1 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Tue, 21 Jul 2026 17:20:59 -0400 Subject: [PATCH] Several UI fixes (#1730) ## Description - Fixes not scrolling to the last season on series overview if there is a Specials season - Apply the "bouncing" fix from #1543 to all applicable card types - Fix card images not automatically reloading when changing image type on grid pages - Get rid of the sticky headers which overlap items on view options dialogs and use a fixed header as elsewhere in the app ### Related issues Related to #1708 which didn't introduce the bug, but made it much more likely to occur ### Testing Emulator & unit tests ## Screenshots N/A, the sticky header UI changes are very minor ## AI or LLM usage None --- .../wholphin/ui/cards/BannerCard.kt | 13 +--- .../wholphin/ui/cards/DiscoverItemCard.kt | 12 +--- .../wholphin/ui/cards/EpisodeCard.kt | 12 +--- .../damontecres/wholphin/ui/cards/GridCard.kt | 12 +--- .../wholphin/ui/cards/ItemCardImage.kt | 2 +- .../wholphin/ui/cards/PersonCard.kt | 24 +------ .../wholphin/ui/cards/SlidingCardText.kt | 46 +++++++++++++ .../wholphin/ui/cards/ViewMoreCard.kt | 13 +--- .../ui/components/ViewOptionsDialog.kt | 69 +++++++++++-------- .../detail/livetv/LiveTvViewOptionsDialog.kt | 59 +++++++++------- .../ui/detail/series/SeriesViewModel.kt | 2 +- .../test/TestFindIndexByNumberOrId.kt | 25 +++++++ 12 files changed, 159 insertions(+), 130 deletions(-) create mode 100644 app/src/main/java/com/github/damontecres/wholphin/ui/cards/SlidingCardText.kt diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt index 8d00edfd..d4d2ac50 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt @@ -1,6 +1,5 @@ package com.github.damontecres.wholphin.ui.cards -import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState @@ -219,13 +218,11 @@ fun BannerCardWithTitle( useSeriesForPrimary: Boolean = item?.useSeriesForPrimary ?: true, ) { val focused by interactionSource.collectIsFocusedAsState() - val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp) - val spaceBelow by animateDpAsState(if (focused) 0.dp else 8.dp) val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource) val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN) val width = cardHeight * aspectRationToUse Column( - verticalArrangement = Arrangement.spacedBy(spaceBetween), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier.width(width), ) { BannerCard( @@ -245,13 +242,7 @@ fun BannerCardWithTitle( imageContentScale = imageContentScale, useSeriesForPrimary = useSeriesForPrimary, ) - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = - Modifier - .padding(bottom = spaceBelow) - .fillMaxWidth(), - ) { + SlidingCardText(focused) { Text( text = title ?: "", style = MaterialTheme.typography.bodyLarge, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/DiscoverItemCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/DiscoverItemCard.kt index a6476bd5..20cab060 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/DiscoverItemCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/DiscoverItemCard.kt @@ -59,8 +59,6 @@ fun DiscoverItemCard( width: Dp = Cards.height2x3 * AspectRatios.TALL, ) { val focused by interactionSource.collectIsFocusedAsState() - val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp) - val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp) var focusedAfterDelay by remember { mutableStateOf(false) } val hideOverlayDelay = 500L @@ -78,7 +76,7 @@ fun DiscoverItemCard( } Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(spaceBetween), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier.size(width, Dp.Unspecified), ) { Card( @@ -180,13 +178,7 @@ fun DiscoverItemCard( } } } - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = - Modifier - .padding(bottom = spaceBelow) - .fillMaxWidth(), - ) { + SlidingCardText(focused) { Text( text = item?.title ?: "", maxLines = 1, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt index 78080c3a..3cad76ab 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt @@ -51,8 +51,6 @@ fun EpisodeCard( ) { val dto = item?.data val focused by interactionSource.collectIsFocusedAsState() - val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp) - val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp) var focusedAfterDelay by remember { mutableStateOf(false) } val hideOverlayDelay = 500L @@ -74,7 +72,7 @@ fun EpisodeCard( val density = LocalDensity.current val imageWidthPx = remember(imageWidth) { with(density) { imageWidth.roundToPx() } } Column( - verticalArrangement = Arrangement.spacedBy(spaceBetween), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier.size(width, height), ) { Card( @@ -126,13 +124,7 @@ fun EpisodeCard( } } } - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = - Modifier - .padding(bottom = spaceBelow) - .fillMaxWidth(), - ) { + SlidingCardText(focused) { Text( text = dto?.seriesName ?: "", maxLines = 1, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/GridCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/GridCard.kt index 5cf16a61..f49f2d0b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/GridCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/GridCard.kt @@ -52,8 +52,6 @@ fun GridCard( ) { val dto = item?.data val focused by interactionSource.collectIsFocusedAsState() - val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp) - val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp) var focusedAfterDelay by remember { mutableStateOf(false) } val hideOverlayDelay = 500L @@ -71,7 +69,7 @@ fun GridCard( } Column( - verticalArrangement = Arrangement.spacedBy(spaceBetween), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier, ) { Card( @@ -107,13 +105,7 @@ fun GridCard( ) } AnimatedVisibility(showTitle) { - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = - Modifier - .padding(bottom = spaceBelow) - .fillMaxWidth(), - ) { + SlidingCardText(focused) { Text( text = item?.title ?: "", maxLines = 1, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ItemCardImage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ItemCardImage.kt index 63f6bdd8..41a3aca4 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ItemCardImage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ItemCardImage.kt @@ -67,7 +67,7 @@ fun ItemCardImage( ) { val imageUrlService = LocalImageUrlService.current val imageUrl = - remember(item) { + remember(item, imageType, fillWidth, fillHeight) { item?.let { imageUrlService.getItemImageUrl( item, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt index 4e146134..3154d257 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt @@ -1,6 +1,5 @@ package com.github.damontecres.wholphin.ui.cards -import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource @@ -11,7 +10,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape @@ -26,11 +24,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.layout import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.tv.material3.Border @@ -99,10 +95,6 @@ fun PersonCard( focusedAfterDelay = false } - // Do not use `by` here, this way we are Defer reads and recompositions to only when modifier calculates - val spaceBetweenState = animateDpAsState(if (focused) 12.dp else 4.dp, label = "spaceBetween") - val spaceBelowState = animateDpAsState(if (focused) 4.dp else 12.dp, label = "spaceBelow") - Column( verticalArrangement = Arrangement.spacedBy(4.dp), // Fixed base spacing modifier = modifier, @@ -170,21 +162,7 @@ fun PersonCard( .clip(CircleShape), ) } - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = - Modifier - // Optimization: move animation reads to layout/draw phase - .offset { - IntOffset(0, (spaceBetweenState.value - 4.dp).roundToPx()) - }.layout { measurable, constraints -> - val paddingPx = spaceBelowState.value.roundToPx() - val placeable = measurable.measure(constraints) - layout(placeable.width, placeable.height + paddingPx) { - placeable.placeRelative(0, 0) - } - }.fillMaxWidth(), - ) { + SlidingCardText(focused) { Text( text = name ?: "", maxLines = 1, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SlidingCardText.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SlidingCardText.kt new file mode 100644 index 00000000..90d99281 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SlidingCardText.kt @@ -0,0 +1,46 @@ +package com.github.damontecres.wholphin.ui.cards + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +/** + * Slides the [content] up/down as the card is [focused] or not + * + * This is used for the card's title, moving it down when focused as the card itself scales up + */ +@Composable +fun SlidingCardText( + focused: Boolean, + modifier: Modifier = Modifier, + spaceBetweenFocused: Dp = 4.dp, + spaceBetweenUnfocused: Dp = 12.dp, + content: @Composable ColumnScope.() -> Unit, +) { + val spaceBetween = + animateDpAsState( + if (focused) spaceBetweenFocused else spaceBetweenUnfocused, + label = "spaceBetween", + ) + Column( + verticalArrangement = Arrangement.spacedBy(0.dp), + modifier = + modifier + .layout { measurable, constraints -> + val topPaddingPx = (spaceBetweenUnfocused - spaceBetween.value).roundToPx() + val bottomPaddingPx = spaceBetween.value.roundToPx() + val placeable = measurable.measure(constraints) + layout(placeable.width, placeable.height + bottomPaddingPx + topPaddingPx) { + placeable.placeRelative(0, topPaddingPx) + } + }.fillMaxWidth(), + content = content, + ) +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ViewMoreCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ViewMoreCard.kt index 32265e70..fa7df8d6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ViewMoreCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ViewMoreCard.kt @@ -53,8 +53,6 @@ fun ViewMoreCard( showTitle: Boolean = true, ) { val focused by interactionSource.collectIsFocusedAsState() - val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp) - val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp) var focusedAfterDelay by remember { mutableStateOf(false) } val hideOverlayDelay = 500L @@ -79,7 +77,7 @@ fun ViewMoreCard( size.height.takeIf { it.isSpecified } ?: (size.height * (1f / aspectRatio.ratio)) } Column( - verticalArrangement = Arrangement.spacedBy(spaceBetween), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier, ) { Card( @@ -108,14 +106,7 @@ fun ViewMoreCard( } } if (showTitle) { - Column( - verticalArrangement = Arrangement.spacedBy(0.dp), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = - Modifier - .width(width) - .padding(bottom = spaceBelow), - ) { + SlidingCardText(focused) { Text( text = stringResource(R.string.view_more), maxLines = 1, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/ViewOptionsDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/ViewOptionsDialog.kt index 4f4d7588..c9c8f98b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/ViewOptionsDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/ViewOptionsDialog.kt @@ -4,8 +4,11 @@ import android.view.Gravity import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -19,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties @@ -74,45 +78,52 @@ fun ViewOptionsDialog( ViewOptionsType.DENSE_LIST -> ViewOptions.LIST_OPTIONS } } - LazyColumn( - state = columnState, - contentPadding = PaddingValues(16.dp), + Column( verticalArrangement = Arrangement.spacedBy(0.dp), modifier = Modifier .width(256.dp) .heightIn(max = 380.dp) - .focusRequester(focusRequester) .background( MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp), shape = RoundedCornerShape(8.dp), ), ) { - stickyHeader { - Text( - text = stringResource(R.string.view_options), - style = MaterialTheme.typography.titleMedium, - ) - } - items(options, key = { it.title }) { pref -> - pref as AppPreference - val interactionSource = remember { MutableInteractionSource() } - val value = pref.getter.invoke(viewOptions) - ComposablePreference( - preference = pref, - value = value, - onNavigate = {}, - onValueChange = { newValue -> - onViewOptionsChange.invoke(pref.setter(viewOptions, newValue)) - }, - interactionSource = interactionSource, - modifier = Modifier, - onClickPreference = { pref -> - if (pref == ViewOptions.ViewOptionsReset) { - onViewOptionsChange.invoke(defaultViewOptions) - } - }, - ) + Text( + text = stringResource(R.string.view_options), + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center, + modifier = + Modifier + .padding(vertical = 8.dp) + .fillMaxWidth(), + ) + LazyColumn( + state = columnState, + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(0.dp), + modifier = Modifier.focusRequester(focusRequester), + ) { + items(options, key = { it.title }) { pref -> + pref as AppPreference + val interactionSource = remember { MutableInteractionSource() } + val value = pref.getter.invoke(viewOptions) + ComposablePreference( + preference = pref, + value = value, + onNavigate = {}, + onValueChange = { newValue -> + onViewOptionsChange.invoke(pref.setter(viewOptions, newValue)) + }, + interactionSource = interactionSource, + modifier = Modifier, + onClickPreference = { pref -> + if (pref == ViewOptions.ViewOptionsReset) { + onViewOptionsChange.invoke(defaultViewOptions) + } + }, + ) + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewOptionsDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewOptionsDialog.kt index a0971f8e..320ef649 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewOptionsDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewOptionsDialog.kt @@ -4,8 +4,11 @@ import android.view.Gravity import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -19,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties @@ -54,40 +58,47 @@ fun LiveTvViewOptionsDialog( window.setGravity(Gravity.END) window.setDimAmount(0f) } - LazyColumn( - state = columnState, - contentPadding = PaddingValues(16.dp), + Column( verticalArrangement = Arrangement.spacedBy(0.dp), modifier = Modifier .width(256.dp) .heightIn(max = 380.dp) - .focusRequester(focusRequester) .background( MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp), shape = RoundedCornerShape(8.dp), ), ) { - stickyHeader { - Text( - text = stringResource(R.string.view_options), - style = MaterialTheme.typography.titleMedium, - ) - } - items(liveTvPreferences) { pref -> - pref as AppPreference - val interactionSource = remember { MutableInteractionSource() } - val value = pref.getter.invoke(preferences) - ComposablePreference( - preference = pref, - value = value, - onNavigate = {}, - onValueChange = { newValue -> - onViewOptionsChange.invoke(pref.setter(preferences, newValue)) - }, - interactionSource = interactionSource, - modifier = Modifier, - ) + Text( + text = stringResource(R.string.view_options), + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center, + modifier = + Modifier + .padding(vertical = 8.dp) + .fillMaxWidth(), + ) + LazyColumn( + state = columnState, + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(0.dp), + modifier = Modifier.focusRequester(focusRequester), + ) { + items(liveTvPreferences) { pref -> + pref as AppPreference + val interactionSource = remember { MutableInteractionSource() } + val value = pref.getter.invoke(preferences) + ComposablePreference( + preference = pref, + value = value, + onNavigate = {}, + onValueChange = { newValue -> + onViewOptionsChange.invoke(pref.setter(preferences, newValue)) + }, + interactionSource = interactionSource, + modifier = Modifier, + ) + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 033ae5f6..202c6a06 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -746,7 +746,7 @@ suspend fun findIndexByNumberOrId( // Start searching from the target number and choose direction from there val num = list.getBlocking(listIndex)?.indexNumber if (num.lt(targetNum)) { - for (i in listIndex + 1 until list.lastIndex) { + for (i in listIndex + 1 until list.size) { val item = list.getBlocking(i) if (checkNumberOrId(targetNum, targetId, item?.indexNumber, item?.id)) { return i diff --git a/app/src/test/java/com/github/damontecres/wholphin/test/TestFindIndexByNumberOrId.kt b/app/src/test/java/com/github/damontecres/wholphin/test/TestFindIndexByNumberOrId.kt index f50ed223..6fe8bd3a 100644 --- a/app/src/test/java/com/github/damontecres/wholphin/test/TestFindIndexByNumberOrId.kt +++ b/app/src/test/java/com/github/damontecres/wholphin/test/TestFindIndexByNumberOrId.kt @@ -28,6 +28,7 @@ class TestFindIndexByNumberOrId { val epS02E03 = create(3, 2) val epS02E04 = create(4, 2) val epS02E05 = create(5, 2) + val epS02E06 = create(6, 2) val epS00E08 = create(8, 0) val epS00E02 = create(2, 0) @@ -70,6 +71,30 @@ class TestFindIndexByNumberOrId { } } + @Test + fun `Test last index with zero start`() = + runTest { + val episodes = + BlockingList.of( + listOf( + create(0, 2), + epS02E01, + epS02E02, + epS02E03, + epS02E04, + epS02E05, + epS02E06, + ), + ) + findIndexByNumberOrId( + targetNum = 6, + targetId = epS02E06.id, + list = BlockingList.of(episodes), + ).let { index -> + Assert.assertEquals(6, index) + } + } + @Test fun `Test missing episodes`() = runTest {