Skip to content

feat: MyAnimeList community score on anime details (#45) - #136

Merged
ProdigyV21 merged 2 commits into
mainfrom
feat/mal-score-anime
Apr 5, 2026
Merged

feat: MyAnimeList community score on anime details (#45)#136
ProdigyV21 merged 2 commits into
mainfrom
feat/mal-score-anime

Conversation

@ProdigyV21

Copy link
Copy Markdown
Owner

Closes #45.

Displays a MAL community score badge next to the IMDb badge on anime details pages. Only renders for anime content and only when Jikan returns a score — non-anime and unscored entries see no visual change.

Flow

IMDB id -> ARM API (imdb -> mal_id) -> Jikan v4 (/anime/{mal_id} -> score)

ARVIO already uses the ARM API in SkipIntroRepository for the AniSkip intro-marker feature, so the IMDB to MAL hop is a known working path. Jikan v4 is the unofficial MyAnimeList REST API and is widely used by anime apps for community score display.

Changes

  • JikanApi.kt (new) — Retrofit interface with GET /anime/{malId} and response models that deserialize the score field.
  • AnimeScoreRepository.kt (new, @singleton) — wraps IMDB to MAL to score with in-memory LRU caching (256 entries each for imdbId -> malId and malId -> score, including negative caching to avoid re-hitting ARM for titles that aren't in its database). Uses withTimeoutOrNull(2_000L) on each hop so slow responses don't stall details load. All exceptions swallowed to null — caller hides the badge.
  • AppModule.kt — new Retrofit instance for https://api.jikan.moe/v4/ and @Provides binding for JikanApi, matching the existing ArmApi provider pattern.
  • DetailsUiState — new malScore: Double? field (nullable, default null).
  • DetailsViewModel — now takes AnimeScoreRepository. Fetches the MAL score in a detached launch off the existing external-IDs resolver, gated on AnimeMapper.isAnimeContentStatic(tmdbId, genreIds, originalLanguage) so we don't hit Jikan for live-action content. Runs in parallel with the main details load — never blocks rendering.
  • DetailsScreen — renders a MAL badge in the metadata row after IMDb, only when uiState.malScore > 0.0. Added to both the mobile layout and the TV layout. Uses the MAL brand color #2E51A2 so it's visually distinct from the yellow IMDb badge.

Rate limits

Jikan unofficial rate limit is about 3 req/s, 60 req/min. The LRU cache handles this — a typical session performs at most one Jikan call per unique anime visited, and results persist for the lifetime of the process. Rebrowsing a catalog of anime costs zero network.

Risk

Low. Three entirely new files plus minor additive changes to three existing files. No existing flow is touched; the MAL badge renders only for anime AND only when Jikan returns a score. Jikan being down = no badge; ARM being down = no badge; 2s timeout on each hop means worst case is 4 seconds of background work that never surfaces to the UI.

Test

  1. Open an anime title (e.g. Attack on Titan, Demon Slayer, Your Name).
  2. Within about 2 seconds of the details loading, a blue "MAL 8.4" style badge should appear after the IMDb badge.
  3. Open a live-action title — verify no MAL badge (no Jikan request made either, per the isAnimeContentStatic gate).
  4. Open the same anime again — verify it still shows (cache hit, zero network).

Arvin added 2 commits April 5, 2026 16:00
Adds a MAL score badge next to the IMDb badge on anime details pages.
The flow is:
  IMDB id -> ARM API (imdb -> mal_id) -> Jikan v4 (mal_id -> score)

ARVIO already used the ARM API in SkipIntroRepository for the AniSkip
intro-marker feature, so the IMDB->MAL hop is a known working path.
Jikan v4 is the unofficial MyAnimeList REST API and is widely used by
anime apps for community score display.

Changes:
- New JikanApi interface (GET /anime/{malId}) with JikanAnimeResponse
  and JikanAnimeData models that deserialize the `score` field.
- New AnimeScoreRepository (@singleton) that wraps the IMDB->MAL->score
  resolution chain with in-memory LRU caching (up to 256 entries each
  for imdbId->malId and malId->score, including negative caching to
  avoid re-hitting ARM for titles that aren't in its database). Uses
  withTimeoutOrNull(2s) on each hop so slow responses don't stall the
  details load. All exceptions are swallowed to null \u2014 the caller hides
  the badge on null.
- AppModule provides a new Retrofit instance for Jikan and binds
  JikanApi via Hilt, matching the existing ArmApi provider pattern.
- DetailsUiState gains `malScore: Double?` (nullable, default null).
- DetailsViewModel now takes AnimeScoreRepository and fetches the MAL
  score in a detached launch off the existing external-IDs resolver.
  Gated on AnimeMapper.isAnimeContentStatic(tmdbId, genreIds,
  originalLanguage) so we don't hit Jikan for live-action content.
  Runs in parallel with the main details load; never blocks rendering.
- DetailsScreen shows a cyan/blue MAL badge in the metadata row after
  the IMDb badge, only when `uiState.malScore > 0.0`. Rendered in both
  the mobile layout (~line 1100) and the TV layout (~line 1530), using
  the MAL brand color `#2E51A2` to distinguish it from IMDB yellow.

Jikan rate limit (~3 req/s, 60 req/min unofficial) is handled by the
LRU cache \u2014 a typical session performs at most one Jikan call per
unique anime visited, and results persist until the app process dies.

Closes #45
@ProdigyV21
ProdigyV21 force-pushed the feat/mal-score-anime branch from 5e69cd6 to eb48ccc Compare April 5, 2026 14:01
@ProdigyV21
ProdigyV21 merged commit 2b432ce into main Apr 5, 2026
2 checks passed
@ProdigyV21 ProdigyV21 mentioned this pull request Apr 5, 2026
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.

MAL Score

1 participant