Merge pull request #16856 from Shadowghost/movie-recommendations
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Format / format-check (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled

Fix movie recommendations
This commit is contained in:
Bond-009
2026-05-27 20:59:18 +02:00
committed by GitHub
10 changed files with 630 additions and 289 deletions

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Library;
/// <summary>
/// A local similar items provider that supports batch queries across multiple source items.
/// Implementations share access filtering and entity loading across all sources for better performance.
/// </summary>
public interface IBatchLocalSimilarItemsProvider : ISimilarItemsProvider
{
/// <summary>
/// Gets similar items for multiple source items in a single batch.
/// </summary>
/// <param name="sourceItems">The source items to find similar items for.</param>
/// <param name="query">The query options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Per-source-item results keyed by source item ID.</returns>
Task<Dictionary<Guid, IReadOnlyList<BaseItem>>> GetBatchSimilarItemsAsync(
IReadOnlyList<BaseItem> sourceItems,
SimilarItemsQuery query,
CancellationToken cancellationToken);
}

View File

@@ -597,6 +597,15 @@ namespace MediaBrowser.Controller.Library
/// <returns>List&lt;System.String&gt;.</returns>
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query);
/// <summary>
/// Gets distinct people names for multiple items.
/// </summary>
/// <param name="itemIds">The item IDs.</param>
/// <param name="personTypes">The person types to include.</param>
/// <param name="limit">Maximum number of names.</param>
/// <returns>The distinct people names.</returns>
IReadOnlyList<string> GetPeopleNamesByItems(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes, int limit);
/// <summary>
/// Queries the items.
/// </summary>

View File

@@ -6,6 +6,7 @@ using Jellyfin.Database.Implementations.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Library;
@@ -47,4 +48,23 @@ public interface ISimilarItemsManager
int? limit,
LibraryOptions? libraryOptions,
CancellationToken cancellationToken);
/// <summary>
/// Builds movie recommendations for a user: a mix of similar-items and person-based categories,
/// scheduled round-robin and capped to <paramref name="categoryLimit"/>.
/// </summary>
/// <param name="user">The user the recommendations are for. May be <see langword="null"/> for anonymous access.</param>
/// <param name="parentId">The library/folder to localize the search to. Pass <see cref="Guid.Empty"/> to use the root.</param>
/// <param name="categoryLimit">Maximum number of recommendation categories to return.</param>
/// <param name="itemLimit">Maximum number of items per category.</param>
/// <param name="dtoOptions">DTO options used when querying the library.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The list of recommendation categories, ordered by <see cref="RecommendationType"/>.</returns>
Task<IReadOnlyList<SimilarItemsRecommendation>> GetMovieRecommendationsAsync(
User? user,
Guid parentId,
int categoryLimit,
int itemLimit,
DtoOptions dtoOptions,
CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Library;
/// <summary>
/// A recommendation category derived from a baseline item, holding similar items prior to DTO conversion.
/// </summary>
public sealed class SimilarItemsRecommendation
{
/// <summary>
/// Gets the display name of the baseline item the recommendation is based on.
/// </summary>
public required string BaselineItemName { get; init; }
/// <summary>
/// Gets an identifier for the recommendation category.
/// </summary>
public required Guid CategoryId { get; init; }
/// <summary>
/// Gets the recommendation type.
/// </summary>
public required RecommendationType RecommendationType { get; init; }
/// <summary>
/// Gets the similar items for the baseline, ordered by relevance.
/// </summary>
public required IReadOnlyList<BaseItem> Items { get; init; }
}

View File

@@ -32,4 +32,13 @@ public interface IPeopleRepository
/// <param name="filter">The query.</param>
/// <returns>The list of people names matching the filter.</returns>
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery filter);
/// <summary>
/// Gets distinct people names for multiple items efficiently by querying from the mapping table.
/// </summary>
/// <param name="itemIds">The item IDs to get people for.</param>
/// <param name="personTypes">The person types to include (e.g. "Actor", "Director").</param>
/// <param name="limit">Maximum number of names to return.</param>
/// <returns>The distinct people names.</returns>
IReadOnlyList<string> GetPeopleNamesByItems(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes, int limit);
}