feat: Extend MetaDetails and MetaDetailsParser to include status and hasScheduledVideos fields; enhance DetailSeriesContent for better episode handling

This commit is contained in:
tapframe 2026-03-28 21:52:18 +05:30
parent d96908d124
commit 4dd08f29a6
3 changed files with 45 additions and 1 deletions

View file

@ -9,6 +9,7 @@ data class MetaDetails(
val logo: String? = null,
val description: String? = null,
val releaseInfo: String? = null,
val status: String? = null,
val imdbRating: String? = null,
val runtime: String? = null,
val genres: List<String> = emptyList(),
@ -18,6 +19,7 @@ data class MetaDetails(
val awards: String? = null,
val language: String? = null,
val website: String? = null,
val hasScheduledVideos: Boolean = false,
val links: List<MetaLink> = emptyList(),
val videos: List<MetaVideo> = emptyList(),
)

View file

@ -4,6 +4,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject
@ -26,6 +27,7 @@ internal object MetaDetailsParser {
logo = meta.string("logo"),
description = meta.string("description"),
releaseInfo = meta.string("releaseInfo"),
status = meta.string("status"),
imdbRating = meta.string("imdbRating"),
runtime = meta.string("runtime"),
genres = meta.stringList("genres"),
@ -35,6 +37,7 @@ internal object MetaDetailsParser {
awards = meta.string("awards"),
language = meta.string("language"),
website = meta.string("website"),
hasScheduledVideos = meta.behaviorHints().boolean("hasScheduledVideos") == true,
links = links,
videos = meta.videos(),
)
@ -77,6 +80,12 @@ internal object MetaDetailsParser {
private fun JsonObject.int(name: String): Int? =
this[name]?.jsonPrimitive?.intOrNull
private fun JsonObject.boolean(name: String): Boolean? =
this[name]?.jsonPrimitive?.booleanOrNull
private fun JsonObject.behaviorHints(): JsonObject =
this["behaviorHints"] as? JsonObject ?: JsonObject(emptyMap())
private fun JsonObject.directors(links: List<MetaLink>): List<String> {
val appExtras = this["app_extras"] as? JsonObject
val topLevel = stringListOrCsv("director")

View file

@ -57,6 +57,27 @@ fun DetailSeriesContent(
progressByVideoId: Map<String, WatchProgressEntry> = emptyMap(),
onEpisodeClick: ((MetaVideo) -> Unit)? = null,
) {
if (meta.type != "series") return
if (meta.videos.isEmpty()) {
DetailSection(
title = "Episodes",
modifier = modifier,
) {
Text(
text = when {
meta.status.equals("Not yet aired", ignoreCase = true) || meta.hasScheduledVideos ->
"Episodes have not been published by this addon yet."
else ->
"This addon did not provide episode metadata for this series."
},
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
return
}
val groupedEpisodes = remember(meta.videos) {
log.d { "videos count=${meta.videos.size}, type=${meta.type}" }
val withSeasonOrEp = meta.videos.filter { it.season != null || it.episode != null }
@ -69,7 +90,19 @@ fun DetailSeriesContent(
.groupBy { normalizeSeasonNumber(it.season) }
}
if (groupedEpisodes.isEmpty()) return
if (groupedEpisodes.isEmpty()) {
DetailSection(
title = "Episodes",
modifier = modifier,
) {
Text(
text = "This addon returned videos for the series, but none included season or episode numbers.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
return
}
val seasons = groupedEpisodes.keys.sortedBy(::seasonSortKey)
val defaultSeason = seasons.first()