diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/details/DetailsScreen.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/details/DetailsScreen.kt index 2d2bd8ed2..1544e8d92 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/details/DetailsScreen.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/details/DetailsScreen.kt @@ -166,6 +166,7 @@ import com.arflix.tv.ui.theme.Purple import com.arflix.tv.ui.theme.TextPrimary import com.arflix.tv.ui.theme.TextSecondary import com.arflix.tv.util.LocalDeviceType +import com.arflix.tv.util.formatGenreName import com.arflix.tv.util.isInCinema import com.arflix.tv.util.parseRatingValue import java.text.SimpleDateFormat @@ -1177,7 +1178,7 @@ private fun DetailsContent( } } - val genreText = genres.take(2).joinToString(" / ").ifBlank { + val genreText = genres.take(2).map(::formatGenreName).joinToString(" / ").ifBlank { if (item.mediaType == MediaType.TV) "TV Series" else "Movie" } val displayDate = item.year.takeIf { it.isNotBlank() } @@ -1805,7 +1806,7 @@ private fun DetailsContent( Spacer(modifier = Modifier.height(4.dp)) - val genreText = genres.take(2).joinToString(" / ").ifEmpty { + val genreText = genres.take(2).map(::formatGenreName).joinToString(" / ").ifEmpty { if (item.mediaType == MediaType.TV) "TV Series" else "Movie" } val isCompactHeight = configuration.screenHeightDp < 720 @@ -3959,7 +3960,7 @@ private fun GenreBadge(genre: String) { .padding(horizontal = 10.dp, vertical = 5.dp) ) { Text( - text = genre.uppercase(), + text = formatGenreName(genre), style = ArflixTypography.label, color = Pink ) diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/MiniPlayer.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/MiniPlayer.kt index 691499ec1..b8801b729 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/MiniPlayer.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/MiniPlayer.kt @@ -46,6 +46,7 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api import androidx.tv.material3.Text import com.arflix.tv.data.model.IptvNowNext import com.arflix.tv.data.model.IptvProgram +import com.arflix.tv.util.formatGenreName @OptIn(ExperimentalTvMaterial3Api::class) @Composable @@ -264,7 +265,7 @@ private fun ChannelIdentityRow(channel: EnrichedChannel?) { style = LiveType.SectionTag.copy(color = LiveColors.FgMute), ) Text( - text = channel.genre.name.uppercase(), + text = formatGenreName(channel.genre.name), style = LiveType.SectionTag.copy(color = LiveColors.FgMute), ) } diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/SearchOverlay.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/SearchOverlay.kt index b23a6583d..b4348aabf 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/SearchOverlay.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/tv/live/SearchOverlay.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.tv.material3.ExperimentalTvMaterial3Api import androidx.tv.material3.Text +import com.arflix.tv.util.formatGenreName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext @@ -283,7 +284,7 @@ private fun SearchResultRow( overflow = TextOverflow.Ellipsis, ) Text( - text = channel.genre.name.uppercase(), + text = formatGenreName(channel.genre.name), style = LiveType.SectionTag.copy(color = LiveColors.FgMute), ) } diff --git a/app/src/main/kotlin/com/arflix/tv/util/MediaBadges.kt b/app/src/main/kotlin/com/arflix/tv/util/MediaBadges.kt index 4ec666792..09153ad98 100644 --- a/app/src/main/kotlin/com/arflix/tv/util/MediaBadges.kt +++ b/app/src/main/kotlin/com/arflix/tv/util/MediaBadges.kt @@ -5,8 +5,11 @@ import com.arflix.tv.data.model.MediaType import java.time.LocalDate import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit +import java.util.Locale private val releaseDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE +private val genreWordStartRegex = Regex("(^|[\\s/&-])([\\p{L}])") +private val tvWordRegex = Regex("\\bTv\\b") fun isInCinema(item: MediaItem, now: LocalDate = LocalDate.now()): Boolean { if (item.mediaType != MediaType.MOVIE) return false @@ -23,3 +26,12 @@ fun parseRatingValue(raw: String): Float { if (raw.isBlank()) return 0f return raw.trim().replace(',', '.').toFloatOrNull() ?: 0f } + +fun formatGenreName(raw: String): String { + val trimmed = raw.trim() + if (trimmed.isEmpty()) return trimmed + val titled = trimmed.lowercase(Locale.ROOT).replace(genreWordStartRegex) { match -> + match.groupValues[1] + match.groupValues[2].replaceFirstChar { it.titlecase(Locale.ROOT) } + } + return titled.replace(tvWordRegex, "TV") +}