Skip to content

feat(title-card): display ratings on media cards#3154

Open
u61d wants to merge 5 commits into
seerr-team:developfrom
u61d:feat/title-card-ratings
Open

feat(title-card): display ratings on media cards#3154
u61d wants to merge 5 commits into
seerr-team:developfrom
u61d:feat/title-card-ratings

Conversation

@u61d

@u61d u61d commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Displays ratings on movie and series cards when the card details are shown.

Rotten Tomatoes and IMDb ratings load after a card is briefly opened, while the existing TMDB score is available immediately. The layout remains compact on smaller screens.

How Has This Been Tested?

  • Ran eslint, prettier check, typecheck, production build, full test suite, translation extraction, and git diff --check
  • Checked cards with Rotten Tomatoes, IMDb, and TMDB ratings
  • Checked cards where only some rating providers returned data
  • Verified brief hovers do not trigger unnecessary requests
  • Verified ratings remain visible during the card fade-out
  • Tested desktop and Galaxy S10-sized mobile layouts

Screenshots / Logs (if applicable)

rating-normal

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • New Features

    • Ratings badges on title cards showing IMDb, Rotten Tomatoes (critics/audience) and TMDB scores with localized labels, icons and accessibility support; badges render only when scores exist.
  • Improvements

    • Concurrent fetching of rating providers with clearer handling when providers fail.
    • Ratings requests delayed briefly after cards become visible to reduce unnecessary requests.
    • Minor title heading style refinement.

@u61d u61d requested a review from a team as a code owner June 12, 2026 00:52
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 21e98924-55e9-4428-b431-4b3b2d16c3e2

📥 Commits

Reviewing files that changed from the base of the PR and between 4e50292 and 068c05b.

📒 Files selected for processing (2)
  • src/components/TitleCard/TitleCardRatings.tsx
  • src/i18n/locale/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/i18n/locale/en.json
  • src/components/TitleCard/TitleCardRatings.tsx

📝 Walkthrough

Walkthrough

Adds a delayed-loading TitleCardRatings React component and i18n strings, and refactors the movie ratings route to fetch RottenTomatoes and IMDb concurrently, combining settled results via a new helper that returns provider ratings and an allProvidersFailed flag.

Changes

Title Card Ratings Display

Layer / File(s) Summary
Rating combination helper and API refactor
server/api/ratings.ts, server/routes/movie.ts
New combineMovieRatingResults merges Promise.allSettled results; /movies/:id/ratingscombined now fetches RT and conditionally IMDb concurrently, logs provider failures, and returns 500 when all attempted providers failed, 404 when none returned ratings.
TitleCardRatings React component
src/components/TitleCard/TitleCardRatings.tsx
New component delays SWR requests by 300ms after visible, fetches movie/tv rating endpoints, disables focus retry, derives available badges (RT audience/critics precedence, IMDb, TMDB from userScore), and renders a compact row of icons and formatted scores.
TitleCard integration and styling
src/components/TitleCard/index.tsx
Imports and renders TitleCardRatings, forwards id, mediaType, userScore, and visible={showDetail}, and updates title <h1> class from text-xl to text-lg.
Localization strings
src/i18n/locale/en.json
Adds components.TitleCard.TitleCardRatings.* keys for IMDb, Rotten Tomatoes (audience/critics), TMDB, and a generic Ratings label with {score} placeholders.
sequenceDiagram
  participant UI as TitleCard
  participant RatingsAPI as /movies/:id/ratingscombined
  participant RTService as RottenTomatoesService
  participant IMDbService as IMDbService

  UI->>RatingsAPI: request combined ratings
  RatingsAPI->>RTService: fetch RT
  RatingsAPI->>IMDbService: conditional fetch IMDb (if imdb_id)
  RTService-->>RatingsAPI: settled result
  IMDbService-->>RatingsAPI: settled result
  RatingsAPI->>RatingsAPI: combineMovieRatingResults(rtResult, imdbResult, imdbAttempted)
  RatingsAPI-->>UI: 200 { ratings } or 404/500
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Ratings now hop onto the card with glee,
Critics, crowds, and IMDb in a tree,
Tiny badges gleam with numbers bright,
A hop, a nibble, and the scores take flight,
I celebrate the badges — hip hooray for me!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding ratings display functionality to media cards in the title-card component.
Linked Issues check ✅ Passed All coding requirements from issue #1269 are met: ratings from multiple providers (RT, IMDb, TMDB) are displayed on selection cards, ratings load when cards are opened, and the implementation supports both movies and TV series.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing ratings display on media cards. Backend refactoring for concurrent rating fetches, new React component for rendering ratings, internationalization support, and component integration are all in-scope for the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/TitleCard/TitleCardRatings.tsx`:
- Around line 72-78: The logic for hasRtCriticsScore incorrectly suppresses
critics when an audience score exists; remove the "!hasRtAudienceScore" gating
so hasRtCriticsScore independently checks RT data (i.e., verify typeof
rtRating?.criticsScore === 'number' && rtRating.criticsRating !== undefined)
while keeping hasRtAudienceScore as is, so both badges can be rendered when both
audience and critics ratings are present.
- Around line 70-80: The ratings logic in TitleCardRatings.tsx is not gated to
only 'movie' or 'tv', so collection (and other) mediaTypes can incorrectly show
TMDB/RT badges; update the component to only compute and allow badges when
mediaType === 'movie' || mediaType === 'tv' by adding a media-type guard (either
an early return/null render for other types or wrap the computed flags like
rtRating, imdbRating, hasRtAudienceScore, hasRtCriticsScore, hasImdbScore,
hasTmdbScore with a condition checking (mediaType === 'movie' || mediaType ===
'tv')); ensure you reference the existing symbols rtRating, imdbRating,
hasRtAudienceScore, hasRtCriticsScore, hasImdbScore, hasTmdbScore and apply the
guard so collections cannot light up TMDB/RT badges.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b34d20c8-b089-4644-a38a-967b56d3fc3d

