Skip to content

Add support for Jellyfin#24

Closed
benscobie wants to merge 203 commits into
mainfrom
remaining-issues/abstraction-cleanup
Closed

Add support for Jellyfin#24
benscobie wants to merge 203 commits into
mainfrom
remaining-issues/abstraction-cleanup

Conversation

@benscobie

@benscobie benscobie commented Jan 2, 2026

Copy link
Copy Markdown
Collaborator

To everyone coming here and want to test. Work continues here: Maintainerr#2266

- Create jellyfin development branch for Maintainerr 3.0
- Add GitHub Action to sync jellyfin branch with main daily
- On conflict, creates PR for manual resolution (preserves both sides)
- Include implementation plan documentation
- Phase A: Foundation (abstraction layer, database schema, MediaServerModule)
- Phase B: Jellyfin Service (SDK integration, API methods)
- Phase C: Rules Engine (JellyfinGetterService, add jellyfin as application)
- Phase D: Collections (service updates, graceful handling)
- Phase E: UI Integration (settings, media server selector)
- Phase F: Testing & Documentation

Updated per maintainer feedback:
- Add jellyfin as application in RuleConstants (not availability flags)
- No migration path support (users start fresh to switch servers)
- Removed UI noise/warnings about feature limitations
Phase A establishes the abstraction layer for multi-media-server support:

Contracts Package (@maintainerr/contracts):
- Add EMediaServerType enum (plex, jellyfin)
- Add EMediaDataType enum (movies, shows, seasons, episodes)
- Add EMediaServerFeature enum for feature detection
- Add MediaItem, MediaLibrary, MediaUser, WatchRecord types
- Add MediaCollection, MediaServerStatus types
- Export all from media-server package

Database Migration:
- Add media_server_type column (default: 'plex')
- Add jellyfin_url, jellyfin_api_key columns
- Add jellyfin_user_id, jellyfin_server_name columns

MediaServer Module:
- IMediaServerService interface (~25 methods)
- MediaServerFactory for runtime service selection
- MEDIA_SERVER_FEATURES constant for feature support matrix
- MediaServerModule NestJS module

Plex Adapter:
- PlexAdapterService implementing IMediaServerService
- Wraps existing PlexApiService for compatibility
- PlexMapper for type conversions with full test coverage

Tests:
- 35 unit tests for PlexMapper covering all mapping functions
- All 269 existing tests continue to pass

Ref: jellyfin-dev-documents/PHASE_A_FOUNDATION.md
…nsibility docs

Media Server Switch (Halfway House Solution):
- Add SwitchMediaServerRequestDto, SwitchMediaServerResponseDto, MediaServerSwitchPreviewDto
- Implement switchMediaServer() method in SettingsService
- Implement previewMediaServerSwitch() to show what will be cleared/kept
- Add endpoints: GET /api/settings/media-server/switch/preview/:targetServerType
                 POST /api/settings/media-server/switch

When switching servers:
- CLEARS: collections, collection_media, exclusions, collection_logs, old server credentials
- KEEPS: general settings, radarr/sonarr settings, overseerr/jellyseerr, tautulli, notifications

Documentation Updates:
- Add future extensibility notes for multi-server support (per-rule server selection)
- Document media server switch API in PHASE_A_FOUNDATION.md
- Add notes to PHASE_C and PHASE_D about future per-rule server support
- Update main IMPLEMENTATION_PLAN.md with switch functionality details

Ref: Discord discussion with Scoob about migration approach
Implements JellyfinService with full API coverage using @jellyfin/sdk.

- Add @jellyfin/sdk dependency (v0.13.0)
- Create JellyfinService implementing IMediaServerService
- Create JellyfinMapper for SDK to Maintainerr type conversion
- Add constants, types, and module files
- Update MediaServerModule and MediaServerFactory for Jellyfin
- Add jellyfin cache to cacheManager
- Add unit tests for mapper and service
- Add dev-setup.sh script for development environment

