aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server.Implementations/Item/ItemPersistenceService.cs4
-rw-r--r--Jellyfin.Server/Migrations/Routines/20260113120000_MigrateLinkedChildren.cs29
2 files changed, 32 insertions, 1 deletions
diff --git a/Jellyfin.Server.Implementations/Item/ItemPersistenceService.cs b/Jellyfin.Server.Implementations/Item/ItemPersistenceService.cs
index ffa5cff1f2..7c0cfe7c15 100644
--- a/Jellyfin.Server.Implementations/Item/ItemPersistenceService.cs
+++ b/Jellyfin.Server.Implementations/Item/ItemPersistenceService.cs
@@ -557,9 +557,11 @@ public class ItemPersistenceService : IItemPersistenceService
}
}
+ // Deduplicate; local (file-based) relationships take priority over linked (user-merged)
+ // ones, matching the LinkedChildren migration.
newLinkedChildren = newLinkedChildren
.GroupBy(c => c.ChildId)
- .Select(g => g.Last())
+ .Select(g => g.OrderBy(c => c.Type == LinkedChildType.LocalAlternateVersion ? 0 : 1).First())
.ToList();
var childIdsToCheck = newLinkedChildren.Select(c => c.ChildId).ToList();
diff --git a/Jellyfin.Server/Migrations/Routines/20260113120000_MigrateLinkedChildren.cs b/Jellyfin.Server/Migrations/Routines/20260113120000_MigrateLinkedChildren.cs
index 74f03f5107..c433c1d043 100644
--- a/Jellyfin.Server/Migrations/Routines/20260113120000_MigrateLinkedChildren.cs
+++ b/Jellyfin.Server/Migrations/Routines/20260113120000_MigrateLinkedChildren.cs
@@ -223,6 +223,35 @@ internal class MigrateLinkedChildren : IDatabaseMigrationRoutine
toInsert = toInsert.Where(lc => existingChildIds.Contains(lc.ChildId)).ToList();
+ // Drop linked (user-merged) entries that point at items the parent owns (local
+ // file-based alternates or extras). These stem from legacy data that merged an
+ // owned item onto its own primary and would wrongly mark server-merged groups
+ // as user-merged (splittable).
+ var linkedChildIds = toInsert
+ .Where(lc => lc.ChildType == LinkedChildType.LinkedAlternateVersion)
+ .Select(lc => lc.ChildId)
+ .Distinct()
+ .ToList();
+
+ if (linkedChildIds.Count > 0)
+ {
+ var ownerIdByChildId = context.BaseItems
+ .WhereOneOrMany(linkedChildIds, b => b.Id)
+ .Where(b => b.OwnerId.HasValue)
+ .Select(b => new { b.Id, b.OwnerId })
+ .ToDictionary(b => b.Id, b => b.OwnerId!.Value);
+
+ var removedCount = toInsert.RemoveAll(lc =>
+ lc.ChildType == LinkedChildType.LinkedAlternateVersion
+ && ownerIdByChildId.TryGetValue(lc.ChildId, out var ownerId)
+ && ownerId.Equals(lc.ParentId));
+
+ if (removedCount > 0)
+ {
+ _logger.LogInformation("Skipped {Count} LinkedAlternateVersion records pointing at items owned by their parent.", removedCount);
+ }
+ }
+
context.LinkedChildren.AddRange(toInsert);
context.SaveChanges();