Conversation
- Introduced migration to add `enablePlexPlaylistProtection`, `plexProtectionPlaylistName`, and `plexServerUrl` columns to the `configs` table. - The `plexServerUrl` is optional, allowing for auto-detection of the Plex server URL. feat(plex-server): implement PlexServerService for playlist protection - Created `PlexServerService` class to manage interactions with Plex Media Server. - Added methods for user authentication, playlist management, and protection checks. - Implemented caching for server connections, users, and shared server info to optimize performance. - Introduced functionality to create and manage protection playlists for users. - Added methods to retrieve and check protected items based on user playlists.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThe changes introduce Plex playlist protection to the application's delete synchronization workflow. This includes new configuration options, database schema updates, UI form and result enhancements, and a comprehensive Plex server integration service. The delete sync logic now checks Plex protection playlists to prevent deletion of protected media. Error handling in configuration routes is standardized, and relevant schemas and types are updated throughout the codebase. Changes
Sequence Diagram(s)Plex Playlist Protection in Delete Sync WorkflowsequenceDiagram
participant User
participant UI
participant DeleteSyncService
participant PlexServerService
participant Database
User->>UI: Enable Plex playlist protection and configure playlist name
UI->>Database: Save config (enablePlexPlaylistProtection, playlist name, server URL)
User->>UI: Start delete sync
UI->>DeleteSyncService: Trigger delete sync
DeleteSyncService->>PlexServerService: initialize()
PlexServerService->>PlexServerService: Discover server, load users, playlists, protected GUIDs
DeleteSyncService->>PlexServerService: getProtectedItems()
PlexServerService-->>DeleteSyncService: Set of protected GUIDs
loop For each item to delete
DeleteSyncService->>PlexServerService: isItemProtected(item GUIDs)
alt Item is protected
DeleteSyncService-->>DeleteSyncService: Increment protected count, skip deletion
else Not protected
DeleteSyncService-->>DeleteSyncService: Proceed with deletion
end
end
DeleteSyncService->>UI: Return deletion summary (including protected counts)
Configuration Error Handling (GET/PUT /config)sequenceDiagram
participant Client
participant ConfigRouteHandler
participant Database
Client->>ConfigRouteHandler: GET /config or PUT /config
ConfigRouteHandler->>Database: Retrieve or update config
alt Error occurs
ConfigRouteHandler-->>Client: Return error object with status code and message
else Success
ConfigRouteHandler-->>Client: Return config object
end
PlexServerService: Protection GUIDs GatheringsequenceDiagram
participant PlexServerService
participant PlexAPI
participant Users
PlexServerService->>PlexAPI: Discover server connections
PlexServerService->>PlexAPI: Fetch users and tokens
loop For each user
PlexServerService->>PlexAPI: Find or create protection playlist
PlexServerService->>PlexAPI: Fetch playlist items
PlexServerService->>PlexAPI: Fetch item metadata for GUIDs
end
PlexServerService-->>PlexServerService: Cache protected GUIDs
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 0
🔭 Outside diff range comments (3)
src/routes/v1/config/config.ts (2)
18-22:⚠️ Potential issueAdd 400-response schema for consistency with runtime behaviour
The catch-block below can return a
400(e.g. if upstream helpers throwfastify.badRequest).
The current response schema only lists200|404|500, which causes Fastify to return500validation errors for otherwise valid400responses.response: { + 400: ConfigErrorSchema, },
75-79:⚠️ Potential issuePUT handler returns 404 but schema doesn’t declare it
Later in the handler (
reply.status(404)), the code legitimately answers with a 404 when the config cannot be re-loaded.
Declare the status so Fastify’s response validation passes.response: { + 404: ConfigErrorSchema, },src/services/delete-sync.service.ts (1)
770-813: 🛠️ Refactor suggestionIterating over raw
movie.guids/show.guidsrisks character-by-character loops
RadarrItem.guidsandSonarrItem.guidscan be eitherstring(JSON / CSV) orstring[].
Directly doingfor … of movie.guidsormovie.guids.some():
- Treats a single string as an iterable of characters, producing false positives.
- Silently bypasses the imported
parseGuidshelper, leaving malformed data un-normalised.Fix by parsing once and re-using the result:
- const exists = movie.guids.some((guid) => watchlistGuids.has(guid)) + const movieGuidList = parseGuids(movie.guids) + const exists = movieGuidList.some((guid) => watchlistGuids.has(guid)) … - for (const guid of movie.guids) { + for (const guid of movieGuidList) { if (this.protectedGuids.has(guid)) { … - const exists = show.guids.some((guid) => watchlistGuids.has(guid)) + const showGuidList = parseGuids(show.guids) + const exists = showGuidList.some((guid) => watchlistGuids.has(guid)) … - for (const guid of show.guids) { + for (const guid of showGuidList) { if (this.protectedGuids.has(guid)) {This eliminates incorrect character-level comparisons and leverages the already-imported utility.
Don’t forget to usemovieGuidList[0]/showGuidList[0]when pushing to the summary arrays.Also applies to: 909-977
🧹 Nitpick comments (3)
src/utils/plex.ts (1)
1114-1120: Improved error handling for non-existent itemsThis change optimizes performance by immediately returning an empty set when a 404 is encountered rather than retrying. Both direct HTTP 404 responses and error messages containing "HTTP 404" are now properly handled.
Consider consolidating the duplicate warning messages for 404 errors to maintain consistency:
- log.warn( - `Item "${item.title}" not found in Plex database (HTTP 404) - skipping retries`, - ) + log.warn( + `Item "${item.title}" not found in Plex's database (HTTP 404) - skipping retries`, + )Also applies to: 1200-1206
src/services/delete-sync.service.ts (1)
1131-1134: Clear localprotectedGuidscache between runs
plexServer.clearWorkflowCaches()purges its internal caches, but the service-levelthis.protectedGuidsset persists, leading to stale protection data on the next run if playlist protection is later disabled or playlists change.// Release cached resources after processing completes this.plexServer.clearWorkflowCaches() + this.protectedGuids = nullsrc/utils/plex-server.ts (1)
286-297: Add request timeout to avoid hanging on Plex.tvA network glitch can leave the
fetchto/api/v2/resourcesunresolved indefinitely, stalling initialisation.
Pass anAbortSignal.timeout(...)(Node 18+) or a manual timeout wrapper.- const resourcesResponse = await fetch(resourcesUrl.toString(), { + const resourcesResponse = await fetch(resourcesUrl.toString(), { headers: { Accept: 'application/json', 'X-Plex-Token': adminToken, 'X-Plex-Client-Identifier': 'Pulsarr', - }, + }, + signal: AbortSignal.timeout(8000), })Apply the same pattern to other external
fetchcalls for resiliency.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (15)
migrations/migrations/022_20250508_add_plex_playlist_protection.ts(1 hunks)package.json(1 hunks)src/client/features/utilities/components/delete-sync/delete-sync-dry-run-modal.tsx(2 hunks)src/client/features/utilities/components/delete-sync/delete-sync-form.tsx(1 hunks)src/client/features/utilities/hooks/useDeleteSyncForm.ts(6 hunks)src/plugins/external/env.ts(1 hunks)src/routes/v1/config/config.ts(6 hunks)src/schemas/config/config.schema.ts(1 hunks)src/schemas/scheduler/scheduler.schema.ts(2 hunks)src/services/database.service.ts(2 hunks)src/services/delete-sync.service.ts(21 hunks)src/types/config.types.ts(1 hunks)src/types/delete-sync.types.ts(1 hunks)src/utils/plex-server.ts(1 hunks)src/utils/plex.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/services/delete-sync.service.ts (1)
Learnt from: jamcalli
PR: jamcalli/Pulsarr#110
File: src/services/watchlist-workflow.service.ts:345-349
Timestamp: 2025-04-25T06:50:49.751Z
Learning: In the watchlist workflow service, the `createItemMap` function intentionally uses only the first GUID from `parseGuids(item.guids)` as the map key. This is by design because the change detection logic relies on a 1:1 mapping between items and their primary GUIDs. The `parseGuids()` function handles GUID normalization and prioritization, ensuring consistent primary GUID selection that matches how Plex, Sonarr, and Radarr handle primary identifiers.
🧬 Code Graph Analysis (5)
src/client/features/utilities/components/delete-sync/delete-sync-form.tsx (3)
src/client/components/ui/form.tsx (5)
FormField(182-182)FormItem(177-177)FormControl(179-179)FormLabel(178-178)FormMessage(181-181)src/client/components/ui/switch.tsx (1)
Switch(46-46)src/client/components/ui/input.tsx (1)
Input(25-25)
src/routes/v1/config/config.ts (1)
src/schemas/config/config.schema.ts (3)
ConfigResponseSchema(72-75)ConfigErrorSchema(77-79)ConfigSchema(28-70)
src/services/delete-sync.service.ts (1)
src/utils/plex-server.ts (1)
PlexServerService(158-1518)
src/utils/plex-server.ts (4)
src/services/delete-sync.service.ts (1)
config(79-81)src/types/config.types.ts (1)
Config(38-106)src/utils/plex.ts (1)
toItemsSingle(1054-1231)src/utils/guid-handler.ts (1)
parseGuids(8-61)
src/services/database.service.ts (5)
src/services/delete-sync.service.ts (1)
config(79-81)src/services/discord-notifications.service.ts (1)
config(49-51)src/services/apprise-notifications.service.ts (1)
config(21-23)src/services/watchlist-workflow.service.ts (1)
config(110-112)src/services/plex-watchlist.service.ts (1)
config(35-37)
🔇 Additional comments (23)
package.json (1)
96-96:Details
✅ Verification successful
Good addition of the XML parser dependency.
The
fast-xml-parserdependency is appropriate for handling Plex API responses, which are typically in XML format. This is necessary for the new Plex playlist protection feature.
🏁 Script executed:
#!/bin/bash # Check for the latest version of fast-xml-parser and any security issues # Get the latest version from npm echo "Current specified version: ^5.2.2" echo "Checking latest version on npm..." npm view fast-xml-parser version # Check for any security advisories echo -e "\nChecking for security advisories..." npm audit --json fast-xml-parser | grep -i vulner || echo "No direct vulnerabilities found"Length of output: 755
fast-xml-parser dependency version is up-to-date and secure
- package.json (line 96):
"fast-xml-parser": "^5.2.2"has been confirmed as the latest release (v5.2.2).- No direct vulnerabilities were reported by
npm audit(pending creation of a lockfile).This dependency is well-suited for parsing Plex’s XML responses and supports the new playlist protection feature.
src/schemas/config/config.schema.ts (1)
62-65: Well-structured schema additions for Plex playlist protection.The new configuration options are properly organized and follow the codebase's existing patterns. All fields are correctly marked as optional.
A few observations:
- The
enablePlexPlaylistProtectionboolean toggle controls the feature- The
plexProtectionPlaylistNameallows customizing the playlist name- The
plexServerUrlprovides the connection endpoint for the Plex serversrc/client/features/utilities/components/delete-sync/delete-sync-dry-run-modal.tsx (2)
262-264: Good responsive grid adaptation for the new card.The conditional grid layout adjustment based on the presence of protected items is well-implemented. It maintains proper spacing and alignment while accommodating the new information card.
292-306: Well-implemented protected items card with appropriate conditional rendering.The card follows the same design pattern as other summary cards and only appears when there are protected items to report. This keeps the UI clean and focused.
src/schemas/scheduler/scheduler.schema.ts (2)
103-104: Good schema extension for tracking protected items.Adding the optional
protectedfield to the content type result schema maintains backward compatibility while supporting the new feature.
112-113: Well-integrated protection tracking in total summary.The optional
protectedfield in the total object ensures consistent reporting of protected items at all levels of the result schema.src/types/config.types.ts (1)
92-94: Well-structured Plex protection config additionsThe new configuration properties for Plex playlist protection are clearly named and well-documented, especially the helpful comment explaining when to set the optional
plexServerUrl.src/types/delete-sync.types.ts (1)
6-6: Good approach with optional protected countsMaking the
protectedproperties optional ensures backward compatibility with existing code that might not be aware of this new field.Also applies to: 11-11, 17-17
src/plugins/external/env.ts (1)
168-179: Sensible defaults for Plex protection settingsThe default values are well-chosen:
- Protection disabled by default (safer option)
- Clear and intuitive "Do Not Delete" playlist name
- Standard Plex port for server URL
src/utils/plex.ts (1)
1054-1054: Function now exported for use by other modulesThe
toItemsSinglefunction has been exported, making it available to other modules that need to use it, like the newPlexServerService.src/client/features/utilities/components/delete-sync/delete-sync-form.tsx (2)
433-449: LGTM - Plex playlist protection toggle implemented correctly.The implementation of the toggle switch for enabling Plex playlist protection follows the established pattern of other safety settings in the form.
451-485: Input field correctly handles dependent state.The implementation properly:
- Disables the input when protection is turned off
- Uses consistent placeholder value ("Do Not Delete")
- Handles form submission state
- Adapts to mobile layouts
- Includes proper error message display
The conditional disabling based on the
enablePlexPlaylistProtectiontoggle is a good UX choice.migrations/migrations/022_20250508_add_plex_playlist_protection.ts (3)
3-15: Excellent documentation of the migration purpose.The comments clearly explain the purpose of each added column and provide context about the auto-detection feature for the Plex server URL.
16-22: LGTM - Proper implementation of database schema changes.The migration correctly adds the three required columns with appropriate default values:
enablePlexPlaylistProtectiondefaulting to falseplexProtectionPlaylistNamedefaulting to "Do Not Delete"plexServerUrldefaulting to "http://localhost:32400"
24-33: LGTM - Down migration is properly implemented.The down migration correctly drops all columns added in the up migration, enabling clean rollback if needed.
src/client/features/utilities/hooks/useDeleteSyncForm.ts (6)
17-18: Schema validation is properly defined.The schema correctly defines:
enablePlexPlaylistProtectionas a booleanplexProtectionPlaylistNameas a string with minimum length validation
130-132: Default values align with database defaults.The default values match those defined in the migration:
enablePlexPlaylistProtectiondefaults to falseplexProtectionPlaylistNamedefaults to "Do Not Delete"
159-163: Form reset logic handles all fields consistently.The form reset logic properly includes the new Plex protection fields with appropriate fallbacks to default values.
197-199: Config update includes new protection fields.The form submission properly includes the Plex protection fields in the configuration update payload.
262-266: Success handler properly resets form with updated values.After a successful save, the form is correctly reset with the updated configuration values for the Plex protection fields.
308-312: Cancel handler properly resets to current config values.The cancel handler correctly resets the form to the current configuration values for all fields, including the Plex protection fields.
src/services/database.service.ts (2)
547-553: Appropriate implementation of Plex playlist protection configuration.The new properties for Plex playlist protection follow the existing patterns in the codebase with proper default values and type conversions.
607-613: Well-implemented default values for Plex playlist protection.The configuration creation logic correctly handles defaults and properly maintains consistency with the retrieval method.
- improve GUID parsing in delete sync service -add timeout to Plex server requests
…e delete sync operation
Description
Add deletion prevention playlists
Auto create playlists for all Plex users
Anything added to these playlists will be checked against to prevent deletion during delete sync
Related Issues
Type of Change
Testing Performed
Screenshots
Checklist
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores
fast-xml-parserdependency for improved XML parsing capabilities.