API methods: initialize, getStatus, getUsers, getLibraries,
getLibraryContents, getMetadata, searchContent, getWatchHistory,
getCollections, createCollection, deleteCollection, playlists, etc.
- Add @jellyfin/sdk to Jest transform patterns for ESM compatibility
- Update transform pattern to handle both .ts and .js files
- Fix JellyfinMapper test data to use proper type assertions
- Refactor JellyfinService tests to use @suites/unit TestBed pattern
- Add DateLastSaved field to mapper test data
- All 332 tests passing
- Add JellyfinGetterService with property getters for Jellyfin items
- Add JELLYFIN = 6 to Application enum in rules constants
- Add comprehensive Jellyfin properties for rule builder:
  - Basic properties: addDate, releaseDate, ratings, genres, labels
  - Viewing metrics: seenBy, viewCount, lastViewedAt
  - File properties: resolution, bitrate, codec
  - Show/season properties: episodeSeenBy, episodeAirDate, lastEpisodeAddedAt
  - People and collections
- Update GetterService to dispatch JELLYFIN rules to JellyfinGetterService
- Add unit tests for JellyfinGetterService (25 tests)

Phase C of Jellyfin integration - enables rule creation for Jellyfin libraries
…layer

- Rename plexId to mediaServerId throughout entities and DTOs
- Add mediaServerType column to Collection entity
- Update CollectionHandler, RadarrActionHandler, SonarrActionHandler to use MediaServerFactory
- Migrate MediaIdFinder to use abstraction layer for metadata lookups
- Add CollectionMediaWithMetadata type (deprecate CollectionMediaWithPlexData)
- Update exclusion.entities.ts with mediaData field
- Migrate getter services to use EMediaDataType
- Update RulesDto.dataType and IAlterableMediaDto.type to EMediaDataType
- Add createMediaLibrary/createMediaLibraries test helpers
- Fix test utilities for collection type detection
- Add database migration for plexId -> mediaServerId rename
- Document remaining services needing migration in PHASE_D_COLLECTIONS.md

BREAKING CHANGE: plexId renamed to mediaServerId in Collection and CollectionMedia entities
…ebase

- Updated rules.constants.ts to use EMediaDataType for showType filtering.
- Refactored getter services (jellyseerr, overseerr, sonarr, tautulli) to utilize MediaServerFactory for metadata operations.
- Adjusted exclusion-corrector.service.ts to fetch metadata using the new media server abstraction.
- Modified rule-executor.service.ts to replace PlexApiService calls with MediaServerFactory methods.
- Enhanced yaml.service.ts to use EMediaDataType and MediaDataTypeStrings for YAML serialization.
- Updated contracts to include MediaDataTypeStrings for better integration with YAML.
- Ensured all references to plexId are replaced with mediaServerId throughout the codebase.
- Verified that existing Plex collections and functionalities remain intact post-migration.
- Add Jellyfin settings page with connection test, save, and delete
- Add MediaServerSelector component for choosing between Plex/Jellyfin
- Add useMediaServerType hook for conditional UI rendering
- Update NavBar to show 'Box Sets' for Jellyfin, 'Collections' for Plex
- Update AddModal with dynamic terminology (box set vs collection)
- Hide Plex-only visibility options (showHome, showRecommended) for Jellyfin
- Update SettingsWrapper to show appropriate media server tabs
- Update Layout redirect logic based on media server type
- Add Jellyfin API hooks (useTestJellyfin, useSaveJellyfinSettings, etc.)
- Add Jellyfin settings endpoints to backend controller/service
- Add Jellyfin settings DTO and Zod schema in contracts package
- Update migration to not default media_server_type (null until chosen)
- Add Jellyfin logo SVG
- Reorganize contracts: move plex types under media-server/plex

Part of Jellyfin integration - Phase E (UI Integration)
Jellyfin's user-facing GUI calls them 'Collections', not 'Box Sets'.
The 'BoxSet' is just the internal API type (BaseItemKind.BoxSet).

