feat(plex): add content filtering for ignored editions and episode titles#2603
feat(plex): add content filtering for ignored editions and episode titles#2603Rubeanie wants to merge 5 commits intoseerr-team:developfrom
Conversation
📝 WalkthroughWalkthroughAdds configurable Plex content filtering: new settings and UI to ignore specific movie editions and episode titles, utilities to evaluate ignores, and integrations that skip ignored media in Plex scanners and availability sync. Changes
sequenceDiagram
participant User
participant UI as Settings UI
participant Server
participant SettingsStore as Settings DB
participant Scanner as Plex Scanner
participant Plex as Plex Media Server
User->>UI: configure ignored editions / episode titles & mode
UI->>Server: POST /api/v1/settings/plex (includes ignored lists + mode)
Server->>SettingsStore: save plex.ignoredEditions, plex.ignoredEpisodeTitles, plex.ignoredEpisodeFilterMode
Scanner->>SettingsStore: getSettings()
Scanner->>Plex: fetch library items / seasons / episodes
Scanner->>Server: call isIgnoredEdition / isIgnoredEpisode
Server->>SettingsStore: read plex ignore settings
Server-->>Scanner: return ignore decision (true/false)
alt not ignored
Scanner->>Server: process item (match, add, update)
else ignored
Scanner->>Scanner: skip item (log and do not process)
end
Scanner->>Server: availability sync uses same ignore checks before marking available/missing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/lib/scanners/plex/index.ts (1)
431-435:⚠️ Potential issue | 🟠 MajorFix unreachable TVDB fallback before calling
processShow.Line 431 currently requires
mediaIds.tvdbId, which prevents processing when TVDB is only available viatvShow.external_ids.tvdb_id. That makes the fallback on Line 434 effectively dead.💡 Proposed fix
- if (mediaIds.tvdbId && hasAnyEpisodes) { + const resolvedTvdbId = mediaIds.tvdbId ?? tvShow.external_ids.tvdb_id; + if (resolvedTvdbId && hasAnyEpisodes) { await this.processShow( mediaIds.tmdbId, - mediaIds.tvdbId ?? tvShow.external_ids.tvdb_id, + resolvedTvdbId, processableSeasons, { mediaAddedAt: new Date(metadata.addedAt * 1000),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/lib/scanners/plex/index.ts` around lines 431 - 435, The condition currently gates processShow on mediaIds.tvdbId, making the tvShow.external_ids.tvdb_id fallback dead; change the if to check for either TVDB id (e.g., if ((mediaIds.tvdbId || tvShow.external_ids?.tvdb_id) && hasAnyEpisodes)) and call processShow with the chosen id (mediaIds.tvdbId ?? tvShow.external_ids?.tvdb_id) so processShow receives the correct TVDB id even when mediaIds.tvdbId is absent; ensure optional chaining on tvShow.external_ids to avoid runtime errors.
🧹 Nitpick comments (1)
src/components/Settings/SettingsPlex.tsx (1)
425-427: Normalize tag arrays before persisting settings.Consider trimming, filtering empty strings, and case-insensitive deduplication before POST to avoid redundant config entries.
♻️ Suggested hardening
+ const normalizeTags = (tags: string[]) => + [...new Set(tags.map((t) => t.trim()).filter(Boolean))]; + await axios.post('/api/v1/settings/plex', { ip: values.hostname, port: Number(values.port), useSsl: values.useSsl, webAppUrl: values.webAppUrl, - ignoredEditions: values.ignoredEditions, - ignoredEpisodeTitles: values.ignoredEpisodeTitles, + ignoredEditions: normalizeTags(values.ignoredEditions), + ignoredEpisodeTitles: normalizeTags(values.ignoredEpisodeTitles), ignoredEpisodeFilterMode: values.ignoredEpisodeFilterMode, } as PlexSettings);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Settings/SettingsPlex.tsx` around lines 425 - 427, The ignoredEditions and ignoredEpisodeTitles arrays are assigned directly from values and should be normalized before the POST: trim each string, remove any empty strings, lowercase for case-insensitive comparisons, and deduplicate (preserve order) so the payload has no redundant entries; modify the code that builds the request body where ignoredEditions and ignoredEpisodeTitles are set (the block assigning ignoredEditions: values.ignoredEditions and ignoredEpisodeTitles: values.ignoredEpisodeTitles) to map/trim/filter/toLowerCase and run a dedupe step before sending.
🤖 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/availabilitySync.ts`:
- Line 15: The availability sync file imports isIgnoredEdition directly from the
scanner module which causes scanner singletons to initialize as a side effect;
extract the pure helper isIgnoredEdition into a new small shared module (e.g.,
export a function named isIgnoredEdition from a new plex filtering utility) and
update both the scanner module and availabilitySync to import that helper
instead of importing from the scanner module; remove the import of the scanner
module from availabilitySync so availability sync no longer triggers scanner
initialization and ensure both places import the same new utility function name
isIgnoredEdition.
---
Outside diff comments:
In `@server/lib/scanners/plex/index.ts`:
- Around line 431-435: The condition currently gates processShow on
mediaIds.tvdbId, making the tvShow.external_ids.tvdb_id fallback dead; change
the if to check for either TVDB id (e.g., if ((mediaIds.tvdbId ||
tvShow.external_ids?.tvdb_id) && hasAnyEpisodes)) and call processShow with the
chosen id (mediaIds.tvdbId ?? tvShow.external_ids?.tvdb_id) so processShow
receives the correct TVDB id even when mediaIds.tvdbId is absent; ensure
optional chaining on tvShow.external_ids to avoid runtime errors.
---
Nitpick comments:
In `@src/components/Settings/SettingsPlex.tsx`:
- Around line 425-427: The ignoredEditions and ignoredEpisodeTitles arrays are
assigned directly from values and should be normalized before the POST: trim
each string, remove any empty strings, lowercase for case-insensitive
comparisons, and deduplicate (preserve order) so the payload has no redundant
entries; modify the code that builds the request body where ignoredEditions and
ignoredEpisodeTitles are set (the block assigning ignoredEditions:
values.ignoredEditions and ignoredEpisodeTitles: values.ignoredEpisodeTitles) to
map/trim/filter/toLowerCase and run a dedupe step before sending.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
server/api/plexapi.tsserver/lib/availabilitySync.tsserver/lib/scanners/plex/index.tsserver/lib/settings/index.tssrc/components/Settings/SettingsPlex.tsxsrc/i18n/locale/en.json
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/Settings/SettingsPlex.tsx (1)
420-428: Consider usingSetfor deduplication.The
Map-based approach works butSetis more idiomatic for simple deduplication:♻️ Suggested simplification
- const dedupe = (arr: string[]) => [ - ...new Map(arr.map((s) => [s, s])).values(), - ]; + const dedupe = (arr: string[]) => [...new Set(arr)];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Settings/SettingsPlex.tsx` around lines 420 - 428, The deduplication is implemented with a Map in the dedupe function; replace that with an idiomatic Set-based approach by changing dedupe(arr: string[]) to return [...new Set(arr)] and ensure normalize (which calls dedupe) still trims, lowercases and filters entries before deduplication; update references to dedupe and normalize accordingly so types (arr?: string[]) and behavior remain the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/components/Settings/SettingsPlex.tsx`:
- Around line 420-428: The deduplication is implemented with a Map in the dedupe
function; replace that with an idiomatic Set-based approach by changing
dedupe(arr: string[]) to return [...new Set(arr)] and ensure normalize (which
calls dedupe) still trims, lowercases and filters entries before deduplication;
update references to dedupe and normalize accordingly so types (arr?: string[])
and behavior remain the same.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
server/lib/availabilitySync.tsserver/lib/scanners/plex/index.tsserver/utils/plexFilter.tssrc/components/Settings/SettingsPlex.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- server/lib/availabilitySync.ts
|
One alternative approach to this problem is filtering ignored media based on file paths using regular expressions |
Leaving this PR open just in case this implementation is preferred. |
ee7cc5a to
62697d6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/components/Settings/SettingsPlex.tsx (2)
420-438: Prefer Set-based dedupe (and consider coercing mode when titles are empty).
new Map(arr.map((s) => [s, s]))is a common TS footgun becausemap()often infersstring[]rather than[string, string]tuples, depending on TS/version/config. UsingSetis simpler and avoids tuple inference issues.Minor UX consistency: if
ignoredEpisodeTitlesis empty, you may want to forceignoredEpisodeFilterModeback to'season'before persisting, otherwise the dropdown can appear “stuck” on a non-default mode (while disabled) after reload.Proposed refactor
- const dedupe = (arr: string[]) => [ - ...new Map(arr.map((s) => [s, s])).values(), - ]; - const normalize = (arr?: string[]) => - dedupe( - (arr ?? []) - .map((s) => s.trim().toLowerCase()) - .filter((s) => s.length > 0) - ); + const normalize = (arr?: string[]) => { + const cleaned = (arr ?? []) + .map((s) => s.trim().toLowerCase()) + .filter((s) => s.length > 0); + return [...new Set(cleaned)]; + }; await axios.post('/api/v1/settings/plex', { ip: values.hostname, port: Number(values.port), useSsl: values.useSsl, webAppUrl: values.webAppUrl, - ignoredEditions: normalize(values.ignoredEditions), - ignoredEpisodeTitles: normalize(values.ignoredEpisodeTitles), - ignoredEpisodeFilterMode: values.ignoredEpisodeFilterMode, + ignoredEditions: normalize(values.ignoredEditions), + ignoredEpisodeTitles: normalize(values.ignoredEpisodeTitles), + ignoredEpisodeFilterMode: normalize(values.ignoredEpisodeTitles) + .length + ? values.ignoredEpisodeFilterMode + : 'season', } as PlexSettings);
- Please confirm TS compilation for the current Map-based dedupe under this repo’s TS config (or adopt the Set version).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Settings/SettingsPlex.tsx` around lines 420 - 438, The dedupe implementation using new Map(...) can cause tuple inference issues; replace the dedupe function with a Set-based approach (e.g., Array.from(new Set(arr))) and update normalize to use that dedupe; additionally, when preparing the payload for axios.post in the SettingsPlex component, ensure ignoredEpisodeFilterMode is coerced to 'season' if normalized ignoredEpisodeTitles is empty (so the persisted mode can't remain a non-default when titles are blank); adjust references to dedupe, normalize, ignoredEpisodeTitles, and ignoredEpisodeFilterMode in that submission block accordingly.
643-766: AddinputId/instanceIdfor react-select fields so the<label htmlFor>actually works (a11y).Right now the labels’
htmlFor="ignoredEditions"/htmlFor="ignoredEpisodeTitles"won’t reliably focus/associate with the underlying react-select input unless you wireinputId(and typicallyinstanceId). This improves keyboard UX and screen-reader association.Proposed patch
<CreatableSelect<TagOption, true> + inputId="ignoredEditions" + instanceId="ignoredEditions" components={{ DropdownIndicator: null, }} isClearable isMulti @@ <CreatableSelect<TagOption, true> + inputId="ignoredEpisodeTitles" + instanceId="ignoredEpisodeTitles" components={{ DropdownIndicator: null, }} isClearable isMulti
- Please confirm
inputId/instanceIdare the preferred props for react-select v5.10.2 in this repo’s usage (some codebases standardize onaria-labelledbyinstead).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Settings/SettingsPlex.tsx` around lines 643 - 766, The react-select controls (CreatableSelect components used for ignoredEditions and ignoredEpisodeTitles) need inputId and instanceId props so the existing <label htmlFor="ignoredEditions"> and <label htmlFor="ignoredEpisodeTitles"> correctly associate and focus the underlying input; update the CreatableSelect for ignoredEditions and ignoredEpisodeTitles to include inputId="ignoredEditions" (and inputId="ignoredEpisodeTitles") and matching unique instanceId values (e.g., "ignoredEditions" / "ignoredEpisodeTitles"), leaving existing value handlers (values.ignoredEditions, values.ignoredEpisodeTitles) and onChange/setFieldValue logic untouched; use inputId and instanceId as react-select v5.10.2 standard props rather than aria-labelledby.
🤖 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/scanners/plex/index.ts`:
- Around line 395-407: The current guard skips calling processShow when
hasAnyEpisodes is false, which leaves existing media availability stale; always
invoke processShow for the show when it already exists so season availability
and media.status are recalculated even if all episodes were filtered.
Specifically, remove or bypass the hasAnyEpisodes-only condition around the call
to this.processShow (referencing mediaIds.tmdbId, tvdbId, hasAnyEpisodes) and
ensure you check for an existing show via the BaseScanner.getExisting pattern
(or call getExisting first) and then call this.processShow with the same payload
({ mediaAddedAt: new Date(metadata.addedAt * 1000), ratingKey, title:
metadata.title }) so the DB seasons/status are updated even when no episodes
survive filtering. Ensure you preserve creating new shows only when appropriate
and follow existing scanner semantics used by Sonarr/Jellyfin.
---
Nitpick comments:
In `@src/components/Settings/SettingsPlex.tsx`:
- Around line 420-438: The dedupe implementation using new Map(...) can cause
tuple inference issues; replace the dedupe function with a Set-based approach
(e.g., Array.from(new Set(arr))) and update normalize to use that dedupe;
additionally, when preparing the payload for axios.post in the SettingsPlex
component, ensure ignoredEpisodeFilterMode is coerced to 'season' if normalized
ignoredEpisodeTitles is empty (so the persisted mode can't remain a non-default
when titles are blank); adjust references to dedupe, normalize,
ignoredEpisodeTitles, and ignoredEpisodeFilterMode in that submission block
accordingly.
- Around line 643-766: The react-select controls (CreatableSelect components
used for ignoredEditions and ignoredEpisodeTitles) need inputId and instanceId
props so the existing <label htmlFor="ignoredEditions"> and <label
htmlFor="ignoredEpisodeTitles"> correctly associate and focus the underlying
input; update the CreatableSelect for ignoredEditions and ignoredEpisodeTitles
to include inputId="ignoredEditions" (and inputId="ignoredEpisodeTitles") and
matching unique instanceId values (e.g., "ignoredEditions" /
"ignoredEpisodeTitles"), leaving existing value handlers
(values.ignoredEditions, values.ignoredEpisodeTitles) and onChange/setFieldValue
logic untouched; use inputId and instanceId as react-select v5.10.2 standard
props rather than aria-labelledby.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f5e257b0-f204-4652-b4d3-f02a9f70b463
📒 Files selected for processing (7)
server/api/plexapi.tsserver/lib/availabilitySync.tsserver/lib/scanners/plex/index.tsserver/lib/settings/index.tsserver/utils/plexFilter.tssrc/components/Settings/SettingsPlex.tsxsrc/i18n/locale/en.json
🚧 Files skipped from review as they are similar to previous changes (5)
- server/lib/availabilitySync.ts
- server/api/plexapi.ts
- server/lib/settings/index.ts
- src/i18n/locale/en.json
- server/utils/plexFilter.ts
| const hasAnyEpisodes = processableSeasons.some( | ||
| (s) => s.episodes > 0 || s.episodes4k > 0 | ||
| ); | ||
|
|
||
| const tvdbId = mediaIds.tvdbId ?? tvShow.external_ids?.tvdb_id; | ||
|
|
||
| if (tvdbId && hasAnyEpisodes) { | ||
| await this.processShow(mediaIds.tmdbId, tvdbId, processableSeasons, { | ||
| mediaAddedAt: new Date(metadata.addedAt * 1000), | ||
| ratingKey: ratingKey, | ||
| title: metadata.title, | ||
| }); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "index.ts" -path "*/scanners/plex/*" | head -1Repository: seerr-team/seerr
Length of output: 95
🏁 Script executed:
find . -name "baseScanner.ts" -path "*/scanners/*" | head -1Repository: seerr-team/seerr
Length of output: 96
🏁 Script executed:
wc -l server/lib/scanners/plex/index.tsRepository: seerr-team/seerr
Length of output: 97
🏁 Script executed:
sed -n '390,415p' server/lib/scanners/plex/index.tsRepository: seerr-team/seerr
Length of output: 835
🏁 Script executed:
wc -l server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 98
🏁 Script executed:
sed -n '240,310p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 2265
🏁 Script executed:
sed -n '1,50p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 1274
🏁 Script executed:
sed -n '310,390p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 3360
🏁 Script executed:
rg "getExisting" server/lib/scanners/baseScanner.ts -A 5 -B 2Repository: seerr-team/seerr
Length of output: 807
🏁 Script executed:
rg "availability" server/lib/scanners/ -i --max-count=20Repository: seerr-team/seerr
Length of output: 239
🏁 Script executed:
sed -n '376,420p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 1544
🏁 Script executed:
sed -n '370,390p' server/lib/scanners/plex/index.tsRepository: seerr-team/seerr
Length of output: 710
🏁 Script executed:
rg "processShow|processMedia" server/lib/scanners/plex/index.ts -B 5 -A 2Repository: seerr-team/seerr
Length of output: 335
🏁 Script executed:
sed -n '410,460p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 1652
🏁 Script executed:
rg "mediaRepository.save|mediaRepository.update" server/lib/scanners/baseScanner.ts -B 2 -A 2Repository: seerr-team/seerr
Length of output: 712
🏁 Script executed:
sed -n '350,410p' server/lib/scanners/plex/index.tsRepository: seerr-team/seerr
Length of output: 1956
🏁 Script executed:
rg "ignored|filter|delete.*season|availability.*sync" server/lib/scanners/ -i -B 2 -A 2 | head -50Repository: seerr-team/seerr
Length of output: 3072
🏁 Script executed:
grep -rn "MediaStatus.UNKNOWN\|MediaStatus.DELETED" server/lib/scanners/baseScanner.ts | head -20Repository: seerr-team/seerr
Length of output: 1136
🏁 Script executed:
sed -n '455,480p' server/lib/scanners/baseScanner.tsRepository: seerr-team/seerr
Length of output: 1159
🏁 Script executed:
rg "MediaStatus.UNKNOWN|refresh|cleanup|sync.*availability" server/lib -i -B 2 -A 2 | head -60Repository: seerr-team/seerr
Length of output: 3025
🏁 Script executed:
rg "this\.getExisting" server/lib/scanners/ -B 2 -A 2Repository: seerr-team/seerr
Length of output: 727
🏁 Script executed:
sed -n '1,100p' server/lib/scanners/plex/index.ts | head -50Repository: seerr-team/seerr
Length of output: 1946
🏁 Script executed:
grep -A 20 "class PlexScanner" server/lib/scanners/plex/index.tsRepository: seerr-team/seerr
Length of output: 637
🏁 Script executed:
rg "availability-sync" server/lib -B 5 -A 10Repository: seerr-team/seerr
Length of output: 1807
🏁 Script executed:
grep -n "private\|protected\|public" server/lib/scanners/baseScanner.ts | grep -i "getexisting\|processshow"Repository: seerr-team/seerr
Length of output: 165
🏁 Script executed:
find server -name "*availability*" -o -name "*sync*" | grep -i availabilityRepository: seerr-team/seerr
Length of output: 90
🏁 Script executed:
rg "availability.sync|availabilitySync" server/ -B 5 -A 15Repository: seerr-team/seerr
Length of output: 10499
🏁 Script executed:
grep -rn "MediaStatus.UNKNOWN" server/lib/scanners/baseScanner.ts | grep -i "season\|status ="Repository: seerr-team/seerr
Length of output: 42
🏁 Script executed:
wc -l server/lib/availabilitySync.tsRepository: seerr-team/seerr
Length of output: 95
🏁 Script executed:
sed -n '1,100p' server/lib/availabilitySync.tsRepository: seerr-team/seerr
Length of output: 3779
🏁 Script executed:
rg "MediaStatus\." server/lib/availabilitySync.ts -B 2 -A 2Repository: seerr-team/seerr
Length of output: 4020
🏁 Script executed:
sed -n '200,350p' server/lib/availabilitySync.ts | grep -A 30 "showExists"Repository: seerr-team/seerr
Length of output: 3798
🏁 Script executed:
sed -n '300,450p' server/lib/availabilitySync.tsRepository: seerr-team/seerr
Length of output: 4859
🏁 Script executed:
sed -n '600,700p' server/lib/availabilitySync.ts | head -50Repository: seerr-team/seerr
Length of output: 1423
🏁 Script executed:
rg "mediaExistsInPlex" server/lib/availabilitySync.ts -A 20 | head -40Repository: seerr-team/seerr
Length of output: 1546
🏁 Script executed:
git log --oneline -p server/lib/scanners/plex/index.ts | grep -B 20 -A 5 "hasAnyEpisodes" | head -50Repository: seerr-team/seerr
Length of output: 1043
🏁 Script executed:
rg "processShow" server/lib/scanners/ -B 3 -A 1 | head -60Repository: seerr-team/seerr
Length of output: 1798
Risk: Skipping processShow when all episodes are filtered leaves stale TV availability in the database.
When a show already exists in the database marked as AVAILABLE and a subsequent scan finds hasAnyEpisodes === false (all episodes filtered by the ignored titles setting), processShow is skipped entirely. This means the show's season statuses and overall availability status are never updated in the database. Since processShow is where season availability is recalculated and media.status is refreshed (lines 455–495 in baseScanner.ts), skipping it allows stale AVAILABLE/PARTIALLY_AVAILABLE records to persist indefinitely.
The availability-sync job (scheduled daily) does not address this scenario—it only marks shows as DELETED if they're genuinely missing from the external source, not if all episodes are filtered by user settings.
Note: The Sonarr and Jellyfin scanners do not have this guard and always call processShow, creating inconsistent behavior across scanners.
Recommended action: Consider updating the show if it already exists in the database, even when hasAnyEpisodes === false, so that season and media statuses reflect the current filtered state. The getExisting method is protected in BaseScanner; confirm the preferred approach within this codebase's patterns.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@server/lib/scanners/plex/index.ts` around lines 395 - 407, The current guard
skips calling processShow when hasAnyEpisodes is false, which leaves existing
media availability stale; always invoke processShow for the show when it already
exists so season availability and media.status are recalculated even if all
episodes were filtered. Specifically, remove or bypass the hasAnyEpisodes-only
condition around the call to this.processShow (referencing mediaIds.tmdbId,
tvdbId, hasAnyEpisodes) and ensure you check for an existing show via the
BaseScanner.getExisting pattern (or call getExisting first) and then call
this.processShow with the same payload ({ mediaAddedAt: new
Date(metadata.addedAt * 1000), ratingKey, title: metadata.title }) so the DB
seasons/status are updated even when no episodes survive filtering. Ensure you
preserve creating new shows only when appropriate and follow existing scanner
semantics used by Sonarr/Jellyfin.
…, and normalize filter inputs
62697d6 to
0d494e6
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/Settings/SettingsPlex.tsx (1)
420-428: Normalization logic is correct butdedupeimplementation could be simplified.The
dedupefunction usingMapworks but is unnecessarily complex since aftertoLowerCase().trim(), simple[...new Set(arr)]would suffice for deduplication.♻️ Optional simplification
- const dedupe = (arr: string[]) => [ - ...new Map(arr.map((s) => [s, s])).values(), - ]; - const normalize = (arr?: string[]) => - dedupe( - (arr ?? []) - .map((s) => s.trim().toLowerCase()) - .filter((s) => s.length > 0) - ); + const normalize = (arr?: string[]) => + [...new Set( + (arr ?? []) + .map((s) => s.trim().toLowerCase()) + .filter((s) => s.length > 0) + )];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Settings/SettingsPlex.tsx` around lines 420 - 428, The dedupe implementation is more complex than necessary; replace the Map-based logic in the dedupe function with a Set-based deduplication (e.g., using [...new Set(arr)]) and keep normalize as-is (it already trims, lowercases, filters empty strings, and calls dedupe). Update the dedupe function referenced in this block (and any callers like normalize) to return an array of unique strings using Set to simplify and clarify the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/components/Settings/SettingsPlex.tsx`:
- Around line 420-428: The dedupe implementation is more complex than necessary;
replace the Map-based logic in the dedupe function with a Set-based
deduplication (e.g., using [...new Set(arr)]) and keep normalize as-is (it
already trims, lowercases, filters empty strings, and calls dedupe). Update the
dedupe function referenced in this block (and any callers like normalize) to
return an array of unique strings using Set to simplify and clarify the intent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 434e28f3-7c9f-415d-a461-848379da4d15
📒 Files selected for processing (7)
server/api/plexapi.tsserver/lib/availabilitySync.tsserver/lib/scanners/plex/index.tsserver/lib/settings/index.tsserver/utils/plexFilter.tssrc/components/Settings/SettingsPlex.tsxsrc/i18n/locale/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
- server/api/plexapi.ts
- server/lib/availabilitySync.ts
- src/i18n/locale/en.json
Description
Adds configurable content filtering to Plex settings, allowing users to exclude specific media from being detected during library scans. This prevents placeholder files (e.g., trailers) from incorrectly marking content as available.
Movie Edition Filtering:
Trailer) are skipped during library scans and availability syncTV Episode Title Filtering:
processShowfrom running when no episodes remain after filteringUI:
Related
How Has This Been Tested?
Built and published to DockerHub
rubeanie/seerr:ignored-editions. Self tested, with more users actively testing.Screenshots / Logs (if applicable)
Default:

Example:

Checklist:
pnpm buildpnpm i18n:extractSummary by CodeRabbit