Show PWA source ratings (RT, IMDb, TMDB from Seerr) on the TV #24
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fm/wolfskin-23-ratings"
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#23
Wolfskin TV now shows ratings from the same source as the phone app (PWA): Seerr. Rotten Tomatoes critics and audience, IMDb (movies only) and TMDB, each as a badge plus number, using the PWA's rules. No Conductarr client, no new credentials, no new screens or settings.
What changed
Data and rules (
SourceRatings,SeerrRatingsService)ratingscombined(RT critics, RT audience, IMDb). Series:tv/{id}/ratings(RT critics and audience). TMDB:voteAverage/voteCount, fetched only when no other source has a score.ratingscombinedis treated as an answer ("no RT/IMDb data") and TMDB is still tried.seerr-api.yml): IMDbcriticsScoreis now a number (it wasinteger, which would throw on 8.8), addedcriticsScoreCount, added RTaudienceScore/audienceRatingto/tv/{id}/ratings, andCertified Freshto the TV critics enum so an unexpected label cannot break parsing.voteAverage/voteCountwere already present on movie and TV details.Discover screens (focused header on
SeerrDiscoverPage,DiscoverMovieDetailsHeader,DiscoverSeriesDetails): use the new model. The old "audience percent / 10 shown as a star" is gone. The Discover page now cancels the previous in-flight rating request when focus moves.Jellyfin-owned movie and series DETAILS headers: the TMDB id from
providerIds["Tmdb"]is resolved to the same Seerr ratings through an async side channel (MovieState.sourceRatings,SeriesState.sourceRatings). Until Seerr answers, the header shows the Jellyfin star and tomato, so there is no blank flash. Jellyfin's values stay as the fallback when Seerr is not configured, the title has no TMDB id, the call fails, or Seerr has no score at all for the title. Seerr's connection is established asynchronously after login, so the lookup follows the "Seerr active" flow rather than checking once.Left as they were: episodes, music, slideshow, TV guide, and the home focused header (below).
Open choices and how they were resolved
COMMUNITY_RATINGnow controls IMDb/TMDB,CRITIC_RATINGcontrols Rotten Tomatoes (critics and audience). Existing preferences keep working. The Discover headers never honoured the toggles and still do not.ic_rt_upright,ic_rt_spilled,ic_rating_imdb,ic_rating_tmdb); fresh and rotten tomatoes already existed and are identical to the PWA's. The conversion was mechanical (paths, circles, one gradient) with no loss. IMDb/TMDB are drawn 1.8x as wide as tall, like the PWA. I did not check the licence of the PWA's IMDb/TMDB artwork.8.8,7.6). The PWA prints the raw number, which would show an unrounded TMDB average like 7.633 [inferred, not seen live]. The PWA also links each badge to its source page; that is dropped on the TV.ProviderIds, so a focused home item has no TMDB id. Enriching it would need a changed field list for every home row plus a per-focus request, which is not the simple, safe change the brief asked for.Tmdbid on a box set is a TMDB collection id, not a movie id, so resolving it as a movie would show the wrong title's ratings.Tests
make test: 423 tests, 0 failures. New:SourceRatingsTest(12: PWA rules, zero-vote, TMDB-only-when-nothing-else, range checks, status-to-icon mapping and defaults, toggle mapping, Jellyfin fallback inwithSourceRatings) andSeerrRatingsServiceTest(11: endpoint choice per type, TMDB only as fallback, cache keying movie vs series with the same id, success cached once, failures not cached and retried, 404 as an answer, fallback to null for no TMDB id / episode / collection / call failure / Seerr inactive).make apk-debug: build successful.c54acc9c):make test0 failures (423 tests),make apk-debugsuccessful.Review follow-ups (head
04d659b1)MovieViewModel.init()runs again on every resume and the ratings flow never completes, so collectors stacked up. It now keeps aratingsJoband cancels it before relaunching. The series screen launches once from the VMinit, so it was already fine.criticsRating/audienceRatingfields are plain strings in the spec (they were enums), so an unexpected or new label can no longer fail the whole response; the score still shows, with the muted tomato for an unknown critics label. New test parses real JSON with unknown labels through the generated client.cachedFor), so a revisit shows the Seerr ratings immediately instead of flashing the Jellyfin values. A later null from Seerr keeps what is already shown.make test: 424 tests, 0 failures.make apk-debug: successful.Before/after capture
Real composables (
MovieDetailsHeader,DiscoverMovieDetailsHeader) rendered off-device with Robolectric native graphics at 1920x1080 (960x540dp, xhdpi), same title and same data in both states; the BEFORE images come from main (d78a23a8), the AFTER images from this branch. Files:/root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-23-ratings/capture/(before-jellyfin-movie.png,after-jellyfin-movie.png,before-discover-movie.png,after-discover-movie.png). Data: Jellyfin holds 8.1 community and 92 critic; Seerr has RT 92 Certified Fresh, RT audience 95 Upright, IMDb 8.5. Before on Discover:9.5 star / 92% tomato; after:92% tomato, 95% popcorn, IMDb 8.5. Not captured: backdrop, the real font on a TV, and the Jellyfin "Ends at" time differs between runs because it is clock-based.Not verified
ratingscombined/tv/{id}/ratingsresponse has been parsed. The spec changes follow Conductarr's Go client, its tests, and the PWA's fixtures.CriticRating, and so which owned titles fall back to it.Code review (Claude): PASS, no blocking findings. Reviewed head
c54acc9c1d5f37533760fd9da43a7326b43a9f7e.Checks:
make teston that head, 423 tests, 0 failures; PWA rules compared line by line with conductarrratings.go/TitleRatings.vue(observed-only, TMDB only as fallback, zero-vote unrated, range checks, RT icon from label with PWA defaults); Seerr spec edit exercised with a throwaway parse test on the generated client (decimal IMDb, int64 vote count, RT audience on TV,Certified Fresh, missing/null/unknown fields all parse); oldDiscoverRatingfully removed; owned-header fallback unchanged when Seerr is inactive / no TMDB id; toggle mapping; four capture PNGs; scope (no home/episode/collection/music/TV guide change, no versionCode/credential/Conductarr); tests are additions only and assert behaviour.Non-blocking notes (full detail in the review report):
MovieViewModel.init()runs from the VMinitand from every resume, so the new never-endingforItem(...).collectis launched repeatedly (2 duplicateratingscombinedcalls at open, one extra collector per resume). Keep and cancel a Job, or launch it once.cached()is only used by tests and could seed the initial state.%.1f(PWA prints the raw number); disclosed in the PR body.Certified Freshwas added.Not verified: a real Seerr answer (no Seerr on this host); nothing run on a TV.
Scoped re-check of the review-fix delta (
c54acc9c..04d659b1, 5 files, +89/-38), read in full by firstmate: (1) movie screen keeps aratingsJoband cancels it before relaunching, so resumes no longer stack collectors; the series screen already launches once. (2) A null answer no longer overwrites a value already shown (ratings ?: it.sourceRatings), and the initial state is seeded fromcachedFor(item), so a revisit does not flash Jellyfin values. (3) Successful cache entries expire after 6 hours (bound 300 kept). (4) The Seerr spec now types the RTcriticsRating/audienceRatinglabels as free strings (examples only, no enum), so an unexpected label can no longer drop the whole response; the model maps the string label. Tests: 424 pass per the author (one more than before, an unknown-label case). Nothing else changed from the reviewed headc54acc9c, which passed the full review. Status posted by firstmate for the delta.