Keep the configurable variable infrastructure for potential future
customization, but set both Plex and Jellyfin to use 'Collections'.
Implements rule migration when switching between media servers:
- New RuleMigrationService for analyzing and migrating rules
- Preview migration before switching to show compatible/incompatible rules
- Skip rules using Plex-only properties (watchlist, IMDB ratings, smart collections)
- UI checkbox to opt-in to rule migration during server switch
- Updates settings service to integrate rule migration
- Fixes PLEX_ONLY_PROPERTIES to include IDs 36-42

Also includes:
- Code formatting with Prettier
- Docker test environment for branch testing (jellyfin-dev-documents/docker/)
Moved from jellyfin-dev-documents/docker/ to docker/dev/
for proper version control tracking.
- Remove obsolete 'version' attribute
- Escape shell variables ($${ep}) to prevent Docker Compose interpolation
- Default to enoch85/Maintainerr fork where jellyfin branches exist
- Use named volume instead of bind mount for proper ownership
- Add init-permissions container to set correct permissions
- Explicitly set user: 1000:1000 to match Dockerfile
- Default branch now jellyfin-rule-migration
- Use named volumes for all services (fixes permission issues)
- Remove media-generator (mount your own media)
- Add comments showing how to mount media directories
- Streamline documentation
- Move dev files from docker/dev/ to docker/
- Remove nginx-reverse-subfolder (not needed for local testing)
- Remove PlexSetupGuard from plex-api controller to allow fresh installations
- Add new MediaServerSetupGuard that checks if any media server is configured
- Fix ServeStaticModule configuration for proper SPA routing
- Apply code formatting with prettier (semi: false for contracts)
Changed innerJoinAndSelect to leftJoinAndSelect for collection relationship
in getRuleGroups, getRuleGroupsByIds, and getRuleGroup queries.

After rule migration, rules have collectionId=null since collections are
cleared. The inner join was filtering out these migrated rules, making
them invisible in the UI.
When rules are migrated, they have collectionId=null. The updateRules
method was failing with 'Cannot read properties of null' when trying
to access dbCollection.manualCollection.

Now we check if dbCollection exists before comparing its properties.
Changed from ICollection to using collectionId directly to avoid
type mismatch between createCollection and updateCollection return types.
The validation logic was using array index (apps[appId]) to look up
applications, which worked coincidentally because Application enum
values matched their array positions. Changed to use apps.find()
for more robust application lookup by ID.

This may help fix issues with community rules showing empty values
in dropdowns after import.
- Add isPlexSetup() checks to getUsers() and getUser() methods
  to prevent errors when Plex isn't configured but API is called
- Ensure rules array is always defined in getRuleGroup, getRuleGroups,
  and getRuleGroupsByIds methods
- Add Array.isArray() checks in UI components when accessing rules
  to prevent 'r.map is not a function' errors after migration

This comment was marked as resolved.

Comment thread apps/server/src/modules/collections/collections.controller.ts Outdated
Comment thread apps/server/src/modules/collections/collections.controller.ts Outdated
Comment thread apps/server/src/modules/collections/collections.controller.ts Outdated
Comment thread apps/server/src/modules/api/plex-api/plex-api.controller.ts
…ionId

- Changed mediaId parameter from number to string (it was always toString()'d)
- Removed unnecessary toString() calls
- Use ParseIntPipe with optional:true for collectionId query param
- Follows NestJS best practices for parameter validation

Addresses PR review comments on collections.controller.ts lines 118, 133, 136
- collections.controller.ts: Add ParseIntPipe to all @Param and @query with number types
- tmdb.controller.ts: Fix imdbId to be string type (IMDB IDs are strings like 'tt1234567')
- Use ParseIntPipe({ optional: true }) for optional numeric query params
- Consistent with NestJS best practices for parameter validation
@enoch85 enoch85 force-pushed the remaining-issues/abstraction-cleanup branch from 7e7720e to d468b03 Compare January 7, 2026 22:23
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