From 0dfb3788e8d4dfe5bcd9c5c11f7335d8db672d23 Mon Sep 17 00:00:00 2001
From: beyond
Date: Mon, 7 Sep 2026 17:13:05 +0800
Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../myapplication/data/EmbyRepository.kt | 2 +-
.../myapplication/screens/HomeScreen.kt | 36 ++++++++++++-------
.../myapplication/ui/design/LumaComponents.kt | 26 ++++++++++++++
3 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/app/src/main/kotlin/com/example/myapplication/data/EmbyRepository.kt b/app/src/main/kotlin/com/example/myapplication/data/EmbyRepository.kt
index a92713a..98de38b 100644
--- a/app/src/main/kotlin/com/example/myapplication/data/EmbyRepository.kt
+++ b/app/src/main/kotlin/com/example/myapplication/data/EmbyRepository.kt
@@ -295,7 +295,7 @@ object EmbyRepository {
limit: Int = 6,
): EmbyLibraryPage = withContext(Dispatchers.IO) {
try {
- val url = "${server.fullUrl}/emby/Users/${server.userId}/Items?ParentId=$libraryId&SortBy=$sortBy&SortOrder=$sortOrder&StartIndex=${startIndex.coerceAtLeast(0)}&Limit=${limit.coerceAtLeast(1)}&IncludeItemTypes=Movie,Series&Fields=PrimaryImageAspectRatio,Overview,ProductionYear,Genres,RecursiveChildCount,ChildCount,DateCreated,RunTimeTicks,UserData&Recursive=true"
+ val url = "${server.fullUrl}/emby/Users/${server.userId}/Items?ParentId=$libraryId&SortBy=$sortBy&SortOrder=$sortOrder&StartIndex=${startIndex.coerceAtLeast(0)}&Limit=${limit.coerceAtLeast(1)}&IncludeItemTypes=Movie,Series&Fields=PrimaryImageAspectRatio,Overview,ProductionYear,Genres,RecursiveChildCount,ChildCount,DateCreated,RunTimeTicks,UserData,CommunityRating,OfficialRating&Recursive=true"
val request = Request.Builder()
.url(url)
.header("X-Emby-Token", server.apiKey)
diff --git a/app/src/main/kotlin/com/example/myapplication/screens/HomeScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/HomeScreen.kt
index 0292c17..fc0e6ea 100644
--- a/app/src/main/kotlin/com/example/myapplication/screens/HomeScreen.kt
+++ b/app/src/main/kotlin/com/example/myapplication/screens/HomeScreen.kt
@@ -490,9 +490,17 @@ private fun MediaPoster(
ImageRequest.Builder(context).data(it).crossfade(true).build()
}
}
+ val subtitle = listOfNotNull(
+ item.communityRating?.takeIf { it > 0.0 }?.let { "★ ${String.format("%.1f", it)}" },
+ item.productionYear?.let { "$it" },
+ ).takeIf { it.isNotEmpty() }?.joinToString(" ")
+
LumaMediaCard(
model = imageRequest,
title = item.seriesName ?: item.name,
+ subtitle = subtitle,
+ subtitleColor = homeSecondaryColor(),
+ subtitleStarColor = LumaColors.ratingStar,
modifier = modifier.width(115.dp),
imageAspectRatio = 115f / 165f,
titleColor = homeContentColor(),
@@ -590,6 +598,7 @@ private fun LazyLibrarySection(
refreshToken: Int,
onItemClick: (EmbyItem) -> Unit = {},
onViewAll: () -> Unit = {},
+ onEmpty: () -> Unit = {},
) {
val cacheKey = remember(activeServer.id, activeServer.fullUrl, activeServer.userId) {
homeCacheKey(activeServer)
@@ -616,6 +625,7 @@ private fun LazyLibrarySection(
EmbyLibraryPage()
}.also { loaded ->
HomeContentCache.putLibraryPage(cacheKey, folder.id, loaded)
+ if (loaded.items.isEmpty()) onEmpty()
}
}
// Fetch accurate episode counts for series items (parallel)
@@ -637,7 +647,7 @@ private fun LazyLibrarySection(
}
}
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
SectionTitle(title = folder.name, count = page?.totalCount ?: 0, onMoreClick = onViewAll)
val list = page?.items
if (list == null) {
@@ -676,13 +686,12 @@ private fun SectionTitle(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
- Text(text = title, color = homeContentColor(), fontSize = 18.sp, fontWeight = FontWeight.Bold)
+ Text(text = title, color = homeContentColor(), fontSize = 15.sp, fontWeight = FontWeight.Bold)
if (count > 0) {
Box(
Modifier
- .heightIn(min = 48.dp)
.clickable(onClick = { onMoreClick?.invoke() })
- .padding(horizontal = 8.dp),
+ .padding(horizontal = 8.dp, vertical = 4.dp),
contentAlignment = Alignment.Center,
) {
Text(
@@ -698,7 +707,7 @@ private fun SectionTitle(
@Composable
private fun MyMediaGrid(libraries: List, serverUrl: String, onSelect: (EmbyMediaFolder) -> Unit) {
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
SectionTitle(title = stringResource(Res.string.section_library))
LazyRow(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
items(libraries, key = { it.id }) { folder ->
@@ -898,6 +907,7 @@ fun HomeScreen(
var reloadToken by remember(homeKey) { mutableIntStateOf(0) }
var showResourceStats by remember(homeKey) { mutableStateOf(false) }
var serverMenuVisible by remember(homeKey) { mutableStateOf(false) }
+ val emptyLibraries = remember(homeKey) { mutableStateMapOf() }
var serverMenuMounted by remember(homeKey) { mutableStateOf(false) }
var serverAnchorBounds by remember { mutableStateOf(null) }
var homeRootOrigin by remember { mutableStateOf(Offset.Zero) }
@@ -1064,8 +1074,8 @@ fun HomeScreen(
if (resumeItems.isNotEmpty()) {
item(key = "resume", contentType = "resume") {
- HomeSection(first = true) {
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ HomeSection {
+ Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
SectionTitle(title = stringResource(Res.string.continue_watching))
LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
items(resumeItems, key = { it.id }) { item ->
@@ -1081,7 +1091,7 @@ fun HomeScreen(
if (libraries.isNotEmpty()) {
item(key = "media", contentType = "media") {
- HomeSection(first = resumeItems.isEmpty()) {
+ HomeSection {
MyMediaGrid(libraries, server.fullUrl) { folder ->
onViewAll(server.id, folder.id, folder.name)
}
@@ -1089,7 +1099,8 @@ fun HomeScreen(
}
}
- items(libraries, key = { "library_${it.id}" }, contentType = { "library" }) { folder ->
+ val visibleLibraries = libraries.filter { emptyLibraries[it.id] != true }
+ items(visibleLibraries, key = { "library_${it.id}" }, contentType = { "library" }) { folder ->
HomeSection {
LazyLibrarySection(
folder = folder,
@@ -1097,12 +1108,13 @@ fun HomeScreen(
refreshToken = reloadToken,
onItemClick = { onOpenDetail(it.id) },
onViewAll = { onViewAll(server.id, folder.id, folder.name) },
+ onEmpty = { emptyLibraries[folder.id] = true },
)
}
}
item(key = "stats", contentType = "stats") {
- HomeSection(first = resumeItems.isEmpty() && libraries.isEmpty()) {
+ HomeSection {
ResourceStatsCard(
stats = resourceStats,
onClick = { showResourceStats = true },
@@ -1172,12 +1184,12 @@ fun HomeScreen(
}
@Composable
-private fun HomeSection(first: Boolean = false, content: @Composable () -> Unit) {
+private fun HomeSection(content: @Composable () -> Unit) {
Box(
Modifier.fillMaxWidth().padding(
start = LumaSpacing.md,
end = LumaSpacing.md,
- top = if (first) LumaSpacing.md else LumaSpacing.xl,
+ top = LumaSpacing.md,
),
) {
content()
diff --git a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt
index 451c003..ce30a22 100644
--- a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt
+++ b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt
@@ -192,6 +192,9 @@ fun LumaMediaCard(
titleSize: TextUnit = LumaTypography.body,
titleWeight: FontWeight = FontWeight.Medium,
titleSpacing: Dp = LumaSpacing.xs,
+ subtitle: String? = null,
+ subtitleColor: Color = titleColor.copy(alpha = 0.6f),
+ subtitleStarColor: Color = subtitleColor,
progress: Float? = null,
onClick: (() -> Unit)? = null,
overlay: @Composable BoxScope.() -> Unit = {},
@@ -283,6 +286,29 @@ fun LumaMediaCard(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
+ if (!subtitle.isNullOrBlank()) {
+ val parts = subtitle.split(" ", limit = 2)
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = parts[0],
+ color = subtitleStarColor,
+ fontSize = (titleSize.value - 1).sp,
+ fontWeight = FontWeight.Normal,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ if (parts.size > 1) {
+ Text(
+ text = " ${parts[1]}",
+ color = subtitleColor,
+ fontSize = (titleSize.value - 1).sp,
+ fontWeight = FontWeight.Normal,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+ }
}
}