diff options
Diffstat (limited to 'MediaBrowser.Controller/Library')
10 files changed, 354 insertions, 1 deletions
diff --git a/MediaBrowser.Controller/Library/IBatchLocalSimilarItemsProvider.cs b/MediaBrowser.Controller/Library/IBatchLocalSimilarItemsProvider.cs new file mode 100644 index 0000000000..af49711606 --- /dev/null +++ b/MediaBrowser.Controller/Library/IBatchLocalSimilarItemsProvider.cs @@ -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); +} diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index f5e3d7034e..0b64da291c 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -598,6 +598,14 @@ namespace MediaBrowser.Controller.Library IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query); /// <summary> + /// Gets the distinct people names per item for multiple items. + /// </summary> + /// <param name="itemIds">The item IDs.</param> + /// <param name="personTypes">The person types to include.</param> + /// <returns>A dictionary mapping each item ID to its distinct people names. Items with no matching people are omitted.</returns> + IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItems(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes); + + /// <summary> /// Queries the items. /// </summary> /// <param name="query">The query.</param> @@ -784,5 +792,12 @@ namespace MediaBrowser.Controller.Library /// <param name="query">The query filter.</param> /// <returns>Aggregated filter values.</returns> QueryFiltersLegacy GetQueryFiltersLegacy(InternalItemsQuery query); + + /// <summary> + /// Gets a list of all language codes of the provided stream type. + /// </summary> + /// <param name="mediaStreamType">The stream type.</param> + /// <returns>List of language codes.</returns> + IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType); } } diff --git a/MediaBrowser.Controller/Library/ILocalSimilarItemsProvider.cs b/MediaBrowser.Controller/Library/ILocalSimilarItemsProvider.cs new file mode 100644 index 0000000000..b8e41ec810 --- /dev/null +++ b/MediaBrowser.Controller/Library/ILocalSimilarItemsProvider.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Library; + +/// <summary> +/// Provides similar items from the local library. +/// Returns fully resolved BaseItems directly - no additional resolution needed. +/// </summary> +public interface ILocalSimilarItemsProvider : ISimilarItemsProvider +{ + /// <summary> + /// Determines whether the provider can handle items of the specified type. + /// </summary> + /// <param name="itemType">The item type.</param> + /// <returns><c>true</c> if the provider handles this item type; otherwise <c>false</c>.</returns> + bool Supports(Type itemType); + + /// <summary> + /// Gets similar items from the local library. + /// </summary> + /// <param name="item">The source item to find similar items for.</param> + /// <param name="query">The query options (user, limit, exclusions, etc.).</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>The list of similar items from the library.</returns> + Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync( + BaseItem item, + SimilarItemsQuery query, + CancellationToken cancellationToken); +} + +/// <summary> +/// Provides similar items from the local library for a specific item type. +/// Returns fully resolved BaseItems directly - no additional resolution needed. +/// </summary> +/// <typeparam name="TItemType">The type of item this provider handles.</typeparam> +public interface ILocalSimilarItemsProvider<TItemType> : ILocalSimilarItemsProvider + where TItemType : BaseItem +{ + /// <summary> + /// Gets similar items from the local library. + /// </summary> + /// <param name="item">The source item to find similar items for.</param> + /// <param name="query">The query options (user, limit, exclusions, etc.).</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>The list of similar items from the library.</returns> + Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync( + TItemType item, + SimilarItemsQuery query, + CancellationToken cancellationToken); + + bool ILocalSimilarItemsProvider.Supports(Type itemType) + => typeof(TItemType).IsAssignableFrom(itemType); + + Task<IReadOnlyList<BaseItem>> ILocalSimilarItemsProvider.GetSimilarItemsAsync( + BaseItem item, + SimilarItemsQuery query, + CancellationToken cancellationToken) + => GetSimilarItemsAsync((TItemType)item, query, cancellationToken); +} diff --git a/MediaBrowser.Controller/Library/IRemoteSimilarItemsProvider.cs b/MediaBrowser.Controller/Library/IRemoteSimilarItemsProvider.cs new file mode 100644 index 0000000000..3803e51769 --- /dev/null +++ b/MediaBrowser.Controller/Library/IRemoteSimilarItemsProvider.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Library; + +/// <summary> +/// Provides similar item references from remote/external sources. +/// Returns lightweight references with ProviderIds that the manager resolves to library items. +/// </summary> +public interface IRemoteSimilarItemsProvider : ISimilarItemsProvider +{ + /// <summary> + /// Determines whether the provider can handle items of the specified type. + /// </summary> + /// <param name="itemType">The item type.</param> + /// <returns><c>true</c> if the provider handles this item type; otherwise <c>false</c>.</returns> + bool Supports(Type itemType); + + /// <summary> + /// Gets similar item references from an external source as an async stream. + /// </summary> + /// <param name="item">The source item to find similar items for.</param> + /// <param name="query">The query options (user, limit, exclusions).</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>An async enumerable of similar item references.</returns> + IAsyncEnumerable<SimilarItemReference> GetSimilarItemsAsync( + BaseItem item, + SimilarItemsQuery query, + CancellationToken cancellationToken); +} + +/// <summary> +/// Provides similar item references from remote/external sources for a specific item type. +/// Returns lightweight references with ProviderIds that the manager resolves to library items. +/// </summary> +/// <typeparam name="TItemType">The type of item this provider handles.</typeparam> +public interface IRemoteSimilarItemsProvider<TItemType> : IRemoteSimilarItemsProvider + where TItemType : BaseItem +{ + /// <summary> + /// Gets similar item references from an external source as an async stream. + /// </summary> + /// <param name="item">The source item to find similar items for.</param> + /// <param name="query">The query options (user, limit, exclusions).</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>An async enumerable of similar item references.</returns> + IAsyncEnumerable<SimilarItemReference> GetSimilarItemsAsync( + TItemType item, + SimilarItemsQuery query, + CancellationToken cancellationToken); + + bool IRemoteSimilarItemsProvider.Supports(Type itemType) + => typeof(TItemType).IsAssignableFrom(itemType); + + IAsyncEnumerable<SimilarItemReference> IRemoteSimilarItemsProvider.GetSimilarItemsAsync( + BaseItem item, + SimilarItemsQuery query, + CancellationToken cancellationToken) + => GetSimilarItemsAsync((TItemType)item, query, cancellationToken); +} diff --git a/MediaBrowser.Controller/Library/ISimilarItemsManager.cs b/MediaBrowser.Controller/Library/ISimilarItemsManager.cs new file mode 100644 index 0000000000..36fa547eeb --- /dev/null +++ b/MediaBrowser.Controller/Library/ISimilarItemsManager.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +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; + +/// <summary> +/// Interface for managing similar items providers and operations. +/// </summary> +public interface ISimilarItemsManager +{ + /// <summary> + /// Registers similar items providers discovered through dependency injection. + /// </summary> + /// <param name="providers">The similar items providers to register.</param> + void AddParts(IEnumerable<ISimilarItemsProvider> providers); + + /// <summary> + /// Gets the similar items providers for a specific item type. + /// </summary> + /// <typeparam name="T">The item type.</typeparam> + /// <returns>The list of similar items providers for that type.</returns> + IReadOnlyList<ISimilarItemsProvider> GetSimilarItemsProviders<T>() + where T : BaseItem; + + /// <summary> + /// Gets similar items for the specified item. + /// </summary> + /// <param name="item">The source item to find similar items for.</param> + /// <param name="excludeArtistIds">Artist IDs to exclude from results.</param> + /// <param name="user">The user context.</param> + /// <param name="dtoOptions">The DTO options.</param> + /// <param name="limit">Maximum number of results.</param> + /// <param name="libraryOptions">The library options for provider configuration.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The list of similar items.</returns> + Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync( + BaseItem item, + IReadOnlyList<Guid> excludeArtistIds, + User? user, + DtoOptions dtoOptions, + 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); +} diff --git a/MediaBrowser.Controller/Library/ISimilarItemsProvider.cs b/MediaBrowser.Controller/Library/ISimilarItemsProvider.cs new file mode 100644 index 0000000000..0d089369a8 --- /dev/null +++ b/MediaBrowser.Controller/Library/ISimilarItemsProvider.cs @@ -0,0 +1,26 @@ +using System; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.Controller.Library; + +/// <summary> +/// Base marker interface for similar items providers. +/// </summary> +public interface ISimilarItemsProvider +{ + /// <summary> + /// Gets the name of the provider. + /// </summary> + string Name { get; } + + /// <summary> + /// Gets the type of the provider. + /// </summary> + MetadataPluginType Type { get; } + + /// <summary> + /// Gets the cache duration for results from this provider. + /// If null, results will not be cached. + /// </summary> + TimeSpan? CacheDuration => null; +} diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index b558ef73d5..c5e7ae4913 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -117,7 +117,7 @@ namespace MediaBrowser.Controller.Library get { var paths = string.IsNullOrEmpty(Path) ? Array.Empty<string>() : [Path]; - return AdditionalLocations is null ? paths : [..paths, ..AdditionalLocations]; + return AdditionalLocations is null ? paths : [.. paths, .. AdditionalLocations]; } } diff --git a/MediaBrowser.Controller/Library/SimilarItemReference.cs b/MediaBrowser.Controller/Library/SimilarItemReference.cs new file mode 100644 index 0000000000..2a40c93bdd --- /dev/null +++ b/MediaBrowser.Controller/Library/SimilarItemReference.cs @@ -0,0 +1,22 @@ +namespace MediaBrowser.Controller.Library; + +/// <summary> +/// A reference to a similar item by provider ID with a similarity score. +/// </summary> +public class SimilarItemReference +{ + /// <summary> + /// Gets or sets the provider name (e.g., "Tmdb", "MusicBrainzArtist"). + /// </summary> + public required string ProviderName { get; set; } + + /// <summary> + /// Gets or sets the provider ID value. + /// </summary> + public required string ProviderId { get; set; } + + /// <summary> + /// Gets or sets the similarity score (0.0 to 1.0). + /// </summary> + public float? Score { get; set; } +} diff --git a/MediaBrowser.Controller/Library/SimilarItemsQuery.cs b/MediaBrowser.Controller/Library/SimilarItemsQuery.cs new file mode 100644 index 0000000000..1ed3ceec16 --- /dev/null +++ b/MediaBrowser.Controller/Library/SimilarItemsQuery.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Jellyfin.Database.Implementations.Entities; +using MediaBrowser.Controller.Dto; + +namespace MediaBrowser.Controller.Library; + +/// <summary> +/// Query options for similar items requests. +/// </summary> +public class SimilarItemsQuery +{ + /// <summary> + /// Gets or sets the user context. + /// </summary> + public User? User { get; set; } + + /// <summary> + /// Gets or sets the maximum number of results. + /// </summary> + public int? Limit { get; set; } + + /// <summary> + /// Gets or sets the DTO options. + /// </summary> + public DtoOptions? DtoOptions { get; set; } + + /// <summary> + /// Gets or sets the item IDs to exclude from results. + /// </summary> + public IReadOnlyList<Guid> ExcludeItemIds { get; set; } = []; + + /// <summary> + /// Gets or sets the artist IDs to exclude from results. + /// </summary> + public IReadOnlyList<Guid> ExcludeArtistIds { get; set; } = []; +} diff --git a/MediaBrowser.Controller/Library/SimilarItemsRecommendation.cs b/MediaBrowser.Controller/Library/SimilarItemsRecommendation.cs new file mode 100644 index 0000000000..71346fcadf --- /dev/null +++ b/MediaBrowser.Controller/Library/SimilarItemsRecommendation.cs @@ -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; } +} |
