Server changes/Sonarr
This commit is contained in:
@@ -69,16 +69,25 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AutoAwesome
|
||||
import androidx.compose.material.icons.filled.BrokenImage
|
||||
import androidx.compose.material.icons.filled.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.ChevronLeft
|
||||
import androidx.compose.material.icons.filled.ChevronRight
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.LiveTv
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.PlayCircleFilled
|
||||
import androidx.compose.material.icons.filled.Recommend
|
||||
import androidx.compose.material.icons.filled.SentimentVerySatisfied
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.SkipNext
|
||||
import androidx.compose.material.icons.filled.TheaterComedy
|
||||
import androidx.compose.material.icons.filled.Tv
|
||||
import androidx.compose.material.icons.filled.VideoLibrary
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Text
|
||||
@@ -120,6 +129,58 @@ data class HomeBrowseRow(
|
||||
val showSecondaryMetadata: Boolean = true,
|
||||
)
|
||||
|
||||
private data class HomeRowVisual(
|
||||
val icon: ImageVector,
|
||||
val colors: List<Color>,
|
||||
)
|
||||
|
||||
private fun homeRowVisual(row: HomeBrowseRow): HomeRowVisual = when {
|
||||
row.id == "continue" -> HomeRowVisual(
|
||||
Icons.Default.PlayCircleFilled,
|
||||
listOf(Color(0xFF3A7BD5), Color(0xFF6C4DFF)),
|
||||
)
|
||||
row.id == "next-up" -> HomeRowVisual(
|
||||
Icons.Default.SkipNext,
|
||||
listOf(Color(0xFF00A896), Color(0xFF28C2A0)),
|
||||
)
|
||||
row.kind == MediaRowKind.FAVORITES -> HomeRowVisual(
|
||||
Icons.Default.Favorite,
|
||||
listOf(Color(0xFFFF5C8A), Color(0xFFB84DFF)),
|
||||
)
|
||||
row.id == "latest-movies" -> HomeRowVisual(
|
||||
Icons.Default.Movie,
|
||||
listOf(Color(0xFFFF8A3D), Color(0xFFE34B6F)),
|
||||
)
|
||||
row.id == "sonarr-airing-today" -> HomeRowVisual(
|
||||
Icons.Default.CalendarMonth,
|
||||
listOf(Color(0xFF00A9CE), Color(0xFF4169E1)),
|
||||
)
|
||||
row.id == "curated:apple-tv" -> HomeRowVisual(
|
||||
Icons.Default.LiveTv,
|
||||
listOf(Color(0xFF252A31), Color(0xFF66717E)),
|
||||
)
|
||||
row.id == "curated:drama-shows" -> HomeRowVisual(
|
||||
Icons.Default.TheaterComedy,
|
||||
listOf(Color(0xFF6A3FB5), Color(0xFFB64272)),
|
||||
)
|
||||
row.id == "curated:comedy-shows" -> HomeRowVisual(
|
||||
Icons.Default.SentimentVerySatisfied,
|
||||
listOf(Color(0xFFFFB52E), Color(0xFFFF7433)),
|
||||
)
|
||||
row.id.startsWith("similar:") -> HomeRowVisual(
|
||||
Icons.Default.AutoAwesome,
|
||||
listOf(Color(0xFF8A5CF6), Color(0xFFE458A3)),
|
||||
)
|
||||
row.id == "recommended" -> HomeRowVisual(
|
||||
Icons.Default.Recommend,
|
||||
listOf(Color(0xFF45B649), Color(0xFF00A896)),
|
||||
)
|
||||
else -> HomeRowVisual(
|
||||
Icons.Default.VideoLibrary,
|
||||
listOf(Color(0xFF536976), Color(0xFF738A96)),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TvNavigationRail(
|
||||
selected: BrowseDestination,
|
||||
@@ -160,11 +221,27 @@ fun TvNavigationRail(
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.emby_logo),
|
||||
contentDescription = "Emby",
|
||||
contentDescription = "Memby",
|
||||
modifier = Modifier.width(32.dp).height(27.dp),
|
||||
)
|
||||
if (expanded) {
|
||||
Text("Emby", color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
||||
Column(verticalArrangement = Arrangement.Center) {
|
||||
Text(
|
||||
"Memby",
|
||||
color = Color.White,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
"Matt’s Android TV client",
|
||||
color = QuietText,
|
||||
fontSize = 9.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
@@ -528,6 +605,10 @@ fun MediaQuickActionsOverlay(
|
||||
|
||||
@Composable
|
||||
private fun MetadataContent(item: BaseItem, sectionLabel: String) {
|
||||
if (item.isSonarrSchedule) {
|
||||
ScheduleMetadataContent(item, sectionLabel)
|
||||
return
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(7.dp),
|
||||
@@ -574,6 +655,62 @@ private fun MetadataContent(item: BaseItem, sectionLabel: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScheduleMetadataContent(item: BaseItem, sectionLabel: String) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(7.dp),
|
||||
) {
|
||||
Text(
|
||||
sectionLabel.uppercase(),
|
||||
color = EmbyGreen,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
letterSpacing = 1.2.sp,
|
||||
)
|
||||
Text(
|
||||
item.name,
|
||||
color = Color.White,
|
||||
fontSize = 30.sp,
|
||||
lineHeight = 34.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
listOfNotNull(
|
||||
item.membyEpisodeCode,
|
||||
item.membyEpisodeTitle?.takeIf(String::isNotBlank),
|
||||
).joinToString(" • "),
|
||||
color = MutedText,
|
||||
fontSize = 16.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
item.membyAirLabel?.takeIf(String::isNotBlank)?.let { MediaBadge(it.uppercase()) }
|
||||
item.membyAvailabilityText?.takeIf(String::isNotBlank)?.let {
|
||||
MediaBadge(it.uppercase())
|
||||
}
|
||||
}
|
||||
Text(
|
||||
item.overview?.takeIf(String::isNotBlank)
|
||||
?: "Episode information will appear when Sonarr receives it.",
|
||||
color = Color(0xFFD0D4D7),
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 20.sp,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.fillMaxWidth(0.74f),
|
||||
)
|
||||
Text(
|
||||
"Schedule information from Sonarr",
|
||||
color = QuietText,
|
||||
fontSize = 12.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetadataStatus(item: BaseItem) {
|
||||
val position = item.userData?.playbackPositionTicks ?: 0L
|
||||
@@ -656,6 +793,23 @@ fun MediaRow(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 36.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val visual = homeRowVisual(row)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Brush.linearGradient(visual.colors))
|
||||
.border(1.dp, Color.White.copy(alpha = 0.20f), CircleShape),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = visual.icon,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(15.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(10.dp))
|
||||
Text(
|
||||
row.title,
|
||||
color = Color(0xFFF1F3F4),
|
||||
@@ -978,6 +1132,12 @@ private fun MediaCard(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.isSonarrSchedule) {
|
||||
ScheduleStatusBadge(
|
||||
status = item.membyAvailability.orEmpty(),
|
||||
modifier = Modifier.align(Alignment.TopEnd).padding(8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
item.name,
|
||||
@@ -1004,6 +1164,28 @@ private fun MediaCard(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScheduleStatusBadge(status: String, modifier: Modifier = Modifier) {
|
||||
val (label, color) = when (status) {
|
||||
"available" -> "ADDED" to EmbyGreen
|
||||
"downloading" -> "DOWNLOADING" to Color(0xFF5DA9FF)
|
||||
"awaiting" -> "AWAITING" to Color(0xFFFFB454)
|
||||
"unmonitored" -> "UNMONITORED" to QuietText
|
||||
else -> "TODAY" to Color(0xFFE1E5E8)
|
||||
}
|
||||
Text(
|
||||
text = label,
|
||||
color = if (status == "available") Color(0xFF071008) else Color(0xFF090B0D),
|
||||
fontSize = 9.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
letterSpacing = 0.5.sp,
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(color)
|
||||
.padding(horizontal = 7.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FocusScaleContainer(
|
||||
onFocused: () -> Unit,
|
||||
@@ -1127,6 +1309,7 @@ private const val BACKDROP_SETTLE_DELAY_MS = 240L
|
||||
|
||||
private fun cardSubtitle(item: BaseItem, showProgress: Boolean, positionTicks: Long): String = when {
|
||||
showProgress && positionTicks > 0L -> "Resume at ${formatTvPosition(positionTicks)}"
|
||||
item.isSonarrSchedule -> item.membyAirLabel ?: "Airing today"
|
||||
item.isEpisode -> item.seriesName ?: "Up next"
|
||||
item.productionYear != null && item.runtimeMinutes != null ->
|
||||
"${item.productionYear} • ${formatTvRuntime(requireNotNull(item.runtimeMinutes))}"
|
||||
@@ -1141,6 +1324,10 @@ private fun cardDescription(item: BaseItem, progress: Float): String = buildStri
|
||||
if (progress > 0f) append(", ${(progress * 100).toInt()} percent watched")
|
||||
if (item.userData?.played == true) append(", watched")
|
||||
if (item.isFavorite) append(", favourite")
|
||||
if (item.isSonarrSchedule) {
|
||||
item.membyAirLabel?.let { append(", ").append(it) }
|
||||
item.membyAvailabilityText?.let { append(", ").append(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatTvRuntime(minutes: Int): String {
|
||||
|
||||
Reference in New Issue
Block a user