aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateLinkedChildren.cs36
1 files changed, 31 insertions, 5 deletions
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateLinkedChildren.cs b/Jellyfin.Server/Migrations/Routines/MigrateLinkedChildren.cs
index b00f53bbe7..d43cd785cd 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateLinkedChildren.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateLinkedChildren.cs
@@ -278,7 +278,7 @@ internal class MigrateLinkedChildren : IDatabaseMigrationRoutine
{
_logger.LogInformation("Starting cleanup of orphaned alternate version BaseItems...");
- // Find BaseItems that have PrimaryVersionId set (they were alternate versions)
+ // Check 1: Find BaseItems that have PrimaryVersionId set (they were alternate versions)
// but no LinkedChild entry references them — meaning they're orphaned.
// This happens when a version file is renamed: the old BaseItem remains
// in the DB with a stale PrimaryVersionId but nothing links to it anymore.
@@ -288,15 +288,41 @@ internal class MigrateLinkedChildren : IDatabaseMigrationRoutine
.Select(b => b.Id)
.ToList();
- if (orphanedVersionIds.Count == 0)
+ _logger.LogInformation("Found {Count} orphaned alternate versions with stale PrimaryVersionId.", orphanedVersionIds.Count);
+
+ // Check 2: Find generic Video items that share a parent folder with a Movie/Episode
+ // but are not linked as alternate versions. These are orphaned entries from renamed
+ // alternate version files where PrimaryVersionId was already cleared by the old server.
+ var specificVideoTypes = new[]
+ {
+ "MediaBrowser.Controller.Entities.Movies.Movie",
+ "MediaBrowser.Controller.Entities.TV.Episode"
+ };
+
+ var orphanedVideoIds = context.BaseItems
+ .Where(b => b.Type == "MediaBrowser.Controller.Entities.Video")
+ .Where(b => b.ExtraType == null && !b.OwnerId.HasValue)
+ .Where(b => !context.LinkedChildren.Any(lc => lc.ChildId.Equals(b.Id)))
+ .Where(b => b.ParentId.HasValue
+ && context.BaseItems.Any(sibling =>
+ sibling.ParentId.Equals(b.ParentId)
+ && specificVideoTypes.Contains(sibling.Type)))
+ .Select(b => b.Id)
+ .ToList();
+
+ _logger.LogInformation("Found {Count} orphaned generic Video items in movie/episode folders.", orphanedVideoIds.Count);
+
+ var allOrphanedIds = orphanedVersionIds.Union(orphanedVideoIds).ToList();
+
+ if (allOrphanedIds.Count == 0)
{
_logger.LogInformation("No orphaned alternate version BaseItems found.");
return;
}
- _logger.LogInformation("Found {Count} orphaned alternate version BaseItems to remove.", orphanedVersionIds.Count);
+ _logger.LogInformation("Removing {Count} total orphaned alternate version BaseItems.", allOrphanedIds.Count);
- foreach (var id in orphanedVersionIds)
+ foreach (var id in allOrphanedIds)
{
var item = _libraryManager.GetItemById(id);
if (item is not null)
@@ -305,7 +331,7 @@ internal class MigrateLinkedChildren : IDatabaseMigrationRoutine
}
}
- _logger.LogInformation("Removed {Count} orphaned alternate version BaseItems.", orphanedVersionIds.Count);
+ _logger.LogInformation("Removed {Count} orphaned alternate version BaseItems.", allOrphanedIds.Count);
}
private void CleanupOrphanedLinkedChildren(JellyfinDbContext context)