Skip to content

feat: add default watchlist sync toggles for newly-created users#3048

Open
csmarshall wants to merge 5 commits into
seerr-team:developfrom
csmarshall:feature/default-user-watchlist-sync
Open

feat: add default watchlist sync toggles for newly-created users#3048
csmarshall wants to merge 5 commits into
seerr-team:developfrom
csmarshall:feature/default-user-watchlist-sync

Conversation

@csmarshall

@csmarshall csmarshall commented May 19, 2026

Copy link
Copy Markdown

Description

WatchlistSync.syncUserWatchlist returns early for any user whose watchlistSyncMovies and watchlistSyncTv are both null or false. New users created via Plex OAuth, Plex friend import, Jellyfin OAuth, and Jellyfin import all land with both flags undefined, so they're silently skipped by server-level watchlist sync until someone toggles the setting per-user. For multi-user installs that's a footgun: an admin enables watchlist sync at the server level expecting it to apply to everyone, and it doesn't.

This PR adds two new MainSettings booleans — defaultWatchlistSyncMovies and defaultWatchlistSyncTv — exposed as toggles in Settings → General. Whatever's set there is applied to UserSettings at every new-user creation site. Defaults are false, so behavior on upgrade is unchanged. Existing users are not modified by anything in this PR; only newly-created users get the admin defaults.

Changes:

  • MainSettings interface + Settings constructor defaults (false)
  • Propagation to new UserSettings({...}) at all four user-creation sites:
    • POST /auth/plex
    • POST /auth/jellyfin
    • POST /api/v1/user/import-from-plex
    • POST /api/v1/user/import-from-jellyfin
  • Two toggles in Settings → General Settings, following the existing hideAvailable / enableSpecialEpisodes form pattern
  • OpenAPI schema additions in seerr-api.yml
  • i18n keys in en.json (non-en locales left for Weblate per CONTRIBUTING.md)
  • Seven automated tests covering propagation under all default permutations, plus a regression test that existing users' settings aren't overwritten on subsequent sign-ins

How Has This Been Tested?

Automated:

  • pnpm test — 40/40 pass; 7 new in this PR (server/routes/auth.test.ts, server/routes/user/index.test.ts). Tests mock PlexTvAPI.prototype.{getUser, getUsers, checkUserAccess} so they run without a live Plex.tv account. Verified stable across 3 consecutive runs.
  • pnpm buildbuild:next and build:server both succeed.
  • pnpm typecheck:server — clean.
  • pnpm lint — clean for files in this PR; pre-existing warnings in untouched files only.
  • pnpm i18n:extract — adds 4 keys to en.json only.

Manual:

  • Wiped seerr/config/, started pnpm dev, confirmed the auto-written settings.json contains both defaultWatchlistSyncMovies and defaultWatchlistSyncTv defaulted to false at the top of main.
  • Opened Settings → General Settings in a browser; both toggles render with their final label ("Default: Auto-Request Movies/Series added to Plex Watchlist") and tip ("Default value applied to newly-created users. Existing users are unaffected; each user can override on their own profile.").

Screenshots / Logs (if applicable)

Screenshot 2026-05-18 at 15 22 05

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

Claude Code (Anthropic Opus 4.7) drafted the backend changes, the UI toggles, the seven propagation tests, and an initial version of this description. I reviewed every diff, ran pnpm build, pnpm test, pnpm typecheck:server, and pnpm lint locally, and verified the toggles on a pnpm dev instance. During review I caught that the original toggle labels and tips on Settings → General didn't mention "Plex Watchlist" anywhere — admins had no way to know what feature these toggles controlled. I had Claude revise the wording twice before it read clearly; the final labels are "Default: Auto-Request Movies/Series added to Plex Watchlist". I will respond to review feedback myself, without LLM assistance.

Summary by CodeRabbit

  • New Features
    • Added configurable default watchlist sync options for Movies and TV in Settings, with tooltips explaining they apply to newly created users (users can still override).
    • Defaults are applied automatically when users are created or imported via sign-in/import flows.
  • Tests
    • Added coverage verifying default watchlist sync behavior for user creation and import scenarios.

