using System; using System.Linq; using System.Threading; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; namespace Jellyfin.Server.Migrations.Routines; /// /// Remove duplicate playlist entries. /// #pragma warning disable CS0618 // Type or member is obsolete [JellyfinMigration("2025-04-20T19:00:00", nameof(RemoveDuplicatePlaylistChildren), "96C156A2-7A13-4B3B-A8B8-FB80C94D20C0")] internal class RemoveDuplicatePlaylistChildren : IMigrationRoutine #pragma warning restore CS0618 // Type or member is obsolete { private readonly ILibraryManager _libraryManager; private readonly IPlaylistManager _playlistManager; public RemoveDuplicatePlaylistChildren( ILibraryManager libraryManager, IPlaylistManager playlistManager) { _libraryManager = libraryManager; _playlistManager = playlistManager; } /// public void Perform() { var playlists = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.Playlist] }) .Cast() .Where(p => !p.OpenAccess || !p.OwnerUserId.Equals(Guid.Empty)) .ToArray(); if (playlists.Length > 0) { foreach (var playlist in playlists) { var linkedChildren = playlist.LinkedChildren; if (linkedChildren.Length > 0) { var newLinkedChildren = linkedChildren .Where(c => c.ItemId is null || c.ItemId.Value.Equals(Guid.Empty)) .Concat(linkedChildren .Where(c => c.ItemId.HasValue && !c.ItemId.Value.Equals(Guid.Empty)) .DistinctBy(c => c.ItemId)) .ToArray(); playlist.LinkedChildren = newLinkedChildren; playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); _playlistManager.SavePlaylistFile(playlist); } } } } }