mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-31 08:39:24 +00:00
feat(details): support behaviorHints.defaultVideoId for series initial play
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dfc8accab1
commit
36f50fa4a5
6 changed files with 93 additions and 1 deletions
|
|
@ -30,6 +30,7 @@ data class MetaDetails(
|
|||
val language: String? = null,
|
||||
val website: String? = null,
|
||||
val hasScheduledVideos: Boolean = false,
|
||||
val defaultVideoId: String? = null,
|
||||
val moreLikeThis: List<MetaPreview> = emptyList(),
|
||||
val moreLikeThisSource: MoreLikeThisSource? = null,
|
||||
val collectionName: String? = null,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ internal object MetaDetailsParser {
|
|||
language = meta.string("language"),
|
||||
website = meta.string("website"),
|
||||
hasScheduledVideos = meta.behaviorHints().boolean("hasScheduledVideos") == true,
|
||||
defaultVideoId = meta.behaviorHints().string("defaultVideoId"),
|
||||
trailers = meta.trailers(),
|
||||
links = links,
|
||||
videos = meta.videos(),
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ internal fun MetaDetails.seriesPrimaryAction(
|
|||
todayIsoDate = todayIsoDate,
|
||||
preferFurthestEpisode = preferFurthestEpisode,
|
||||
showUnairedNextUp = showUnairedNextUp,
|
||||
defaultVideoId = defaultVideoId,
|
||||
)?.toLegacySeriesPrimaryAction()
|
||||
|
||||
internal fun MetaVideo.playLabel(): String =
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ fun decideSeriesPrimaryAction(
|
|||
todayIsoDate: String,
|
||||
preferFurthestEpisode: Boolean = true,
|
||||
showUnairedNextUp: Boolean = false,
|
||||
defaultVideoId: String? = null,
|
||||
): WatchingSeriesPrimaryAction? {
|
||||
val resumeRecord = resumeProgressForSeries(
|
||||
content = content,
|
||||
|
|
@ -127,7 +128,9 @@ fun decideSeriesPrimaryAction(
|
|||
available = episode.available,
|
||||
)
|
||||
}
|
||||
released.firstOrNull { normalizeSeasonNumber(it.seasonNumber) > 0 } ?: released.firstOrNull()
|
||||
defaultVideoId?.let { videoId -> released.firstOrNull { it.videoId == videoId } }
|
||||
?: released.firstOrNull { normalizeSeasonNumber(it.seasonNumber) > 0 }
|
||||
?: released.firstOrNull()
|
||||
}
|
||||
|
||||
return nextEpisode?.let { episode ->
|
||||
|
|
|
|||
|
|
@ -65,4 +65,24 @@ class MetaDetailsParserTest {
|
|||
assertFalse(result.videos[0].available)
|
||||
assertTrue(result.videos[1].available)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse reads defaultVideoId from behavior hints`() {
|
||||
val result = MetaDetailsParser.parse(
|
||||
"""
|
||||
{
|
||||
"meta": {
|
||||
"id": "show",
|
||||
"type": "series",
|
||||
"name": "Show",
|
||||
"behaviorHints": {
|
||||
"defaultVideoId": "show:1:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
assertEquals("show:1:2", result.defaultVideoId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,72 @@ class SeriesPlaybackResolverTest {
|
|||
assertEquals(15, action.episodeNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun seriesPrimaryAction_prefers_default_video_id_when_nothing_watched() {
|
||||
val meta = MetaDetails(
|
||||
id = "show",
|
||||
type = "series",
|
||||
name = "Show",
|
||||
defaultVideoId = "show:1:2",
|
||||
videos = listOf(
|
||||
MetaVideo(id = "show:1:1", title = "Episode 1", season = 1, episode = 1, released = "2026-03-01"),
|
||||
MetaVideo(id = "show:1:2", title = "Episode 2", season = 1, episode = 2, released = "2026-03-08"),
|
||||
MetaVideo(id = "show:1:3", title = "Episode 3", season = 1, episode = 3, released = "2026-03-15"),
|
||||
),
|
||||
)
|
||||
|
||||
val action = meta.seriesPrimaryAction(
|
||||
entries = emptyList(),
|
||||
watchedItems = emptyList(),
|
||||
todayIsoDate = "2026-03-30",
|
||||
)
|
||||
|
||||
assertNotNull(action)
|
||||
assertEquals("Play S1E2", action.label)
|
||||
assertEquals("show:1:2", action.videoId)
|
||||
assertEquals(1, action.seasonNumber)
|
||||
assertEquals(2, action.episodeNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun seriesPrimaryAction_ignores_default_video_id_when_progress_exists() {
|
||||
val meta = MetaDetails(
|
||||
id = "show",
|
||||
type = "series",
|
||||
name = "Show",
|
||||
defaultVideoId = "show:1:2",
|
||||
videos = listOf(
|
||||
MetaVideo(id = "show:1:1", title = "Episode 1", season = 1, episode = 1, released = "2026-03-01"),
|
||||
MetaVideo(id = "show:1:2", title = "Episode 2", season = 1, episode = 2, released = "2026-03-08"),
|
||||
MetaVideo(id = "show:1:3", title = "Episode 3", season = 1, episode = 3, released = "2026-03-15"),
|
||||
),
|
||||
)
|
||||
|
||||
val action = meta.seriesPrimaryAction(
|
||||
entries = listOf(
|
||||
WatchProgressEntry(
|
||||
contentType = "series",
|
||||
parentMetaId = "show",
|
||||
parentMetaType = "series",
|
||||
videoId = "show:1:1",
|
||||
title = "Show",
|
||||
seasonNumber = 1,
|
||||
episodeNumber = 1,
|
||||
lastPositionMs = 1_000L,
|
||||
durationMs = 10_000L,
|
||||
lastUpdatedEpochMs = 100L,
|
||||
isCompleted = false,
|
||||
),
|
||||
),
|
||||
watchedItems = emptyList(),
|
||||
todayIsoDate = "2026-03-30",
|
||||
)
|
||||
|
||||
assertNotNull(action)
|
||||
assertEquals("Resume S1E1", action.label)
|
||||
assertEquals("show:1:1", action.videoId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextReleasedEpisodeAfter_global_index_fallback_ignores_specials() {
|
||||
val meta = MetaDetails(
|
||||
|
|
|
|||
Loading…
Reference in a new issue