📥 Commits

Reviewing files that changed from the base of the PR and between 784faa9 and 0c53a3a.

📒 Files selected for processing (5)
  • server/api/ratings.ts
  • server/routes/movie.ts
  • src/components/TitleCard/TitleCardRatings.tsx
  • src/components/TitleCard/index.tsx
  • src/i18n/locale/en.json

Comment thread src/components/TitleCard/TitleCardRatings.tsx Outdated
Comment thread src/components/TitleCard/TitleCardRatings.tsx
Comment thread src/components/TitleCard/index.tsx Outdated
userScore={userScore}
visible={showDetail}
/>
{(mediaType === 'movie' || mediaType === 'tv') && (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the rating card should only appear if there is a userScore not because it's a TV or movie.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I will render the ratings row when userScore is available and keep the external provider requests conditional on the media type.

interface TitleCardRatingsProps {
id: number;
mediaType: MediaType;
mediaType: Extract<MediaType, 'movie' | 'tv'>;

@M0NsTeRRR M0NsTeRRR Jun 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here it should be handled as you did below with mediaType === 'xxxx' when making a request to the provider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I will restore the general MediaType here and keep the movie/TV checks only on the relevant provider requests.

<span
className="flex min-w-0 items-center gap-0.5 md:gap-1"
aria-label={intl.formatMessage(messages.tmdbUserScore, {
score: Math.round(userScore * 10),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the original rating scale (I prefer this, as users will see the same value as on the external website) or standardize everything to a 0–100% scale?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep the original rating scale. We just display the score from the websites, we're not customizing / modifying it on Seerr.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. I will keep the original provider scales and display the TMDB score on its 0-10 scale.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction after checking the provider display: TMDB presents its User Score as a percentage, including in the approved Design A examples. I will therefore keep RT and TMDB as percentages and IMDb on its 0-10 scale.


const messages = defineMessages('components.TitleCard.TitleCardRatings', {
ratings: 'Ratings',
rottenTomatoesAudienceScore: 'Rotten Tomatoes Audience Score: {score}%',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use the score inside the aria-label ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provider logos are hidden from assistive technologies, so the score in the label lets a screen reader announce the provider and its value together. Without it, the visible number would not have useful context.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but the note is already inside the tag that’s why I’m asking. It seems a bit redundant 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, the visible score is already announced. I will remove the parent aria-label and add visually hidden provider text before the visible value instead, so the score is only announced once while still having provider context.

@gauthier-th

Copy link
Copy Markdown
Member

@u61d are you doing all your contributions with AI? Or some AI agent?
We suspect all of your contributions are just AI-generated, and your answers also make us think you're heavily using LLM to respond to our comments.
You also didn't disclose AI usage, as asked in our contribution guide and in the PR todolist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display the score at the selection page

3 participants