Skip to content

feat-recs - Incorporate JF12's Jellyfin Recommends and Speedy Server-Side Scoring (take 2) - #1456

Open
mattsigal wants to merge 5 commits into
Moonfin-Client:mainfrom
mattsigal:feature/recommendations-unified-v2
Open

feat-recs - Incorporate JF12's Jellyfin Recommends and Speedy Server-Side Scoring (take 2)#1456
mattsigal wants to merge 5 commits into
Moonfin-Client:mainfrom
mattsigal:feature/recommendations-unified-v2

Conversation

@mattsigal

@mattsigal mattsigal commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Pull Request

Summary

JF12 did a lot of neat things with their recommendation system. This PR unifies the recommendation system source nomenclature across the Item Details screen and the "Since You Watched" home screen rows, introduces native support for Jellyfin 12's extensible similar items backend (GET /Items/{id}/Similar), and adds client-side capability auto-detection for server-backed recommendations via Moonbase with graceful fallback to local client scoring. What this means is that if Moonbase is installed and the server is running JF12, we can fully utilize the new pathing for super quick recommendation results without straining clients. If Moonbase isn't installed or the server is not on JF12 yet, we have the following fallbacks:

  • JF 10.x + "Moonfin Recommends": Runs custom client-side scoring.
  • JF 10.x + "Jellyfin Recommends": Runs Jellyfin 10's stock genre/tag similarity.
  • JF 12 + "Moonfin Recommends" (with Moonbase updated): Automatically takes advantage of server-side scoring and disk caching.
  • JF 12 + "Jellyfin Recommends": Uses Jellyfin 12's extensible provider system.

Related Issues

Link related issues or tickets separated by commas.

  • Closes #
  • Fixes #
  • Related to #

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Performance improvement
  • UI/UX update
  • Documentation update
  • Build/CI change
  • Other (describe):

Changes Made

List the key changes included in this PR.

  • Recommendation Options Unification: Unified the recommendation source options across both Item Details settings (details_screen_settings_screen.dart) and Since You Watched settings (home_row_toggles_screen.dart, preference_constants.dart) into three clear, standardized options:
    • Moonfin Recommends (Custom weighted algorithm)
    • Jellyfin Recommends (Native Jellyfin 12 extensible backend)
    • TMDb Similarity (Online TMDb API)
  • Jellyfin 12 Extensible Provider Support: Added server source handling to item_detail_view_model.dart and row_data_source.dart calling native GET /Items/{id}/Similar, utilizing Jellyfin 12's extensible ISimilarItemsProvider pipeline and built-in server disk/memory caching (TryReadSimilarItemsCacheAsync).
  • Moonbase Capability Auto-Detection: Enhanced plugin_sync_service.dart to detect recommendationsSupported from /Moonfin/Ping. When "Moonfin Recommends" is chosen and connected to a server running Moonbase with the similar items provider active, recommendations are automatically offloaded to the server for near-instant response times; if the server lacks the capability, returns empty, or encounters errors, it seamlessly falls back to our client-side Dart scoring algorithm with zero disruption.
  • Idempotent Database Migration: Protected the schema migration in offline_database.dart so duplicate column checks on developer databases do not crash app initialization.
  • AggregatedItem Cleanup: Removed duplicate dateCreated getter in aggregated_item.dart that was blocking test compilation.
  • Localization: Added recommendationSystemJellyfin key to app_en.arb and regenerated localization files across all supported languages.

2nd Commit: Note a second commit was also made to this PR which does the following:

  • Adds fetchSimilarItems to PluginSyncService calling ${client.baseUrl}/Moonfin/Items/$itemId/Similar.
  • Passes bypass: 'moonfin' when querying Jellyfin stock similarity.
  • Updates ItemDetailViewModel and RowDataSource to route between the two endpoints.
    Basically, Jellyfin treats its stock suggestions as a "fallback" only if a custom one isn't selected. Since we want to give an option, it is important to allow the fetch to bypass Moonfin's algorithm.

