Merge branch 'cmp-rewrite' of https://github.com/NuvioMedia/NuvioMobile into cmp-rewrite

This commit is contained in:
tapframe 2026-05-16 03:56:56 +05:30
commit efb23c2102
7 changed files with 96 additions and 3 deletions

View file

@ -248,6 +248,19 @@
<string name="collections_editor_tmdb_sort_popular">Popular</string>
<string name="collections_editor_tmdb_sort_top_rated">Top Rated</string>
<string name="collections_editor_tmdb_sort_recent">Recent</string>
<string name="collections_editor_tmdb_sort_vote_count">Most Voted</string>
<string name="collections_editor_tmdb_watch_region">Watch region</string>
<string name="collections_editor_tmdb_watch_region_helper">ISO 3166-1 country code where the title is available. Example: US, GB.</string>
<string name="collections_editor_tmdb_quick_watch_regions">Quick watch regions</string>
<string name="collections_editor_tmdb_watch_providers">Watch provider IDs</string>
<string name="collections_editor_tmdb_watch_providers_helper">Use TMDB watch provider IDs. Separate multiple with commas for AND, or pipes for OR.</string>
<string name="collections_editor_tmdb_watch_providers_placeholder">8|337|350</string>
<string name="collections_editor_tmdb_quick_watch_providers">Quick watch providers</string>
<string name="collections_editor_tmdb_watch_provider_netflix">Netflix</string>
<string name="collections_editor_tmdb_watch_provider_prime">Prime Video</string>
<string name="collections_editor_tmdb_watch_provider_disney">Disney+</string>
<string name="collections_editor_tmdb_watch_provider_apple">Apple TV+</string>
<string name="collections_editor_tmdb_watch_provider_hulu">Hulu</string>
<string name="collections_editor_tmdb_subtitle_list">TMDB List</string>
<string name="collections_editor_tmdb_subtitle_movie_collection">TMDB Movie Collection</string>
<string name="collections_editor_tmdb_subtitle_production">Production</string>

View file

@ -471,6 +471,11 @@ fun App() {
AuthScreen(modifier = Modifier.fillMaxSize())
}
AppGateScreen.ProfileSelection.name -> {
PlatformBackHandler(enabled = gateScreen == AppGateScreen.ProfileSelection.name) {
if (!autoSkipProfileSelection) {
gateScreen = AppGateScreen.Main.name
}
}
ProfileSelectionScreen(
onProfileSelected = { profile ->
ProfileRepository.selectProfile(profile.profileIndex)
@ -493,6 +498,9 @@ fun App() {
)
}
AppGateScreen.ProfileEdit.name -> {
PlatformBackHandler(enabled = gateScreen == AppGateScreen.ProfileEdit.name) {
gateScreen = AppGateScreen.ProfileSelection.name
}
ProfileEditScreen(
profile = editingProfile,
onBack = { gateScreen = AppGateScreen.ProfileSelection.name },

View file

@ -1125,6 +1125,7 @@ private fun TmdbSourcePickerScreen(
val sorts = listOf(
TmdbCollectionSort.POPULAR_DESC,
TmdbCollectionSort.VOTE_AVERAGE_DESC,
TmdbCollectionSort.VOTE_COUNT_DESC,
if (state.tmdbMediaType == TmdbCollectionMediaType.TV && !state.tmdbMediaBoth) {
TmdbCollectionSort.FIRST_AIR_DATE_DESC
} else {
@ -1353,6 +1354,54 @@ private fun TmdbSourcePickerScreen(
}
},
)
TmdbQuickChips(
label = stringResource(Res.string.collections_editor_tmdb_quick_watch_providers),
chips = listOf(
stringResource(Res.string.collections_editor_tmdb_watch_provider_netflix) to "8",
stringResource(Res.string.collections_editor_tmdb_watch_provider_prime) to "119",
stringResource(Res.string.collections_editor_tmdb_watch_provider_disney) to "337",
stringResource(Res.string.collections_editor_tmdb_watch_provider_apple) to "350",
stringResource(Res.string.collections_editor_tmdb_watch_provider_hulu) to "15",
),
onSelect = { value ->
CollectionEditorRepository.updateTmdbFilters { it.copy(withWatchProviders = value) }
},
)
TmdbFilterField(
label = stringResource(Res.string.collections_editor_tmdb_watch_providers),
helper = stringResource(Res.string.collections_editor_tmdb_watch_providers_helper),
value = state.tmdbFilters.withWatchProviders.orEmpty(),
placeholder = stringResource(Res.string.collections_editor_tmdb_watch_providers_placeholder),
onValueChange = { value ->
CollectionEditorRepository.updateTmdbFilters {
it.copy(withWatchProviders = value.ifBlank { null })
}
},
)
TmdbQuickChips(
label = stringResource(Res.string.collections_editor_tmdb_quick_watch_regions),
chips = listOf(
stringResource(Res.string.collections_editor_tmdb_country_us) to "US",
stringResource(Res.string.collections_editor_tmdb_country_uk) to "GB",
"Canada" to "CA",
"Australia" to "AU",
"Germany" to "DE",
),
onSelect = { value ->
CollectionEditorRepository.updateTmdbFilters { it.copy(watchRegion = value) }
},
)
TmdbFilterField(
label = stringResource(Res.string.collections_editor_tmdb_watch_region),
helper = stringResource(Res.string.collections_editor_tmdb_watch_region_helper),
value = state.tmdbFilters.watchRegion.orEmpty(),
placeholder = "US",
onValueChange = { value ->
CollectionEditorRepository.updateTmdbFilters {
it.copy(watchRegion = value.ifBlank { null })
}
},
)
}
}
}
@ -2255,6 +2304,7 @@ private fun tmdbSortLabel(sort: TmdbCollectionSort): String =
TmdbCollectionSort.ORIGINAL -> stringResource(Res.string.collections_editor_tmdb_sort_original)
TmdbCollectionSort.POPULAR_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_popular)
TmdbCollectionSort.VOTE_AVERAGE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_top_rated)
TmdbCollectionSort.VOTE_COUNT_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_vote_count)
TmdbCollectionSort.RELEASE_DATE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_recent)
TmdbCollectionSort.FIRST_AIR_DATE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_recent)
}

