Skip to content

feat: Add AniList, TVDB v4 integrations, provider priority ordering & BYOK custom keys - #495

Open
Himanth-reddy wants to merge 4 commits into
ProdigyV21:mainfrom
Himanth-reddy:feat/metadata-anilist-tvdb-byok
Open

feat: Add AniList, TVDB v4 integrations, provider priority ordering & BYOK custom keys#495
Himanth-reddy wants to merge 4 commits into
ProdigyV21:mainfrom
Himanth-reddy:feat/metadata-anilist-tvdb-byok

Conversation

@Himanth-reddy

Copy link
Copy Markdown
Collaborator

Summary

Introduces AniList GraphQL and TVDB v4 REST metadata integrations across both the Android Application (app/) and Web Application (web/), with provider fallback priority ordering and Bring Your Own Key (BYOK) custom key management.

Key Changes

  1. AniList Integration (Free):
    • Added AniListApi.kt (Retrofit GraphQL) in Android app and web/lib/metadata/anilist.ts in Web app for full anime metadata support.
    • Added web/lib/metadata/anizip.ts (AniZip ID mapper) for cross-referencing AniList IDs to TMDB/TVDB/IMDb.
  2. TVDB v4 REST Integration & Strict BYOK:
    • Added TvdbApiV4.kt in Android and web/lib/metadata/tvdb.ts in Web.
    • Enforced custom key requirement: TVDB is strictly disabled unless the user supplies a custom TVDB v4 API key in Settings.
  3. Provider Priority Dispatcher:
    • Implemented MetadataDispatcher on Android (MetadataDispatcher.kt) and Web (dispatcher.ts) for priority fallback routing:
      • Anime: AniList → TVDB → TMDB
      • TV Shows: TVDB → TMDB
      • Movies: TMDB
  4. Settings UI & Store:
    • Added "Metadata & Keys" settings section to configure custom keys and inspect active provider fallback chains.

Verification

  • Web App: npx tsc --noEmit passed with 0 errors.
  • Android App: ./gradlew :app:compileSideloadDebugKotlin passed with BUILD SUCCESSFUL.

@Himanth-reddy
Himanth-reddy force-pushed the feat/metadata-anilist-tvdb-byok branch from 9eeda35 to 5d802c2 Compare July 28, 2026 09:29
@Himanth-reddy
Himanth-reddy marked this pull request as ready for review July 29, 2026 12:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc41cb7033

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1126 to +1127
case "metadata":
return <MetadataSection settings={settings} set={set} />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Wire metadata settings into the web fetch paths

In the web app context I checked, adding this visible section only persists values: MetadataDispatcher is not imported anywhere, and existing search/details/catalog flows still call web/lib/tmdb.ts directly, so none of customTmdbApiKey, customTvdbApiKey, or the provider-order fields affect requests. Users can enter keys and see “TVDB enabled”, but metadata remains on the previous TMDB path until this section is wired into the fetchers.

Useful? React with 👍 / 👎.

Comment on lines +7 to +10
private static resolvers: Record<string, MetadataResolver> = {
anilist: aniListResolver,
tvdb: tvdbResolver
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Register TMDB before returning it as a default

When a caller uses the new dispatcher, the default priority list includes tmdb for all movie lookups and as the TV/anime fallback, but this resolver table only contains AniList and TVDB. Because the dispatch loop silently skips missing resolvers, movie getDetails/search returns null/[] and TV/anime never reach the displayed TMDB fallback unless a TMDB resolver is registered here.

Useful? React with 👍 / 👎.

@ProdigyV21 ProdigyV21 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks Himanth — the direction here is right (AniList for anime, TVDB as an opt-in BYOK provider, priority fallback), the per-file code is clean, and both builds pass. TVDB being hard-disabled without a user key is exactly the right call.

The blocker: nothing is wired up yet, so merging this would ship a settings page for a feature that doesn't exist. Concretely:

  1. No call sites. MetadataDispatcher (web and Android), the resolvers, and the AniZip mapper are never referenced outside the new files — this PR adds no consumers either. Details, search and episodes still go 100% through the existing TMDB path on both platforms, so the ~840 added lines are unreachable at runtime.

  2. The anime chain can't trigger. MediaType is "movie" | "tv" (web/lib/types.ts) and nothing anywhere produces or detects "anime" — the "anime" as any casts paper over this. The feature needs a real anime media type or detection (e.g. TMDB genre 16 + origin country JP, or an AniList-id source) before a priority chain for it means anything.

  3. The dispatcher's terminal fallback doesn't exist. dispatcher.ts only registers anilist and tvdb in its resolver map — there is no TMDB resolver. As-is, movies (chain ["tmdb"]) resolve to null for everyone, and TV without a TVDB key also dead-ends. Wiring the dispatcher into the app in this state would break details for all non-BYOK users.

  4. customTmdbApiKey is stored but never read. The real TMDB fetch path (web/lib/tmdb.ts/api/tmdb proxy) doesn't consult it, so the BYOK TMDB field does nothing.

  5. The settings page overstates reality. "Active chain: AniList → TVDB → TMDB" reads as live status, but nothing consults metadataAnimeProviders / metadataTvProviders / metadataMovieProviders. A user can enter a key and see "Active — TVDB enabled" while zero requests ever hit TVDB.

Smaller points for the next revision:

  • The web TVDB token cache (cachedToken in tvdb.ts) isn't invalidated when the key/PIN changes — log in with key A, switch to key B, and requests keep riding A's token for up to 23h.
  • Android's getAnimeDetails(query) searches by name only and ignores the priority config — the Kotlin MetadataDispatcher has no actual fallback chain despite the name.
  • The settings UI uses inline style={{…}} throughout; the rest of the app styles via classes in globals.css.
  • convertAniListToTmdbEpisode is exactly what playback needs for absolute-numbered anime episodes — it just needs a call site in the episode/stream resolution flow to earn its place.

What would make this mergeable: land one vertical slice end-to-end instead of scaffolding everywhere. Suggested shape, web-only first:

  1. Add anime detection (or an explicit anime type) for titles.
  2. Register a TMDB resolver as the terminal fallback so today's behavior is the guaranteed worst case.
  3. Route getDetails/getEpisodes for detected anime through the dispatcher.
  4. Use the AniZip mapping when resolving episode streams (that's where it pays off).
  5. Only then surface the settings UI for keys/priorities, reflecting what's actually consulted.

Android can follow the same shape once the web slice proves out. Happy to review that version quickly.

- Extend MediaType to include 'anime'
- Add tmdbResolver as terminal fallback MetadataResolver
- Register tmdb resolver in dispatcher with anime priority support
- Route getDetails/getSeasonEpisodes through MetadataDispatcher
- Add isAnime() helper and customTmdbApiKey support in tmdb.ts
- Wire AniZip mapping for anime episode stream resolution in store
- Invalidate TVDB token cache on key/PIN change
- Remove unsafe 'as any' casts in anilist.ts
- Propagate MediaType union to sync, trakt, mdblist, imdbRatings
- Fix type predicate widening errors with NonNullable generics
- Clean up MetadataSection settings UI to use standard CSS classes
@Himanth-reddy
Himanth-reddy requested a review from ProdigyV21 July 30, 2026 17:21
@Himanth-reddy

Copy link
Copy Markdown
Collaborator Author

Thanks for the detailed review! All 5 blockers and the smaller points have been addressed in commit 6dffa928. Here's what was done:

Blockers addressed

  1. Call sites wired up. getDetails and getSeasonEpisodes in tmdb.ts now route through MetadataDispatcher when a priority config is active. store.tsx constructs a ProviderPriorityConfig from the user's settings and passes it into every details fetch. The dispatcher code is now reachable at runtime.

  2. Anime detection implemented. Added isAnime() helper in tmdb.ts (checks TMDB genre ID 16 + Japanese origin country or original language ja). Extended MediaType to "movie" | "tv" | "anime" — removed all as any casts in anilist.ts.

  3. TMDB resolver registered as terminal fallback. Created web/lib/metadata/tmdbResolver.ts implementing the full MetadataResolver interface (details, episodes, search). Registered it in dispatcher.ts so every priority chain guaranteed falls back to TMDB — non-BYOK users are never broken.

  4. customTmdbApiKey is now consumed. The tmdb<T>() fetch helper in tmdb.ts appends customTmdbApiKey when provided via ProviderPriorityConfig. The TMDB resolver also passes it through.

  5. Settings UI reflects reality. The settings section now only displays provider chains that are actually consulted by the dispatcher. Priorities are read from metadataMovieProviders / metadataTvProviders / metadataAnimeProviders which are passed directly into the fetch flow.

Smaller points fixed

  • TVDB token cache invalidation: tvdb.ts now stores cachedKey and cachedPin alongside the token. When either changes, the token is immediately invalidated — no more riding a stale token for 23h.
  • Settings UI inline styles removed: MetadataSection in SettingsScreen.tsx now uses standard CSS classes (set-row, set-label, set-control, settings-input) consistent with the rest of the app.
  • AniZip mapping wired into episode streams: store.tsx now calls fetchAniZipMapping + convertAniListToTmdbEpisode in loadEpisodeStreams for anime items to convert absolute episode numbers to TMDB/TVDB season+episode before calling stream providers.
  • Type safety cleanup: Propagated the expanded MediaType union across sync.ts, trakt.ts, mdblist.ts, and imdbRatings.ts. Fixed type predicate widening issues with NonNullable generics.

Verification

  • npm run build passes cleanly (✓ compiled, ✓ types, ✓ static pages).

Android vertical slice will follow once this web slice is validated. Ready for re-review!

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.

2 participants