Skip to content

fix: preserve blocklist on media deletion & optimise watchlist-sync#2478

Merged
fallenbagel merged 4 commits intodevelopfrom
fallenbagel/fix/blocklist-watchlist-sync
Feb 18, 2026
Merged

fix: preserve blocklist on media deletion & optimise watchlist-sync#2478
fallenbagel merged 4 commits intodevelopfrom
fallenbagel/fix/blocklist-watchlist-sync

Conversation

@fallenbagel
Copy link
Copy Markdown
Collaborator

@fallenbagel fallenbagel commented Feb 17, 2026

Description

When a user blocklists media and then removes it from Sonarr/Radarr via the Seerr interface, the Media entity and its associated Blocklist record were both destroyed due to the cascade delete on the relationship. On the next watchlist sync cycle, no record existed to check against, so a fresh request was created and auto-approved, completely bypassing the blocklist.

This PR fixes the media delete endpoint to detect blocklisted status and preserve the entity by nulling out service-related fields rather than removing the row entirely. It also improves the watchlist sync filtering to exclude blocklisted media as a safety net and to skip items where the user already has an existing auto-request, eliminating the repeated unnecessary api calls and database lookups that were happening every sync cycle just to throw a duplicate request error.

How Has This Been Tested?

Screenshots / Logs (if applicable)

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)

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Watchlist sync now excludes items the user has already auto-requested, reducing duplicate requests.
    • Deleting a blocklisted media item now preserves the record while clearing external/service identifiers instead of removing it.
    • Watchlist sync still respects user permissions and settings; existing error handling for request creation remains unchanged.

…watchlist sync

Blocklisted media was being re-requested via watchlist sync because deleting media from
Sonarr/Radarr through the UI also deleted the Media entity and its associated Blocklist record via
cascade. The media delete endpoint now preserves blocklisted Media entities by clearing service
fields instead of removing the row. Additionally, the watchlist sync filter now excludes blocklisted
items and skips media that already has an existing auto-request for the user, avoiding unnecessary
API calls and database queries on every sync cycle.

fix #2475
@fallenbagel fallenbagel requested a review from Copilot February 17, 2026 10:01
@fallenbagel fallenbagel requested a review from a team as a code owner February 17, 2026 10:01
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 17, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds filtering in watchlist sync to skip items the user has already auto-requested by querying existing MediaRequest records; adds Media.resetServiceData() to clear service-related fields; changes DELETE /:id to call resetServiceData() and save when media.status is BLOCKLISTED instead of deleting the record.

Changes

Cohort / File(s) Summary
Watchlist Sync Auto-Request Filtering
server/lib/watchlistsync.ts
Collects watchlist TMDB IDs, queries MediaRequest for the current user's matching TMDB IDs to build a "mediaType:tmdbId" set, and filters watchlist items to exclude those already auto-requested before attempting request creation.
Media Deletion / Blocklist Preservation
server/routes/media.ts
Modifies DELETE /:id handler to, when media.status is BLOCKLISTED, call resetServiceData() and save the entity instead of removing the row; for other statuses, proceeds with removal.
Media Entity Service Data Reset
server/entity/Media.ts
Adds public resetServiceData(): void method that nulls service-related fields (serviceId*, externalServiceId*, externalServiceSlug*, ratingKey*, jellyfinMediaId*, ratingKey*, etc.) to allow preserving blocklisted records without external identifiers.

Sequence Diagram(s)

sequenceDiagram
    participant Plex as Plex Watchlist API
    participant Job as Watchlist Sync Job
    participant DB as Database
    participant Creator as MediaRequest Creator

    Plex->>Job: return watchlist items (tmdbId, mediaType)
    Job->>DB: query MediaRequest where userId = X and tmdbId IN (watchlistTmdbIds)
    DB-->>Job: existing auto-request set (mediaType:tmdbId)
    Job->>Job: filter watchlist items excluding existing set and blocked/unavailable
    Job->>Creator: create media request for each remaining item
    Creator-->>DB: persist new MediaRequest (if created)
    Creator-->>Job: return result (success/failure)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hopped through watchlists, sniffed each seed,