3rd Commit: Candidate Pool Scaling for Server-Side Recommendations

  • In Jellyfin 12, MovieSimilarItemsProvider applies Take(limit) to raw candidate score matches before filtering by library membership and played status (IsPlayed = false). In real-world libraries where users have watched many relevant movies, low query limits (such as 30 for home rows or 15 on details pages) severely starve the candidate pool down to 1–4 items.
  • Increases the server similarity query limit to 100 in row_data_source.dart, item_detail_view_model.dart, and plugin_sync_service.dart.
  • Preserves bounded client-side candidate limits (limit: 40 per genre/tag, 10 per person) for the pure local fallback when Moonbase is not installed, preventing heavy network payloads on client devices while ensuring server-backed recommendations deliver full, rich rows.

Platform

  • Android
  • Android TV
  • iOS
  • tvOS
  • Web
  • macOS
  • Windows
  • Linux
  • All / Shared code

Testing

Describe how this change was tested.

  • Tested on emulator / simulator
  • Tested on physical device
  • Manual testing completed
  • Not tested (explain why):

Test Steps

  1. Navigate to Settings -> Details Screen -> Recommendation System Source.
  2. Verify all three options ("Moonfin Recommends", "Jellyfin Recommends", "TMDb Similarity") are selectable and properly localized.
  3. Navigate to Settings -> Home -> "Since you watched" Settings -> Recommendation Source.
  4. Verify all three options match and switch correctly.
  5. Open an item details screen under each source to verify recommendations load properly.
  6. Verify "Since You Watched" home rows populate as expected.

Screenshots (if applicable)

Include screenshots or recordings for UI changes.

Previous Menus

pre-detailspage pre-sinceyouwatched

Post-PR Menus

2026-09-08_10-05-31_moonfin 2026-09-08_10-05-52_moonfin

Jellyfin Recommends Search Results (with filter for "include previous watched items" OFF)

2026-09-08_10-08-17_moonfin

Jellyfin Recommends Search Results (with filter for "include previous watched items" ON)

2026-09-08_10-06-28_moonfin

Checklist

  • Code builds successfully
  • Code follows project style and conventions
  • No unnecessary commented-out code
  • No new warnings introduced

@github-actions github-actions Bot added All Feature Request New feature or request Performance This doesn't seem right Refactor Further information is requested UI labels Sep 8, 2026
@mattsigal
mattsigal force-pushed the feature/recommendations-unified-v2 branch from 01a3a6f to 9f731f7 Compare September 8, 2026 19:02
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

✅ Build Successful

All platform builds and tests passed. You can download the test artifacts below.

Tests ✅ 2039 passed, analyzer clean

Platform Status Artifact
Android ✅ Passed Moonfin_Android_v* + Moonfin_AndroidTV_v*
iOS ✅ Passed Moonfin_iOS_v*_unsigned.ipa
macOS ✅ Passed Moonfin_macOS_v*.dmg
tvOS ✅ Passed Moonfin_tvOS_v*_unsigned.ipa
Windows x64 ✅ Passed Moonfin_Windows_v*.exe
Windows ARM64 ✅ Passed Moonfin_WindowsARM64_v*.exe
Linux x64 ✅ Passed Moonfin_Linux_v* (deb/rpm/AppImage/snap/flatpak/tar.gz)
Linux ARM64 ✅ Passed Moonfin_LinuxARM64_v* (deb/rpm/AppImage/snap/flatpak/tar.gz)
Property Value
Commit ac2bcd3
Workflow run Build #1342

…e starvation

In Jellyfin 12, MovieSimilarItemsProvider applies Take(limit) to raw score matches
before evaluating library membership and filtering played titles (IsPlayed = false).
When querying with low limits (e.g. 30 for home rows or 15 on detail pages),
the candidate pool is prematurely starved, often yielding only 1-4 accessible
titles in real-world libraries.

- Increase query limit to 100 for server similarity queries in RowDataSource
  and ItemDetailViewModel.
- Update PluginSyncService.fetchSimilarItems default to 100 so server-side
  scoring via Moonbase returns a full set of recommendations.
- Preserve bounded client-side candidate limits (40 per genre/tag, 10 per person)
  for local fallback when Moonbase is not installed, preventing network and
  memory exhaustion on constrained client devices.
In app_fi.arb, Weblate translated the ICU plural keyword 'other' to Finnish 'muu'
in queuedMoreCount, causing flutter gen-l10n to fail across all CI build jobs with:
"ICU Syntax Error: Plural expressions must have an 'other' case."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

All Feature Request New feature or request Performance This doesn't seem right Refactor Further information is requested UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant