diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt index 1a484f90..df9488ba 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt @@ -184,21 +184,23 @@ class SeerrService ) } } - val current = seerrServerRepository.current.firstOrNull() ?: return null - val cacheImages = current.serverConfig.cacheImages == true - val base = - if (cacheImages) { - current.server.url.removeSuffix("/") + "/imageproxy/tmdb" - } else { - "https://image.tmdb.org" - } - val prefix = - when (imageType) { - ImageType.PRIMARY -> "/t/p/w500" - ImageType.BACKDROP -> "/t/p/w${backdropWidth}_and_h${backdropHeight}_multi_faces" - else -> throw IllegalArgumentException("Image type not supported: $imageType") - } - return "${base}${prefix}$path" + return path?.takeIf { it.isNotNullOrBlank() }?.let { + val current = seerrServerRepository.current.firstOrNull() ?: return null + val cacheImages = current.serverConfig.cacheImages == true + val base = + if (cacheImages) { + current.server.url.removeSuffix("/") + "/imageproxy/tmdb" + } else { + "https://image.tmdb.org" + } + val prefix = + when (imageType) { + ImageType.PRIMARY -> "/t/p/w500" + ImageType.BACKDROP -> "/t/p/w${backdropWidth}_and_h${backdropHeight}_multi_faces" + else -> throw IllegalArgumentException("Image type not supported: $imageType") + } + return "${base}${prefix}$path" + } } suspend fun getProfilesAndFolders(type: SeerrItemType): SeerrRequestData { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt index 9afde46e..86c55d38 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt @@ -11,6 +11,7 @@ import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverRating import com.github.damontecres.wholphin.data.model.RemoteTrailer +import com.github.damontecres.wholphin.data.model.RequestStatus import com.github.damontecres.wholphin.data.model.SeerrItemType import com.github.damontecres.wholphin.data.model.SeerrPermission import com.github.damontecres.wholphin.data.model.Trailer @@ -20,6 +21,7 @@ import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.SeerrServerRepository import com.github.damontecres.wholphin.services.SeerrService import com.github.damontecres.wholphin.services.SeerrUserConfig +import com.github.damontecres.wholphin.ui.equalsNotNull import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination @@ -100,9 +102,13 @@ class DiscoverMovieViewModel updateCanCancel() viewModelScope.launchIO { - val result = - seerrService.api.moviesApi.movieMovieIdRatingsGet(movieId = item.id) - _state.update { it.copy(rating = DiscoverRating(result)) } + val rating = + getDiscoverRating(item.id) { + DiscoverRating( + seerrService.api.moviesApi.movieMovieIdRatingsGet(movieId = item.id), + ) + } + _state.update { it.copy(rating = rating) } } if (state.value.similar.isEmpty()) { viewModelScope.launchIO { @@ -248,7 +254,12 @@ fun canUserCancelRequest( ( // User requested this user.hasPermission(SeerrPermission.REQUEST) && - requests?.any { it.requestedBy?.id == user?.id } == true + requests + .orEmpty() + .any { + equalsNotNull(it.requestedBy?.id, user?.id) && + RequestStatus.from(it.status) < RequestStatus.APPROVED + } ) data class DiscoverMovieState( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesViewModel.kt index 3efc3f6e..b473d510 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesViewModel.kt @@ -105,8 +105,11 @@ class DiscoverSeriesViewModel updateCanCancel() viewModelScope.launchIO { - val result = seerrService.api.tvApi.tvTvIdRatingsGet(tvId = item.id) - _state.update { it.copy(rating = DiscoverRating(result)) } + val rating = + getDiscoverRating(item.id) { + DiscoverRating(seerrService.api.tvApi.tvTvIdRatingsGet(tvId = item.id)) + } + _state.update { it.copy(rating = rating) } } if (state.value.similar.isEmpty()) { viewModelScope.launchIO { @@ -183,6 +186,18 @@ class DiscoverSeriesViewModel seasonAvailability[seasonNumber] = availability } } + tv.mediaInfo?.seasons?.forEach { + it.seasonNumber?.let { seasonNumber -> + val status = if (is4k) it.status4k else it.status + val availability = + SeerrAvailability.from(status) ?: SeerrAvailability.UNKNOWN + val current = + seasonAvailability.getOrDefault(seasonNumber, SeerrAvailability.UNKNOWN) + if (availability > current) { + seasonAvailability[seasonNumber] = availability + } + } + } tv.mediaInfo ?.requests @@ -209,10 +224,6 @@ class DiscoverSeriesViewModel tv.seasons?.firstOrNull { it.seasonNumber == seasonNumber }?.let { val availability = when (status) { - RequestStatus.UNKNOWN -> { - SeerrAvailability.UNKNOWN - } - RequestStatus.PENDING -> { SeerrAvailability.PENDING } @@ -229,22 +240,39 @@ class DiscoverSeriesViewModel SeerrAvailability.UNKNOWN } - RequestStatus.COMPLETED -> { + RequestStatus.UNKNOWN, + RequestStatus.COMPLETED, + -> { seasonAvailability.getOrDefault( seasonNumber, SeerrAvailability.UNKNOWN, ) } } + val defaultEditable = + availability != SeerrAvailability.AVAILABLE && + availability != SeerrAvailability.PARTIALLY_AVAILABLE && + availability != SeerrAvailability.PROCESSING && + availability != SeerrAvailability.BLOCKLISTED RequestSeason( season = it, status = status, availability = availability, - editable = editable.getOrDefault(seasonNumber, true), + editable = editable.getOrDefault(seasonNumber, defaultEditable), ) } } Timber.v("Got %s seasons, is4k=%s", requestSeasons.size, is4k) +// requestSeasons.forEach { +// Timber.v( +// "is4k=%s, season %s: availability=%s, status=%s, editable=%s", +// is4k, +// it.season.seasonNumber, +// it.availability, +// it.status, +// it.editable, +// ) +// } _state.update { if (is4k) { it.copy(seasons4k = requestSeasons) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverUtils.kt new file mode 100644 index 00000000..8dfc4290 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverUtils.kt @@ -0,0 +1,26 @@ +package com.github.damontecres.wholphin.ui.detail.discover + +import com.github.damontecres.wholphin.api.seerr.infrastructure.ClientException +import com.github.damontecres.wholphin.data.model.DiscoverRating +import kotlinx.coroutines.CancellationException +import timber.log.Timber + +suspend fun getDiscoverRating( + id: Int, + func: suspend () -> DiscoverRating, +): DiscoverRating? = + try { + func.invoke() + } catch (ex: CancellationException) { + throw ex + } catch (ex: ClientException) { + if (ex.statusCode == 404) { + Timber.w("No rating for %s", id) + } else { + Timber.e(ex, "Error fetching rating for %s", id) + } + null + } catch (ex: Exception) { + Timber.e(ex, "Error fetching rating for %s", id) + null + } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/RequestSeasons.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/RequestSeasons.kt index 46c74c58..c482484e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/RequestSeasons.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/RequestSeasons.kt @@ -83,11 +83,13 @@ fun RequestSeasons( mutableStateSetOf( *seasons .filter { season -> - season.status == RequestStatus.PENDING || - season.status == RequestStatus.APPROVED || - season.status == RequestStatus.COMPLETED || - season.availability == SeerrAvailability.PARTIALLY_AVAILABLE || - season.availability == SeerrAvailability.AVAILABLE + !season.editable && ( + season.status == RequestStatus.PENDING || + season.status == RequestStatus.APPROVED || + season.status == RequestStatus.COMPLETED || + season.availability == SeerrAvailability.PARTIALLY_AVAILABLE || + season.availability == SeerrAvailability.AVAILABLE + ) }.mapNotNull { season -> season.season.seasonNumber } .toTypedArray(), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt index 360bdfb5..9702f9fa 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt @@ -30,7 +30,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.R -import com.github.damontecres.wholphin.api.seerr.infrastructure.ClientException import com.github.damontecres.wholphin.data.filter.DiscoverFilter import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverRating @@ -42,6 +41,7 @@ import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.SeerrService import com.github.damontecres.wholphin.ui.components.Genre import com.github.damontecres.wholphin.ui.data.RowColumn +import com.github.damontecres.wholphin.ui.detail.discover.getDiscoverRating import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.listToDotString import com.github.damontecres.wholphin.ui.main.HomePageHeader @@ -262,13 +262,11 @@ class SeerrDiscoverViewModel return@launchIO } val result = - try { + getDiscoverRating(id) { when (type) { SeerrItemType.MOVIE -> { DiscoverRating( - seerrService.api.moviesApi.movieMovieIdRatingsGet( - movieId = id, - ), + seerrService.api.moviesApi.movieMovieIdRatingsGet(movieId = id), ) } @@ -284,18 +282,8 @@ class SeerrDiscoverViewModel DiscoverRating(null, null) } } - } catch (ex: ClientException) { - if (ex.statusCode == 404) { - Timber.w("No rating found for %s", id) - DiscoverRating(null, null) - } else { - Timber.e(ex, "Error getting rating for %s", id) - return@launchIO - } - } catch (ex: Exception) { - Timber.e(ex, "Error getting rating for %s", id) - return@launchIO - } + } ?: DiscoverRating(null, null) + ratingCache.put(id, result) rating.update { ratingCache.asMap().toMap() diff --git a/app/src/main/seerr/seerr-api.yml b/app/src/main/seerr/seerr-api.yml index c9c617cf..354f3298 100644 --- a/app/src/main/seerr/seerr-api.yml +++ b/app/src/main/seerr/seerr-api.yml @@ -1304,6 +1304,12 @@ components: type: integer example: 0 description: Availability of the media. 1 = `UNKNOWN`, 2 = `PENDING`, 3 = `PROCESSING`, 4 = `PARTIALLY_AVAILABLE`, 5 = `AVAILABLE`, 6 = `DELETED` + seasons: + type: array + readOnly: true + nullable: true + items: + $ref: '#/components/schemas/Season' requests: type: array readOnly: true