mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-10-11 22:50:00 +00:00
Backport pull request #18039 from jellyfin/release-12.z
Stop resolving items with every field where only stored columns are read
Original-merge: 408e5e01b1
Merged-by: crobibero <[email protected]>
Backported-by: Cody Robibero <[email protected]>
This commit is contained in:
committed by
Cody Robibero
parent
8cd2293ff9
commit
2151b992f9
@@ -9,6 +9,7 @@ using Jellyfin.Database.Implementations.Entities;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Collections;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
@@ -235,7 +236,7 @@ namespace Emby.Server.Implementations.Collections
|
||||
|
||||
List<BaseItem>? itemList = null;
|
||||
|
||||
var linkedChildrenList = collection.GetLinkedChildren();
|
||||
var linkedChildrenList = collection.GetLinkedChildren(DtoOptions.StoredColumnsOnly);
|
||||
var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList();
|
||||
|
||||
foreach (var id in ids)
|
||||
|
||||
@@ -60,26 +60,10 @@ namespace Emby.Server.Implementations.Library
|
||||
var folderViewType = collectionFolder?.CollectionType;
|
||||
|
||||
// Playlist and BoxSet libraries require special handling because the folder only references linked items
|
||||
if (folderViewType == CollectionType.boxsets)
|
||||
if ((folderViewType == CollectionType.playlists || folderViewType == CollectionType.boxsets)
|
||||
&& !HasVisibleChild(folder, user))
|
||||
{
|
||||
// Only the existence of one visible box set matters here, so probe the children
|
||||
// lazily and stop at the first hit.
|
||||
if (!folder.Children.Any(item => item.IsVisible(user)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (folderViewType == CollectionType.playlists)
|
||||
{
|
||||
var items = folder.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
ParentId = folder.ParentId
|
||||
});
|
||||
|
||||
if (!items.Any(item => item.IsVisible(user)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UserView.IsUserSpecific(folder))
|
||||
@@ -168,6 +152,32 @@ namespace Emby.Server.Implementations.Library
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private bool HasVisibleChild(Folder folder, User user)
|
||||
{
|
||||
// Folder.Children answers this too, but a collection folder delegates it to its physical
|
||||
// folders, which resolve and then hold on to every child with every field.
|
||||
var parentIds = folder is CollectionFolder collectionFolder && collectionFolder.PhysicalFolderIds.Length > 0
|
||||
? collectionFolder.PhysicalFolderIds
|
||||
: [folder.Id];
|
||||
|
||||
foreach (var parentId in parentIds)
|
||||
{
|
||||
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
ParentId = parentId,
|
||||
GroupByPresentationUniqueKey = false,
|
||||
DtoOptions = DtoOptions.StoredColumnsOnly
|
||||
});
|
||||
|
||||
if (items.Any(item => item.IsVisible(user)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public UserView GetUserSubViewWithName(string name, Guid parentId, CollectionType? type, string sortName)
|
||||
{
|
||||
var uniqueId = parentId + "subview" + type;
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace Emby.Server.Implementations.Playlists
|
||||
if (extension.Equals(".wpl", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var playlist = new WplPlaylist();
|
||||
foreach (var child in item.GetLinkedChildren())
|
||||
foreach (var child in item.GetLinkedChildren(DtoOptions.StoredColumnsOnly))
|
||||
{
|
||||
var entry = new WplPlaylistEntry()
|
||||
{
|
||||
@@ -404,7 +404,7 @@ namespace Emby.Server.Implementations.Playlists
|
||||
else if (extension.Equals(".zpl", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var playlist = new ZplPlaylist();
|
||||
foreach (var child in item.GetLinkedChildren())
|
||||
foreach (var child in item.GetLinkedChildren(DtoOptions.StoredColumnsOnly))
|
||||
{
|
||||
var entry = new ZplPlaylistEntry()
|
||||
{
|
||||
@@ -440,7 +440,7 @@ namespace Emby.Server.Implementations.Playlists
|
||||
{
|
||||
IsExtended = true
|
||||
};
|
||||
foreach (var child in item.GetLinkedChildren())
|
||||
foreach (var child in item.GetLinkedChildren(DtoOptions.StoredColumnsOnly))
|
||||
{
|
||||
var entry = new M3uPlaylistEntry()
|
||||
{
|
||||
@@ -472,7 +472,7 @@ namespace Emby.Server.Implementations.Playlists
|
||||
IsExtended = true
|
||||
};
|
||||
|
||||
foreach (var child in item.GetLinkedChildren())
|
||||
foreach (var child in item.GetLinkedChildren(DtoOptions.StoredColumnsOnly))
|
||||
{
|
||||
var entry = new M3uPlaylistEntry()
|
||||
{
|
||||
@@ -500,7 +500,7 @@ namespace Emby.Server.Implementations.Playlists
|
||||
else if (extension.Equals(".pls", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var playlist = new PlsPlaylist();
|
||||
foreach (var child in item.GetLinkedChildren())
|
||||
foreach (var child in item.GetLinkedChildren(DtoOptions.StoredColumnsOnly))
|
||||
{
|
||||
var entry = new PlsPlaylistEntry()
|
||||
{
|
||||
|
||||
@@ -46,6 +46,20 @@ namespace MediaBrowser.Controller.Dto
|
||||
ImageTypes = AllImageTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets options that populate nothing beyond the item's own stored columns.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each enabled field group is a collection the item query left-joins, so the rows returned are
|
||||
/// the product of the item's provider, image and user data counts. Never use this for items that
|
||||
/// get saved back: saving rewrites the owned rows from what the instance holds.
|
||||
/// </remarks>
|
||||
public static DtoOptions StoredColumnsOnly => new(false)
|
||||
{
|
||||
EnableImages = false,
|
||||
EnableUserData = false
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fields to populate on the DTO.
|
||||
/// </summary>
|
||||
|
||||
@@ -1687,7 +1687,17 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
public List<BaseItem> GetLinkedChildren()
|
||||
{
|
||||
var resolved = ResolveLinkedChildren(LinkedChildren);
|
||||
return GetLinkedChildren(new DtoOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the linked children, populating only what <paramref name="options"/> asks for.
|
||||
/// </summary>
|
||||
/// <param name="options">Fields to populate on the resolved children.</param>
|
||||
/// <returns>The resolved children.</returns>
|
||||
public List<BaseItem> GetLinkedChildren(DtoOptions options)
|
||||
{
|
||||
var resolved = ResolveLinkedChildren(LinkedChildren, options);
|
||||
var list = new List<BaseItem>(resolved.Count);
|
||||
foreach (var (_, item) in resolved)
|
||||
{
|
||||
@@ -1804,8 +1814,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// path (legacy path-based resolution).
|
||||
/// </summary>
|
||||
/// <param name="linkedChildren">Linked children to resolve.</param>
|
||||
/// <param name="options">Fields to populate on the resolved items; all fields when null.</param>
|
||||
/// <returns>Each input entry paired with its resolved item; entries that fail to resolve are dropped.</returns>
|
||||
private List<(LinkedChild Info, BaseItem Item)> ResolveLinkedChildren(IReadOnlyList<LinkedChild> linkedChildren)
|
||||
private List<(LinkedChild Info, BaseItem Item)> ResolveLinkedChildren(IReadOnlyList<LinkedChild> linkedChildren, DtoOptions options = null)
|
||||
{
|
||||
var resolved = new List<(LinkedChild Info, BaseItem Item)>(linkedChildren.Count);
|
||||
if (linkedChildren.Count == 0)
|
||||
@@ -1827,7 +1838,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
var batched = LibraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
ItemIds = [.. idsToBatch]
|
||||
ItemIds = [.. idsToBatch],
|
||||
DtoOptions = options ?? new DtoOptions()
|
||||
});
|
||||
byId = new Dictionary<Guid, BaseItem>(batched.Count);
|
||||
foreach (var item in batched)
|
||||
|
||||
@@ -11,6 +11,7 @@ using Jellyfin.Data;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Database.Implementations.Entities;
|
||||
using Jellyfin.Database.Implementations.Enums;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
||||
@@ -89,7 +90,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
return base.GetNonCachedChildren(directoryService);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<BaseItem>();
|
||||
return [];
|
||||
}
|
||||
|
||||
protected override IReadOnlyList<BaseItem> LoadChildren()
|
||||
@@ -172,7 +173,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
var libraryFolderIds = LibraryFolderIds;
|
||||
if (libraryFolderIds is null)
|
||||
{
|
||||
linkedItems = GetLinkedChildren();
|
||||
linkedItems = GetLinkedChildren(DtoOptions.StoredColumnsOnly);
|
||||
libraryFolderIds = GetLibraryFolderIds(linkedItems);
|
||||
}
|
||||
|
||||
@@ -191,7 +192,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
// If user has parental controls, hide the BoxSet when all children are restricted
|
||||
if (user.MaxParentalRatingScore.HasValue)
|
||||
{
|
||||
linkedItems ??= GetLinkedChildren();
|
||||
linkedItems ??= GetLinkedChildren(DtoOptions.StoredColumnsOnly);
|
||||
if (linkedItems.Count > 0 && linkedItems.All(child => !child.IsParentalAllowed(user, true)))
|
||||
{
|
||||
return false;
|
||||
@@ -248,7 +249,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
|
||||
public Guid[] GetLibraryFolderIds()
|
||||
{
|
||||
return GetLibraryFolderIds(GetLinkedChildren());
|
||||
return GetLibraryFolderIds(GetLinkedChildren(DtoOptions.StoredColumnsOnly));
|
||||
}
|
||||
|
||||
private Guid[] GetLibraryFolderIds(IEnumerable<BaseItem> linkedChildren)
|
||||
@@ -280,13 +281,13 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
{
|
||||
expandedFolders.Add(item.Id);
|
||||
|
||||
return FlattenItems(boxset.GetLinkedChildren(), expandedFolders);
|
||||
return FlattenItems(boxset.GetLinkedChildren(DtoOptions.StoredColumnsOnly), expandedFolders);
|
||||
}
|
||||
|
||||
return Array.Empty<BaseItem>();
|
||||
return [];
|
||||
}
|
||||
|
||||
return new[] { item };
|
||||
return [item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
@@ -500,7 +501,8 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||
{
|
||||
var batched = LibraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
ItemIds = [.. idsToResolve]
|
||||
ItemIds = [.. idsToResolve],
|
||||
DtoOptions = DtoOptions.StoredColumnsOnly
|
||||
});
|
||||
pathById = new Dictionary<Guid, string?>(batched.Count);
|
||||
foreach (var batchedItem in batched)
|
||||
|
||||
Reference in New Issue
Block a user