csmarshall added 3 commits May 1, 2026 11:12
Plex Watchlist Sync silently skips users where both watchlistSyncMovies
and watchlistSyncTv are null (the default), even when the user has
AUTO_REQUEST permission. This means every new user — whether from Plex
OAuth signin or from the admin Import Users from Plex/Jellyfin button —
inherits null/null settings and never has their watchlist synced unless
an admin manually toggles their settings.

Add two MainSettings fields, defaulting to false to preserve existing
behavior:
  - defaultWatchlistSyncMovies
  - defaultWatchlistSyncTv

Apply these to UserSettings on new-user creation in:
  - server/routes/auth.ts (Plex + Jellyfin OAuth signin paths)
  - server/routes/user/index.ts (import-from-plex + import-from-jellyfin)

Skips the admin-bootstrap creation paths since the very first user is
created with full ADMIN permissions and configures their own settings
deliberately.

OpenAPI schema updated. Frontend toggles in SettingsMain are not yet
added — backend can land first and UI can be a follow-up.
Mirror the existing hideAvailable/hideBlocklisted pattern in
SettingsMain to expose the two new MainSettings booleans (added in
8c17233) as labeled checkbox toggles, placed after the
enableSpecialEpisodes toggle.

Each toggle includes a label-tip explaining that the value is the
default applied to newly-created users on creation, and that existing
users are unaffected (they may still override their own setting on
their user profile).

i18n keys generated via pnpm i18n:extract; non-en locales are left for
Weblate translators per CONTRIBUTING.md.
Add four tests in auth.test.ts (POST /auth/plex new-user creation) and
three in user/index.test.ts (POST /api/v1/user/import-from-plex), each
mocking PlexTvAPI prototype methods so they need no network or real
Plex.tv account. Cover the false/true/mixed permutations of
defaultWatchlistSyncMovies and defaultWatchlistSyncTv plus a regression
test asserting that an existing user's settings are not overwritten by
the MainSettings defaults on subsequent sign-ins.
@csmarshall csmarshall requested a review from a team as a code owner May 19, 2026 21:03
@coderabbitai

coderabbitai Bot commented May 19, 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: 224440ee-fa85-4d4e-8041-8918d8766cb8

📥 Commits

Reviewing files that changed from the base of the PR and between 6debbae and 94f0d88.

📒 Files selected for processing (5)
  • seerr-api.yml
  • server/lib/settings/index.ts
  • server/routes/user/index.ts
  • src/components/Settings/SettingsMain/index.tsx
  • src/i18n/locale/en.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/i18n/locale/en.json
  • server/lib/settings/index.ts
  • seerr-api.yml
  • server/routes/user/index.ts
  • src/components/Settings/SettingsMain/index.tsx

📝 Walkthrough

Walkthrough

Adds two admin-configurable boolean defaults (movies/TV) to MainSettings, exposes checkboxes in the Settings UI, and applies those defaults when creating or importing users via Plex and Jellyfin. Tests verify persistence and that existing user settings are not overwritten.

Changes

Watchlist sync defaults for new users

Layer / File(s) Summary
Settings schema and contract
seerr-api.yml, server/lib/settings/index.ts
MainSettings adds defaultWatchlistSyncMovies and defaultWatchlistSyncTv booleans; Settings initializes them to false; OpenAPI documents the new fields.
Auth-based user creation with defaults
server/routes/auth.ts, server/routes/auth.test.ts
Plex and Jellyfin sign-in handlers construct UserSettings for new users seeded from settings.main.defaultWatchlistSyncMovies/defaultWatchlistSyncTv. Tests mock Plex behavior and assert new-user persistence and non-overwrite of existing users.
Bulk user import with defaults
server/routes/user/index.ts, server/routes/user/index.test.ts
Import endpoints for Plex/Jellyfin initialize newUser.settings from main defaults. Test suite spins up an Express app, authenticates an admin, mocks Plex API, imports users, and asserts persisted watchlist sync flags for multiple default permutations.
Admin UI for default settings
src/components/Settings/SettingsMain/index.tsx, src/i18n/locale/en.json
SettingsMain adds localized labels/tooltips and two checkboxes bound to Formik; form reads/writes defaultWatchlistSyncMovies and defaultWatchlistSyncTv via the settings API.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • seerr-team/seerr#2884: Updates server/lib/settings/index.ts to persist newly introduced default settings keys during Settings.load, which complements these added default fields.

