Skip to content

Conversation

@jugarrit
Copy link

@jugarrit jugarrit commented Sep 6, 2025

Previously, meta posters were always pulled from the source addon.

This meant that if you added a show to your library (or started watching) from an AIOStreams catalog entry, the poster would change from the RPDB version to the original source version when viewed there or in "Continue Watching".

  • Adds a new option to all addons with meta category to enable RPDB for meta posters
  • Adds RPDB option to all custom addons
  • Pulls RPDB posters on meta fetch if option is enabled

Summary by CodeRabbit

  • New Features
    • Add optional RPDB configuration for addons and presets to enable RPDB-backed posters and redirect behaviour.
  • Bug Fixes
    • Consistent conversion of Discover deep links across catalogue and metadata views.
  • Performance
    • Parallelised per-item poster/link processing for faster catalogue and metadata loading.
  • Refactor
    • Centralised poster and link handling into a single shared routine for uniform behaviour and easier maintenance.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 6, 2025

Walkthrough

Adds a private AIOStreams helper updatePosterAndLinks(item, options) that centralises RPDB poster resolution (direct or redirect), optional poster enhancement, Discover deep‑link conversion, and catalogModifications handling; propagates rpdb through presets/addons and exposes rpdb?: boolean on Addon shapes and schema.

Changes

Cohort / File(s) Summary
Core poster/link refactor
packages/core/src/main.ts
Introduced private updatePosterAndLinks(item, options) to centralise RPDB poster resolution (redirect or fetch), poster enhancement (probabilistic), catalogModifications/overrideType handling, and Discover deep‑link conversion. Replaced inline poster/link logic in getCatalog and getMeta with calls to this helper.
Addon shape & DB schema
packages/core/src/db/schemas.ts
Added optional rpdb boolean to AddonSchema (z.boolean().optional()) to carry RPDB configuration in persisted addon objects.
Presets – custom
packages/core/src/presets/custom.ts
Added rpdb boolean option to CustomPreset METADATA OPTIONS (default false) so presets can declare RPDB behaviour.
Presets – base
packages/core/src/presets/preset.ts
Added conditional rpdb option to base preset options (included when resources include 'meta'), default false.
Presets application
packages/core/src/main.ts (applyPresets)
applyPresets now assigns rpdb: preset.options.rpdb onto generated addon objects so RPDB config propagates to addons.

Sequence Diagram(s)

sequenceDiagram
  participant U as Consumer
  participant A as AIOStreams (core)
  participant R as RPDB
  participant E as PosterEnhancer
  participant D as DeepLinkConverter

  U->>A: getCatalog(type, id)
  activate A
  A->>A: fetch catalog items
  loop per item
    A->>A: updatePosterAndLinks(item, { itemType: 'catalog', ... })
    activate A
    A->>A: compute catalogModifications / overrideType
    alt rpdb configured & redirect enabled
      A->>R: build redirect URL (rpdb redirect API)
      R-->>A: poster redirect URL
    else rpdb configured & no redirect
      A->>R: fetch poster URL
      R-->>A: poster URL
    else no rpdb
      A-->>A: use existing poster / enhancements only
    end
    opt enhancement occurs
      A->>E: enhance poster
      E-->>A: enhanced poster
    end
    A->>D: convert Discover deep links (item.links)
    D-->>A: updated links
    A-->>A: return modified item
    deactivate A
  end
  A-->>U: catalog with updated posters/links
  deactivate A

  U->>A: getMeta(type, id)
  activate A
  A->>A: fetch meta
  A->>A: updatePosterAndLinks(meta, { itemType: 'meta', ... })
  A-->>U: meta with updated poster/links
  deactivate A
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my whiskers at one clear trail,
One helper burrow where posters prevail.
RPDB carrots, links neatly spun,
Catalog and meta hop together as one.
A rabbit cheers: tidy code, quick run. 🥕🐇

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/core/src/main.ts (4)

447-449: Assign the returned object for clarity (or make the helper return void)

Even though the helper mutates in place, assigning its return value avoids surprises if it later returns a clone.

Apply this diff:

-        await this.updatePosterAndLinks(type, id, meta);
+        meta = await this.updatePosterAndLinks(type, id, meta);

1352-1356: Make the helper generic over Meta/MetaPreview and tighten types

Currently it’s typed to MetaPreview but used with meta objects too. Widen safely and return the same type.

-  private async updatePosterAndLinks(
-    type: string,
-    id: string,
-    item: MetaPreview
-  ) {
+  private async updatePosterAndLinks<T extends { id: string; poster?: string; links?: Meta['links'] }>(
+    type: string,
+    catalogId: string,
+    item: T
+  ): Promise<T> {
     let modification;
 
     if (this.userData.catalogModifications) {
       modification = this.userData.catalogModifications.find(
         (mod) =>
-          mod.id === id && (mod.type === type || mod.overrideType === type)
+          mod.id === catalogId && (mod.type === type || mod.overrideType === type)
       );
     }

Also applies to: 1359-1364


1379-1379: Optional: apply RPDB even when no existing poster, and avoid overwriting an RPDB poster with an “enhanced” one

  • Dropping the && item.poster guard lets RPDB provide a poster when the source didn’t.
  • Avoid replacing a freshly applied RPDB poster with an enhanced placeholder.
-    if (this.userData.enhancePosters && Math.random() < 0.2) {
+    if (this.userData.enhancePosters && Math.random() < 0.2 && !item.poster?.includes('ratingposterdb.com')) {
       item.poster = Buffer.from(
         constants.DEFAULT_POSTERS[
           Math.floor(Math.random() * constants.DEFAULT_POSTERS.length)
         ],
         'base64'
       ).toString('utf-8');
     }

If desired, also remove the need for an existing poster before applying RPDB:

-    if (rpdbApiKey && item.poster) {
+    if (rpdbApiKey) {
       let posterUrl = item.poster;
       …
-        url.searchParams.set('fallback', item.poster);
+        if (item.poster) url.searchParams.set('fallback', item.poster);

Also applies to: 1406-1414


1384-1391: Avoid parameter shadowing and reuse the computed RPDB id

Shadowing id makes the code harder to read and risks mistakes. Hoist a posterId and use it in both branches.

-      let posterUrl = item.poster;
+      let posterUrl = item.poster;
+      const posterId = (item as any).imdb_id ?? item.id;
       if (posterUrl.includes('api.ratingposterdb.com')) {
         // already a RPDB poster, do nothing
       } else if (this.userData.rpdbUseRedirectApi !== false && Env.BASE_URL) {
-        const id = (item as any).imdb_id || item.id;
         const url = new URL(Env.BASE_URL);
         url.pathname = '/api/v1/rpdb';
-        url.searchParams.set('id', id);
+        url.searchParams.set('id', posterId);
         url.searchParams.set('type', type);
         url.searchParams.set('fallback', item.poster);
-        url.searchParams.set('apiKey', rpdbApiKey);
         posterUrl = url.toString();
       } else {
-        const rpdbPosterUrl = await rpdbApi!.getPosterUrl(
-          type,
-          (item as any).imdb_id || item.id,
-          false
-        );
+        const rpdbPosterUrl = await rpdbApi!.getPosterUrl(type, posterId, false);

Also applies to: 1393-1397

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7569ce and edcfff6.

📒 Files selected for processing (1)
  • packages/core/src/main.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/main.ts (3)
packages/core/src/db/schemas.ts (1)
  • MetaPreview (758-758)
packages/core/src/utils/rpdb.ts (1)
  • RPDB (15-118)
packages/core/src/utils/env.ts (1)
  • Env (231-1666)
🔇 Additional comments (1)
packages/core/src/main.ts (1)

333-334: Centralising poster/link updates per item looks good

Mapping through a single helper keeps behaviour consistent between catalog and meta. Parallelism via Promise.all is appropriate here.

@jugarrit jugarrit closed this Sep 6, 2025
@jugarrit jugarrit reopened this Sep 6, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/core/src/main.ts (1)

1365-1445: Do not leak rpdbApiKey in client-facing URLs; also avoid id shadowing and support missing posters

Leaking the RPDB API key via query string exposes a secret to clients, logs, and referrers. Build the redirect URL without the key and have the server resolve the key from user context. While here, avoid shadowing the id parameter and allow RPDB substitution even when item.poster is initially missing.

Apply this diff:

   // Apply RPDB poster modification
-  if (rpdbApiKey && item.poster) {
-    let posterUrl = item.poster;
-    if (posterUrl.includes('api.ratingposterdb.com')) {
-      // already a RPDB poster, do nothing
-    } else if (this.userData.rpdbUseRedirectApi !== false && Env.BASE_URL) {
-      const id = (item as any).imdb_id || item.id;
-      const url = new URL(Env.BASE_URL);
-      url.pathname = '/api/v1/rpdb';
-      url.searchParams.set('id', id);
-      url.searchParams.set('type', type);
-      url.searchParams.set('fallback', item.poster);
-      url.searchParams.set('apiKey', rpdbApiKey);
-      posterUrl = url.toString();
-    } else {
-      const rpdbPosterUrl = await rpdbApi!.getPosterUrl(
-        type,
-        (item as any).imdb_id || item.id,
-        false
-      );
-      if (rpdbPosterUrl) {
-        posterUrl = rpdbPosterUrl;
-      }
-    }
-
-    item.poster = posterUrl;
-  }
+  if (rpdbApiKey) {
+    const posterId = (item as any).imdb_id || item.id;
+    const basePoster = item.poster ?? undefined;
+    let posterUrl = basePoster;
+    if (posterUrl?.includes('api.ratingposterdb.com')) {
+      // already an RPDB poster, do nothing
+    } else if (this.userData.rpdbUseRedirectApi !== false && Env.BASE_URL) {
+      const url = new URL(Env.BASE_URL);
+      url.pathname = '/api/v1/rpdb';
+      url.searchParams.set('id', posterId);
+      url.searchParams.set('type', type);
+      if (basePoster) url.searchParams.set('fallback', basePoster);
+      // Never put the API key in a client-facing URL
+      posterUrl = url.toString();
+    } else {
+      const rpdbPosterUrl = await rpdbApi!.getPosterUrl(
+        type,
+        posterId,
+        false
+      );
+      if (rpdbPosterUrl) {
+        posterUrl = rpdbPosterUrl;
+      }
+    }
+    if (posterUrl) {
+      item.poster = posterUrl;
+    }
+  }

Run this to confirm the backend endpoint doesn’t require apiKey in the query and to locate the handler for /api/v1/rpdb:

#!/bin/bash
set -euo pipefail
# Locate the rpdb endpoint implementation
rg -n -C3 -i 'api/v1/rpdb' --type-add 'ts:*.ts' --type-add 'js:*.js'

# Check for any usages expecting `apiKey` in query for RPDB
rg -n -C2 -i 'apiKey.*(rpdb|ratingposterdb)|(rpdb|ratingposterdb).*apiKey' --type-add 'ts:*.ts' --type-add 'js:*.js'
🧹 Nitpick comments (1)
packages/core/src/presets/custom.ts (1)

82-90: Optional: hide when meta isn’t selected

To reduce UI noise, consider gating this option (in docs or UI) when the custom addon doesn’t provide the meta resource, since the flag only affects meta posters.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between edcfff6 and 7029df5.

📒 Files selected for processing (4)
  • packages/core/src/db/schemas.ts (1 hunks)
  • packages/core/src/main.ts (4 hunks)
  • packages/core/src/presets/custom.ts (1 hunks)
  • packages/core/src/presets/preset.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/core/src/presets/preset.ts (1)
packages/core/src/db/schemas.ts (1)
  • Option (254-254)
packages/core/src/main.ts (3)
packages/core/src/db/schemas.ts (2)
  • MetaPreview (759-759)
  • Addon (143-143)
packages/core/src/utils/rpdb.ts (1)
  • RPDB (15-118)
packages/core/src/utils/env.ts (1)
  • Env (231-1666)
🔇 Additional comments (6)
packages/core/src/presets/preset.ts (1)

66-75: LGTM: rpdb option and conditional inclusion look correct

Adding the boolean rpdb option and showing it only when resources includes meta is a sensible default and matches the intended scope.

Also applies to: 111-113

packages/core/src/db/schemas.ts (1)

128-129: Schema addition aligns with runtime usage

rpdb?: boolean on AddonSchema matches how addons are annotated in applyPresets() and how updatePosterAndLinks() reads addon.rpdb.

packages/core/src/presets/custom.ts (1)

82-90: LGTM: Custom preset exposes rpdb toggle

Option shape and copy are consistent with the base preset option.

packages/core/src/main.ts (3)

332-341: LGTM: Poster/link handling centralised for catalog items

Shifting per-item tweaks into a single helper reduces duplication and keeps the code path uniform.


455-461: LGTM: Meta path reuses the same helper

Using the same helper for meta ensures RPDB and deep-link logic stays consistent.


645-649: LGTM: Propagating rpdb from preset options into addons

This wires the configuration through cleanly so updatePosterAndLinks() can rely on addon.rpdb.

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.

1 participant