diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2026-02-07 21:17:01 +0100 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2026-02-07 21:17:01 +0100 |
| commit | bb6c3b4eecee46a0a6222ffe17657cabc7da97f4 (patch) | |
| tree | 2cb88aa14642d203c7a98e6beabc30b3686be6c5 /src | |
| parent | 2420ece5fe47c3d990641add1648b9c220215a62 (diff) | |
Fix BoxSet collapse handling and deletion
Diffstat (limited to 'src')
| -rw-r--r-- | src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs b/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs index e6fa6ca458..3bc36dca7a 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs @@ -31,6 +31,25 @@ public static class DescendantQueryHelper } /// <summary> + /// Gets a queryable of all owned descendant IDs for a parent item. + /// Traverses only AncestorIds (hierarchical ownership), NOT LinkedChildren (associations). + /// Use this for deletion to avoid destroying items that are merely linked (e.g. movies in a BoxSet). + /// </summary> + /// <param name="context">Database context.</param> + /// <param name="parentId">Parent item ID.</param> + /// <returns>Queryable of owned descendant item IDs.</returns> + public static IQueryable<Guid> GetOwnedDescendantIds(JellyfinDbContext context, Guid parentId) + { + ArgumentNullException.ThrowIfNull(context); + + var descendants = TraverseHierarchyDownOwned(context, [parentId]); + + descendants.Remove(parentId); + + return descendants.AsQueryable(); + } + + /// <summary> /// Gets a queryable of all folder IDs that have any descendant matching the specified criteria. /// Can be used in LINQ .Contains() expressions. /// </summary> @@ -125,6 +144,47 @@ public static class DescendantQueryHelper } /// <summary> + /// Traverses DOWN the hierarchy using only AncestorIds (ownership), not LinkedChildren. + /// </summary> + private static HashSet<Guid> TraverseHierarchyDownOwned(JellyfinDbContext context, ICollection<Guid> startIds) + { + var visited = new HashSet<Guid>(startIds); + var folderStack = new HashSet<Guid>(startIds); + + while (folderStack.Count != 0) + { + var currentFolders = folderStack.ToArray(); + folderStack.Clear(); + + var directChildren = context.AncestorIds + .WhereOneOrMany(currentFolders, e => e.ParentItemId) + .Select(e => e.ItemId) + .ToArray(); + + if (directChildren.Length == 0) + { + break; + } + + var childFolders = context.BaseItems + .WhereOneOrMany(directChildren, e => e.Id) + .Where(e => e.IsFolder) + .Select(e => e.Id) + .ToHashSet(); + + foreach (var childId in directChildren) + { + if (visited.Add(childId) && childFolders.Contains(childId)) + { + folderStack.Add(childId); + } + } + } + + return visited; + } + + /// <summary> /// Traverses UP the hierarchy from items to find all ancestor folders. /// </summary> private static HashSet<Guid> TraverseHierarchyUp(JellyfinDbContext context, ICollection<Guid> startIds) |