View file

@ -100,6 +100,7 @@ enum class TmdbCollectionSort(val value: String) {
ORIGINAL("original"),
POPULAR_DESC("popularity.desc"),
VOTE_AVERAGE_DESC("vote_average.desc"),
VOTE_COUNT_DESC("vote_count.desc"),
RELEASE_DATE_DESC("primary_release_date.desc"),
FIRST_AIR_DATE_DESC("first_air_date.desc"),
}
@ -149,6 +150,8 @@ data class TmdbCollectionFilters(
val withCompanies: String? = null,
val withNetworks: String? = null,
val year: Int? = null,
val watchRegion: String? = null,
val withWatchProviders: String? = null,
)
data class TmdbSourceImportMetadata(

View file

@ -325,6 +325,11 @@ object TmdbCollectionSourceResolver {
putIfNotBlank("with_original_language", filters.withOriginalLanguage)
putIfNotBlank("with_origin_country", filters.withOriginCountry)
putIfNotBlank("with_keywords", filters.withKeywords)
if (!filters.withWatchProviders.isNullOrBlank()) {
put("with_watch_providers", filters.withWatchProviders)
put("watch_region", filters.watchRegion?.takeIf { it.isNotBlank() } ?: "US")
put("with_watch_monetization_types", "flatrate|free|ads|rent|buy")
}
putIfNotBlank("year", filters.year?.takeIf { mediaType == TmdbCollectionMediaType.MOVIE }?.toString())
putIfNotBlank("first_air_date_year", filters.year?.takeIf { mediaType == TmdbCollectionMediaType.TV }?.toString())
putIfNotBlank(
@ -358,6 +363,7 @@ object TmdbCollectionSourceResolver {
compareByDescending<MetaPreview> { it.imdbRating?.toDoubleOrNull() ?: -1.0 }
.thenByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() },
)
TmdbCollectionSort.VOTE_COUNT_DESC.value -> sortedByDescending { it.voteCount ?: 0 }
TmdbCollectionSort.RELEASE_DATE_DESC.value,
TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> sortedByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() }
TmdbCollectionSort.POPULAR_DESC.value,
@ -395,6 +401,7 @@ object TmdbCollectionSourceResolver {
TmdbCollectionMediaType.TV -> firstAirDate
},
popularity = popularity,
voteCount = voteCount,
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
)
}
@ -412,6 +419,7 @@ object TmdbCollectionSourceResolver {
releaseInfo = releaseDate?.take(4),
rawReleaseDate = releaseDate,
popularity = popularity,
voteCount = voteCount,
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
)
}
@ -440,6 +448,7 @@ object TmdbCollectionSourceResolver {
TmdbCollectionMediaType.TV -> firstAirDate
},
popularity = popularity,
voteCount = voteCount,
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
)
}
@ -468,6 +477,7 @@ object TmdbCollectionSourceResolver {
TmdbCollectionMediaType.TV -> firstAirDate
},
popularity = popularity,
voteCount = voteCount,
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
)
}
@ -508,6 +518,7 @@ object TmdbCollectionSourceResolver {
when (sortBy) {
TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> TmdbCollectionSort.RELEASE_DATE_DESC.value
TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value
TmdbCollectionSort.VOTE_COUNT_DESC.value -> TmdbCollectionSort.VOTE_COUNT_DESC.value
null, "" -> TmdbCollectionSort.POPULAR_DESC.value
else -> sortBy
}
@ -516,6 +527,7 @@ object TmdbCollectionSourceResolver {
when (sortBy) {
TmdbCollectionSort.RELEASE_DATE_DESC.value -> TmdbCollectionSort.FIRST_AIR_DATE_DESC.value
TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value
TmdbCollectionSort.VOTE_COUNT_DESC.value -> TmdbCollectionSort.VOTE_COUNT_DESC.value
null, "" -> TmdbCollectionSort.POPULAR_DESC.value
else -> sortBy
}
@ -640,6 +652,7 @@ private data class TmdbPersonCreditCast(
@SerialName("release_date") val releaseDate: String? = null,
@SerialName("first_air_date") val firstAirDate: String? = null,
@SerialName("vote_average") val voteAverage: Double? = null,
@SerialName("vote_count") val voteCount: Int? = null,
val popularity: Double? = null,
)
@ -658,6 +671,7 @@ private data class TmdbPersonCreditCrew(
@SerialName("first_air_date") val firstAirDate: String? = null,
val job: String? = null,
@SerialName("vote_average") val voteAverage: Double? = null,
@SerialName("vote_count") val voteCount: Int? = null,
val popularity: Double? = null,
)
@ -675,6 +689,7 @@ private data class TmdbListItem(
@SerialName("release_date") val releaseDate: String? = null,
@SerialName("first_air_date") val firstAirDate: String? = null,
@SerialName("vote_average") val voteAverage: Double? = null,
@SerialName("vote_count") val voteCount: Int? = null,
val popularity: Double? = null,
)
@ -687,5 +702,6 @@ private data class TmdbCollectionPart(
@SerialName("backdrop_path") val backdropPath: String? = null,
@SerialName("release_date") val releaseDate: String? = null,
@SerialName("vote_average") val voteAverage: Double? = null,
@SerialName("vote_count") val voteCount: Int? = null,
val popularity: Double? = null,
)

View file

@ -80,10 +80,12 @@ fun DetailMetaInfo(
val runtimeText = formatRuntimeForDisplay(meta.runtime)
val ageBadge = meta.ageRating?.trim()?.takeIf { it.isNotBlank() }
val hasMdbImdbRating = meta.externalRatings.any { it.source == PROVIDER_IMDB }
val validImdbRating = meta.imdbRating
?.takeIf { raw -> raw.toDoubleOrNull()?.let { it > 0.0 } == true }
val hasMetaRow = releaseLine != null ||
runtimeText != null ||
ageBadge != null ||
(meta.imdbRating != null && !hasMdbImdbRating)
(validImdbRating != null && !hasMdbImdbRating)
if (hasMetaRow) {
Row(
verticalAlignment = Alignment.CenterVertically,
@ -108,7 +110,7 @@ fun DetailMetaInfo(
ageBadge?.let { badge ->
DetailHeroMetaBadge(text = badge)
}
if (meta.imdbRating != null && !hasMdbImdbRating) {
if (validImdbRating != null && !hasMdbImdbRating) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
@ -129,7 +131,7 @@ fun DetailMetaInfo(
}
Spacer(modifier = Modifier.width(5.dp))
Text(
text = meta.imdbRating,
text = validImdbRating,
style = MaterialTheme.typography.titleMedium,
color = ImdbYellow,
fontWeight = FontWeight.Bold,

View file

@ -15,6 +15,7 @@ data class MetaPreview(
val releaseInfo: String? = null,
val rawReleaseDate: String? = null,
val popularity: Double? = null,
val voteCount: Int? = null,
val imdbRating: String? = null,
val genres: List<String> = emptyList(),
)