feat(settings): add 'Hide Requested Media' option to settings#1855
feat(settings): add 'Hide Requested Media' option to settings#18550xSysR3ll wants to merge 5 commits into
Conversation
|
This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged. |
6510445 to
4974f31
Compare
|
This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged. |
|
@0xSysR3ll would you be willing to resolve the merge conflicts so it can be merged ? :) Thanks a lot for your contribution ! |
I could, but since we're currently in feature freeze, it's not relevant at the moment. |
This PR introduces a new setting that allows users to hide media that has been requested but is not yet available from the "Discover" home page and related categories. Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
…tter type safety Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
…y status Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
4974f31 to
ed467b7
Compare
📝 WalkthroughWalkthroughAdds a new boolean setting Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as SettingsMain UI
participant API as Server API
participant Context as SettingsContext
participant Hook as useDiscover Hook
participant Filter as Filter Logic
User->>UI: Toggle "Hide Requested" ON
UI->>API: PATCH /api/v1/settings/main { hideRequested: true }
API-->>UI: 200 OK (updated)
UI->>Context: Update currentSettings.hideRequested
Context-->>UI: Provide updated settings
User->>Hook: Request discovery data
Hook->>API: GET /api/v1/discover
API-->>Hook: Return media (includes requests relation)
Hook->>Filter: Apply filters (hideAvailable, hideBlocklisted, hideRequested)
Filter-->>Hook: Filtered media list
Hook-->>UI: Render filtered results
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/hooks/useDiscover.ts (1)
16-19: Consider improving type precision forrequests.The
requestsfield is typed asunknown[], but based on the server entity (server/entity/Media.ts), this is actuallyMediaRequest[]. Whileunknown[]works since only.lengthis accessed, a more precise type would improve maintainability if future code needs to access request properties.🔧 Suggested type refinement
interface BaseMedia { id: number; mediaType: string; mediaInfo?: { status: MediaStatus; - requests?: unknown[]; + requests?: { length: number }[]; }; }Alternatively, if
MediaRequestis importable from@server/entity/Media, a full type could be used.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useDiscover.ts` around lines 16 - 19, The mediaInfo.requests property is typed too loosely as unknown[]; change its type to MediaRequest[] (importing MediaRequest from `@server/entity/Media`) so the mediaInfo definition in useDiscover.ts becomes mediaInfo?: { status: MediaStatus; requests?: MediaRequest[]; } to improve type safety and future property access.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/MediaSlider/index.tsx`:
- Around line 85-94: The filter in titles.filter incorrectly requires
(i.mediaType === 'movie' || i.mediaType === 'tv') for all entries, which drops
person results when settings.currentSettings.hideRequested is true; change the
predicate to explicitly allow person entries through (e.g., (i.mediaType ===
'person') || ((i.mediaType === 'movie' || i.mediaType === 'tv') &&
(i.mediaInfo?.status === MediaStatus.AVAILABLE || i.mediaInfo?.status ===
MediaStatus.PARTIALLY_AVAILABLE || !i.mediaInfo?.requests ||
i.mediaInfo.requests.length === 0))). Keep the null-safe accesses (i.mediaInfo?)
and use the existing symbols settings.currentSettings.hideRequested,
titles.filter, mediaType, MediaStatus, and mediaInfo to locate and update the
code.
---
Nitpick comments:
In `@src/hooks/useDiscover.ts`:
- Around line 16-19: The mediaInfo.requests property is typed too loosely as
unknown[]; change its type to MediaRequest[] (importing MediaRequest from
`@server/entity/Media`) so the mediaInfo definition in useDiscover.ts becomes
mediaInfo?: { status: MediaStatus; requests?: MediaRequest[]; } to improve type
safety and future property access.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7cd9fd0b-4b5d-4007-aff4-907919a1d6bd
📒 Files selected for processing (11)
docs/using-seerr/settings/general.mdseerr-api.ymlserver/entity/Media.tsserver/interfaces/api/settingsInterfaces.tsserver/lib/settings/index.tssrc/components/MediaSlider/index.tsxsrc/components/Search/index.tsxsrc/components/Settings/SettingsMain/index.tsxsrc/context/SettingsContext.tsxsrc/hooks/useDiscover.tssrc/pages/_app.tsx
There was a problem hiding this comment.
Pull request overview
Adds a new main setting to optionally hide requested-but-unavailable media from Discover surfaces (home “Discover” and related recommendation/similar lists), while keeping requested items visible in search.
Changes:
- Introduces
hideRequestedin server/public settings and wires it through the Settings UI. - Implements client-side filtering in
useDiscoverandMediaSliderbased on requested state. - Updates docs, OpenAPI schema, and English locale strings for the new option.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/_app.tsx | Adds hideRequested to initial public settings fallback. |
| src/i18n/locale/en.json | Adds English strings for the new toggle + tooltip. |
| src/hooks/useDiscover.ts | Adds hideRequested option and filtering logic for discover-style queries. |
| src/context/SettingsContext.tsx | Adds hideRequested to default settings context. |
| src/components/Settings/SettingsMain/index.tsx | Adds the “Hide Requested Media” checkbox and includes it in save payload. |
| src/components/Search/index.tsx | Explicitly disables requested filtering for search results. |
| src/components/MediaSlider/index.tsx | Adds requested filtering for slider-based lists. |
| server/lib/settings/index.ts | Adds hideRequested to settings models, defaults, and public settings exposure. |
| server/interfaces/api/settingsInterfaces.ts | Extends PublicSettingsResponse with hideRequested. |
| server/entity/Media.ts | Loads media.requests in getRelatedMedia to support requested detection. |
| seerr-api.yml | Adds hideRequested to the documented MainSettings schema. |
| docs/using-seerr/settings/general.md | Documents the new setting and expected behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| titles = titles.filter( | ||
| (i) => | ||
| (i.mediaType === 'movie' || i.mediaType === 'tv') && | ||
| (i.mediaInfo?.status === MediaStatus.AVAILABLE || | ||
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | ||
| !i.mediaInfo?.requests || | ||
| i.mediaInfo.requests.length === 0) | ||
| ); |
There was a problem hiding this comment.
The hideRequested filter currently applies i.mediaType === 'movie' || i.mediaType === 'tv' as part of the filter predicate, which means any non-movie/TV results (e.g. person results in Trending) will be filtered out entirely when this setting is enabled. Adjust the predicate so non-movie/TV results are preserved and the requested check is only applied to movie/TV items.
| titles = titles.filter( | |
| (i) => | |
| (i.mediaType === 'movie' || i.mediaType === 'tv') && | |
| (i.mediaInfo?.status === MediaStatus.AVAILABLE || | |
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | |
| !i.mediaInfo?.requests || | |
| i.mediaInfo.requests.length === 0) | |
| ); | |
| titles = titles.filter((i) => { | |
| // Preserve non-movie/TV results (e.g. people), and only apply | |
| // the requested/availability checks to movie and TV items. | |
| if (i.mediaType !== 'movie' && i.mediaType !== 'tv') { | |
| return true; | |
| } | |
| return ( | |
| i.mediaInfo?.status === MediaStatus.AVAILABLE || | |
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | |
| !i.mediaInfo?.requests || | |
| i.mediaInfo.requests.length === 0 | |
| ); | |
| }); |
| titles = titles.filter( | ||
| (i) => | ||
| (i.mediaType === 'movie' || i.mediaType === 'tv') && | ||
| (i.mediaInfo?.status === MediaStatus.AVAILABLE || | ||
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | ||
| !i.mediaInfo?.requests || | ||
| i.mediaInfo.requests.length === 0) | ||
| ); |
There was a problem hiding this comment.
hideRequested treats any existing request record as "requested" (requests.length > 0), which will also hide items that only have DECLINED (and potentially COMPLETED/FAILED) historical requests. Consider filtering requests by status (e.g., exclude DECLINED/COMPLETED) or basing this on mediaInfo.status so only actively-requested items are hidden.
| titles = titles.filter( | |
| (i) => | |
| (i.mediaType === 'movie' || i.mediaType === 'tv') && | |
| (i.mediaInfo?.status === MediaStatus.AVAILABLE || | |
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | |
| !i.mediaInfo?.requests || | |
| i.mediaInfo.requests.length === 0) | |
| ); | |
| titles = titles.filter((i) => { | |
| // Only consider movies and TV shows for hideRequested; keep others. | |
| if (i.mediaType !== 'movie' && i.mediaType !== 'tv') { | |
| return true; | |
| } | |
| const status = i.mediaInfo?.status; | |
| // Hide only items that are actively requested (e.g., pending or processing). | |
| // Items with only historical (declined/completed) requests will not be hidden. | |
| return ( | |
| status !== MediaStatus.PENDING && | |
| status !== MediaStatus.PROCESSING | |
| ); | |
| }); |
| return ( | ||
| i.mediaInfo?.status === MediaStatus.AVAILABLE || | ||
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | ||
| !i.mediaInfo?.requests || | ||
| i.mediaInfo.requests.length === 0 |
There was a problem hiding this comment.
This requested-media filter relies on mediaInfo.requests.length without considering request status, so titles with only DECLINED (and possibly COMPLETED/FAILED) request history will be hidden as well. It would be safer to treat a title as requested only when it has an active request (e.g., status not DECLINED/COMPLETED), or derive this from mediaInfo.status if that’s the canonical state.
| return ( | |
| i.mediaInfo?.status === MediaStatus.AVAILABLE || | |
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | |
| !i.mediaInfo?.requests || | |
| i.mediaInfo.requests.length === 0 | |
| const hasActiveRequests = | |
| !!i.mediaInfo?.requests && | |
| i.mediaInfo.requests.some( | |
| (request) => | |
| request.status !== 'DECLINED' && | |
| request.status !== 'COMPLETED' && | |
| request.status !== 'FAILED' | |
| ); | |
| return ( | |
| i.mediaInfo?.status === MediaStatus.AVAILABLE || | |
| i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || | |
| !hasActiveRequests |
| { userId: user?.id } | ||
| ) //, | ||
| ) | ||
| .leftJoinAndSelect('media.requests', 'requests') |
There was a problem hiding this comment.
Adding leftJoinAndSelect('media.requests', 'requests') in getRelatedMedia will load full MediaRequest entities for every discover/search result. Because MediaRequest has eager relations (e.g. requestedBy, modifiedBy, seasons), this can significantly increase payload size and may expose requester user details in endpoints that previously only returned mediaInfo.status. Consider returning a lightweight signal instead (e.g., relation count / existence of non-declined/non-completed requests via loadRelationCountAndMap, or a subquery that selects only request IDs/statuses) rather than selecting the full relation.
| .leftJoinAndSelect('media.requests', 'requests') | |
| .leftJoin('media.requests', 'requests') | |
| .loadRelationCountAndMap('media.requestCount', 'media.requests') |
Description
This PR introduces a new setting that allows users to hide media that has been requested but is not yet available from the "Discover" home page and related categories.
How Has This Been Tested?
Screenshots / Logs (if applicable)
Checklist:
pnpm buildpnpm i18n:extractIssues Fixed or Closed
Summary by CodeRabbit
New Features
Documentation
Localization