Skip to content

feat(settings): add 'Hide Requested Media' option to settings#1855

Open
0xSysR3ll wants to merge 5 commits into
seerr-team:developfrom
0xSysR3ll:feat-hide-requested-media
Open

feat(settings): add 'Hide Requested Media' option to settings#1855
0xSysR3ll wants to merge 5 commits into
seerr-team:developfrom
0xSysR3ll:feat-hide-requested-media

Conversation

@0xSysR3ll
Copy link
Copy Markdown
Contributor

@0xSysR3ll 0xSysR3ll commented Aug 18, 2025

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)

image

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)

Issues Fixed or Closed

Summary by CodeRabbit

  • New Features

    • Added "Hide Requested Media" toggle in Settings to hide requested-but-unavailable items from Discover, Recommended, Similar, and media detail lists; setting is off by default and requested items stay searchable.
  • Documentation

    • Added documentation describing the "Hide Requested Media" behavior and usage.
  • Localization

    • Added UI labels and tooltip text for the new setting.

@gauthier-th gauthier-th mentioned this pull request Sep 10, 2025
@github-actions github-actions Bot added the merge conflict Cannot merge due to merge conflicts label Oct 6, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 6, 2025

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@0xSysR3ll 0xSysR3ll force-pushed the feat-hide-requested-media branch from 6510445 to 4974f31 Compare October 8, 2025 17:10
@0xSysR3ll 0xSysR3ll requested a review from a team as a code owner October 8, 2025 17:10
@github-actions github-actions Bot removed the merge conflict Cannot merge due to merge conflicts label Oct 8, 2025
@github-actions
Copy link
Copy Markdown

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@github-actions github-actions Bot added the merge conflict Cannot merge due to merge conflicts label Oct 30, 2025
@Altheran88
Copy link
Copy Markdown

@0xSysR3ll would you be willing to resolve the merge conflicts so it can be merged ? :) Thanks a lot for your contribution !

@0xSysR3ll
Copy link
Copy Markdown
Contributor Author

0xSysR3ll commented Nov 10, 2025

@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.
Don’t worry - this will be merged once the merger is complete. :)

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>
@0xSysR3ll 0xSysR3ll force-pushed the feat-hide-requested-media branch from 4974f31 to ed467b7 Compare March 5, 2026 20:39
@github-actions github-actions Bot removed the merge conflict Cannot merge due to merge conflicts label Mar 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Adds a new boolean setting hideRequested to backend and frontend settings, exposes it in the public API and docs, and implements filtering to exclude requested-but-unavailable media from discovery, sliders, and search flows while keeping such media searchable.

Changes

Cohort / File(s) Summary
Documentation
docs/using-seerr/settings/general.md
Adds documentation for the "Hide Requested Media" setting and its behavior.
API Schema
seerr-api.yml
Introduces hideRequested boolean in MainSettings schema with example.
Backend Settings & Interfaces
server/lib/settings/index.ts, server/interfaces/api/settingsInterfaces.ts
Adds hideRequested: boolean to MainSettings, FullPublicSettings, and PublicSettingsResponse; default initialized to false.
Backend Data Access
server/entity/Media.ts
Includes media.requests relation in getRelatedMedia query via leftJoinAndSelect to surface request info.
Frontend Settings UI
src/components/Settings/SettingsMain/index.tsx, src/i18n/locale/en.json
Adds checkbox, form integration, submission payload, and translation keys (hideRequested, hideRequestedTip).
Frontend Settings Infrastructure
src/context/SettingsContext.tsx, src/pages/_app.tsx
Initializes hideRequested in default settings and app-level currentSettings (default false).
Frontend Filtering & Discovery
src/hooks/useDiscover.ts, src/components/MediaSlider/index.tsx, src/components/Search/index.tsx
Adds requests?: unknown[] to BaseMedia, introduces hideRequested option and runtime filtering to exclude media that are requested and not AVAILABLE/PARTIALLY_AVAILABLE.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I spotted requests in a tidy row,
I hid the pending so discovery can glow,
Still searchable, waiting in queue,
Quiet and neat — patience peeks through,
Hop, skip, fetch: the catalog's anew! 🎋

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a 'Hide Requested Media' option to settings as evidenced by all file modifications.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #1048: filtering requested content from Discovery pages [#1048]. Files modified include documentation, API schema, database entity relations, settings interfaces, and UI components for the new hideRequested filter.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the 'Hide Requested Media' feature. No unrelated modifications detected across documentation, API schema, backend settings, frontend components, and localization files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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
Copy Markdown

@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 (1)
src/hooks/useDiscover.ts (1)

16-19: Consider improving type precision for requests.

The requests field is typed as unknown[], but based on the server entity (server/entity/Media.ts), this is actually MediaRequest[]. While unknown[] works since only .length is 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 MediaRequest is 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

📥 Commits

Reviewing files that changed from the base of the PR and between e25c1a5 and ed467b7.

📒 Files selected for processing (11)
  • docs/using-seerr/settings/general.md
  • seerr-api.yml
  • server/entity/Media.ts
  • server/interfaces/api/settingsInterfaces.ts
  • server/lib/settings/index.ts
  • src/components/MediaSlider/index.tsx
  • src/components/Search/index.tsx
  • src/components/Settings/SettingsMain/index.tsx
  • src/context/SettingsContext.tsx
  • src/hooks/useDiscover.ts
  • src/pages/_app.tsx

Comment thread src/components/MediaSlider/index.tsx
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 hideRequested in server/public settings and wires it through the Settings UI.
  • Implements client-side filtering in useDiscover and MediaSlider based 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.

Comment thread src/hooks/useDiscover.ts
Comment on lines +141 to +148
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)
);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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
);
});

Copilot uses AI. Check for mistakes.
Comment thread src/hooks/useDiscover.ts
Comment on lines +141 to +148
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)
);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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
);
});

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +95
return (
i.mediaInfo?.status === MediaStatus.AVAILABLE ||
i.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE ||
!i.mediaInfo?.requests ||
i.mediaInfo.requests.length === 0
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment thread server/entity/Media.ts
{ userId: user?.id }
) //,
)
.leftJoinAndSelect('media.requests', 'requests')
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
.leftJoinAndSelect('media.requests', 'requests')
.leftJoin('media.requests', 'requests')
.loadRelationCountAndMap('media.requestCount', 'media.requests')

Copilot uses AI. Check for mistakes.
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.

Filter requested content from Discovery

3 participants