Show PWA source ratings on every surface through one shared rating row #26
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fm/wolfskin-25-ratings-everywhere"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes justinzeus/wolfskin#25
Every place the TV drew a Jellyfin rating now draws the PWA's ratings (Seerr: Rotten Tomatoes critics/audience, IMDb for movies, TMDB only when nothing else has a score) through ONE row component, fed by ONE data path. The Jellyfin star/tomato drawing code is deleted.
What was wrong
ProviderIds, so a focused home item had no TMDB id to ask Seerr about.Tmdbprovider id is the episode's id, not the series'.The design
SourceRatingsRow(ui/components/SourceRatingsRow.kt) draws badge + number pairs in the PWA order (RT critics, RT audience, IMDb or TMDB), same drawables, same spacing, honouring theCRITIC_RATING(RT) andCOMMUNITY_RATING(IMDb/TMDB) display toggles.QuickDetailsis the only caller; every header gets its ratings throughQuickDetails(ratings = ...)orQuickDetails(item, ...).rememberSourceRatings(item)->SeerrRatingsService.forItem/cachedFor. Which items have ratings is decided in exactly one place,SeerrRatingsService, by the PWA's rule (ratings belong to the title, never to cards or lists):ratingscombined+ TMDB fallback)tv/{id}/ratings+ TMDB fallback)Ep, from Seerr's season data (see below)ProviderIdsadded to the home row field lists (HomeItemFields, newHomeSlimItemFieldsfor continue watching / next up). Small, only on home requests.SeerrRatingsService.resolveTargetfinds the TMDB id for anything that arrives without it: an episode/season resolves through itsseriesId(one JellyfingetItem), a movie/series without provider ids looks itself up. Results are cached per Jellyfin id for 6 h in a second Guava cache (including "this series has no TMDB id"); failures are not cached. Ratings keep the existing type-qualified(movie|tv, tmdbId)cache with the 6 h TTL, so the 2nd..nth episode of a series needs no wait and no request.forItememits cached ratings at once, otherwise waits 350 ms, then asks Seerr.rememberSourceRatingscollects it in aLaunchedEffect(item.id), so moving focus cancels the wait or the in-flight request. A fast scroll never reaches Seerr. It also resets to the new item's cached value so the previous item's ratings are never shown.QuickDetailsData.communityRating/criticRating/criticRatingFirst, theAnnotatedStringbuilding inBaseItem.kt, the inline-contentstar/fresh/rottenplumbing,SourceRatings.withSourceRatings/appendRatings,StarRating,StarRatingPrecision,TomatoRating, the old preview, the per-ViewModel ratings state/collectors inMovieViewModel/SeriesViewModel(which also removes the duplicate-collector shape from the PR 24 review).SimpleStarRating"7+" thresholds in the rating filter menus, the subtitle-search result rating inDownloadSubtitlesDialog.kt, and the community/critic rating sort and filter (server-side Jellyfin queries).SeerrRatingsService.get(type, id)directly because they start from a Seerr id, not a Jellyfin item; it is the same service and cache.Per-surface inventory
HomePage.kt,HomePageHeader)CollectionFolderGrid.kt)HomePageHeaderCollectionDetails.kt)HomePageHeaderCollectionFolderList.kt)QuickDetails(item)CollectionDetailsHeader.kt)MovieDetailsHeader.kt)SeriesDetails.kt)EpisodeDetailsHeader.kt, the S05E01 case)TMDB Ep 8.1)FocusedEpisodeHeader.kt), home focused episodeAlbumDetailsPage.kt), artist page (ArtistDetailsPage.kt)ImageDetailsHeader.kt)TvGuideHeader.kt)Episode TMDB score (second commit)
Owner asked for each episode's own TMDB score. Episodes now show: series RT critics, series RT audience, then the TMDB badge with
Epand the episode's score (e.g.[TMDB] Ep 8.1).Epso nobody mistakes it for the series score. (The series TMDB is only ever shown when the series has no RT score, and in that case it is replaced by the episode score.) Showing both TMDB numbers would put two identical badges side by side.GET /tv/{tvId}/season/{seasonNumber}, episodes'voteAverage/voteCount. The series TMDB id comes from the existingresolveTarget. The season is fetched once and cached by(tmdbId, seasonNumber)for 6 h in a third Guava cache, so every other episode of that season costs no request and no wait; failures are not cached, a 404 (TMDB has no such season) is cached as an answer. Same 350 ms debounce/cancel as everything else.RatingEntry.EpisodeTmdbgoes through the sameSourceRatingsRow/QuickDetailsas everything else, drawn with the same TMDB drawable, and is controlled by the community rating (IMDb/TMDB) display setting while RT stays under the critic rating setting.Season/Episodemodel (non-nullable name/overview/etc.), which would throw on a null TMDB field. The season endpoint now returns newSeasonDetails/SeasonEpisodeschemas with every TMDB-optional field nullable andvoteAveragea decimal,voteCountint64. TheSeasonmodel used by Discover is untouched.Tests
make test(viadocker/gradle.sh): BUILD SUCCESSFUL, 462 tests, 0 failures, 0 skipped (446 before the episode commit).SourceRatingsRowTest(Robolectric compose): whatQuickDetailsdraws for movie, series, episode, season, and collection/music/photo/program; JellyfincommunityRating/criticRating(7.8 / 59%) set on every item and asserted never drawn; Seerr-has-nothing draws no rating; both display toggles; text/dot order.SeerrRatingsItemsTest: the per-kind decision, an episode's own TMDB id is never used, home items resolve through Jellyfin once and are cached, "no TMDB id" cached, failed lookups retried, scrolling past 20 items makes zero Seerr calls, resting on one asks once after 350 ms, moving on cancels an in-flight request.SourceRatingsTest,SeerrRatingsServiceTestupdated to the new model.Ep 8.1drawn next to RT and following the display settings.make apk-debug: BUILD SUCCESSFUL on both commits.Not verified
ratingscombined/tv/{id}/ratingsanswers and the focus-driven debounce were exercised only against mocks and Robolectric, not on the TV.ProviderIds.Tmdbfor the series of every episode is assumed from Jellyfin's normal metadata; a series without it shows no rating.main853a3989vs after = this branch, same data).Gates and captures.
853a3989, after = this branch) are in /root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-25-ratings-everywhere/capture : before-/after- for home-movie, home-episode, series-page, episode-page (Desperate Housewives S05E01), season-overview-episode (focused episode on the series overview), collection-header, movie-page (control).Code review (Claude): PASS, no blocking findings. Reviewed head
2ccce98dc1c85a9ed533ba686ce43a5221daf25f.Checks
make test: BUILD SUCCESSFUL, 446 tests, 0 failures/errors/skipped (matches the PR body). No apk build, no device.origin/main): ratings are rendered once per title inTitleWorkspace.vue(TitleRatingsinside the title heading);EpisodePanel.vuehas no rating at all. The claim "an episode is a row inside the series title page whose header carries the series ratings" holds, so episode/season = series ratings, collection/music/photo/program = none matches the PWA.resolveTarget/caches: an episode's ownTmdbid is never used (owner =seriesId); missingseriesId, series without TMDB, season, box set all end in no rating without a call or crash; failures are not cached; both caches bounded (300, 6 h).LaunchedEffect(item.id)means no stale rating of the previous item, and fast scrolling makes zero Seerr calls (covered by tests). Seerr inactive: no request at all, nothing drawn, no stray dot/separator (also with both toggles off); the PR body states this consequence.ProviderIdsonly added to the home lists (HomeItemFields, newHomeSlimItemFields);SlimItemFieldsand its other consumers are untouched.git grepfinds no other drawing path; onlySimpleStarRating(filter thresholds), subtitle-search rating and sort/filter by rating remain, as intended. Removed tests were for the deletedwithSourceRatings/Jellyfin fallback; replaced bySeerrRatingsItemsTestandSourceRatingsRowTest. Captures show the row correct at TV scale.app/src; no build/version/signing change, no new screen/setting, no Conductarr client.Non-blocking notes
chosenStreamsinMovieViewModel.kt:308andSeriesViewModel.kt:898.getItem.rememberSourceRatingsthrough an item change.Full report:
/root/.treehouse/firstmate-7bab20/2/firstmate/data/review-wolfskin-26/report.mdEpisode TMDB commit
85365aa3: make test 462 tests, 0 failures; make apk-debug BUILD SUCCESSFUL. Captures in /root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-25-ratings-everywhere/capture : episode-tmdb-before-/episode-tmdb-after- for episode-page, home-episode and season-overview-episode (before =2ccce98d, series RT only; after = TMDB badge with 'Ep 8.1' next to the series RT 63% / 71%). Same data, Robolectric 1920x1080.Code review (Claude), follow-up for the per-episode TMDB badge: PASS, no blocking findings. Reviewed head
85365aa3c6c66ef10dffb8dfc91a8de54b21a382(delta2ccce98d..85365aa3; the earlier pass on2ccce98dstands).Checks
make test: 462 tests, 0 failures/errors/skipped (matches the author). Two earlier runs hit HTTP 429 from Maven Central while Robolectric downloaded its jars; I reran with the jars from a mirror in a scratch worktree (not part of the PR).episode-tmdb-after-*show... tomato 63% / popcorn 71% / TMDB "Ep 8.1". The "Ep" label sits in front of the number; RT stays the series' scores and the series' own TMDB is dropped, so there is one TMDB number and it reads as the episode's.RatingEntry.EpisodeTmdbis drawn bySourceRatingsRow/ratingsText, and belongs to the community-rating toggle (RT still follows the critic toggle).GET /tv/:id/season/:nreturns episodes withepisodeNumber,voteAverage,voteCount; the yml models them nullable (decimal average, int64 count). One season request is cached per (series TMDB id, season) for 6 h and serves every episode of it. The key uses the series id fromresolveTarget, never the episode's ownTmdbid. Failures are not cached (404 is), debounce/cancel are unchanged, null/zero votes or out-of-range scores mean unrated (never 0.0), same one-decimal format. Seasons and other kinds are unchanged; episode header, focused-episode header and home/grid headers all go through the same path.ratingsTextcalls were updated for the new label parameter; nothing weakened.Non-blocking notes
chosenStreamsinMovieViewModel.kt:308andSeriesViewModel.kt:898(earlier note) was not cleaned up.Full report:
/root/.treehouse/firstmate-7bab20/2/firstmate/data/review-wolfskin-26/report.md