Privacy-first personalized recommendations for Jellyfin based entirely on local watch history and metadata similarity. No cloud services, no tracking. Works on all Jellyfin clients (even TVs).
Please report any issues or feedback on GitHub Issues.
- Per-user personalization - Tailored recommendations for each user's viewing history
- Content-based filtering - TF-IDF embeddings with cosine similarity matching
- Temporal similarity - Decade-based grouping finds content from similar time periods
- Virtual library integration - Works on all Jellyfin clients (web, mobile, Roku, etc.)
- Play status sync - Watch state syncs from recommendations back to your real library
- Privacy-first - All processing happens locally on your server
- Performance optimized - Handles 2,000+ item libraries efficiently
- Jellyfin Server: 10.11.5+
- .NET Runtime: 9.0
- Target ABI: 10.11.0.0
-
Add plugin repository:
Dashboard → Plugins → Repositories → Add
https://raw.githubusercontent.com/rdpharr/jellyfin-plugin-localrecs/main/manifest.json -
Install plugin:
Dashboard → Plugins → Catalog → Install "Local Recommendations" -
Restart Jellyfin server
-
Configure virtual libraries (see Setup below)
- Navigate to: Dashboard → Plugins → Local Recommendations → Setup Guide
- Copy the library paths for each user (two per user: Movies and TV)
For each user, create two libraries:
Movies:
- Dashboard → Libraries → Add Media Library
- Content Type: Movies
- Add media location: Paste the Movie Library Path from Setup Guide
- Library name: User's suggested name (e.g., "John's Recommended Movies")
TV Shows:
- Content Type: Shows
- Add media location: Paste the TV Library Path from Setup Guide
- Library name: User's suggested name (e.g., "John's Recommended TV")
For each user:
- Dashboard → Users → [Username] → Library Access
- Enable only that user's recommendation libraries
- Disable other users' recommendation libraries
- Dashboard → Scheduled Tasks → "Refresh Local Recommendations" → Run Now
- Wait ~1-5 minutes (depending on library size)
- Manually scan recommendation libraries to see results
Access via: Dashboard → Plugins → Local Recommendations → Settings
Recommendation Counts
- Movies and TV shows to recommend per user (default: 25 each)
- Minimum watched items for personalization (default: 3)
Filtering
- Exclude abandoned TV series (default: enabled, 90 days threshold)
Weighting Factors
- Favorite boost: 2.0x (configurable)
- Rewatch boost: 1.5x (configurable)
- Recency decay half-life: 365 days (configurable)
Optional Features
- Rating proximity scoring (boost items with similar ratings)
- Decade-based temporal similarity (finds content from similar eras)
Performance
- Vocabulary limits for actors/tags (default: 500 each) — see note below
- Parallel processing options
Vocabulary size controls how many distinct actors, directors, and tags are included in the TF-IDF model. A higher value (e.g. 1000) captures more niche contributors and gives richer signals for large, varied libraries. A lower value (e.g. 200) is faster and uses less memory but may miss less-common cast members. The default of 500 is a good starting point for most libraries; raise it if recommendations feel too genre-driven and ignore specific actors you watch often.
Recency decay controls how much your recent watch history influences recommendations relative to older watches. It is expressed as a half-life in days: a value of 365 means a film watched a year ago contributes half as much to your taste profile as one watched today. Lower values (e.g. 90) make recommendations react quickly to recent binges; higher values (e.g. 730) give a more stable, long-term taste profile.
Update Schedule
- Default: Daily at 4:00 AM
- Customize in Dashboard → Scheduled Tasks
- Manual refresh available anytime
Content-based filtering using TF-IDF embeddings and cosine similarity:
- Feature extraction - Genres, actors, directors, tags, decades, ratings
- TF-IDF embeddings - Numerical vectors for each item (~1200-1500 dimensions)
- User profiles - Aggregated taste vector from weighted watch history
- Similarity scoring - Cosine similarity between user profile and unwatched items
- Ranking - Top N items sorted by similarity score
Weighting factors:
- Favorites (2x boost)
- Rewatches (1.5x boost)
- Recency decay (365-day half-life)
- Decade similarity (items from similar time periods)
- Optional rating proximity (items with similar ratings)
Recommendations appear as separate libraries for each user:
- Plugin creates
.strmfiles pointing to original media files - Admin creates Jellyfin libraries pointing to plugin directories (one-time setup)
- Each user gets Movies and TV libraries with personalized recommendations
- Play status sync: Watch state on recommendation items is synced back to the source library
- Watched items are cleaned up at the next scheduled recommendation refresh
100% local processing:
- No external services or cloud dependencies
- No tracking or telemetry
- Only uses data already in your Jellyfin database
- All computation happens on your server
- Duplicate "Continue Watching" / "Next Up": Partially watched recommendations appear twice — once for the
.strmitem and once for the source media file. This resolves on the next recommendation refresh, or you can manually trigger a refresh from Scheduled Tasks. - Metadata display: Virtual library items may not show full metadata (runtime, ratings, cast) in the UI due to Jellyfin's
.strmfile handling. Playback and posters work normally. - Manual setup required: Admin must manually create libraries and set permissions (Jellyfin API limitation)
- Library scanning: Manually scan recommendation libraries after refresh to see updates
Prerequisites: .NET 9.0 SDK, Git
git clone https://github.com/rdpharr/jellyfin-plugin-localrecs.git
cd jellyfin-plugin-localrecs
# Build (uses dotnet-helper.sh wrapper)
bash dotnet-helper.sh build
# Run tests
bash dotnet-helper.sh test
# Output: Jellyfin.Plugin.LocalRecs/bin/Debug/net9.0/Windows: Use Git Bash or WSL to run the helper script.
Contributions welcome! See DESIGN.md for technical details and architecture.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: DESIGN.md
GNU General Public License v3.0 - see LICENSE.txt