mirror of
https://github.com/NuvioMedia/NuvioMobile.git
synced 2026-08-06 22:19:01 +00:00
Merge pull request #1683 from skoruppa/cmp-rewrite
CW flicker, image cache revalidation, Simkl anime movies watched status
This commit is contained in:
commit
3902edd7ee
8 changed files with 45 additions and 15 deletions
|
|
@ -434,6 +434,9 @@ kotlin {
|
|||
implementation("io.coil-kt.coil3:coil-network-ktor3:${libs.versions.coil.get()}") {
|
||||
exclude(group = "org.jetbrains.skiko", module = "skiko")
|
||||
}
|
||||
implementation("io.coil-kt.coil3:coil-network-cache-control:${libs.versions.coil.get()}") {
|
||||
exclude(group = "org.jetbrains.skiko", module = "skiko")
|
||||
}
|
||||
implementation("io.coil-kt.coil3:coil-svg:${libs.versions.coil.get()}") {
|
||||
exclude(group = "org.jetbrains.skiko", module = "skiko")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -446,6 +446,11 @@ fun App(
|
|||
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||
.components {
|
||||
add(SvgDecoder.Factory())
|
||||
add(
|
||||
coil3.network.ktor3.KtorNetworkFetcherFactory(
|
||||
cacheStrategy = { coil3.network.cachecontrol.CacheControlCacheStrategy() },
|
||||
)
|
||||
)
|
||||
}
|
||||
.configurePlatformImageLoader()
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -1796,12 +1796,16 @@ private fun ContinueWatchingItem.withFallbackMetadata(
|
|||
hasPlaceholderHomeTitle() && fallbackTitle != null -> fallbackTitle
|
||||
else -> title
|
||||
},
|
||||
subtitle = subtitle.takeIf { it.isNotBlank() }
|
||||
?: fallback?.subtitle?.takeIf { it.isNotBlank() }.orEmpty(),
|
||||
subtitle = when {
|
||||
subtitle.isBlank() -> fallback?.subtitle?.takeIf { it.isNotBlank() }.orEmpty()
|
||||
fallback?.subtitle.isNullOrBlank() -> subtitle
|
||||
else -> fallback.subtitle
|
||||
},
|
||||
imageUrl = imageUrl.orNonBlank(fallback?.imageUrl),
|
||||
logo = logo.orNonBlank(fallback?.logo),
|
||||
poster = poster.orNonBlank(fallback?.poster),
|
||||
background = background.orNonBlank(fallback?.background),
|
||||
videoId = fallback?.videoId?.takeIf { it.isNotBlank() } ?: videoId,
|
||||
episodeTitle = episodeTitle.orNonBlank(fallback?.episodeTitle),
|
||||
episodeThumbnail = episodeThumbnail.orNonBlank(fallback?.episodeThumbnail),
|
||||
pauseDescription = pauseDescription.orNonBlank(fallback?.pauseDescription),
|
||||
|
|
|
|||
|
|
@ -290,7 +290,11 @@ private fun HomeContinueWatchingSectionContent(
|
|||
key(dataSourceKey) {
|
||||
val disintegration = remember {
|
||||
ScopedDisintegrationTracker<WatchProgressSource, String, ContinueWatchingItem>(
|
||||
itemKey = ContinueWatchingItem::videoId,
|
||||
itemKey = { item ->
|
||||
val season = item.seasonNumber ?: -1
|
||||
val episode = item.episodeNumber ?: -1
|
||||
"${item.parentMetaId}:$season:$episode"
|
||||
},
|
||||
)
|
||||
}
|
||||
val displayEntries = disintegration.sync(dataSourceKey, items)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ internal fun SimklSyncSnapshot.toSimklWatchedProjection(): SimklWatchedProjectio
|
|||
entries.forEach { entry ->
|
||||
val media = entry.media ?: return@forEach
|
||||
val contentId = media.canonicalContentId() ?: return@forEach
|
||||
val contentType = if (entry.mediaType == SimklMediaType.MOVIES) "movie" else "series"
|
||||
val contentType = if (entry.isMovieEntry()) "movie" else "series"
|
||||
val title = media.title?.takeIf(String::isNotBlank) ?: contentId
|
||||
val poster = entry.resolvedPosterUrl()
|
||||
val trackingProviderItemId = media.simklTrackingProviderItemId()
|
||||
|
|
@ -37,7 +37,7 @@ internal fun SimklSyncSnapshot.toSimklWatchedProjection(): SimklWatchedProjectio
|
|||
?: parseSimklUtcEpochMs(entry.addedToWatchlistAt)
|
||||
?: 0L
|
||||
|
||||
if (entry.mediaType == SimklMediaType.MOVIES) {
|
||||
if (entry.isMovieEntry()) {
|
||||
if (entry.lastWatchedAt != null || entry.status == SimklListStatus.COMPLETED) {
|
||||
watchedItems += WatchedItem(
|
||||
id = contentId,
|
||||
|
|
@ -115,6 +115,7 @@ internal fun SimklSyncSnapshot.animeAlternateWatchedKeys(): Set<String> {
|
|||
val extraKeys = linkedSetOf<String>()
|
||||
entries.forEach { entry ->
|
||||
if (entry.mediaType != SimklMediaType.ANIME) return@forEach
|
||||
if (entry.isMovieEntry()) return@forEach
|
||||
val media = entry.media ?: return@forEach
|
||||
val contentId = media.canonicalContentId() ?: return@forEach
|
||||
val contentType = "series"
|
||||
|
|
@ -150,7 +151,7 @@ internal fun SimklSyncSnapshot.animeAlternateWatchedKeys(): Set<String> {
|
|||
internal fun SimklSyncSnapshot.movieAlternateWatchedKeys(): Set<String> {
|
||||
val extraKeys = linkedSetOf<String>()
|
||||
entries.forEach { entry ->
|
||||
if (entry.mediaType != SimklMediaType.MOVIES) return@forEach
|
||||
if (!entry.isMovieEntry()) return@forEach
|
||||
if (entry.lastWatchedAt == null && entry.status != SimklListStatus.COMPLETED) return@forEach
|
||||
val media = entry.media ?: return@forEach
|
||||
val contentId = media.canonicalContentId() ?: return@forEach
|
||||
|
|
@ -379,7 +380,8 @@ internal fun parseSimklUtcEpochMs(value: String?): Long? {
|
|||
internal fun SimklPlaybackSession.toWatchProgressEntry(): WatchProgressEntry? {
|
||||
val media = media ?: return null
|
||||
val parentId = media.canonicalContentId() ?: return null
|
||||
val isMovie = mediaType == SimklMediaType.MOVIES
|
||||
val isMovie = mediaType == SimklMediaType.MOVIES ||
|
||||
(mediaType == SimklMediaType.ANIME && episode == null)
|
||||
val season = episode?.tvdbSeason ?: episode?.season
|
||||
val episodeNumber = episode?.tvdbNumber ?: episode?.number
|
||||
if (!isMovie && episodeNumber == null) return null
|
||||
|
|
|
|||
|
|
@ -32,15 +32,17 @@ private fun SimklSyncSnapshot.withPausedScrobble(
|
|||
.mergeMissing(existingEntry?.media)
|
||||
.mergeMissing(existingSession?.media)
|
||||
val mediaType = existingEntry?.mediaType ?: result.mediaType
|
||||
val isAnimeMovie = mediaType == SimklMediaType.ANIME &&
|
||||
(existingEntry?.animeType == "movie" || result.episode == null)
|
||||
val session = SimklPlaybackSession(
|
||||
id = result.playbackId ?: existingSession?.id,
|
||||
progress = result.progress,
|
||||
pausedAt = committedAt,
|
||||
type = if (mediaType == SimklMediaType.MOVIES) "movie" else "episode",
|
||||
type = if (mediaType == SimklMediaType.MOVIES || isAnimeMovie) "movie" else "episode",
|
||||
episode = result.episode?.mergeMissing(existingSession?.episode),
|
||||
show = media.takeIf { mediaType == SimklMediaType.SHOWS },
|
||||
anime = media.takeIf { mediaType == SimklMediaType.ANIME },
|
||||
movie = media.takeIf { mediaType == SimklMediaType.MOVIES },
|
||||
anime = media.takeIf { mediaType == SimklMediaType.ANIME && !isAnimeMovie },
|
||||
movie = media.takeIf { mediaType == SimklMediaType.MOVIES || isAnimeMovie },
|
||||
)
|
||||
return copy(
|
||||
playback = playback.filterNot { candidate ->
|
||||
|
|
@ -56,11 +58,13 @@ private fun SimklSyncSnapshot.withCompletedScrobble(
|
|||
val updatedEntries = entries.toMutableList()
|
||||
val index = updatedEntries.indexOfMatchingEntry(result)
|
||||
val existing = updatedEntries.getOrNull(index)
|
||||
val updated = when (result.mediaType) {
|
||||
SimklMediaType.MOVIES -> existing
|
||||
val updated = when {
|
||||
result.mediaType == SimklMediaType.MOVIES ||
|
||||
(result.mediaType == SimklMediaType.ANIME &&
|
||||
(existing?.animeType == "movie" || result.episode == null)) -> existing
|
||||
?.withWatchedMovie(result, committedAt)
|
||||
?: result.toWatchedMovieEntry(committedAt)
|
||||
SimklMediaType.SHOWS, SimklMediaType.ANIME -> existing
|
||||
else -> existing
|
||||
?.withWatchedEpisode(result, committedAt)
|
||||
?: result.toWatchedSeriesEntry(committedAt)
|
||||
}
|
||||
|
|
@ -81,7 +85,8 @@ private fun SimklLibraryEntry.withWatchedMovie(
|
|||
result: SimklScrobbleResult,
|
||||
committedAt: String,
|
||||
): SimklLibraryEntry = copy(
|
||||
mediaType = SimklMediaType.MOVIES,
|
||||
mediaType = result.mediaType,
|
||||
animeType = if (result.mediaType == SimklMediaType.ANIME) "movie" else animeType,
|
||||
lastWatchedAt = committedAt,
|
||||
status = SimklListStatus.COMPLETED,
|
||||
movie = result.media.mergeMissing(media),
|
||||
|
|
@ -90,7 +95,8 @@ private fun SimklLibraryEntry.withWatchedMovie(
|
|||
|
||||
private fun SimklScrobbleResult.toWatchedMovieEntry(committedAt: String): SimklLibraryEntry =
|
||||
SimklLibraryEntry(
|
||||
mediaType = SimklMediaType.MOVIES,
|
||||
mediaType = mediaType,
|
||||
animeType = if (mediaType == SimklMediaType.ANIME) "movie" else null,
|
||||
lastWatchedAt = committedAt,
|
||||
status = SimklListStatus.COMPLETED,
|
||||
movie = media,
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ data class SimklLibraryEntry(
|
|||
fun stableKey(): String? = media?.ids?.stableMediaId()?.let { id ->
|
||||
"${mediaType.apiValue}:$id"
|
||||
}
|
||||
|
||||
/** True when this entry represents a movie — either a regular movie or an anime movie. */
|
||||
fun isMovieEntry(): Boolean =
|
||||
mediaType == SimklMediaType.MOVIES ||
|
||||
(mediaType == SimklMediaType.ANIME && animeType == "movie")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ compottie = { module = "io.github.alexzhirkevich:compottie", version.ref = "comp
|
|||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" }
|
||||
coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
||||
coil-network-cache-control = { module = "io.coil-kt.coil3:coil-network-cache-control", version.ref = "coil" }
|
||||
coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomicfu" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue