Skip to content

feat(media-library): added a new Discover page Slider for My Media Library#2600

Open
ryanmfransen wants to merge 2 commits intoseerr-team:developfrom
ryanmfransen:feature-media-library-filter
Open

feat(media-library): added a new Discover page Slider for My Media Library#2600
ryanmfransen wants to merge 2 commits intoseerr-team:developfrom
ryanmfransen:feature-media-library-filter

Conversation

@ryanmfransen
Copy link

@ryanmfransen ryanmfransen commented Feb 28, 2026

…brary (#658)

Created a new Slider on the Discover page with links to the full local library view.

Description

I created a new 'My Media Library' Slider at the bottom of the Discover page. This lists 20 of your local media in the standard slider form, and if more than 20 media is found in your library, a 'See More' card is display.

You can also go to the full page My Media Library using the links beside the heading of the Slider.

My Media Library is all your local media, movies and shows with status|status4k of available, and partiallyavailable.

Allows users to quickly see the contents of their (Partially)|Available library.

How Has This Been Tested?

Tested on my local machine using my Jellyfin media library, containing a partially available series and available movies, totalling > 20.

Add Jellyfin library.
Load Discover page, view new Slider.
Click link to library in header.
Scroll slider all the way to the right, click 'See More'

Load '/discover/library' observer full page view of library, available and partiallyavailable.

Manual tests for local (containerized server) Jellyfin library.
Added cypress test for basic navigation of new components.
All testing done with build running locally on my Macbook using Chrome.

I tried to introduce a minimal footprint of these changes, avoiding as many touchpoints in existing codebase as possible.

I heavily relied on AI for the following:

  • Understand modern open source contribution hygiene and best practices.
  • Grok current app architecture and TMDB integration.
  • Evaluate my design and implementation ideas.
  • Review my code, provide feedback from a sw engineering perspective.
  • Review changes for a maintainer PR perspective, assess missing things, etc.

Screenshots / Logs (if applicable)

Screenshot 2026-02-27 at 6 16 26 PM Screenshot 2026-02-27 at 6 16 13 PM Screenshot 2026-02-27 at 6 16 05 PM

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
    • Introduced "My Media Library" slider on Discover (enabled by default) with a link to the full library.
    • Added a dedicated "My Media Library" full-page view with infinite scrolling to browse your entire media collection; shows "My Media Library" header.
  • Tests
    • Added an end-to-end test verifying navigation from the slider to the My Media Library page.

@ryanmfransen ryanmfransen requested a review from a team as a code owner February 28, 2026 04:17
@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b16fcee and 1eedaa8.

📒 Files selected for processing (2)
  • src/components/Discover/index.tsx
  • src/i18n/locale/en.json

📝 Walkthrough

Walkthrough

Adds a "My Media Library" slider and a full library page with infinite scrolling, registers a new Discover slider type, standardizes discover route paging defaults, adds a Cypress navigation test, and inserts an i18n key.

Changes

Cohort / File(s) Summary
Library UI
src/components/Discover/MyMediaLibrary/index.tsx, src/components/Discover/MyMediaLibrarySlider/index.tsx, src/pages/discover/library.tsx
New MyMediaLibrary component with useSWRInfinite-backed infinite scroll (20/page) and a MyMediaLibrarySlider preview; new /discover/library page exporting the library view.
Discover integration
src/components/Discover/index.tsx, src/components/Discover/DiscoverSliderEdit/index.tsx, src/components/Discover/constants.ts
Imported and wired MyMediaLibrarySlider; added DiscoverSliderType.MY_MEDIA_LIBRARY handling and slider title translation key myMediaLibrary.
Server config & routes
server/constants/discover.ts, server/routes/discover.ts
Added MY_MEDIA_LIBRARY enum/default slider entry; standardized page parsing (page variable defaulting to 1) for TMDB discover movie/TV routes.
Tests & i18n
cypress/e2e/discover.cy.ts, src/i18n/locale/en.json
Added Cypress test verifying navigation from the slider to /discover/library; added English translation components.Discover.myMediaLibrary.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant DiscoverPage as "Discover Page"
    participant Slider as "MyMediaLibrarySlider"
    participant API as "Server API"
    participant LibraryPage as "MyMediaLibrary Page"

    User->>DiscoverPage: Visit /discover
    DiscoverPage->>Slider: Render slider component
    Slider->>API: GET /api/v1/media?filter=allavailable&take=20
    API-->>Slider: Return media results (pageInfo)
    Slider->>DiscoverPage: Display slider items / Show More link

    User->>Slider: Click slider header or Show More
    Slider->>LibraryPage: Navigate to /discover/library
    LibraryPage->>API: Fetch page=1 & page=2 (useSWRInfinite)
    API-->>LibraryPage: Return paginated media
    LibraryPage->>User: Render scrollable items
    User->>LibraryPage: Scroll near bottom
    LibraryPage->>API: Fetch next page
    API-->>LibraryPage: Return more items
    LibraryPage->>User: Append items (placeholders while loading)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped into sliders, soft and bright,
Found my library glowing in the light.
Pages that fetch as I nudge and peek,
Cards that appear when the scroll is meek.
Hooray — my media, tidy and right. 🥕📚

🚥 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 summarizes the main change: adding a new 'My Media Library' Slider to the Discover page, which is the primary feature in this PR.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #658: displaying available and partially available media items from the user's server, with a dedicated page and slider component for visibility.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the My Media Library feature, including the slider component, library page, routing, and supporting infrastructure—no unrelated changes detected.

✏️ 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.

Copy link

@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.

🧹 Nitpick comments (2)
cypress/e2e/discover.cy.ts (1)

215-224: Add API interception for test consistency and stability.

The MyMediaLibrarySlider component fetches data from /api/v1/media?filter=allavailable&take=20&sort=mediaAdded using useSWR on component mount. This test should intercept that API call before visiting the page, consistent with other tests in this file (trending, popular movies, upcoming movies, etc.). Relying solely on the 15s timeout can lead to flakiness.

♻️ Suggested improvement
   it('navigates from My Media Library slider to the library page', () => {
+    cy.intercept('/api/v1/media?filter=allavailable*').as('getLibraryMedia');
     cy.visit('/');
+    cy.wait('@getLibraryMedia');

     cy.contains('.slider-header .slider-title', 'My Media Library', {
       timeout: 15000,
     }).click();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypress/e2e/discover.cy.ts` around lines 215 - 224, This test "navigates from
My Media Library slider to the library page" is missing an API interception for
the MyMediaLibrarySlider data fetch; before calling cy.visit('/'), intercept the
GET to '/api/v1/media?filter=allavailable&take=20&sort=mediaAdded' (or the
equivalent query) and stub a deterministic response (use the same fixture
pattern as other tests) so useSWR returns immediately; update the test to add
cy.intercept(...) for that endpoint prior to cy.visit('/') and then proceed with
the existing assertions (cy.contains(...).click(), cy.url().should(...),
cy.get(...).should(...)).
src/components/Discover/MyMediaLibrary/index.tsx (1)

45-50: Consider memoizing the scroll callback.

The inline arrow function () => setSize(size + 1) creates a new function reference on each render. Depending on how useVerticalScroll handles its callback dependency, this could potentially cause unnecessary effect re-runs. Wrapping it in useCallback would prevent this.

♻️ Proposed refactor
+import { useCallback } from 'react';
+
 const MyMediaLibrary = () => {
   const intl = useIntl();
   const { data, error, size, setSize, isValidating } =
     useSWRInfinite<MediaResultsResponse>(
       // ... key function
     );

   const lastPage = data?.[data.length - 1];

+  const loadMore = useCallback(() => {
+    setSize((prevSize) => prevSize + 1);
+  }, [setSize]);
+
   useVerticalScroll(
-    () => setSize(size + 1),
+    loadMore,
     !isValidating &&
       !!data &&
       (lastPage?.pageInfo.page ?? 0) < (lastPage?.pageInfo.pages ?? 0)
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Discover/MyMediaLibrary/index.tsx` around lines 45 - 50,
Memoize the scroll callback passed to useVerticalScroll to avoid recreating the
function each render: wrap the inline arrow () => setSize(size + 1) in
React.useCallback and pass that memoized callback to useVerticalScroll, with a
dependency array that includes size (and setSize if not stable) so the callback
updates correctly when size changes; keep the existing second argument (the
boolean conditional using isValidating, data, lastPage) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cypress/e2e/discover.cy.ts`:
- Around line 215-224: This test "navigates from My Media Library slider to the
library page" is missing an API interception for the MyMediaLibrarySlider data
fetch; before calling cy.visit('/'), intercept the GET to
'/api/v1/media?filter=allavailable&take=20&sort=mediaAdded' (or the equivalent
query) and stub a deterministic response (use the same fixture pattern as other
tests) so useSWR returns immediately; update the test to add cy.intercept(...)
for that endpoint prior to cy.visit('/') and then proceed with the existing
assertions (cy.contains(...).click(), cy.url().should(...),
cy.get(...).should(...)).

In `@src/components/Discover/MyMediaLibrary/index.tsx`:
- Around line 45-50: Memoize the scroll callback passed to useVerticalScroll to
avoid recreating the function each render: wrap the inline arrow () =>
setSize(size + 1) in React.useCallback and pass that memoized callback to
useVerticalScroll, with a dependency array that includes size (and setSize if
not stable) so the callback updates correctly when size changes; keep the
existing second argument (the boolean conditional using isValidating, data,
lastPage) unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c34c91 and 3da1ee9.

📒 Files selected for processing (10)
  • cypress/e2e/discover.cy.ts
  • server/constants/discover.ts
  • server/routes/discover.ts
  • src/components/Discover/DiscoverSliderEdit/index.tsx
  • src/components/Discover/MyMediaLibrary/index.tsx
  • src/components/Discover/MyMediaLibrarySlider/index.tsx
  • src/components/Discover/constants.ts
  • src/components/Discover/index.tsx
  • src/i18n/locale/en.json
  • src/pages/discover/library.tsx

…ary (seerr-team#658)

Created a new Slider on the Discover page with links to the full local library view.
@ryanmfransen ryanmfransen force-pushed the feature-media-library-filter branch from 3da1ee9 to b16fcee Compare February 28, 2026 23:40
Copy link

@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.

🧹 Nitpick comments (1)
src/components/Discover/MyMediaLibrary/index.tsx (1)

56-62: Consider more specific error handling.

The generic statusCode={500} error may not accurately represent all failure modes (e.g., network errors, 404s, rate limiting). If the API provides error details, consider passing them through for more informative error pages.

However, this is consistent with common patterns in similar components, so acceptable as-is.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Discover/MyMediaLibrary/index.tsx` around lines 56 - 62, The
current error branch in MyMediaLibrary returns a generic <Error statusCode={500}
/>; update error handling to surface API-provided details by passing the actual
status and/or message from the fetched error object (e.g., use error.status or
error.response?.status and error.message) into the Error component so pages
reflect specific failure modes (network/404/rate-limit) while keeping the same
fallback when details are missing; locate this change around the conditional
using the error and data locals in the MyMediaLibrary component and adjust the
props passed to Error accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/Discover/MyMediaLibrary/index.tsx`:
- Around line 56-62: The current error branch in MyMediaLibrary returns a
generic <Error statusCode={500} />; update error handling to surface
API-provided details by passing the actual status and/or message from the
fetched error object (e.g., use error.status or error.response?.status and
error.message) into the Error component so pages reflect specific failure modes
(network/404/rate-limit) while keeping the same fallback when details are
missing; locate this change around the conditional using the error and data
locals in the MyMediaLibrary component and adjust the props passed to Error
accordingly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3da1ee9 and b16fcee.

📒 Files selected for processing (10)
  • cypress/e2e/discover.cy.ts
  • server/constants/discover.ts
  • server/routes/discover.ts
  • src/components/Discover/DiscoverSliderEdit/index.tsx
  • src/components/Discover/MyMediaLibrary/index.tsx
  • src/components/Discover/MyMediaLibrarySlider/index.tsx
  • src/components/Discover/constants.ts
  • src/components/Discover/index.tsx
  • src/i18n/locale/en.json
  • src/pages/discover/library.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • server/constants/discover.ts
  • src/components/Discover/index.tsx
  • cypress/e2e/discover.cy.ts
  • src/components/Discover/DiscoverSliderEdit/index.tsx
  • src/components/Discover/constants.ts
  • src/i18n/locale/en.json
  • server/routes/discover.ts

@AlexS54
Copy link

AlexS54 commented Mar 8, 2026

This looks good, looking forward to seeing it merged 👌

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.

Option to show what's currently available

2 participants