Suggested reviewers

  • fallenbagel
  • gauthier-th
  • 0xSysR3ll

Poem

🐰 I nibble defaults with a careful hop,

New users bloom and old settings don't stop,
Movies or TV, the choice is set true/false,
Admins tuck them in like seeds on a waltz,
Hop on, little watcher, enjoy your watchlist waltz.

🚥 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 clearly and specifically summarizes the main change: adding default watchlist sync toggles for newly-created users, which is the primary feature introduced across all modified files.
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: 1

🤖 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 `@server/routes/auth.ts`:
- Around line 184-192: Set user.settings to a new UserSettings (using
settings.main.defaultWatchlistSyncMovies and
settings.main.defaultWatchlistSyncTv) before calling userRepository.save so the
new user and their default watchlist flags are persisted in a single atomic
save; update both creation paths that currently call userRepository.save twice
(the block that constructs UserSettings and the other path that repeats this
logic) to assign user.settings first and perform a single save via
userRepository.save(user).
🪄 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: b8a24574-0301-47ce-a146-83883f382cf7

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa2c71 and 3a3d59f.

📒 Files selected for processing (8)
  • seerr-api.yml
  • server/lib/settings/index.ts
  • server/routes/auth.test.ts
  • server/routes/auth.ts
  • server/routes/user/index.test.ts
  • server/routes/user/index.ts
  • src/components/Settings/SettingsMain/index.tsx
  • src/i18n/locale/en.json

Comment thread server/routes/auth.ts Outdated
Comment on lines +23 to +27
session({
secret: 'test-secret',
resave: false,
saveUninitialized: false,
})
@fallenbagel

Copy link
Copy Markdown
Member

How does this close #2820??

Also enabling watchlist sync by default still wouldn't grab their watchlits since their plex token is only populated when they log in once...

- Persist new-user settings atomically: set user.settings before the
  first userRepository.save() and let the cascade-true relation on
  User.settings handle the UserSettings persistence in one transaction.
  Previously two separate saves were issued, so a failure between them
  could leave a user without their default watchlist flags. Applies to
  all four user-creation sites: POST /auth/plex, POST /auth/jellyfin,
  POST /api/v1/user/import-from-plex, POST /api/v1/user/import-from-jellyfin.
  Reported by CodeRabbit on the PR.
- Fix UserSettings import ordering in server/routes/user/index.ts so
  prettier --check . passes in CI.
- Set cookie.secure to 'auto' in the express-session config used by the
  new user/index.test.ts to satisfy CodeQL's clear-text-cookie check
  without breaking the supertest-over-HTTP test harness.

40/40 tests still pass.

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

🧹 Nitpick comments (1)
server/routes/user/index.test.ts (1)

143-162: 💤 Low value

Consider adding test for the remaining permutation (movies=true, tv=false).

The three test cases cover false/false, true/true, and false/true. Adding a fourth test for movies=true with tv=false would provide complete permutation coverage and further demonstrate that the two settings are independently applied.

