diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/RuntimeFormat.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/RuntimeFormat.kt new file mode 100644 index 00000000..375cba10 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/RuntimeFormat.kt @@ -0,0 +1,46 @@ +package com.nuvio.app.features.details + +private val hourTokenRegex = Regex("""(?i)(\d+)\s*h(?:ours?)?""") +private val minuteTokenRegex = Regex("""(?i)(\d+)\s*m(?:in(?:ute)?s?)?""") +private val hourMinuteColonRegex = Regex("""^\s*(\d+)\s*:\s*(\d{1,2})\s*$""") +private val digitsOnlyRegex = Regex("""^\s*(\d+)\s*$""") + +internal fun formatRuntimeForDisplay(rawRuntime: String?): String? { + val normalized = rawRuntime?.trim()?.takeIf { it.isNotBlank() } ?: return null + val totalMinutes = parseRuntimeMinutes(normalized) ?: return normalized + return formatRuntimeFromMinutes(totalMinutes) +} + +internal fun formatRuntimeFromMinutes(totalMinutes: Int): String { + if (totalMinutes <= 0) return "" + val hours = totalMinutes / 60 + val minutes = totalMinutes % 60 + + return when { + hours > 0 && minutes > 0 -> "${hours}h ${minutes}m" + hours > 0 -> "${hours}h" + else -> "${minutes}m" + } +} + +private fun parseRuntimeMinutes(value: String): Int? { + hourMinuteColonRegex.matchEntire(value)?.let { match -> + val hours = match.groupValues[1].toIntOrNull() ?: return null + val minutes = match.groupValues[2].toIntOrNull() ?: return null + return (hours * 60) + minutes + } + + val hoursToken = hourTokenRegex.find(value)?.groupValues?.getOrNull(1)?.toIntOrNull() + val minutesToken = minuteTokenRegex.find(value)?.groupValues?.getOrNull(1)?.toIntOrNull() + if (hoursToken != null || minutesToken != null) { + val hours = (hoursToken ?: 0).coerceAtLeast(0) + val minutes = (minutesToken ?: 0).coerceAtLeast(0) + return (hours * 60) + minutes + } + + digitsOnlyRegex.matchEntire(value)?.let { match -> + return match.groupValues[1].toIntOrNull()?.coerceAtLeast(0) + } + + return null +} diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailAdditionalInfoSection.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailAdditionalInfoSection.kt index 7b796d57..40ec96ed 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailAdditionalInfoSection.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailAdditionalInfoSection.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.nuvio.app.core.format.formatReleaseDateForDisplay import com.nuvio.app.features.details.MetaDetails +import com.nuvio.app.features.details.formatRuntimeForDisplay @Composable fun DetailAdditionalInfoSection( @@ -30,7 +31,7 @@ fun DetailAdditionalInfoSection( val rows = buildList { meta.status?.let { add("Status" to it) } meta.releaseInfo?.let { add("Release Info" to formatReleaseDateForDisplay(it)) } - meta.runtime?.let { add("Runtime" to it.uppercase()) } + formatRuntimeForDisplay(meta.runtime)?.let { add("Runtime" to it) } meta.ageRating?.let { add("Certification" to it) } meta.country?.let { add("Origin Country" to it) } meta.language?.let { add("Original Language" to it.uppercase()) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt index 3d290f2d..52bcde1c 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.nuvio.app.features.details.MetaDetails import com.nuvio.app.features.details.MetaExternalRating +import com.nuvio.app.features.details.formatRuntimeForDisplay import com.nuvio.app.features.details.formatMetaReleaseLineForDetails import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_AUDIENCE import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_IMDB @@ -73,7 +74,7 @@ fun DetailMetaInfo( verticalArrangement = Arrangement.spacedBy(12.dp), ) { val releaseLine = formatMetaReleaseLineForDetails(meta) - val runtimeText = meta.runtime?.trim()?.takeIf { it.isNotBlank() }?.uppercase() + val runtimeText = formatRuntimeForDisplay(meta.runtime) val ageBadge = meta.ageRating?.trim()?.takeIf { it.isNotBlank() } val hasMdbImdbRating = meta.externalRatings.any { it.source == PROVIDER_IMDB } val hasMetaRow = releaseLine != null || diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt index a74a7ac6..efa1857e 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt @@ -64,6 +64,7 @@ import com.nuvio.app.features.details.MetaEpisodeCardStyle import com.nuvio.app.features.details.MetaVideo import com.nuvio.app.features.details.SeasonViewMode import com.nuvio.app.features.details.SeasonViewModeStorage +import com.nuvio.app.features.details.formatRuntimeFromMinutes import com.nuvio.app.features.details.metaVideoSeasonEpisodeComparator import com.nuvio.app.features.details.normalizeSeasonNumber import com.nuvio.app.features.details.seasonSortKey @@ -843,14 +844,7 @@ private fun rememberEpisodeHorizontalCardMetrics(maxWidthDp: Float): EpisodeHori } private fun formatEpisodeRuntime(runtimeMinutes: Int): String { - if (runtimeMinutes <= 0) return "" - val hours = runtimeMinutes / 60 - val minutes = runtimeMinutes % 60 - return when { - hours > 0 && minutes > 0 -> "${hours}h ${minutes}m" - hours > 0 -> "${hours}h" - else -> "${minutes}m" - } + return formatRuntimeFromMinutes(runtimeMinutes) } @OptIn(ExperimentalFoundationApi::class)