Skip to content

feat(discover): add exclusionary filters with per-dimension include/exclude toggles #3166

Open
TheReaperJay wants to merge 6 commits into
seerr-team:developfrom
TheReaperJay:feat-exclusionary-filters
Open

feat(discover): add exclusionary filters with per-dimension include/exclude toggles #3166
TheReaperJay wants to merge 6 commits into
seerr-team:developfrom
TheReaperJay:feat-exclusionary-filters

Conversation

@TheReaperJay

@TheReaperJay TheReaperJay commented Jun 15, 2026

Copy link
Copy Markdown

Description

This PR adds exclusionary (negation) filters across all discover filter dimensions where it is technically feasible.
This allows for a greater variety of filters across the discovery UX and surfaces combinations that weren't possible before. For example, you can include a filter for a production country, while excluding specific language, or exclude a specific production country while excluding keywords and including categories etc.

Multiple filters can stack, allowing for much greater granularity and ease of discovery, whilst the UX toggle allows for quick switching back and forth. Default behavior remains inclusive like the current UX enforces, meaning that there are no awkward surprises.

The coverage affects both TV and Movie discovery pages, specifically:

  • Genres, keywords, studios, watch providers: use TMDB's native without_* parameters.
  • Original language: post-filtered on original_language from the list response; server (user default) is resolved server-side for both include and exclude using the same logic as createTmdbWithRegionLanguage.
  • Production country: movies use a complement query (with_origin_country = all codes except the excluded ones); TV is post-filtered on origin_country. (This is because TMDB surfaces origin_country for TV only)
  • TV status: complement query via with_status.

The UI surfaces a per-section include/exclude toggle using the existing SlideCheckbox component. Streaming services remain include-only because TMDB's without_watch_providers is not surfaced in the discover UI and the user-facing value is low.

The implementation avoids extra TMDB calls in the common case. When a post-filter is active and drops results, the route handler over-fetches up to three additional TMDB pages to keep the requested page full. Pagination is marked as an estimate when this happens.

Feature coverage

  • Added per-dimension include/exclude toggles to the Discover filter panel using the existing SlideCheckbox component.
  • Added a Production Country section to the filter panel.
  • Unified the Keywords section and removed the standalone "Exclude Keywords" heading.
  • Added "All X" placeholders to selectors (All Genres, All Studios, - for Keywords, etc.).
  • Streaming Services remains include-only with a plain heading (no toggle).
  • Added a resultsFilteredHint message when the result count is an estimate due to post-filtering.

Backend coverage

  • Genres, keywords, studios, watch providers: use TMDB's native without_* parameters.
  • Original language: post-filtered on original_language from the list response; server (user default) is resolved server-side for both include and exclude using the same logic as createTmdbWithRegionLanguage.
  • Production country: movies use a complement query (with_origin_country = all codes except the excluded ones); TV is post-filtered on origin_country (TMDB only surfaces this field for TV list responses).
  • TV status: complement query via with_status.
  • Bounded over-fetch of up to 3 extra TMDB pages when post-filter drops items, plus a paginationIsEstimate flag on responses.

Supporting changes

  • Centralized discover dimension registry in server/discover/types.ts; DiscoverFilter type, query schema, and client capability matrix are all derived from it.
  • mergeQueryString reads from router.asPath so consecutive filter updates don't race and overwrite each other.
  • Query schema splits multi-value params on both , and | to match existing Seerr selector conventions.

Partially addresses #1766 (discover filters only; override rules not covered).

The PR has been designed to make UX replacement incredibly easy if the provided UX toggles/naming is not desirable.

How Has This Been Tested?

  • pnpm typecheck — clean
  • pnpm lint — 0 errors (19 pre-existing warnings)
  • pnpm prettier --check — clean
  • pnpm build — successful
  • pnpm i18n:extract — extracted new keys, committed in e4087bf7
  • Manual dev-server testing against a local DB snapshot:
    • Verified /discover/movies and /discover/tv filter panels render correctly.
    • Verified include/exclude toggles transfer values between modes.
    • Verified single and mixed exclusion filters (e.g. excludeLanguages=tl|hi + excludeCountries=IN).
    • Verified server language resolution in both include and exclude modes.
    • Verified all/empty language setting does not hide every result in exclude mode.

Screenshots / Logs (if applicable)

Movies discover page with the filter panel open. Spanish is excluded under Original Language and Mexico is included under Production Country; the estimate hint is visible at the top of the results.
i.e: "Show me all non-Spanish language movies with a Production Country in Mexico".