Skipped the echoes of requests already freed.
For blocklisted hares I left no external trace,
Cleared service prints, kept the record in place.
🥕

🚥 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 two main changes: preserving blocklist on media deletion and optimizing watchlist-sync filtering.
Linked Issues check ✅ Passed The PR implements both key requirements from #2475: preserving blocklisted media by nulling service fields instead of deletion, and optimizing watchlist-sync to skip already auto-requested items.
Out of Scope Changes check ✅ Passed All changes directly address the blocklist preservation and watchlist-sync optimization objectives described in issue #2475, with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/lib/watchlistsync.ts`:
- Around line 73-84: existingAutoRequests mapping assumes every MediaRequest has
a loaded media relation and accessing r.media.tmdbId can throw; update the
mapping that builds autoRequestedTmdbIds to guard against missing media by
filtering or null-checking each request first (e.g., use
existingAutoRequests.filter(r => r.media).map(r => r.media.tmdbId) or
conditional access) so only valid tmdbId values are added; modify the logic
around requestRepository, existingAutoRequests, and autoRequestedTmdbIds to
safely handle requests with null/undefined media.

Comment thread server/lib/watchlistsync.ts
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

Fixes a watchlist-sync + media deletion edge case where deleting a Media row could cascade-delete its Blocklist entry, allowing future watchlist sync cycles to recreate and auto-approve requests that should remain blocked.

Changes:

  • Updates the media delete endpoint to preserve blocklisted media by nulling service-related fields instead of deleting the row.
  • Hardens watchlist sync filtering to skip blocklisted items and avoid repeated duplicate auto-request attempts.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
server/routes/media.ts Preserves blocklisted media on deletion by clearing service identifiers instead of removing the entity.
server/lib/watchlistsync.ts Adds pre-filtering to skip blocklisted media and items with existing auto-requests before attempting request creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/lib/watchlistsync.ts
Comment thread server/lib/watchlistsync.ts Outdated
Comment thread server/routes/media.ts
@fallenbagel fallenbagel changed the title fix(watchlist-sync): preserve blocklist on media deletion & optimise … fix: preserve blocklist on media deletion & optimise watchlist-sync Feb 17, 2026
Comment thread server/routes/media.ts Outdated
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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/routes/media.ts
Comment thread server/routes/media.ts Outdated
Comment thread server/lib/watchlistsync.ts
Copy link
Copy Markdown
Member

@gauthier-th gauthier-th left a comment

Choose a reason for hiding this comment

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

LGTM