✅ Optional test case for completeness
+  it('propagates mixed defaultWatchlistSync flags (movies=true, tv=false)', async () => {
+    const settings = getSettings();
+    settings.main.defaultWatchlistSyncMovies = true;
+    settings.main.defaultWatchlistSyncTv = false;
+
+    const agent = await adminAgent();
+    const res = await agent
+      .post('/api/v1/user/import-from-plex')
+      .send({ plexIds: [String(TARGET_PLEX_ID)] });
+
+    assert.strictEqual(res.status, 201);
+
+    const created = await getRepository(User).findOneOrFail({
+      where: { email: TARGET_EMAIL },
+      relations: { settings: true },
+    });
+    assert.ok(created.settings);
+    assert.strictEqual(created.settings.watchlistSyncMovies, true);
+    assert.strictEqual(created.settings.watchlistSyncTv, false);
+  });
🤖 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/routes/user/index.test.ts` around lines 143 - 162, Add a fourth unit
test mirroring the existing "propagates mixed defaultWatchlistSync flags" test
but with settings.main.defaultWatchlistSyncMovies = true and
settings.main.defaultWatchlistSyncTv = false: call getSettings(), set those two
flags, use adminAgent() to POST to '/api/v1/user/import-from-plex' with the same
plexIds payload, assert 201 response, then load the created User via
getRepository(User).findOneOrFail (relations: { settings: true }) and assert
created.settings.watchlistSyncMovies === true and
created.settings.watchlistSyncTv === false to verify independent propagation.
🤖 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.

Nitpick comments:
In `@server/routes/user/index.test.ts`:
- Around line 143-162: Add a fourth unit test mirroring the existing "propagates
mixed defaultWatchlistSync flags" test but with
settings.main.defaultWatchlistSyncMovies = true and
settings.main.defaultWatchlistSyncTv = false: call getSettings(), set those two
flags, use adminAgent() to POST to '/api/v1/user/import-from-plex' with the same
plexIds payload, assert 201 response, then load the created User via
getRepository(User).findOneOrFail (relations: { settings: true }) and assert
created.settings.watchlistSyncMovies === true and
created.settings.watchlistSyncTv === false to verify independent propagation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d184606-cd28-4372-b5ba-5c155ac8a031

📥 Commits

Reviewing files that changed from the base of the PR and between 3a3d59f and 6debbae.

📒 Files selected for processing (3)
  • server/routes/auth.ts
  • server/routes/user/index.test.ts
  • server/routes/user/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/routes/user/index.ts
  • server/routes/auth.ts

@csmarshall

Copy link
Copy Markdown
Author

How does this close #2820??
It doesn't - I think I mis-read the intent of that request

Also enabling watchlist sync by default still wouldn't grab their watchlits since their plex token is only populated when they log in once...

Eh - it's designed to grab their watch lists on first log in when we do have their credential - so that if someone adds a user and they log in, their automatically going to find that things were requested for them that they had been wanting to watch - for stuff already in the library it's a no-op obviously - and it would populate a bunch of requests for approval if not already auto-approved.

@fallenbagel

Copy link
Copy Markdown
Member

How does this close #2820??
It doesn't - I think I mis-read the intent of that request

Also enabling watchlist sync by default still wouldn't grab their watchlits since their plex token is only populated when they log in once...

Eh - it's designed to grab their watch lists on first log in when we do have their credential - so that if someone adds a user and they log in, their automatically going to find that things were requested for them that they had been wanting to watch - for stuff already in the library it's a no-op obviously - and it would populate a bunch of requests for approval if not already auto-approved.

But that still doesnt close #2820 which was about a support ticket

@csmarshall

Copy link
Copy Markdown
Author

How does this close #2820??
It doesn't - I think I mis-read the intent of that request

Also enabling watchlist sync by default still wouldn't grab their watchlits since their plex token is only populated when they log in once...

Eh - it's designed to grab their watch lists on first log in when we do have their credential - so that if someone adds a user and they log in, their automatically going to find that things were requested for them that they had been wanting to watch - for stuff already in the library it's a no-op obviously - and it would populate a bunch of requests for approval if not already auto-approved.

But that still doesnt close #2820 which was about a support ticket

I specifically went and remove any reference to 2820 as this really is not a bug fix but a feature add.

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.

3 participants