Screenshot From 2026-06-16 02-07-16

Movies discover page with the filter panel open. Hindi is excluded under Original Language and India is included under Production Country, demonstrating the per-section include/exclude toggles.
i.e: "Show me all non-Hindi movies with a Production Country in India"

Screenshot From 2026-06-16 02-07-54

Movie detail page for "KD – The Devil" showing the Original Language (Kannada) and Production Country (India) fields that the filters operate on.
This shows the Production Country is India, but language is not Hindi from filter above.

Screenshot From 2026-06-16 02-08-06

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)

AI Disclosure: I used GLM 5.2 as an aid while exploring the discover filter architecture, verifying TMDB OpenAPI documentation, and finding the best execution surface. The overall architecture and design decisions were hand crafted. All code was reviewed, tested, and manually verified by me before submission.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Include/Exclude toggles for discover filters (genres, studios, watch providers, languages, countries, and TV status) to refine or hide matching values.
    • Added a country/region selector for discover.
    • Added an on-page alert when exclusion filters can change pagination totals (showing estimated results).
  • Documentation

    • Updated discover filter documentation to clarify Include vs Exclude behavior and current streaming-provider filtering limitations in the UI.

…xclude toggles

Adds per-dimension exclusion (negation) support to discover. Each filter
section now has an Included/Excluded slide switch; the active mode drives
whether the selector writes to the include or exclude URL param, and the
value transfers when toggling so users don't need to re-select.

Exclusion is implemented with three mechanisms, dispatched centrally in
buildDiscoverPlan():
  - TMDB-native without_* params (genres, keywords, studio, providers)
  - Post-filter on list-response fields (language on both, country on TV)
  - Complement query where TMDB has no without_* param (movie country,
    TV status)

A bounded over-fetch (max 3 extra pages) fills the requested page when a
post-filter drops items, and the response carries paginationIsEstimate so
the UI can label the total as an estimate.

A capabilities matrix (FILTER_CAPABILITIES) drives which sections render an
exclude toggle, so dimensions TMDB cannot exclude (e.g. watch providers)
show a plain heading instead of a non-functional toggle. The flat and
structured filter shapes are defined once and imported by both the route
handlers and the client.

Route handlers are simplified to parse → plan → fill → enrich → respond.
The TMDB wrapper gains without_* params, with_origin_country, and a country
param, reusing the existing nodeCache layer.
…om it

Introduce a single source of truth for discover filter dimensions in
server/discover/types.ts (DISCOVER_DIMENSIONS). The flat URL-param
schema (server/discover/schema.ts), the structured DiscoverFilter type,
and the client-side FILTER_CAPABILITIES matrix
(src/components/Discover/capabilities.ts) are all derived from this
registry.

This means any future dimension only needs to be added in one place;
downstream consumers will produce compile-time errors until they are
wired up, ensuring that drift is explicitly identified rather than
silently allowed to introduce bugs. It also removes the need for the
client to maintain a parallel copy of the query schema in
src/components/Discover/constants.ts.

