Skip to content

feat: per-user parental controls (content rating limits)#2415

Open
ProgenyAlpha wants to merge 1 commit intoseerr-team:developfrom
ProgenyAlpha:feature/parental-controls
Open

feat: per-user parental controls (content rating limits)#2415
ProgenyAlpha wants to merge 1 commit intoseerr-team:developfrom
ProgenyAlpha:feature/parental-controls

Conversation

@ProgenyAlpha
Copy link

@ProgenyAlpha ProgenyAlpha commented Feb 14, 2026

Description

Adds admin-enforced per-user content rating limits (parental controls). Admins can set maximum movie (MPAA) and TV (US Parental Guidelines) ratings per user, plus a "block unrated" toggle for fail-closed filtering. Restricted users see no indication that filtering exists.

Screenshot

Parental Controls Settings

Admin view: User > Settings > Parental Controls

How It Works

Admin Sets Limits

A new Parental Controls tab appears in user settings (admin-only — restricted users cannot see or modify their own limits). Admins choose:

  • Max Movie Rating: G, PG, PG-13, R, or NC-17 (MPAA)
  • Max TV Rating: TV-Y, TV-Y7, TV-G, TV-PG, TV-14, or TV-MA (US Parental Guidelines)
  • Block Unrated: When enabled, content without a US certification is hidden

Limits are stored as new columns on UserSettings (maxMovieRating, maxTvRating, blockUnrated) with TypeORM migrations for both PostgreSQL and SQLite.

Discover Filtering (Two-Layer)

  1. Pre-filter: TMDB's native certification.lte / certification_country query params remove rated content above the limit at the API level — no extra requests needed
  2. Post-filter: For blockUnrated users, results that slipped through (unrated content TMDB doesn't filter) are caught server-side by fetching each item's US certification and checking against the hierarchy

Search Filtering

Search results don't support TMDB's certification params, so all filtering is server-side. Certifications are fetched in parallel via Promise.allSettled for each result, then filtered against the user's limits.

Backfill

When post-filtering drops a page below 15 results, the next TMDB page is automatically fetched and filtered to prevent sparse/empty pages. This applies to both discover and search routes.

Fail-Closed Design

  • If a certification lookup fails → result is blocked (not leaked)
  • Unknown ratings not in the hierarchy → treated as unrated
  • No rating at all → blocked when blockUnrated is true, allowed when false

Rating Hierarchies

Single source of truth in server/constants/contentRatings.ts:

  • Movies: G < PG < PG-13 < R < NC-17
  • TV: TV-Y < TV-Y7 < TV-G < TV-PG < TV-14 < TV-MA

Scoped to US ratings — TMDB's certification data is most complete for the US market.

Files Changed

File Purpose
server/constants/contentRatings.ts Rating hierarchies, filter functions, types, UI dropdown options
server/entity/UserSettings.ts New columns: maxMovieRating, maxTvRating, blockUnrated
server/migration/ PostgreSQL + SQLite migrations for new columns
server/routes/discover.ts Pre-filter via certificationLte, post-filter for unrated, backfill
server/routes/search.ts Parallel cert lookup, filtering, backfill
server/routes/user/usersettings.ts GET/POST endpoints for parental controls
src/.../UserParentalControlsSettings/ Admin-only settings UI (dropdowns + toggle)
src/i18n/locale/en.json 15 new translation keys

Testing

  • Built and deployed to production Seerr instance (Docker, SQLite)
  • Verified admin can set rating limits per user
  • Verified restricted user sees filtered discover/search results
  • Verified blockUnrated hides content without US certification
  • Verified backfill fills sparse pages from next TMDB page
  • Verified unrestricted users see no change in behavior
  • pnpm build passes clean (both build:next and build:server)

AI Disclosure

This PR was developed with Claude Code (Claude Opus 4.6), with human review, testing, and architectural direction at every step. All code was verified against existing codebase patterns and deployed before submission.

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)

@ProgenyAlpha ProgenyAlpha requested a review from a team as a code owner February 14, 2026 06:30
@ProgenyAlpha ProgenyAlpha force-pushed the feature/parental-controls branch 2 times, most recently from 9cbb6f4 to d4bda48 Compare February 14, 2026 06:45
Admin-enforced content rating limits per user:
- Max movie rating (MPAA: G through NC-17)
- Max TV rating (US Parental Guidelines: TV-Y through TV-MA)
- Block unrated content toggle (fail-closed)

Filtering applied to all discover routes and search with parallel
TMDB certification lookups. Backfills from next page when filtering
drops results below 15.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ProgenyAlpha ProgenyAlpha force-pushed the feature/parental-controls branch from d4bda48 to f8ee51d Compare February 14, 2026 06:53
@fallenbagel
Copy link
Collaborator

Isnt this the same as
#2275

@ProgenyAlpha
Copy link
Author

ProgenyAlpha commented Feb 14, 2026

Isnt this the same as #2275

I wasn't aware of #2275 because it wasn't linked to any of the issues, so it didn't come up when I scoped out existing work before starting. But probably wouldn't have matter as I technically started this back in December 2025 and wanted to use it for an extensive period of time before submitting it.

That said, the implementations are architecturally different. #2275 makes live TMDB API calls per-item at request time to fetch certifications, which adds latency to every discovery and search page and scales poorly with TMDB rate limits. This PR uses a static rating hierarchy; filtering is instant with zero external API overhead.

A few other things this PR brings to the table:

  • Explicit blockUnrated toggle instead of overloading NR into the rating dropdown
  • Dedicated parental controls settings page for cleaner UX separation
  • OpenAPI spec updates for the new endpoints

Happy to collaborate or consolidate, the static rating approach could drop right into #2275 as a performance improvement if the maintainers prefer that path.

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.

Adjustable Age Restrictions for viewing content per user [Feature Request] Limit Visible Requests By Jellyfin Parental Control Ratings

2 participants