Add Jellyfin support#2266
Conversation
- Add MediaServerService abstraction for Plex/Jellyfin - Implement JellyfinService with full API integration - Update rules engine to support multiple media servers - Add Jellyfin settings UI and configuration - Refactor collections to use MediaServerService - Add comprehensive type safety improvements - Update database schema for media server support
…tibility - Add PlexApiLegacyController with all 19 original endpoints - All endpoints delegate to MediaServerFactory abstraction - Add deprecation headers (X-Deprecated, Deprecation, Link) - Uses MediaServerSetupGuard (works with both Plex and Jellyfin) - Maps old route patterns to new abstraction interface - Single file design for easy removal when legacy support ends To remove legacy support: delete plex-api-legacy.controller.ts and remove PlexApiLegacyController from plex-api.module.ts
88d5e45 to
79f4701
Compare
- Use leftJoinAndSelect for rules (allows groups without rules) - Delete unused CollectionDetail/index.tsx - Use useRuleGroupForCollection hook instead of manual fetch - Conditionally show Test Media button only when useRules=true - Simplify addCollectionToDB and RemoveCollectionFromDB methods
The SchemaSync migration from main referenced plexId (integer) but our JellyfinSupport migration already renamed this to mediaServerId (varchar). Updated both UP and DOWN methods to use the correct column name and type.
When useRules=false, the backend creates a rule with empty ruleJson. These rules have no firstVal, causing TypeError when shouldFilterApp tries to access rule.firstVal[0]. Refactored to remove all useEffect usage: - Use useSyncExternalStore for scroll detection (React 19 pattern) - Move rule filtering to event handlers (updateLibraryId, handleUpdateArrAction) - Extract helper functions outside component (parseValidRules, shouldFilterApp, filterRulesForArrSettings) This is cleaner and more aligned with modern React patterns.
e05b507 to
e7b0d0a
Compare
This commit completes the merge from main that was started in 0923d01. The original merge included the leftJoinAndSelect changes but missed removing the empty rule creation code from rules.service.ts. Backend (rules.service.ts): - Remove empty rule creation in createRuleGroup and updateRuleGroup - When useRules=false, the backend was creating rules with empty ruleJson - This aligns with main's fix from commit 730adb5 (Maintainerr#2270) - The RemoveEmptyRules migration (already present) cleans up existing data Frontend (RuleFormPage.tsx): - Add key={id} prop to AddModal component - This ensures the form fully remounts when navigating between rule groups - Replaces the need for useEffect-based form reset logic Frontend (AddModal/index.tsx): - Remove parseValidRules helper (no longer needed since empty rules won't exist) - Remove defensive firstVal check in filterRulesForArrSettings - Simplify rules state initialization to direct parsing The root cause was that commit 0923d01 merged the query changes (leftJoinAndSelect for rules) but not the corresponding code removal that prevents empty rules from being created in the first place.
|
Made these tests, all checkout:
|
|
@benscobie Decided to remove useEffect since it's better React imo. Learned it from developing on Bazarr, and maybe it's placebo but I feel the UI is snappier now. |
- Remove unused useInteraction, useIsTouch hooks and InteractionContext - Add CSS @media (hover: hover/none) for touch-friendly MediaCard - Simplify MediaCard: click always opens modal, CSS handles hover states - Reduces bundle size by ~150 lines of JS with 15 lines of CSS
|
@benscobie Turns out |
|
@benscobie I'm sure you have stuff to do, but just a friendly reminder that this PR exists. :) Would be nice to be able to get this done before Sunday this week. Been running in my prod stable since the last commit (2 weeks ago). |
|
Not sure if this is the right place but since issues are disabled on this branch, i figured it might be. I am pretty sure that the "Jellyfin - Amount of watched episodes" and "Jellyfin - Total views" values dont work correctly for series, or at least not how i expect. The "total views" value seems fine on movies tho |
Can you paste your rules please? Please also include a "Test media" so that I can compare what gets triggered. |
|
Of course, here are my rules: and here are the corresponding tests: and
I actually just realized that the last view date doesnt seem right either, for seasons its always todays date, even tho it wasnt watched today according to my jellystat. |
…e JellyfinGetterService to utilize it
|
/release-pr |
|
🔒 Release builds can only be triggered for branches in the |
* feat: Add Jellyfin support with abstraction layer - Add MediaServerService abstraction for Plex/Jellyfin - Implement JellyfinService with full API integration - Update rules engine to support multiple media servers - Add Jellyfin settings UI and configuration - Refactor collections to use MediaServerService - Add comprehensive type safety improvements - Update database schema for media server support * snc against this branch instead * feat(api): add deprecated /api/plex legacy wrapper for backward compatibility - Add PlexApiLegacyController with all 19 original endpoints - All endpoints delegate to MediaServerFactory abstraction - Add deprecation headers (X-Deprecated, Deprecation, Link) - Uses MediaServerSetupGuard (works with both Plex and Jellyfin) - Maps old route patterns to new abstraction interface - Single file design for easy removal when legacy support ends To remove legacy support: delete plex-api-legacy.controller.ts and remove PlexApiLegacyController from plex-api.module.ts * chore: merge main branch changes - Use leftJoinAndSelect for rules (allows groups without rules) - Delete unused CollectionDetail/index.tsx - Use useRuleGroupForCollection hook instead of manual fetch - Conditionally show Test Media button only when useRules=true - Simplify addCollectionToDB and RemoveCollectionFromDB methods * fix: update SchemaSync migration to use mediaServerId instead of plexId The SchemaSync migration from main referenced plexId (integer) but our JellyfinSupport migration already renamed this to mediaServerId (varchar). Updated both UP and DOWN methods to use the correct column name and type. * regenerated with typeorm * fix: standardize quotation marks and formatting in SchemaSync migration * generate clean typeorm * fix(ui): filter out empty/invalid rules to prevent crash When useRules=false, the backend creates a rule with empty ruleJson. These rules have no firstVal, causing TypeError when shouldFilterApp tries to access rule.firstVal[0]. Refactored to remove all useEffect usage: - Use useSyncExternalStore for scroll detection (React 19 pattern) - Move rule filtering to event handlers (updateLibraryId, handleUpdateArrAction) - Extract helper functions outside component (parseValidRules, shouldFilterApp, filterRulesForArrSettings) This is cleaner and more aligned with modern React patterns. * fix: complete merge of main branch rule editor fixes This commit completes the merge from main that was started in 0923d01. The original merge included the leftJoinAndSelect changes but missed removing the empty rule creation code from rules.service.ts. Backend (rules.service.ts): - Remove empty rule creation in createRuleGroup and updateRuleGroup - When useRules=false, the backend was creating rules with empty ruleJson - This aligns with main's fix from commit 730adb5 (#2270) - The RemoveEmptyRules migration (already present) cleans up existing data Frontend (RuleFormPage.tsx): - Add key={id} prop to AddModal component - This ensures the form fully remounts when navigating between rule groups - Replaces the need for useEffect-based form reset logic Frontend (AddModal/index.tsx): - Remove parseValidRules helper (no longer needed since empty rules won't exist) - Remove defensive firstVal check in filterRulesForArrSettings - Simplify rules state initialization to direct parsing The root cause was that commit 0923d01 merged the query changes (leftJoinAndSelect for rules) but not the corresponding code removal that prevents empty rules from being created in the first place. * replace more useEffect usage * prettier * refactor(ui): replace JS touch detection with CSS media queries - Remove unused useInteraction, useIsTouch hooks and InteractionContext - Add CSS @media (hover: hover/none) for touch-friendly MediaCard - Simplify MediaCard: click always opens modal, CSS handles hover states - Reduces bundle size by ~150 lines of JS with 15 lines of CSS * fix click behaviour on mobile * fix bug on about page showing 0 items in collections * add support for retrieving seasons and episode view counts * retrieve admin user ID for UserData fields in getSeasons and getItems * fix: make watchedAt optional in WatchRecord interface * add temporary debug for viewdate * more temp debug * possible fix for viewdate season/episodes * prettier * feat: add getPlaylistItems method to JellyfinAdapterService and update JellyfinGetterService to utilize it * fix cd/ci tests
This pull request adds comprehensive Jellyfin support to Maintainerr, enabling the application to work with either Plex or Jellyfin as the media server backend. The implementation follows a clean abstraction pattern, treating both media servers through a unified interface while preserving their distinct capabilities.
Key Changes
Modern React Patterns (UI Improvements)
useEffectin favor of better React patterns - The rule editor form now uses React 19'suseSyncExternalStorefor scroll detection and relies on React's declarative `key` prop pattern for form reset on navigation. This eliminates unnecessary effect-based state synchronization, making the UI more predictable and snappier.@benscobie
Since the code is working now and the other PR is getting quite messy, let's work here instead.
Testing