@fallenbagel fallenbagel enabled auto-merge (squash) February 18, 2026 12:25
@fallenbagel fallenbagel merged commit 9da8bb6 into develop Feb 18, 2026
13 checks passed
@fallenbagel fallenbagel deleted the fallenbagel/fix/blocklist-watchlist-sync branch February 18, 2026 20:23
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Feb 27, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/seerr-team/seerr](https://github.com/seerr-team/seerr) | minor | `v3.0.1` → `v3.1.0` |
| [seerr-team/seerr](https://github.com/seerr-team/seerr) | minor | `v3.0.1` → `v3.1.0` |

---

### Release Notes

<details>
<summary>seerr-team/seerr (ghcr.io/seerr-team/seerr)</summary>

### [`v3.1.0`](https://github.com/seerr-team/seerr/releases/tag/v3.1.0)

[Compare Source](seerr-team/seerr@v3.0.1...v3.1.0)

##### 🛡️ Security

- Patch [CVE-2026-27707](GHSA-rc4w-7m3r-c2f7)  - Unauthenticated account registration on Plex-configured Seerr instances via Jellyfin authentication endpoint  - ([4ae2068](seerr-team/seerr@4ae2068))
- Patch [CVE-2026-27793](GHSA-f7xw-jcqr-57hp) - Broken Object-Level Authorization in User Profile Endpoint Exposes Third-Party Notification Credentials - ([4f089b2](seerr-team/seerr@4f089b2))
- Patch [CVE-2026-27792](GHSA-gx3h-3jg5-q65f)  - Missing authentication on pushSubscription endpoints  - ([946bdecec](seerr-team/seerr@946bdec))

##### 🚀 Features

- *(helm)* Use an existing PVC as config volume ([#&#8203;2447](seerr-team/seerr#2447)) - ([8f0c904](seerr-team/seerr@8f0c904))
- *(servarr-api)* Make Servarr API request timeout configurable ([#&#8203;2556](seerr-team/seerr#2556)) - ([3bcb4da](seerr-team/seerr@3bcb4da))
- Self-host font for better privacy ([#&#8203;2540](seerr-team/seerr#2540)) - ([10ea21b](seerr-team/seerr@10ea21b))

##### 🐛 Bug Fixes

- *(helm)* Add "v" as prefix for appVersion tag ([#&#8203;2445](seerr-team/seerr#2445)) - ([04b9d87](seerr-team/seerr@04b9d87))
- *(jellyfin-scanner)* Include unmatched seasons in processable seasons ([#&#8203;2538](seerr-team/seerr#2538)) - ([68f56d2](seerr-team/seerr@68f56d2))
- *(link-account)* Fix error-message override ([#&#8203;2547](seerr-team/seerr#2547)) - ([b843be0](seerr-team/seerr@b843be0))
- *(plex-scanner)* Add TVDb to TMDB fallback in plex scanner ([#&#8203;2537](seerr-team/seerr#2537)) - ([7c60a5c](seerr-team/seerr@7c60a5c))
- *(radarr)* Trigger search for existing monitored movies without files ([#&#8203;2391](seerr-team/seerr#2391)) - ([55776ea](seerr-team/seerr@55776ea))
- *(servarr)* Increase default API timeout from 5000ms to 10000ms ([#&#8203;2442](seerr-team/seerr#2442)) - ([b499976](seerr-team/seerr@b499976))
- *(sonarr)* Use configured metadata provider for season filtering ([#&#8203;2516](seerr-team/seerr#2516)) - ([5013d1d](seerr-team/seerr@5013d1d))
- *(watch-data)* Use sentinel values to avoid invalid SQL syntax ([#&#8203;2552](seerr-team/seerr#2552)) - ([947f70c](seerr-team/seerr@947f70c))
- *(watchlist-sync)* Correct permission typo for TV auto requests ([#&#8203;2488](seerr-team/seerr#2488)) - ([e0e4b6f](seerr-team/seerr@e0e4b6f))
- Preserve blocklist on media deletion & optimise watchlist-sync ([#&#8203;2478](seerr-team/seerr#2478)) - ([9da8bb6](seerr-team/seerr@9da8bb6))

##### 🚜 Refactor

- *(tailwind)* Replace deprecated tailwind utilities ([#&#8203;2542](seerr-team/seerr#2542)) - ([f42a4ec](seerr-team/seerr@f42a4ec))

##### 📖 Documentation

- *(synology)* Add installation guide via SynoCommunity ([#&#8203;2503](seerr-team/seerr#2503)) - ([0e636a3](seerr-team/seerr@0e636a3))
- *(truenas)* Update install/migration guide ([#&#8203;2491](seerr-team/seerr#2491)) - ([dc1734d](seerr-team/seerr@dc1734d))
- *(unraid)* Improve unraid migration guide  ([#&#8203;2470](seerr-team/seerr#2470)) - ([5e64d49](seerr-team/seerr@5e64d49))
- Update Unraid install and migration guides with dual permission methods ([#&#8203;2532](seerr-team/seerr#2532)) - ([a0d0eb1](seerr-team/seerr@a0d0eb1))
- Add a warning in migration-guide for third party installation ([#&#8203;2527](seerr-team/seerr#2527)) - ([7e9dff3](seerr-team/seerr@7e9dff3))
- Remove double quotes (") from DB\_HOST environment variable ([#&#8203;2514](seerr-team/seerr#2514)) - ([fa905be](seerr-team/seerr@fa905be))
- Add Unraid installation and migration guide ([#&#8203;2440](seerr-team/seerr#2440)) - ([b6a9132](seerr-team/seerr@b6a9132))
- Fix migration guide title ([#&#8203;2425](seerr-team/seerr#2425)) - ([39ae32f](seerr-team/seerr@39ae32f))

##### ⚡ Performance

- Add missing indexes on all foreign key columns ([#&#8203;2461](seerr-team/seerr#2461)) - ([c6bcfe0](seerr-team/seerr@c6bcfe0))

##### ⚙️ Miscellaneous Tasks

- *(changelog)* Fix changelog template ([#&#8203;2431](seerr-team/seerr#2431)) - ([c2977f6](seerr-team/seerr@c2977f6))
- *(eslint)* Add react/self-closing-comp ([#&#8203;2563](seerr-team/seerr#2563)) - ([cd8b386](seerr-team/seerr@cd8b386))
- *(github)* Add docs and maintenance issue templates ([#&#8203;2467](seerr-team/seerr#2467)) - ([cf4883a](seerr-team/seerr@cf4883a))
- *(helm)* Add GatewayAPI route support to helm chart ([#&#8203;2544](seerr-team/seerr#2544)) - ([3a42f59](seerr-team/seerr@3a42f59))
- *(helm)* Update ghcr.io/seerr-team/seerr ( 3.0.0 → 3.0.1 ) \[skip-ci] ([#&#8203;2441](seerr-team/seerr#2441)) - ([87fb0df](seerr-team/seerr@87fb0df))
- *(husky)* Fixed husky commit message from bash/zsh syntax to sh syntax ([#&#8203;2572](seerr-team/seerr#2572)) - ([a00c9e5](seerr-team/seerr@a00c9e5))
- *(release)* Prepare ${TAG\_VERSION} - ([94a70bb](seerr-team/seerr@94a70bb))
- Updated the Contributing and Security guides to reflect our current practices ([#&#8203;2579](seerr-team/seerr#2579)) - ([0d40a42](seerr-team/seerr@0d40a42))
- Disable nextjs telemetry ([#&#8203;2517](seerr-team/seerr#2517)) - ([cecdd63](seerr-team/seerr@cecdd63))
- Update contributing guide regarding Automated AI Agent ([#&#8203;2518](seerr-team/seerr#2518)) - ([880fbc9](seerr-team/seerr@880fbc9))
- Remove discord notification from release ([#&#8203;2501](seerr-team/seerr#2501)) - ([fba20c1](seerr-team/seerr@fba20c1))
- Add create-tag workflow to streamline release process ([#&#8203;2493](seerr-team/seerr#2493)) - ([06e5eb0](seerr-team/seerr@06e5eb0))
- Update concurrency logic ([#&#8203;2481](seerr-team/seerr#2481)) - ([4939f13](seerr-team/seerr@4939f13))
- Add semantic-pr workflow to enforce conventional commits ([#&#8203;2472](seerr-team/seerr#2472)) - ([5e57fdc](seerr-team/seerr@5e57fdc))

##### New Contributors ❤️

- [@&#8203;caillou](https://github.com/caillou) made their first contribution
- [@&#8203;Kenshin9977](https://github.com/Kenshin9977) made their first contribution
- [@&#8203;MagicLegend](https://github.com/MagicLegend) made their first contribution
- [@&#8203;wiiaam](https://github.com/wiiaam) made their first contribution
- [@&#8203;mjonkus](https://github.com/mjonkus) made their first contribution
- [@&#8203;nova-api](https://github.com/nova-api) made their first contribution
- [@&#8203;mreid-tt](https://github.com/mreid-tt) made their first contribution
- [@&#8203;DataBitz](https://github.com/DataBitz) made their first contribution
- [@&#8203;Hyperion2220](https://github.com/Hyperion2220) made their first contribution
- [@&#8203;blassley](https://github.com/blassley) made their first contribution
- [@&#8203;JanKleine](https://github.com/JanKleine) made their first contribution
- [@&#8203;koiralasandesh](https://github.com/koiralasandesh) made their first contribution<!-- generated by git-cliff -->

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNS43IiwidXBkYXRlZEluVmVyIjoiNDMuMjUuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4284
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
lucianchauvin pushed a commit to lucianchauvin/jellyseerr that referenced this pull request Apr 20, 2026
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.

Media from watchlist with auto-approve isn't respected in blocklist

4 participants