From 4fe3abdc0eb7d54c6d5f3e1ef411f38f44ea96db Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 26 Mar 2026 19:37:57 +0100 Subject: [PATCH] Delete file-based playlists if empty/removed --- .../Data/CleanDatabaseScheduledTask.cs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 676bb7f816..17355960c3 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -5,10 +5,12 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Database.Implementations; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Playlists; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -35,7 +37,11 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask public async Task Run(IProgress progress, CancellationToken cancellationToken) { - await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false); + var deadItemsProgress = new Progress(val => progress.Report(val * 0.8)); + await CleanDeadItems(cancellationToken, deadItemsProgress).ConfigureAwait(false); + + var playlistProgress = new Progress(val => progress.Report(80 + (val * 0.2))); + await CleanOrphanedFilePlaylistsAsync(cancellationToken, playlistProgress).ConfigureAwait(false); } private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress progress) @@ -116,4 +122,32 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask progress.Report(100); } + + private async Task CleanOrphanedFilePlaylistsAsync(CancellationToken cancellationToken, IProgress progress) + { + var playlists = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Playlist], + Recursive = true + }).OfType().ToList(); + + var numComplete = 0; + var numItems = Math.Max(playlists.Count, 1); + + foreach (var playlist in playlists) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (playlist.IsFile && !File.Exists(playlist.Path)) + { + _logger.LogInformation("Removing file-based playlist {Name} because source file {Path} no longer exists", playlist.Name, playlist.Path); + _libraryManager.DeleteItem(playlist, new DeleteOptions { DeleteFileLocation = false }); + } + + numComplete++; + progress.Report((double)numComplete / numItems * 100); + } + + progress.Report(100); + } }