Add filtering & sorting for discover grid pages (#1614)
## Description This PR adds sort & filter options for the discover grid pages. Sort options: - Popularity - Release date - "Community rating" (TMDB user vote) - Name Filter options: - Genre - Studio/Network - "Community rating" (TMDB user vote) - Parental rating - Production status (TV only) Also, two more tabs are added to the discover page for browsing movies and TV. These are essentially the same as clicking "View more" on the respective rows on the Discover tab. ### Limitations This PR hardcodes the region used for studio listing and filtering to "United States". I'll work on a follow up PR to enable users to specify this. The sort & filters are not persisted. I think they should be on the two new tabs, but not elsewhere, but this will be a follow up PR. ### Related issues Related to #702 Implements some of #1511 Rewrites the filter logic in #1610 ### Testing Emulator, but needs further testing ## Screenshots <img width="1280" height="720" alt="discover_filters Large" src="https://github.com/user-attachments/assets/cdefb95c-c4b7-4e06-9bac-b535d372ba03" /> ## AI or LLM usage None
This commit is contained in:
parent
552c3b5500
commit
d705a3ea01
15 changed files with 1200 additions and 157 deletions
|
|
@ -1,14 +1,24 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvDetails
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.filter.CommunityRatingFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DecadeFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverFilterBy
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverMovieContentRatingFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverMovieGenreFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverMovieStudiosFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverRatingFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverTvContentRatingFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverTvGenreFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverTvStatusFilter
|
||||
import com.github.damontecres.wholphin.data.filter.DiscoverTvStudiosFilter
|
||||
import com.github.damontecres.wholphin.data.filter.FavoriteFilter
|
||||
import com.github.damontecres.wholphin.data.filter.FilterBy
|
||||
import com.github.damontecres.wholphin.data.filter.FilterValueOption
|
||||
import com.github.damontecres.wholphin.data.filter.FilterVideoType
|
||||
import com.github.damontecres.wholphin.data.filter.GenreFilter
|
||||
import com.github.damontecres.wholphin.data.filter.ItemFilterBy
|
||||
import com.github.damontecres.wholphin.data.filter.OfficialRatingFilter
|
||||
import com.github.damontecres.wholphin.data.filter.PlayedFilter
|
||||
import com.github.damontecres.wholphin.data.filter.StudioFilter
|
||||
|
|
@ -20,6 +30,7 @@ import com.mayakapps.kache.InMemoryKache
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.filterApi
|
||||
import org.jellyfin.sdk.api.client.extensions.genresApi
|
||||
|
|
@ -47,6 +58,7 @@ class FilterOptionCache
|
|||
@param:IoCoroutineScope private val ioCoroutineScope: CoroutineScope,
|
||||
private val api: ApiClient,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val seerrService: SeerrService,
|
||||
) {
|
||||
private val cache =
|
||||
InMemoryKache<FilterOptionCacheKey, List<FilterValueOption>>(16) {
|
||||
|
|
@ -61,7 +73,7 @@ class FilterOptionCache
|
|||
*/
|
||||
suspend fun getFilterOptionValues(
|
||||
parentId: UUID?,
|
||||
filterOption: ItemFilterBy<*>,
|
||||
filterOption: FilterBy<*, *>,
|
||||
): List<FilterValueOption> {
|
||||
val cacheKey = FilterOptionCacheKey(serverRepository.currentUser?.id, parentId, filterOption)
|
||||
return try {
|
||||
|
|
@ -81,7 +93,7 @@ class FilterOptionCache
|
|||
private suspend fun getFilterOptionValues(
|
||||
userId: UUID?,
|
||||
parentId: UUID?,
|
||||
filterOption: ItemFilterBy<*>,
|
||||
filterOption: FilterBy<*, *>,
|
||||
) = when (filterOption) {
|
||||
GenreFilter -> {
|
||||
api.genresApi
|
||||
|
|
@ -173,11 +185,86 @@ class FilterOptionCache
|
|||
FilterValueOption("$it", it)
|
||||
}
|
||||
}
|
||||
|
||||
is DiscoverFilterBy -> {
|
||||
getDiscoverFilterOptionValues(filterOption)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getDiscoverFilterOptionValues(filterOption: DiscoverFilterBy<*>): List<FilterValueOption> {
|
||||
if (seerrService.active.firstOrNull() != true) {
|
||||
return emptyList()
|
||||
}
|
||||
return when (filterOption) {
|
||||
DiscoverMovieGenreFilter -> {
|
||||
seerrService.api.searchApi
|
||||
.discoverGenresliderMovieGet()
|
||||
.filter { it.name != null && it.id != null }
|
||||
.map {
|
||||
FilterValueOption(it.name!!, it.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
DiscoverTvGenreFilter -> {
|
||||
seerrService.api.searchApi
|
||||
.discoverGenresliderTvGet()
|
||||
.filter { it.name != null && it.id != null }
|
||||
.map {
|
||||
FilterValueOption(it.name!!, it.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
DiscoverMovieStudiosFilter -> {
|
||||
// TODO region
|
||||
seerrService.api.otherApi
|
||||
.watchprovidersMoviesGet("US")
|
||||
.filter { it.name != null && it.id != null }
|
||||
// TODO not what this is for
|
||||
// .sortedBy { it.displayPriority }
|
||||
.map {
|
||||
// TODO logo?
|
||||
FilterValueOption(it.name!!, it.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
DiscoverTvStudiosFilter -> {
|
||||
// TODO region
|
||||
seerrService.api.otherApi
|
||||
.watchprovidersTvGet("US")
|
||||
.filter { it.name != null && it.id != null }
|
||||
// .sortedBy { it.displayPriority }
|
||||
.map {
|
||||
FilterValueOption(it.name!!, it.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
DiscoverTvStatusFilter -> {
|
||||
TvDetails.Status.entries.map {
|
||||
FilterValueOption(it.value, it)
|
||||
}
|
||||
}
|
||||
|
||||
DiscoverMovieContentRatingFilter -> {
|
||||
listOf("NR", "G", "PG", "PG-13", "R", "NC-17")
|
||||
.map { FilterValueOption(it, it) }
|
||||
}
|
||||
|
||||
DiscoverTvContentRatingFilter -> {
|
||||
listOf("NR", "TV-Y", "TV-Y7", "TV-G", "TV-PG", "TV-14", "TV-MA")
|
||||
.map { FilterValueOption(it, it) }
|
||||
}
|
||||
|
||||
DiscoverRatingFilter -> {
|
||||
(1..10).map {
|
||||
FilterValueOption("$it", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class FilterOptionCacheKey(
|
||||
val userId: UUID?,
|
||||
val parentId: UUID?,
|
||||
val filterOption: ItemFilterBy<*>,
|
||||
val filterOption: FilterBy<*, *>,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue