mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-26 22:42:17 +00:00
fix(simkl): map anime episode coordinates
This commit is contained in:
parent
942a111eec
commit
e020b9700a
4 changed files with 100 additions and 9 deletions
|
|
@ -231,11 +231,17 @@ internal fun buildSimklScrobbleBody(
|
|||
json: Json = SimklMutationJson,
|
||||
): String {
|
||||
val media = event.media.toScrobbleMediaDto()
|
||||
val usesTvStyleAnimeCoordinates = event.media.kind == TrackingMediaKind.ANIME &&
|
||||
event.media.episode?.season != null
|
||||
val request = SimklScrobbleRequestDto(
|
||||
progress = event.progressPercent.clampAndRoundProgress(),
|
||||
movie = media.takeIf { event.media.kind == TrackingMediaKind.MOVIE },
|
||||
show = media.takeIf { event.media.kind == TrackingMediaKind.SHOW },
|
||||
anime = media.takeIf { event.media.kind == TrackingMediaKind.ANIME },
|
||||
show = media.takeIf {
|
||||
event.media.kind == TrackingMediaKind.SHOW || usesTvStyleAnimeCoordinates
|
||||
},
|
||||
anime = media.takeIf {
|
||||
event.media.kind == TrackingMediaKind.ANIME && !usesTvStyleAnimeCoordinates
|
||||
},
|
||||
episode = event.media.episode?.toEpisodeDto(
|
||||
includeSeason = true,
|
||||
includeWatchedAt = false,
|
||||
|
|
@ -316,6 +322,7 @@ private fun buildShowHistoryItem(
|
|||
includeWatchedAt = includeWatchedAt,
|
||||
episodes = flatEpisodes,
|
||||
seasons = seasons,
|
||||
useTvdbAnimeSeasons = first.media.kind == TrackingMediaKind.ANIME && seasons.isNotEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +332,7 @@ private fun TrackingMediaReference.toHistoryItemDto(
|
|||
status: String? = null,
|
||||
episodes: List<SimklEpisodeMutationDto> = emptyList(),
|
||||
seasons: List<SimklSeasonMutationDto> = emptyList(),
|
||||
useTvdbAnimeSeasons: Boolean = false,
|
||||
): SimklHistoryItemDto = SimklHistoryItemDto(
|
||||
title = title.nonBlankOrNull(),
|
||||
year = year,
|
||||
|
|
@ -333,6 +341,7 @@ private fun TrackingMediaReference.toHistoryItemDto(
|
|||
status = status,
|
||||
episodes = episodes,
|
||||
seasons = seasons,
|
||||
useTvdbAnimeSeasons = useTvdbAnimeSeasons,
|
||||
)
|
||||
|
||||
private fun TrackingMediaReference.toScrobbleMediaDto(): SimklScrobbleMediaDto =
|
||||
|
|
@ -508,6 +517,7 @@ private data class SimklHistoryItemDto(
|
|||
val status: String? = null,
|
||||
val episodes: List<SimklEpisodeMutationDto> = emptyList(),
|
||||
val seasons: List<SimklSeasonMutationDto> = emptyList(),
|
||||
@SerialName("use_tvdb_anime_seasons") val useTvdbAnimeSeasons: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -48,9 +48,9 @@ internal fun SimklSyncSnapshot.toSimklWatchedProjection(): SimklWatchedProjectio
|
|||
|
||||
var hasEpisodeHistory = false
|
||||
entry.seasons.forEach seasonLoop@{ season ->
|
||||
val seasonNumber = season.number ?: return@seasonLoop
|
||||
season.episodes.forEach episodeLoop@{ episode ->
|
||||
val episodeNumber = episode.number ?: return@episodeLoop
|
||||
val seasonNumber = episode.tvdb?.season ?: season.number ?: return@episodeLoop
|
||||
val episodeNumber = episode.tvdb?.episode ?: episode.number ?: return@episodeLoop
|
||||
val watchedAt = parseSimklUtcEpochMs(episode.watchedAt) ?: return@episodeLoop
|
||||
hasEpisodeHistory = true
|
||||
watchedItems += WatchedItem(
|
||||
|
|
|
|||
|
|
@ -67,6 +67,40 @@ class SimklMutationRepositoryTest {
|
|||
assertEquals("2023-11-14T22:13:20Z", movieItem.getValue("watched_at").jsonPrimitive.content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `anime history uses tvdb mapping flag only for seasonal coordinates`() {
|
||||
val seasonalBody = buildSimklHistoryMutationBody(
|
||||
listOf(
|
||||
TrackingHistoryItem(
|
||||
anime(TrackingEpisode(season = 2, number = 4)),
|
||||
watchedAtEpochMs = 1_700_000_000_000L,
|
||||
),
|
||||
),
|
||||
).asObject()
|
||||
val seasonalAnime = seasonalBody.getValue("shows").jsonArray.single().jsonObject
|
||||
assertTrue(seasonalAnime.getValue("use_tvdb_anime_seasons").jsonPrimitive.content.toBoolean())
|
||||
|
||||
val flatBody = buildSimklHistoryMutationBody(
|
||||
listOf(
|
||||
TrackingHistoryItem(
|
||||
anime(TrackingEpisode(number = 4)),
|
||||
watchedAtEpochMs = 1_700_000_000_000L,
|
||||
),
|
||||
),
|
||||
).asObject()
|
||||
assertNull(flatBody.getValue("shows").jsonArray.single().jsonObject["use_tvdb_anime_seasons"])
|
||||
|
||||
val showBody = buildSimklHistoryMutationBody(
|
||||
listOf(
|
||||
TrackingHistoryItem(
|
||||
show(TrackingEpisode(season = 2, number = 4)),
|
||||
watchedAtEpochMs = 1_700_000_000_000L,
|
||||
),
|
||||
),
|
||||
).asObject()
|
||||
assertNull(showBody.getValue("shows").jsonArray.single().jsonObject["use_tvdb_anime_seasons"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `anime removal uses shows array and contains no response-only or watch fields`() {
|
||||
val body = buildSimklHistoryRemovalBody(
|
||||
|
|
@ -78,6 +112,12 @@ class SimklMutationRepositoryTest {
|
|||
assertNull(item["watched_at"])
|
||||
assertNull(item["status"])
|
||||
assertEquals(4, item.getValue("episodes").jsonArray.single().jsonObject.getValue("number").jsonPrimitive.content.toInt())
|
||||
|
||||
val seasonalBody = buildSimklHistoryRemovalBody(
|
||||
listOf(anime(TrackingEpisode(season = 2, number = 4))),
|
||||
).asObject()
|
||||
val seasonalItem = seasonalBody.getValue("shows").jsonArray.single().jsonObject
|
||||
assertTrue(seasonalItem.getValue("use_tvdb_anime_seasons").jsonPrimitive.content.toBoolean())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -89,16 +129,27 @@ class SimklMutationRepositoryTest {
|
|||
assertTrue("movie" in movieBody)
|
||||
assertFalse("show" in movieBody)
|
||||
|
||||
val animeBody = buildSimklScrobbleBody(
|
||||
val tvStyleAnimeBody = buildSimklScrobbleBody(
|
||||
TrackingScrobbleEvent(
|
||||
anime(TrackingEpisode(season = 2, number = 4)),
|
||||
progressPercent = 42.236,
|
||||
),
|
||||
).asObject()
|
||||
assertEquals(42.24, animeBody.getValue("progress").jsonPrimitive.content.toDouble())
|
||||
assertTrue("anime" in animeBody)
|
||||
assertEquals(2, animeBody.getValue("episode").jsonObject.getValue("season").jsonPrimitive.content.toInt())
|
||||
assertEquals(4, animeBody.getValue("episode").jsonObject.getValue("number").jsonPrimitive.content.toInt())
|
||||
assertEquals(42.24, tvStyleAnimeBody.getValue("progress").jsonPrimitive.content.toDouble())
|
||||
assertTrue("show" in tvStyleAnimeBody)
|
||||
assertFalse("anime" in tvStyleAnimeBody)
|
||||
assertEquals(2, tvStyleAnimeBody.getValue("episode").jsonObject.getValue("season").jsonPrimitive.content.toInt())
|
||||
assertEquals(4, tvStyleAnimeBody.getValue("episode").jsonObject.getValue("number").jsonPrimitive.content.toInt())
|
||||
|
||||
val nativeAnimeBody = buildSimklScrobbleBody(
|
||||
TrackingScrobbleEvent(
|
||||
anime(TrackingEpisode(number = 4)),
|
||||
progressPercent = 42.236,
|
||||
),
|
||||
).asObject()
|
||||
assertTrue("anime" in nativeAnimeBody)
|
||||
assertFalse("show" in nativeAnimeBody)
|
||||
assertNull(nativeAnimeBody.getValue("episode").jsonObject["season"])
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -142,6 +142,36 @@ class SimklProjectionsTest {
|
|||
assertTrue(projection.fullyWatchedSeriesKeys.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `anime watched projection prefers mapped tvdb coordinates`() {
|
||||
val anime = entry(
|
||||
type = SimklMediaType.ANIME,
|
||||
status = SimklListStatus.WATCHING,
|
||||
id = 439744,
|
||||
imdb = "tt2560140",
|
||||
seasons = listOf(
|
||||
SimklSeason(
|
||||
number = 1,
|
||||
episodes = listOf(
|
||||
SimklEpisode(
|
||||
number = 4,
|
||||
watchedAt = "2023-11-14T23:13:20Z",
|
||||
tvdb = SimklEpisodeMapping(season = 2, episode = 4),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val watched = SimklSyncSnapshot(entries = listOf(anime))
|
||||
.toSimklWatchedProjection()
|
||||
.items
|
||||
.single()
|
||||
|
||||
assertEquals(2, watched.season)
|
||||
assertEquals(4, watched.episode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `playback projection preserves Simkl session identity and percentage`() {
|
||||
val session = SimklPlaybackSession(
|
||||
|
|
|
|||
Loading…
Reference in a new issue