Imports are updated to use the existing @server/* and @app/* aliases,
and both tsconfig.json files are restored to their develop state since
no new path alias is required.
…e selector conventions

The new per-dimension include/exclude toggles make the FilterSlideover
fire several rapid router.replace calls in succession. The existing
mergeQueryString helper read from router.query, which Next.js updates
asynchronously, so later replaces were occasionally building on a stale
query and overwriting earlier filter changes. Reading from router.asPath
instead uses the URL that is actually in the address bar, so mixed
include/exclude combinations stay intact.

LanguageSelector is shared with user settings and therefore emits values
using its own conventions: the pseudo-value "server" means "the user's
default original language", "all" means no language filter, and multiple
selections are joined with "|". The discover route now resolves "server"
on the server using the same logic as createTmdbWithRegionLanguage,
making it valid for both include and exclude modes. If the resolved
setting is "all" or empty, exclude mode resolves to no codes so it does
not hide every result.

Finally, the flat query schema now splits multi-value params on either
"," or "|" to match the native separators used by the existing Seerr
selectors (pipe for language/status, comma for genre/keyword/country).
Adds the new translation keys introduced by the per-dimension
include/exclude filter panel and the 'All X' selector placeholders.
Adds a short paragraph to the general settings docs explaining that
the Discover filter panel supports Included and Excluded modes per
dimension, and that streaming services currently remain include-only.
@TheReaperJay TheReaperJay requested a review from a team as a code owner June 15, 2026 19:58
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0df41126-74b5-4f9f-91ad-2aae048d8a12

📥 Commits

Reviewing files that changed from the base of the PR and between af4152c and cbb9167.

📒 Files selected for processing (8)
  • docs/using-seerr/settings/general.md
  • seerr-api.yml
  • server/discover/planBuilder.ts
  • server/discover/types.ts
  • server/routes/discover.ts
  • src/components/Common/SlideCheckbox/index.tsx
  • src/components/Discover/FilterSlideover/index.tsx
  • src/components/Selector/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • docs/using-seerr/settings/general.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/components/Common/SlideCheckbox/index.tsx
  • seerr-api.yml
  • server/discover/planBuilder.ts
  • src/components/Selector/index.tsx
  • server/routes/discover.ts

📝 Walkthrough

Walkthrough

Adds per-dimension include/exclude toggling to the Discover filter panel. A new server-side pipeline translates a structured DiscoverFilter (backed by a DISCOVER_DIMENSIONS registry and shared Zod schemas) into TMDB query params plus a PostFilterSpec. A fillPage utility over-fetches TMDB pages when post-filtering drops results, returning an HonestPage with a paginationIsEstimate flag surfaced in the UI.

Changes

Discover Include/Exclude Filter Pipeline

Layer / File(s) Summary
Filter type registry and Zod schemas
server/discover/types.ts, server/discover/schema.ts
Introduces DISCOVER_DIMENSIONS registry, all derived types (DimensionKey, DiscoverFilter, PostFilterSpec, DiscoverPlan, HonestPage, ListResult), and the shared Zod schemas (DiscoverFilterQuerySchema, DiscoverFilterSchema) that parse flat URL params into a structured filter used by both server and client.
TMDB API option extensions
server/api/themoviedb/index.ts
Extends DiscoverMovieOptions and DiscoverTvOptions with new exclusion and origin-country fields, and wires them into the TMDB request param construction inside getDiscoverMovies and getDiscoverTv.
Server pipeline utilities
server/discover/countryCodes.ts, server/discover/postFilter.ts, server/discover/fill.ts
Adds getAllCountryCodes/buildComplement for TMDB region lookups and complement-string generation, applyPostFilter for locally filtering results by excluded language/country, and fillPage for over-fetching TMDB pages when a post-filter is active to produce an HonestPage with paginationIsEstimate.
Plan builder: DiscoverFilter → TMDB params
server/discover/planBuilder.ts
Adds buildDiscoverPlan, which translates a structured DiscoverFilter into TmdbDiscoverParams (using native TMDB params for most dimensions, complement strings for movie-country exclusion and TV-status exclusion) plus a PostFilterSpec for fields requiring local handling.
Discover route handler rework
server/routes/discover.ts
Replaces local Zod query schemas with DiscoverFilterSchema, adds language shorthand resolution helpers, and rewires /movies and /tv handlers to use the getAllCountryCodesbuildDiscoverPlanfillPage pipeline. Adds paginationIsEstimate to JSON responses.
Client capabilities matrix and filter constants
src/components/Discover/capabilities.ts, src/components/Discover/constants.ts
Adds FILTER_CAPABILITIES matrix defining per-media-type include/exclude support per DimensionKey. Updates constants.ts to consume DiscoverFilterQuerySchema from server and extends prepareFilterValues to map the new exclude/country fields.
FilterSlideover include/exclude toggle UI
src/components/Discover/FilterSlideover/index.tsx, src/components/Common/SlideCheckbox/index.tsx
Adds per-dimension exclude-mode state, introduces FilterSectionHeading rendering a SlideCheckbox when FILTER_CAPABILITIES permits, implements toggleSection to swap include↔exclude URL params while preserving existing values, and reworks all dimension sections to write to the correct param set. Fixes SlideCheckbox aria-checked, keyboard handling, and removes pt-2 spacing.
CountrySelector, hooks, query params, result views, i18n, API spec, and docs
src/components/Selector/index.tsx, src/hooks/useDiscover.ts, src/hooks/useUpdateQueryParams.ts, src/components/Discover/DiscoverMovies/index.tsx, src/components/Discover/DiscoverTv/index.tsx, src/i18n/locale/en.json, seerr-api.yml, docs/using-seerr/settings/general.md
Adds CountrySelector loading from /api/v1/regions. Adds paginationIsEstimate to BaseSearchResult. Fixes mergeQueryString to parse router.asPath for reliable rapid-update merging. Adds resultsFilteredHint banners in Discover views when paginationIsEstimate is true. Updates i18n keys, OpenAPI spec params and response fields, and user documentation.

Sequence Diagram

sequenceDiagram
  participant Client as Discover Page
  participant FilterSlideover
  participant DiscoverRoute as /discover/movies or /discover/tv
  participant buildDiscoverPlan
  participant fillPage
  participant applyPostFilter
  participant TMDB as TMDB Discover API

  Client->>FilterSlideover: user toggles dimension to Exclude
  FilterSlideover->>Client: update URL query params (include↔exclude keys)
  Client->>DiscoverRoute: GET /discover/movies?excludeGenres=...&country=...
  DiscoverRoute->>DiscoverRoute: DiscoverFilterSchema.parse(query)
  DiscoverRoute->>DiscoverRoute: resolveServerLanguage(codes)
  DiscoverRoute->>DiscoverRoute: getAllCountryCodes(tmdb)
  DiscoverRoute->>buildDiscoverPlan: DiscoverFilter + allCountryCodes
  buildDiscoverPlan-->>DiscoverRoute: { discoverOptions, postFilter }
  DiscoverRoute->>fillPage: tmdb.getDiscoverMovies, postFilter, page
  loop until PAGE_SIZE filled or maxOverFetch exceeded
    fillPage->>TMDB: getDiscoverMovies(discoverOptions, page++)
    TMDB-->>fillPage: TmdbPage results
    fillPage->>applyPostFilter: results, postFilter spec
    applyPostFilter-->>fillPage: filtered results + dropped count
  end
  fillPage-->>DiscoverRoute: HonestPage (paginationIsEstimate)
  DiscoverRoute-->>Client: { results, totalPages, paginationIsEstimate, keywords }
  Client->>Client: show resultsFilteredHint banner if paginationIsEstimate
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • gauthier-th
  • fallenbagel

Poem

🐰 Hoppin' through filters, excluded or in,
Each genre and country — I tweak with a grin!
The complement builds what the TMDB won't hide,
Post-filter drops items, but estimates guide.
"Results may be fewer!" the banner proclaims—
A rabbit who filters has mastered these games! 🎯

🚥 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 describes the main feature: adding exclusionary filters with per-dimension toggles to control inclusion/exclusion of filter values.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/Common/SlideCheckbox/index.tsx (1)

15-18: ⚠️ Potential issue | 🟡 Minor

Spacebar toggle won't trigger—e.key value mismatch.

The spacebar emits e.key === ' ' (single space character), not 'Space'. This prevents keyboard users from toggling with Space. Add preventDefault() to prevent page scroll when Space is pressed.

Proposed fix
       onKeyDown={(e) => {
-        if (e.key === 'Enter' || e.key === 'Space') {
+        if (e.key === 'Enter' || e.key === ' ') {
+          e.preventDefault();
           onClick();
         }
       }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Common/SlideCheckbox/index.tsx` around lines 15 - 18, The
onKeyDown event handler in the SlideCheckbox component has a keyboard detection
issue where the spacebar check uses the wrong string value. The spacebar emits a
single space character as e.key value, not the string 'Space', so the current
condition will never match when Space is pressed. Update the condition to check
for e.key === ' ' instead of e.key === 'Space', and add e.preventDefault()
before calling onClick() to prevent the default page scroll behavior that occurs
when Space is pressed on interactive elements.
seerr-api.yml (1)

5578-5594: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

paginationIsEstimate is missing from both discover response schemas.

Per the feature behavior, discover responses can now include an estimate flag when post-filter over-fetching is used. Omitting it from the OpenAPI contract creates schema drift for client generation and API consumers (Lines 5578 and 5945 response objects).

Suggested OpenAPI patch
   /discover/movies:
     get:
       ...
       responses:
         '200':
           description: Results
           content:
             application/json:
               schema:
                 type: object
                 properties:
                   page:
                     type: number
                     example: 1
                   totalPages:
                     type: number
                     example: 20
                   totalResults:
                     type: number
                     example: 200
+                  paginationIsEstimate:
+                    type: boolean
+                    example: false
+                    description: True when totals/pages are estimated due to exclude post-filtering over-fetch
                   results:
                     type: array
                     items:
                       $ref: '`#/components/schemas/MovieResult`'

   /discover/tv:
     get:
       ...
       responses:
         '200':
           description: Results
           content:
             application/json:
               schema:
                 type: object
                 properties:
                   page:
                     type: number
                     example: 1
                   totalPages:
                     type: number
                     example: 20
                   totalResults:
                     type: number
                     example: 200
+                  paginationIsEstimate:
+                    type: boolean
+                    example: false
+                    description: True when totals/pages are estimated due to exclude post-filtering over-fetch
                   results:
                     type: array
                     items:
                       $ref: '`#/components/schemas/TvResult`'

Also applies to: 5945-5960

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@seerr-api.yml` around lines 5578 - 5594, The discover response schemas are
missing the paginationIsEstimate property which should be included in the 200
response objects to reflect that discover responses can now include an estimate
flag when post-filter over-fetching is used. Add a paginationIsEstimate property
(type boolean) to both discover endpoint response schemas alongside the existing
page, totalPages, totalResults, and results properties to maintain consistency
with the feature behavior and prevent schema drift for client generation.
🧹 Nitpick comments (1)
server/discover/planBuilder.ts (1)

31-33: ⚡ Quick win

Strengthen discoverOptions typing to preserve cross-layer contracts.

discoverOptions as Record<string, unknown> erases key-level safety and forces unsafe casts in route handlers. Type this as the concrete discover-options shape used by DiscoverPlan so key drift is caught at compile time.

💡 Proposed change
-  const discoverOptions: Record<string, unknown> = {};
+  const discoverOptions: DiscoverPlan['discoverOptions'] = {};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/discover/planBuilder.ts` around lines 31 - 33, The `discoverOptions`
variable on line 31 of planBuilder.ts is typed as `Record<string, unknown>`,
which eliminates compile-time type safety and causes unsafe casts in route
handlers. Replace this generic Record type with the concrete discover-options
shape that `DiscoverPlan` expects and uses, ensuring that the type definition
captures all required keys and their specific value types. This change will
enable the TypeScript compiler to catch key drift and type mismatches at compile
time rather than forcing developers to use unsafe type assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/using-seerr/settings/general.md`:
- Line 48: The statement on line 48 claims that streaming services support
inclusion only due to TMDB limitations, which contradicts the documentation of
excludeWatchProviders on discover endpoints elsewhere in this PR. Reword the
sentence in that location to clarify whether the exclusion limitation applies
only to the UI or also to the API level. Ensure the revised text explicitly
distinguishes between what the API supports (excludeWatchProviders) and what the
UI supports, so users receive consistent guidance throughout the documentation.

In `@server/discover/fill.ts`:
- Around line 44-71: The pagination logic starts collecting from requestedPage
instead of page 1, which causes duplicate results when post-filtering removes
items on pages beyond the first. Instead of starting tmdbPage at requestedPage,
initialize it to 1 and modify the while loop condition to collect enough items
to satisfy the entire filtered stream up through the requested page (at least
requestedPage * PAGE_SIZE items total). Then adjust the returned results slice
to extract only the appropriate page range using (requestedPage - 1) * PAGE_SIZE
as the start index and requestedPage * PAGE_SIZE as the end index. This ensures
a stable filtered stream where earlier pages' results are never duplicated in
later pages.

In `@server/routes/discover.ts`:
- Around line 151-158: Replace Promise.all with Promise.allSettled in the
keyword details lookup to prevent a single failed keyword lookup from rejecting
the entire discover endpoint response. Update the filter logic to handle the
PromiseSettledResult structure returned by allSettled: iterate through the
results, check for status === 'fulfilled', and keep only the keyword data from
successful results, discarding rejected ones. This same change must be applied
at both locations: the anchor site in server/routes/discover.ts at lines 151-158
where keywordIds are fetched via tmdb.getKeywordDetails, and the sibling site at
lines 460-466 which has the same pattern and should be fixed identically.
- Around line 123-124: The getAllCountryCodes call in the discover route is
executed unconditionally for every request, but country codes are only needed
when building movie-country exclusions. Refactor the code to conditionally call
getAllCountryCodes only when the filter actually requires country code
filtering, rather than fetching it upfront for all requests. This will reduce
unnecessary latency and remote dependencies on the hot paths. Apply this
optimization to all affected locations in the discover routes where this
unconditional call currently occurs.

In `@src/components/Discover/FilterSlideover/index.tsx`:
- Around line 124-143: The exclude/include mode state variables (studioExclude,
genreExclude, statusExclude, keywordExclude, languageExclude, countryExclude)
are initialized from currentFilters only once in useState, but they do not
update when currentFilters changes due to URL navigation or parent updates. Add
a useEffect hook that depends on currentFilters and updates all six exclude
state variables whenever currentFilters changes, ensuring the toggle switches
and query parameter targets remain synchronized with the URL filters.

In `@src/components/Selector/index.tsx`:
- Around line 663-669: The CountrySelector is not properly resetting state when
defaultValue becomes empty, allowing prior selections to remain visible, and it
only handles comma delimiters when splitting values. Fix this by removing the
early return on empty defaultValue and instead clearing the relevant state, then
update the split operation to handle both comma and pipe separators (e.g., using
a regex pattern like /[,|]/) to normalize multi-value query strings that may use
either delimiter. This ensures defaults are properly hydrated and prior
selections are cleared when the input is emptied.

---

Outside diff comments:
In `@seerr-api.yml`:
- Around line 5578-5594: The discover response schemas are missing the
paginationIsEstimate property which should be included in the 200 response
objects to reflect that discover responses can now include an estimate flag when
post-filter over-fetching is used. Add a paginationIsEstimate property (type
boolean) to both discover endpoint response schemas alongside the existing page,
totalPages, totalResults, and results properties to maintain consistency with
the feature behavior and prevent schema drift for client generation.

In `@src/components/Common/SlideCheckbox/index.tsx`:
- Around line 15-18: The onKeyDown event handler in the SlideCheckbox component
has a keyboard detection issue where the spacebar check uses the wrong string
value. The spacebar emits a single space character as e.key value, not the
string 'Space', so the current condition will never match when Space is pressed.
Update the condition to check for e.key === ' ' instead of e.key === 'Space',
and add e.preventDefault() before calling onClick() to prevent the default page
scroll behavior that occurs when Space is pressed on interactive elements.

---

Nitpick comments:
In `@server/discover/planBuilder.ts`:
- Around line 31-33: The `discoverOptions` variable on line 31 of planBuilder.ts
is typed as `Record<string, unknown>`, which eliminates compile-time type safety
and causes unsafe casts in route handlers. Replace this generic Record type with
the concrete discover-options shape that `DiscoverPlan` expects and uses,
ensuring that the type definition captures all required keys and their specific
value types. This change will enable the TypeScript compiler to catch key drift
and type mismatches at compile time rather than forcing developers to use unsafe
type assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d7d259a-8e92-48c6-a23a-de1448889009

📥 Commits

Reviewing files that changed from the base of the PR and between 6c8527f and af4152c.

📒 Files selected for processing (20)
  • docs/using-seerr/settings/general.md
  • seerr-api.yml
  • server/api/themoviedb/index.ts
  • server/discover/countryCodes.ts
  • server/discover/fill.ts
  • server/discover/planBuilder.ts
  • server/discover/postFilter.ts
  • server/discover/schema.ts
  • server/discover/types.ts
  • server/routes/discover.ts
  • src/components/Common/SlideCheckbox/index.tsx
  • src/components/Discover/DiscoverMovies/index.tsx
  • src/components/Discover/DiscoverTv/index.tsx
  • src/components/Discover/FilterSlideover/index.tsx
  • src/components/Discover/capabilities.ts
  • src/components/Discover/constants.ts
  • src/components/Selector/index.tsx
  • src/hooks/useDiscover.ts
  • src/hooks/useUpdateQueryParams.ts
  • src/i18n/locale/en.json

Comment thread docs/using-seerr/settings/general.md Outdated
Comment thread server/discover/fill.ts
Comment thread server/routes/discover.ts Outdated
Comment thread server/routes/discover.ts Outdated
Comment thread src/components/Discover/FilterSlideover/index.tsx
Comment thread src/components/Selector/index.tsx
- Add concrete TmdbDiscoverMovieParams/TmdbDiscoverTvParams interfaces
  derived from the wrapper's option shapes, replacing the loose
  Record<string, unknown> in DiscoverPlan so key drift is caught at
  compile time.
- Use Promise.allSettled for keyword detail lookups so one missing
  keyword does not fail the entire discover response.
- Fetch country codes only when the filter actually involves country
  inclusion/exclusion, avoiding an unnecessary TMDB call in the
  common case.
- Sync FilterSlideover's local exclude-mode state with currentFilters
  via useEffect so back/forward navigation keeps the switches correct.
- Clear CountrySelector selected state when defaultValue becomes empty.
- Fix SlideCheckbox keyboard handling: Space emits e.key === ' ', not
  'Space', and preventDefault stops page scroll.
- Add paginationIsEstimate to the OpenAPI discover response schemas.
- Clarify in docs that streaming services are include-only in the UI
  (not that TMDB lacks the underlying parameter).
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