mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-18 13:40:45 +01:00
Always apply recursive when filters are requested
This commit is contained in:
@@ -321,24 +321,21 @@ public class ItemsController : BaseJellyfinApiController
|
|||||||
recursive = true;
|
recursive = true;
|
||||||
includeItemTypes = [BaseItemKind.Playlist];
|
includeItemTypes = [BaseItemKind.Playlist];
|
||||||
}
|
}
|
||||||
else if (folder is ICollectionFolder)
|
else if (folder is ICollectionFolder && includeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
if (includeItemTypes.Length == 0)
|
includeItemTypes = collectionType switch
|
||||||
{
|
{
|
||||||
includeItemTypes = collectionType switch
|
CollectionType.boxsets => [BaseItemKind.BoxSet],
|
||||||
{
|
null => [BaseItemKind.Movie, BaseItemKind.Series],
|
||||||
CollectionType.boxsets => [BaseItemKind.BoxSet],
|
_ => []
|
||||||
null => [BaseItemKind.Movie, BaseItemKind.Series],
|
};
|
||||||
_ => []
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the client doesn't specify recursive/includeItemTypes, force the query
|
// includeItemTypes on a library lists its contents recursively rather than just its
|
||||||
// through the database path where all filters (IsHD, genres, etc.) are applied.
|
// immediate children, so default to a recursive query when the client didn't choose.
|
||||||
if (includeItemTypes.Length > 0)
|
if (folder is ICollectionFolder && includeItemTypes.Length > 0)
|
||||||
{
|
{
|
||||||
recursive ??= true;
|
recursive ??= true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is not UserRootFolder
|
if (item is not UserRootFolder
|
||||||
@@ -351,246 +348,248 @@ public class ItemsController : BaseJellyfinApiController
|
|||||||
return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
|
return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder)
|
// Build the query up front so the dispatch below can decide the path from it.
|
||||||
|
// Use search providers when searchTerm is provided. Providers return only IDs and scores;
|
||||||
|
// items are loaded server-side via folder.GetItems below, which applies user-access filtering.
|
||||||
|
Dictionary<Guid, float>? searchResultScores = null;
|
||||||
|
Guid[] itemIds = ids;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||||
{
|
{
|
||||||
// Use search providers when searchTerm is provided. Providers return only IDs and scores;
|
var searchProviderQuery = new SearchProviderQuery
|
||||||
// items are loaded server-side via folder.GetItems below, which applies user-access filtering.
|
|
||||||
Dictionary<Guid, float>? searchResultScores = null;
|
|
||||||
Guid[] itemIds = ids;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
|
||||||
{
|
{
|
||||||
var searchProviderQuery = new SearchProviderQuery
|
SearchTerm = searchTerm,
|
||||||
{
|
UserId = userId,
|
||||||
SearchTerm = searchTerm,
|
|
||||||
UserId = userId,
|
|
||||||
IncludeItemTypes = includeItemTypes,
|
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
|
||||||
MediaTypes = mediaTypes,
|
|
||||||
Limit = limit.HasValue ? limit.Value * 3 : null,
|
|
||||||
ParentId = parentId
|
|
||||||
};
|
|
||||||
|
|
||||||
var searchResults = await _searchManager.GetSearchResultsAsync(searchProviderQuery, HttpContext.RequestAborted).ConfigureAwait(false);
|
|
||||||
if (searchResults.Count > 0)
|
|
||||||
{
|
|
||||||
searchResultScores = searchResults.ToDictionary(r => r.ItemId, r => r.Score);
|
|
||||||
itemIds = ids.Length > 0
|
|
||||||
? ids.Concat(searchResultScores.Keys).Distinct().ToArray()
|
|
||||||
: searchResultScores.Keys.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
IsPlayed = isPlayed,
|
|
||||||
MediaTypes = mediaTypes,
|
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = includeItemTypes,
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = excludeItemTypes,
|
||||||
Recursive = recursive ?? false,
|
MediaTypes = mediaTypes,
|
||||||
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
|
Limit = limit.HasValue ? limit.Value * 3 : null,
|
||||||
IsFavorite = isFavorite,
|
ParentId = parentId
|
||||||
Limit = searchResultScores is null ? limit : null,
|
|
||||||
StartIndex = searchResultScores is null ? startIndex : null,
|
|
||||||
IsMissing = isMissing,
|
|
||||||
IsUnaired = isUnaired,
|
|
||||||
CollapseBoxSetItems = collapseBoxSetItems,
|
|
||||||
NameLessThan = nameLessThan,
|
|
||||||
NameStartsWith = nameStartsWith,
|
|
||||||
NameStartsWithOrGreater = nameStartsWithOrGreater,
|
|
||||||
HasImdbId = hasImdbId,
|
|
||||||
IsPlaceHolder = isPlaceHolder,
|
|
||||||
IsLocked = isLocked,
|
|
||||||
MinWidth = minWidth,
|
|
||||||
MinHeight = minHeight,
|
|
||||||
MaxWidth = maxWidth,
|
|
||||||
MaxHeight = maxHeight,
|
|
||||||
Is3D = is3D,
|
|
||||||
HasTvdbId = hasTvdbId,
|
|
||||||
HasTmdbId = hasTmdbId,
|
|
||||||
IsMovie = isMovie,
|
|
||||||
IsSeries = isSeries,
|
|
||||||
IsNews = isNews,
|
|
||||||
IsKids = isKids,
|
|
||||||
IsSports = isSports,
|
|
||||||
HasOverview = hasOverview,
|
|
||||||
HasOfficialRating = hasOfficialRating,
|
|
||||||
HasParentalRating = hasParentalRating,
|
|
||||||
HasSpecialFeature = hasSpecialFeature,
|
|
||||||
HasSubtitles = hasSubtitles,
|
|
||||||
HasThemeSong = hasThemeSong,
|
|
||||||
HasThemeVideo = hasThemeVideo,
|
|
||||||
HasTrailer = hasTrailer,
|
|
||||||
IsHD = isHd,
|
|
||||||
Is4K = is4K,
|
|
||||||
Tags = tags,
|
|
||||||
OfficialRatings = officialRatings,
|
|
||||||
Genres = genres,
|
|
||||||
ArtistIds = artistIds,
|
|
||||||
AlbumArtistIds = albumArtistIds,
|
|
||||||
ContributingArtistIds = contributingArtistIds,
|
|
||||||
GenreIds = genreIds,
|
|
||||||
StudioIds = studioIds,
|
|
||||||
Person = person,
|
|
||||||
PersonIds = personIds,
|
|
||||||
PersonTypes = personTypes,
|
|
||||||
Years = years,
|
|
||||||
ImageTypes = imageTypes,
|
|
||||||
VideoTypes = videoTypes,
|
|
||||||
AdjacentTo = adjacentTo,
|
|
||||||
ItemIds = itemIds,
|
|
||||||
MinCommunityRating = minCommunityRating,
|
|
||||||
MinCriticRating = minCriticRating,
|
|
||||||
ParentId = parentId ?? Guid.Empty,
|
|
||||||
IndexNumber = indexNumber,
|
|
||||||
ParentIndexNumber = parentIndexNumber,
|
|
||||||
EnableTotalRecordCount = enableTotalRecordCount,
|
|
||||||
ExcludeItemIds = excludeItemIds,
|
|
||||||
DtoOptions = dtoOptions,
|
|
||||||
SearchTerm = searchResultScores is null ? searchTerm : null,
|
|
||||||
MinDateLastSaved = minDateLastSaved?.ToUniversalTime(),
|
|
||||||
MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(),
|
|
||||||
MinPremiereDate = minPremiereDate?.ToUniversalTime(),
|
|
||||||
MaxPremiereDate = maxPremiereDate?.ToUniversalTime(),
|
|
||||||
AudioLanguages = audioLanguages,
|
|
||||||
SubtitleLanguages = subtitleLanguages,
|
|
||||||
LinkedChildAncestorIds = linkedChildAncestorIds,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm))
|
var searchResults = await _searchManager.GetSearchResultsAsync(searchProviderQuery, HttpContext.RequestAborted).ConfigureAwait(false);
|
||||||
|
if (searchResults.Count > 0)
|
||||||
{
|
{
|
||||||
query.CollapseBoxSetItems = false;
|
searchResultScores = searchResults.ToDictionary(r => r.ItemId, r => r.Score);
|
||||||
|
itemIds = ids.Length > 0
|
||||||
|
? ids.Concat(searchResultScores.Keys).Distinct().ToArray()
|
||||||
|
: searchResultScores.Keys.ToArray();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (query.SubtitleLanguages.Count > 0 && query.HasSubtitles.HasValue)
|
var query = new InternalItemsQuery(user)
|
||||||
|
{
|
||||||
|
IsPlayed = isPlayed,
|
||||||
|
MediaTypes = mediaTypes,
|
||||||
|
IncludeItemTypes = includeItemTypes,
|
||||||
|
ExcludeItemTypes = excludeItemTypes,
|
||||||
|
Recursive = recursive ?? false,
|
||||||
|
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
|
||||||
|
IsFavorite = isFavorite,
|
||||||
|
Limit = searchResultScores is null ? limit : null,
|
||||||
|
StartIndex = searchResultScores is null ? startIndex : null,
|
||||||
|
IsMissing = isMissing,
|
||||||
|
IsUnaired = isUnaired,
|
||||||
|
CollapseBoxSetItems = collapseBoxSetItems,
|
||||||
|
NameLessThan = nameLessThan,
|
||||||
|
NameStartsWith = nameStartsWith,
|
||||||
|
NameStartsWithOrGreater = nameStartsWithOrGreater,
|
||||||
|
HasImdbId = hasImdbId,
|
||||||
|
IsPlaceHolder = isPlaceHolder,
|
||||||
|
IsLocked = isLocked,
|
||||||
|
MinWidth = minWidth,
|
||||||
|
MinHeight = minHeight,
|
||||||
|
MaxWidth = maxWidth,
|
||||||
|
MaxHeight = maxHeight,
|
||||||
|
Is3D = is3D,
|
||||||
|
HasTvdbId = hasTvdbId,
|
||||||
|
HasTmdbId = hasTmdbId,
|
||||||
|
IsMovie = isMovie,
|
||||||
|
IsSeries = isSeries,
|
||||||
|
IsNews = isNews,
|
||||||
|
IsKids = isKids,
|
||||||
|
IsSports = isSports,
|
||||||
|
HasOverview = hasOverview,
|
||||||
|
HasOfficialRating = hasOfficialRating,
|
||||||
|
HasParentalRating = hasParentalRating,
|
||||||
|
HasSpecialFeature = hasSpecialFeature,
|
||||||
|
HasSubtitles = hasSubtitles,
|
||||||
|
HasThemeSong = hasThemeSong,
|
||||||
|
HasThemeVideo = hasThemeVideo,
|
||||||
|
HasTrailer = hasTrailer,
|
||||||
|
IsHD = isHd,
|
||||||
|
Is4K = is4K,
|
||||||
|
Tags = tags,
|
||||||
|
OfficialRatings = officialRatings,
|
||||||
|
Genres = genres,
|
||||||
|
ArtistIds = artistIds,
|
||||||
|
AlbumArtistIds = albumArtistIds,
|
||||||
|
ContributingArtistIds = contributingArtistIds,
|
||||||
|
GenreIds = genreIds,
|
||||||
|
StudioIds = studioIds,
|
||||||
|
Person = person,
|
||||||
|
PersonIds = personIds,
|
||||||
|
PersonTypes = personTypes,
|
||||||
|
Years = years,
|
||||||
|
ImageTypes = imageTypes,
|
||||||
|
VideoTypes = videoTypes,
|
||||||
|
AdjacentTo = adjacentTo,
|
||||||
|
ItemIds = itemIds,
|
||||||
|
MinCommunityRating = minCommunityRating,
|
||||||
|
MinCriticRating = minCriticRating,
|
||||||
|
ParentId = parentId ?? Guid.Empty,
|
||||||
|
IndexNumber = indexNumber,
|
||||||
|
ParentIndexNumber = parentIndexNumber,
|
||||||
|
EnableTotalRecordCount = enableTotalRecordCount,
|
||||||
|
ExcludeItemIds = excludeItemIds,
|
||||||
|
DtoOptions = dtoOptions,
|
||||||
|
SearchTerm = searchResultScores is null ? searchTerm : null,
|
||||||
|
MinDateLastSaved = minDateLastSaved?.ToUniversalTime(),
|
||||||
|
MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(),
|
||||||
|
MinPremiereDate = minPremiereDate?.ToUniversalTime(),
|
||||||
|
MaxPremiereDate = maxPremiereDate?.ToUniversalTime(),
|
||||||
|
AudioLanguages = audioLanguages,
|
||||||
|
SubtitleLanguages = subtitleLanguages,
|
||||||
|
LinkedChildAncestorIds = linkedChildAncestorIds,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm))
|
||||||
|
{
|
||||||
|
query.CollapseBoxSetItems = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.SubtitleLanguages.Count > 0 && query.HasSubtitles.HasValue)
|
||||||
|
{
|
||||||
|
if (query.HasSubtitles.Value)
|
||||||
{
|
{
|
||||||
if (query.HasSubtitles.Value)
|
// if we check for specific subtitles we don't need a separate check for subtitle existence
|
||||||
|
query.HasSubtitles = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we search for items without subtitles, we don't need to check for subtitles of a specific language
|
||||||
|
query.SubtitleLanguages = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for filter values that rely on media streams, we need to include alternative and linked versions
|
||||||
|
if (query.HasSubtitles.HasValue
|
||||||
|
|| query.SubtitleLanguages.Count > 0
|
||||||
|
|| query.AudioLanguages.Count > 0
|
||||||
|
|| query.Is3D.HasValue
|
||||||
|
|| query.IsHD.HasValue
|
||||||
|
|| query.Is4K.HasValue
|
||||||
|
|| query.VideoTypes.Length > 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
query.IncludeOwnedItems = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.ApplyFilters(filters);
|
||||||
|
|
||||||
|
// Filter by Series Status
|
||||||
|
if (seriesStatus.Length != 0)
|
||||||
|
{
|
||||||
|
query.SeriesStatuses = seriesStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude Blocked Unrated Items
|
||||||
|
var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems);
|
||||||
|
if (blockedUnratedItems is not null)
|
||||||
|
{
|
||||||
|
query.BlockUnratedItems = blockedUnratedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludeLocationTypes
|
||||||
|
if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
|
||||||
|
{
|
||||||
|
query.IsVirtualItem = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationTypes.Length > 0 && locationTypes.Length < 4)
|
||||||
|
{
|
||||||
|
query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min official rating
|
||||||
|
if (!string.IsNullOrWhiteSpace(minOfficialRating))
|
||||||
|
{
|
||||||
|
query.MinParentalRating = _localization.GetRatingScore(minOfficialRating);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max official rating
|
||||||
|
if (!string.IsNullOrWhiteSpace(maxOfficialRating))
|
||||||
|
{
|
||||||
|
query.MaxParentalRating = _localization.GetRatingScore(maxOfficialRating);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artists
|
||||||
|
if (artists.Length != 0)
|
||||||
|
{
|
||||||
|
query.ArtistIds = artists.Select(i =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// if we check for specific subtitles we don't need a separate check for subtitle existence
|
return _libraryManager.GetArtist(i, new DtoOptions(false));
|
||||||
query.HasSubtitles = null;
|
|
||||||
}
|
}
|
||||||
else
|
catch
|
||||||
{
|
{
|
||||||
// if we search for items without subtitles, we don't need to check for subtitles of a specific language
|
return null;
|
||||||
query.SubtitleLanguages = [];
|
|
||||||
}
|
}
|
||||||
}
|
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
// for filter values that rely on media streams, we need to include alternative and linked versions
|
// ExcludeArtistIds
|
||||||
if (query.HasSubtitles.HasValue
|
if (excludeArtistIds.Length != 0)
|
||||||
|| query.SubtitleLanguages.Count > 0
|
{
|
||||||
|| query.AudioLanguages.Count > 0
|
query.ExcludeArtistIds = excludeArtistIds;
|
||||||
|| query.Is3D.HasValue
|
}
|
||||||
|| query.IsHD.HasValue
|
|
||||||
|| query.Is4K.HasValue
|
if (albumIds.Length != 0)
|
||||||
|| query.VideoTypes.Length > 0
|
{
|
||||||
)
|
query.AlbumIds = albumIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Albums
|
||||||
|
if (albums.Length != 0)
|
||||||
|
{
|
||||||
|
query.AlbumIds = albums.SelectMany(i =>
|
||||||
{
|
{
|
||||||
query.IncludeOwnedItems = true;
|
return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.MusicAlbum], Name = i, Limit = 1 });
|
||||||
}
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
query.ApplyFilters(filters);
|
// Studios
|
||||||
|
if (studios.Length != 0)
|
||||||
// Filter by Series Status
|
{
|
||||||
if (seriesStatus.Length != 0)
|
query.StudioIds = studios.Select(i =>
|
||||||
{
|
{
|
||||||
query.SeriesStatuses = seriesStatus;
|
try
|
||||||
}
|
|
||||||
|
|
||||||
// Exclude Blocked Unrated Items
|
|
||||||
var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems);
|
|
||||||
if (blockedUnratedItems is not null)
|
|
||||||
{
|
|
||||||
query.BlockUnratedItems = blockedUnratedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExcludeLocationTypes
|
|
||||||
if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
|
|
||||||
{
|
|
||||||
query.IsVirtualItem = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locationTypes.Length > 0 && locationTypes.Length < 4)
|
|
||||||
{
|
|
||||||
query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min official rating
|
|
||||||
if (!string.IsNullOrWhiteSpace(minOfficialRating))
|
|
||||||
{
|
|
||||||
query.MinParentalRating = _localization.GetRatingScore(minOfficialRating);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max official rating
|
|
||||||
if (!string.IsNullOrWhiteSpace(maxOfficialRating))
|
|
||||||
{
|
|
||||||
query.MaxParentalRating = _localization.GetRatingScore(maxOfficialRating);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Artists
|
|
||||||
if (artists.Length != 0)
|
|
||||||
{
|
|
||||||
query.ArtistIds = artists.Select(i =>
|
|
||||||
{
|
{
|
||||||
try
|
return _libraryManager.GetStudio(i);
|
||||||
{
|
|
||||||
return _libraryManager.GetArtist(i, new DtoOptions(false));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExcludeArtistIds
|
|
||||||
if (excludeArtistIds.Length != 0)
|
|
||||||
{
|
|
||||||
query.ExcludeArtistIds = excludeArtistIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (albumIds.Length != 0)
|
|
||||||
{
|
|
||||||
query.AlbumIds = albumIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Albums
|
|
||||||
if (albums.Length != 0)
|
|
||||||
{
|
|
||||||
query.AlbumIds = albums.SelectMany(i =>
|
|
||||||
{
|
|
||||||
return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.MusicAlbum], Name = i, Limit = 1 });
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Studios
|
|
||||||
if (studios.Length != 0)
|
|
||||||
{
|
|
||||||
query.StudioIds = studios.Select(i =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _libraryManager.GetStudio(i);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply default sorting if none requested
|
|
||||||
if (query.OrderBy.Count == 0)
|
|
||||||
{
|
|
||||||
// Albums by artist
|
|
||||||
if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.MusicAlbum)
|
|
||||||
{
|
|
||||||
query.OrderBy = [(ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Ascending)];
|
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply default sorting if none requested
|
||||||
|
if (query.OrderBy.Count == 0)
|
||||||
|
{
|
||||||
|
// Albums by artist
|
||||||
|
if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.MusicAlbum)
|
||||||
|
{
|
||||||
|
query.OrderBy = [(ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Ascending)];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query.Parent = null;
|
query.Parent = null;
|
||||||
|
|
||||||
|
// At the user root an unfiltered, non-recursive request is a plain listing of the user's libraries
|
||||||
|
if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder || query.HasFilters)
|
||||||
|
{
|
||||||
// folder.GetItems applies user-access filtering via the InternalItemsQuery's User.
|
// folder.GetItems applies user-access filtering via the InternalItemsQuery's User.
|
||||||
result = folder.GetItems(query);
|
result = folder.GetItems(query);
|
||||||
if (searchResultScores is not null && searchResultScores.Count > 0)
|
if (searchResultScores is not null && searchResultScores.Count > 0)
|
||||||
|
|||||||
@@ -72,6 +72,102 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the query carries any criteria that narrows the
|
||||||
|
/// result set, as opposed to user context, pagination, sorting or DTO options.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFilters =>
|
||||||
|
IncludeItemTypes.Length > 0
|
||||||
|
|| ExcludeItemTypes.Length > 0
|
||||||
|
|| Genres.Count > 0
|
||||||
|
|| GenreIds.Count > 0
|
||||||
|
|| Years.Length > 0
|
||||||
|
|| Tags.Length > 0
|
||||||
|
|| ExcludeTags.Length > 0
|
||||||
|
|| OfficialRatings.Length > 0
|
||||||
|
|| StudioIds.Length > 0
|
||||||
|
|| ArtistIds.Length > 0
|
||||||
|
|| AlbumArtistIds.Length > 0
|
||||||
|
|| ContributingArtistIds.Length > 0
|
||||||
|
|| ExcludeArtistIds.Length > 0
|
||||||
|
|| AlbumIds.Length > 0
|
||||||
|
|| PersonIds.Length > 0
|
||||||
|
|| PersonTypes.Length > 0
|
||||||
|
|| MediaTypes.Length > 0
|
||||||
|
|| VideoTypes.Length > 0
|
||||||
|
|| ImageTypes.Length > 0
|
||||||
|
|| SeriesStatuses.Length > 0
|
||||||
|
|| ItemIds.Length > 0
|
||||||
|
|| ExcludeItemIds.Length > 0
|
||||||
|
|| AudioLanguages.Count > 0
|
||||||
|
|| SubtitleLanguages.Count > 0
|
||||||
|
|| LinkedChildAncestorIds.Length > 0
|
||||||
|
|| AncestorIds.Length > 0
|
||||||
|
|| IsFavorite.HasValue
|
||||||
|
|| IsFavoriteOrLiked.HasValue
|
||||||
|
|| IsLiked.HasValue
|
||||||
|
|| IsPlayed.HasValue
|
||||||
|
|| IsResumable.HasValue
|
||||||
|
|| IsFolder.HasValue
|
||||||
|
|| IsMissing.HasValue
|
||||||
|
|| IsUnaired.HasValue
|
||||||
|
|| IsSpecialSeason.HasValue
|
||||||
|
|| Is3D.HasValue
|
||||||
|
|| IsHD.HasValue
|
||||||
|
|| Is4K.HasValue
|
||||||
|
|| IsLocked.HasValue
|
||||||
|
|| IsPlaceHolder.HasValue
|
||||||
|
|| IsMovie.HasValue
|
||||||
|
|| IsSports.HasValue
|
||||||
|
|| IsKids.HasValue
|
||||||
|
|| IsNews.HasValue
|
||||||
|
|| IsSeries.HasValue
|
||||||
|
|| IsAiring.HasValue
|
||||||
|
|| IsVirtualItem.HasValue
|
||||||
|
|| HasImdbId.HasValue
|
||||||
|
|| HasTmdbId.HasValue
|
||||||
|
|| HasTvdbId.HasValue
|
||||||
|
|| HasOverview.HasValue
|
||||||
|
|| HasOfficialRating.HasValue
|
||||||
|
|| HasParentalRating.HasValue
|
||||||
|
|| HasThemeSong.HasValue
|
||||||
|
|| HasThemeVideo.HasValue
|
||||||
|
|| HasSubtitles.HasValue
|
||||||
|
|| HasSpecialFeature.HasValue
|
||||||
|
|| HasTrailer.HasValue
|
||||||
|
|| HasChapterImages.HasValue
|
||||||
|
|| MinCriticRating.HasValue
|
||||||
|
|| MinCommunityRating.HasValue
|
||||||
|
|| MinParentalRating is not null
|
||||||
|
|| MinIndexNumber.HasValue
|
||||||
|
|| MinParentAndIndexNumber.HasValue
|
||||||
|
|| IndexNumber.HasValue
|
||||||
|
|| ParentIndexNumber.HasValue
|
||||||
|
|| AiredDuringSeason.HasValue
|
||||||
|
|| MinWidth.HasValue
|
||||||
|
|| MinHeight.HasValue
|
||||||
|
|| MaxWidth.HasValue
|
||||||
|
|| MaxHeight.HasValue
|
||||||
|
|| MinPremiereDate.HasValue
|
||||||
|
|| MaxPremiereDate.HasValue
|
||||||
|
|| MinStartDate.HasValue
|
||||||
|
|| MaxStartDate.HasValue
|
||||||
|
|| MinEndDate.HasValue
|
||||||
|
|| MaxEndDate.HasValue
|
||||||
|
|| MinDateCreated.HasValue
|
||||||
|
|| MinDateLastSaved.HasValue
|
||||||
|
|| MinDateLastSavedForUser.HasValue
|
||||||
|
|| AdjacentTo.HasValue
|
||||||
|
|| !string.IsNullOrEmpty(NameStartsWith)
|
||||||
|
|| !string.IsNullOrEmpty(NameStartsWithOrGreater)
|
||||||
|
|| !string.IsNullOrEmpty(NameLessThan)
|
||||||
|
|| !string.IsNullOrEmpty(NameContains)
|
||||||
|
|| !string.IsNullOrEmpty(MinSortName)
|
||||||
|
|| !string.IsNullOrEmpty(Name)
|
||||||
|
|| !string.IsNullOrEmpty(Person)
|
||||||
|
|| !string.IsNullOrEmpty(SearchTerm)
|
||||||
|
|| !string.IsNullOrEmpty(Path);
|
||||||
|
|
||||||
public bool Recursive { get; set; }
|
public bool Recursive { get; set; }
|
||||||
|
|
||||||
public int? StartIndex { get; set; }
|
public int? StartIndex { get; set; }
|
||||||
|
|||||||
@@ -69,8 +69,14 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
if (query.Recursive)
|
// The user root holds no items of its own - a plain listing returns the user's
|
||||||
|
// views. But a request carrying any filter is a search across the libraries, so
|
||||||
|
// resolve it through the recursive query path even when Recursive wasn't set;
|
||||||
|
// otherwise the filters would be silently dropped. Recursive is set so the
|
||||||
|
// downstream query (ancestor/top-parent scoping) treats it as a recursive search.
|
||||||
|
if (query.Recursive || query.HasFilters)
|
||||||
{
|
{
|
||||||
|
query.Recursive = true;
|
||||||
return QueryRecursive(query);
|
return QueryRecursive(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user