diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 782a24e0..8ed05d6c 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -247,6 +247,19 @@
Popular
Top Rated
Recent
+ Most Voted
+ Watch region
+ ISO 3166-1 country code where the title is available. Example: US, GB.
+ Quick watch regions
+ Watch provider IDs
+ Pipe-separated TMDB provider IDs. Example: 8|337 for Netflix and Disney+.
+ 8|337|350
+ Quick watch providers
+ Netflix
+ Prime Video
+ Disney+
+ Apple TV+
+ Hulu
TMDB List
TMDB Movie Collection
Production
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
index 7219395a..97b44414 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
@@ -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_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 })
+ }
+ },
+ )
+ 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 })
+ }
+ },
+ )
}
}
}
@@ -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)
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt
index 31962922..578445ae 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt
@@ -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(
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
index 3f37d3d8..816d1228 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
@@ -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")
+ }
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 { 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,
)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeModels.kt
index b51ed66b..dbf8b793 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeModels.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeModels.kt
@@ -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 = emptyList(),
)