mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-29 19:11:00 +01:00
Added static method GetBaseItemKindsForCollectionType in ItemsController (moved from ContentFolderImageProvider to be shared) Added AudioBook to GetRepresentativeItemTypes for CollectionType.books for consistency Added GetBooks to GetUserItems for CollectionType.books which gets BaseItemKind.Book and BaseItemKind.AudioBook Move GetBaseItemKindsForCollectionType to DtoExtensions Cleaned up the missing null checks and used new collection expressions. Associate Person to Book and AudioBook for related items.
912 lines
31 KiB
C#
912 lines
31 KiB
C#
#nullable disable
|
|
|
|
#pragma warning disable CS1591
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using Jellyfin.Data;
|
|
using Jellyfin.Data.Enums;
|
|
using Jellyfin.Database.Implementations.Entities;
|
|
using Jellyfin.Database.Implementations.Enums;
|
|
using Jellyfin.Extensions;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.TV;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.Model.Querying;
|
|
using Microsoft.Extensions.Logging;
|
|
using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
|
|
|
|
namespace MediaBrowser.Controller.Entities
|
|
{
|
|
public class UserViewBuilder
|
|
{
|
|
private readonly IUserViewManager _userViewManager;
|
|
private readonly ILibraryManager _libraryManager;
|
|
private readonly ILogger<BaseItem> _logger;
|
|
private readonly IUserDataManager _userDataManager;
|
|
private readonly ITVSeriesManager _tvSeriesManager;
|
|
|
|
public UserViewBuilder(
|
|
IUserViewManager userViewManager,
|
|
ILibraryManager libraryManager,
|
|
ILogger<BaseItem> logger,
|
|
IUserDataManager userDataManager,
|
|
ITVSeriesManager tvSeriesManager)
|
|
{
|
|
_userViewManager = userViewManager;
|
|
_libraryManager = libraryManager;
|
|
_logger = logger;
|
|
_userDataManager = userDataManager;
|
|
_tvSeriesManager = tvSeriesManager;
|
|
}
|
|
|
|
public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, CollectionType? viewType, InternalItemsQuery query)
|
|
{
|
|
var user = query.User;
|
|
|
|
// if (query.IncludeItemTypes is not null &&
|
|
// query.IncludeItemTypes.Length == 1 &&
|
|
// string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
|
|
// {
|
|
// if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
|
// {
|
|
// return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
|
|
// }
|
|
// }
|
|
|
|
switch (viewType)
|
|
{
|
|
case CollectionType.folders:
|
|
return GetResult(_libraryManager.GetUserRootFolder().GetChildren(user, true), query);
|
|
|
|
case CollectionType.books:
|
|
return GetBooks(queryParent, user, query);
|
|
|
|
case CollectionType.tvshows:
|
|
return GetTvView(queryParent, user, query);
|
|
|
|
case CollectionType.movies:
|
|
return GetMovieFolders(queryParent, user, query);
|
|
|
|
case CollectionType.tvshowseries:
|
|
return GetTvSeries(queryParent, user, query);
|
|
|
|
case CollectionType.tvgenres:
|
|
return GetTvGenres(queryParent, user, query);
|
|
|
|
case CollectionType.tvgenre:
|
|
return GetTvGenreItems(queryParent, displayParent, user, query);
|
|
|
|
case CollectionType.tvresume:
|
|
return GetTvResume(queryParent, user, query);
|
|
|
|
case CollectionType.tvnextup:
|
|
return GetTvNextUp(queryParent, query);
|
|
|
|
case CollectionType.tvlatest:
|
|
return GetTvLatest(queryParent, user, query);
|
|
|
|
case CollectionType.moviefavorites:
|
|
return GetFavoriteMovies(queryParent, user, query);
|
|
|
|
case CollectionType.movielatest:
|
|
return GetMovieLatest(queryParent, user, query);
|
|
|
|
case CollectionType.moviegenres:
|
|
return GetMovieGenres(queryParent, user, query);
|
|
|
|
case CollectionType.moviegenre:
|
|
return GetMovieGenreItems(queryParent, displayParent, user, query);
|
|
|
|
case CollectionType.movieresume:
|
|
return GetMovieResume(queryParent, user, query);
|
|
|
|
case CollectionType.moviemovies:
|
|
return GetMovieMovies(queryParent, user, query);
|
|
|
|
case CollectionType.moviecollection:
|
|
return GetMovieCollections(user, query);
|
|
|
|
case CollectionType.tvfavoriteepisodes:
|
|
return GetFavoriteEpisodes(queryParent, user, query);
|
|
|
|
case CollectionType.tvfavoriteseries:
|
|
return GetFavoriteSeries(queryParent, user, query);
|
|
|
|
default:
|
|
{
|
|
if (queryParent is UserView)
|
|
{
|
|
return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), query);
|
|
}
|
|
|
|
return queryParent.GetItems(query);
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GetSpecialItemsLimit()
|
|
{
|
|
return 50;
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
if (query.Recursive)
|
|
{
|
|
query.Recursive = true;
|
|
query.SetUser(user);
|
|
|
|
if (query.IncludeItemTypes.Length == 0)
|
|
{
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
}
|
|
|
|
return parent.QueryRecursive(query);
|
|
}
|
|
|
|
var list = new List<BaseItem>
|
|
{
|
|
GetUserView(CollectionType.movieresume, "HeaderContinueWatching", "0", parent),
|
|
GetUserView(CollectionType.movielatest, "Latest", "1", parent),
|
|
GetUserView(CollectionType.moviemovies, "Movies", "2", parent),
|
|
GetUserView(CollectionType.moviecollection, "Collections", "3", parent),
|
|
GetUserView(CollectionType.moviefavorites, "Favorites", "4", parent),
|
|
GetUserView(CollectionType.moviegenres, "Genres", "5", parent)
|
|
};
|
|
|
|
return GetResult(list, query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.IsFavorite = true;
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.IsFavorite = true;
|
|
query.IncludeItemTypes = [BaseItemKind.Series];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.IsFavorite = true;
|
|
query.IncludeItemTypes = [BaseItemKind.Episode];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetBooks(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
|
|
query.IncludeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
|
|
{
|
|
query.Parent = null;
|
|
query.IncludeItemTypes = [BaseItemKind.BoxSet];
|
|
query.SetUser(user);
|
|
query.Recursive = true;
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.OrderBy = [(ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)];
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.Limit = GetSpecialItemsLimit();
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
|
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)];
|
|
query.IsResumable = true;
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.Limit = GetSpecialItemsLimit();
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
|
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
|
}
|
|
|
|
private QueryResult<BaseItem> ConvertToResult(IReadOnlyList<BaseItem> items)
|
|
{
|
|
return new QueryResult<BaseItem>(items);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
|
{
|
|
IncludeItemTypes = [BaseItemKind.Movie],
|
|
Recursive = true,
|
|
EnableTotalRecordCount = false
|
|
}).Items
|
|
.SelectMany(i => i.Genres)
|
|
.DistinctNames()
|
|
.Select(i =>
|
|
{
|
|
try
|
|
{
|
|
return _libraryManager.GetGenre(i);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error getting genre");
|
|
return null;
|
|
}
|
|
})
|
|
.Where(i => i is not null)
|
|
.Select(i => GetUserViewWithName(CollectionType.moviegenre, i.SortName, parent));
|
|
|
|
return GetResult(genres, query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = queryParent;
|
|
query.GenreIds = [displayParent.Id];
|
|
query.SetUser(user);
|
|
|
|
query.IncludeItemTypes = [BaseItemKind.Movie];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvView(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
if (query.Recursive)
|
|
{
|
|
query.Recursive = true;
|
|
query.SetUser(user);
|
|
|
|
if (query.IncludeItemTypes.Length == 0)
|
|
{
|
|
query.IncludeItemTypes =
|
|
[
|
|
BaseItemKind.Series,
|
|
BaseItemKind.Season,
|
|
BaseItemKind.Episode
|
|
];
|
|
}
|
|
|
|
return parent.QueryRecursive(query);
|
|
}
|
|
|
|
var list = new List<BaseItem>
|
|
{
|
|
GetUserView(CollectionType.tvresume, "HeaderContinueWatching", "0", parent),
|
|
GetUserView(CollectionType.tvnextup, "HeaderNextUp", "1", parent),
|
|
GetUserView(CollectionType.tvlatest, "Latest", "2", parent),
|
|
GetUserView(CollectionType.tvshowseries, "Shows", "3", parent),
|
|
GetUserView(CollectionType.tvfavoriteseries, "HeaderFavoriteShows", "4", parent),
|
|
GetUserView(CollectionType.tvfavoriteepisodes, "HeaderFavoriteEpisodes", "5", parent),
|
|
GetUserView(CollectionType.tvgenres, "Genres", "6", parent)
|
|
};
|
|
|
|
return GetResult(list, query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.OrderBy = [(ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)];
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.Limit = GetSpecialItemsLimit();
|
|
query.IncludeItemTypes = [BaseItemKind.Episode];
|
|
query.IsVirtualItem = false;
|
|
|
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
|
|
{
|
|
var parentFolders = GetMediaFolders(parent, query.User, [CollectionType.tvshows]);
|
|
|
|
var result = _tvSeriesManager.GetNextUp(
|
|
new NextUpQuery
|
|
{
|
|
Limit = query.Limit,
|
|
StartIndex = query.StartIndex,
|
|
User = query.User
|
|
},
|
|
parentFolders,
|
|
query.DtoOptions);
|
|
|
|
return result;
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)];
|
|
query.IsResumable = true;
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
query.Limit = GetSpecialItemsLimit();
|
|
query.IncludeItemTypes = [BaseItemKind.Episode];
|
|
|
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = parent;
|
|
query.SetUser(user);
|
|
|
|
query.IncludeItemTypes = [BaseItemKind.Series];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
|
|
{
|
|
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
|
{
|
|
IncludeItemTypes = [BaseItemKind.Series],
|
|
Recursive = true,
|
|
EnableTotalRecordCount = false
|
|
}).Items
|
|
.SelectMany(i => i.Genres)
|
|
.DistinctNames()
|
|
.Select(i =>
|
|
{
|
|
try
|
|
{
|
|
return _libraryManager.GetGenre(i);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error getting genre");
|
|
return null;
|
|
}
|
|
})
|
|
.Where(i => i is not null)
|
|
.Select(i => GetUserViewWithName(CollectionType.tvgenre, i.SortName, parent));
|
|
|
|
return GetResult(genres, query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
|
|
{
|
|
query.Recursive = true;
|
|
query.Parent = queryParent;
|
|
query.GenreIds = [displayParent.Id];
|
|
query.SetUser(user);
|
|
|
|
query.IncludeItemTypes = [BaseItemKind.Series];
|
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
}
|
|
|
|
private QueryResult<BaseItem> GetResult<T>(
|
|
IEnumerable<T> items,
|
|
InternalItemsQuery query)
|
|
where T : BaseItem
|
|
{
|
|
var filtered = Filter(items, query.User, query, _userDataManager, _libraryManager);
|
|
|
|
return SortAndPage(filtered, null, query, _libraryManager);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Batch-aware filter that applies per-item checks.
|
|
/// </summary>
|
|
/// <param name="items">The items to filter.</param>
|
|
/// <param name="user">The user for filtering context.</param>
|
|
/// <param name="query">The query parameters.</param>
|
|
/// <param name="userDataManager">The user data manager.</param>
|
|
/// <param name="libraryManager">The library manager.</param>
|
|
/// <returns>The filtered items.</returns>
|
|
public static IEnumerable<BaseItem> Filter(
|
|
IEnumerable<BaseItem> items,
|
|
User user,
|
|
InternalItemsQuery query,
|
|
IUserDataManager userDataManager,
|
|
ILibraryManager libraryManager)
|
|
{
|
|
var filtered = items.Where(i => Filter(i, user, query, userDataManager, libraryManager));
|
|
|
|
if (query.IsPlayed.HasValue && user is not null)
|
|
{
|
|
var itemList = filtered.ToList();
|
|
var folderIds = itemList.OfType<Folder>().Select(f => f.Id).ToList();
|
|
|
|
if (folderIds.Count > 0)
|
|
{
|
|
var counts = libraryManager.GetPlayedAndTotalCountBatch(folderIds, user);
|
|
var isPlayedValue = query.IsPlayed.Value;
|
|
|
|
return itemList.Where(i =>
|
|
{
|
|
if (i.IsFolder && counts.TryGetValue(i.Id, out var c))
|
|
{
|
|
return (c.Total > 0 && c.Played == c.Total) == isPlayedValue;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
return itemList;
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
public static QueryResult<BaseItem> SortAndPage(
|
|
IEnumerable<BaseItem> items,
|
|
int? totalRecordLimit,
|
|
InternalItemsQuery query,
|
|
ILibraryManager libraryManager)
|
|
{
|
|
if (query.OrderBy.Count > 0)
|
|
{
|
|
items = libraryManager.Sort(items, query.User, query.OrderBy);
|
|
}
|
|
|
|
var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
|
|
var totalCount = itemsArray.Length;
|
|
|
|
if (query.Limit.HasValue && query.Limit.Value > 0)
|
|
{
|
|
itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
|
|
}
|
|
else if (query.StartIndex.HasValue)
|
|
{
|
|
itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
|
|
}
|
|
|
|
return new QueryResult<BaseItem>(
|
|
query.StartIndex,
|
|
totalCount,
|
|
itemsArray);
|
|
}
|
|
|
|
private static bool Filter(
|
|
BaseItem item,
|
|
User user,
|
|
InternalItemsQuery query,
|
|
IUserDataManager userDataManager,
|
|
ILibraryManager libraryManager)
|
|
{
|
|
if (!string.IsNullOrEmpty(query.NameStartsWith) && !item.SortName.StartsWith(query.NameStartsWith, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#pragma warning disable CA1309 // Use ordinal string comparison
|
|
if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater) && string.Compare(query.NameStartsWithOrGreater, item.SortName, StringComparison.InvariantCultureIgnoreCase) == 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(query.NameLessThan) && string.Compare(query.NameLessThan, item.SortName, StringComparison.InvariantCultureIgnoreCase) != 1)
|
|
#pragma warning restore CA1309 // Use ordinal string comparison
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UserItemData userData = null;
|
|
|
|
if (query.IsLiked.HasValue)
|
|
{
|
|
userData = userDataManager.GetUserData(user, item);
|
|
if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.IsFavoriteOrLiked.HasValue)
|
|
{
|
|
userData ??= userDataManager.GetUserData(user, item);
|
|
var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
|
|
|
|
if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.IsFavorite.HasValue)
|
|
{
|
|
userData ??= userDataManager.GetUserData(user, item);
|
|
if (userData.IsFavorite != query.IsFavorite.Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.IsResumable.HasValue)
|
|
{
|
|
userData ??= userDataManager.GetUserData(user, item);
|
|
var isResumable = userData.PlaybackPositionTicks > 0;
|
|
|
|
if (isResumable != query.IsResumable.Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.IsPlayed.HasValue)
|
|
{
|
|
// Folder.IsPlayed() hits the DB per-item (N+1 queries).
|
|
// Folders are batch-filtered by the collection Filter() overload.
|
|
if (!item.IsFolder)
|
|
{
|
|
userData ??= userDataManager.GetUserData(user, item);
|
|
if (item.IsPlayed(user, userData) != query.IsPlayed.Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (query.IsLocked.HasValue)
|
|
{
|
|
var val = query.IsLocked.Value;
|
|
if (item.IsLocked != val)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.HasOverview.HasValue)
|
|
{
|
|
var filterValue = query.HasOverview.Value;
|
|
|
|
var hasValue = !string.IsNullOrEmpty(item.Overview);
|
|
|
|
if (hasValue != filterValue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.HasImdbId.HasValue)
|
|
{
|
|
var filterValue = query.HasImdbId.Value;
|
|
|
|
var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));
|
|
|
|
if (hasValue != filterValue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.HasTmdbId.HasValue)
|
|
{
|
|
var filterValue = query.HasTmdbId.Value;
|
|
|
|
var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));
|
|
|
|
if (hasValue != filterValue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.HasTvdbId.HasValue)
|
|
{
|
|
var filterValue = query.HasTvdbId.Value;
|
|
|
|
var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));
|
|
|
|
if (hasValue != filterValue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.HasParentalRating.HasValue)
|
|
{
|
|
var val = query.HasParentalRating.Value;
|
|
|
|
var rating = item.CustomRating;
|
|
|
|
if (string.IsNullOrEmpty(rating))
|
|
{
|
|
rating = item.OfficialRating;
|
|
}
|
|
|
|
if (val)
|
|
{
|
|
if (string.IsNullOrEmpty(rating))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!string.IsNullOrEmpty(rating))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply genre filter
|
|
if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Apply studio filter
|
|
if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
|
|
{
|
|
var studioItem = libraryManager.GetItemById(id);
|
|
return studioItem is not null && item.Studios.Contains(studioItem.Name, StringComparison.OrdinalIgnoreCase);
|
|
}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Apply genre filter
|
|
if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
|
|
{
|
|
var genreItem = libraryManager.GetItemById(id);
|
|
return genreItem is not null && item.Genres.Contains(genreItem.Name, StringComparison.OrdinalIgnoreCase);
|
|
}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Apply year filter
|
|
if (query.Years.Length > 0)
|
|
{
|
|
if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Apply official rating filter
|
|
if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (query.ItemIds.Length > 0)
|
|
{
|
|
if (!query.ItemIds.Contains(item.Id))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Apply tag filter
|
|
var tags = query.Tags;
|
|
if (tags.Length > 0)
|
|
{
|
|
if (!tags.Any(v => item.Tags.Contains(v, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.MinCommunityRating.HasValue)
|
|
{
|
|
var val = query.MinCommunityRating.Value;
|
|
|
|
if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.MinCriticRating.HasValue)
|
|
{
|
|
var val = query.MinCriticRating.Value;
|
|
|
|
if (!(item.CriticRating.HasValue && item.CriticRating >= val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.MinIndexNumber.HasValue)
|
|
{
|
|
var val = query.MinIndexNumber.Value;
|
|
|
|
if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.MinPremiereDate.HasValue)
|
|
{
|
|
var val = query.MinPremiereDate.Value;
|
|
|
|
if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.MaxPremiereDate.HasValue)
|
|
{
|
|
var val = query.MaxPremiereDate.Value;
|
|
|
|
if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.ParentIndexNumber.HasValue)
|
|
{
|
|
var filterValue = query.ParentIndexNumber.Value;
|
|
|
|
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (query.ExcludeItemIds.Contains(item.Id))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private IEnumerable<BaseItem> GetMediaFolders(User user)
|
|
{
|
|
if (user is null)
|
|
{
|
|
return _libraryManager.RootFolder
|
|
.Children
|
|
.OfType<Folder>()
|
|
.Where(UserView.IsEligibleForGrouping);
|
|
}
|
|
|
|
return _libraryManager.GetUserRootFolder()
|
|
.GetChildren(user, true)
|
|
.OfType<Folder>()
|
|
.Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
|
|
}
|
|
|
|
private BaseItem[] GetMediaFolders(User user, IEnumerable<CollectionType> viewTypes)
|
|
{
|
|
if (user is null)
|
|
{
|
|
return GetMediaFolders(null)
|
|
.Where(i =>
|
|
{
|
|
var folder = i as ICollectionFolder;
|
|
|
|
return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
|
|
}).ToArray();
|
|
}
|
|
|
|
return GetMediaFolders(user)
|
|
.Where(i =>
|
|
{
|
|
var folder = i as ICollectionFolder;
|
|
|
|
return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
|
|
}).ToArray();
|
|
}
|
|
|
|
private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<CollectionType> viewTypes)
|
|
{
|
|
if (parent is null || parent is UserView)
|
|
{
|
|
return GetMediaFolders(user, viewTypes);
|
|
}
|
|
|
|
return [parent];
|
|
}
|
|
|
|
private UserView GetUserViewWithName(CollectionType? type, string sortName, BaseItem parent)
|
|
{
|
|
return _userViewManager.GetUserSubView(parent.Id, type, parent.Id.ToString("N", CultureInfo.InvariantCulture), sortName);
|
|
}
|
|
|
|
private UserView GetUserView(CollectionType? type, string localizationKey, string sortName, BaseItem parent)
|
|
{
|
|
return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName);
|
|
}
|
|
|
|
public static IEnumerable<BaseItem> FilterForAdjacency(List<BaseItem> list, Guid adjacentTo)
|
|
{
|
|
var adjacentToItem = list.FirstOrDefault(i => i.Id.Equals(adjacentTo));
|
|
|
|
var index = list.IndexOf(adjacentToItem);
|
|
|
|
var previousId = Guid.Empty;
|
|
var nextId = Guid.Empty;
|
|
|
|
if (index > 0)
|
|
{
|
|
previousId = list[index - 1].Id;
|
|
}
|
|
|
|
if (index < list.Count - 1)
|
|
{
|
|
nextId = list[index + 1].Id;
|
|
}
|
|
|
|
return list.Where(i => i.Id.Equals(previousId) || i.Id.Equals(nextId) || i.Id.Equals(adjacentTo));
|
|
}
|
|
}
|
|
}
|