From 204146a3a504c4cc417c9eb68c32271e3f585352 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 13 Apr 2024 14:48:40 +0800 Subject: [PATCH 1/5] fix: mark UserRoot as non-root when performing removal Fixes #11269 Signed-off-by: gnattu --- Emby.Server.Implementations/Library/LibraryManager.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bb5cc746e9..baed887e3e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1033,7 +1033,7 @@ namespace Emby.Server.Implementations.Library } } - private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken) + private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken, bool removeRoot = false) { await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); @@ -1046,11 +1046,15 @@ namespace Emby.Server.Implementations.Library await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); + // HACK: override IsRootHere for libraries to be removed + if (removeRoot) GetUserRootFolder().IsRoot = false; await GetUserRootFolder().ValidateChildren( new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); + // HACK: restore IsRoot here after validation + if (removeRoot) GetUserRootFolder().IsRoot = true; // Quickly scan CollectionFolders for changes foreach (var folder in GetUserRootFolder().Children.OfType()) @@ -3118,7 +3122,7 @@ namespace Emby.Server.Implementations.Library if (refreshLibrary) { - await ValidateTopLibraryFolders(CancellationToken.None).ConfigureAwait(false); + await ValidateTopLibraryFolders(CancellationToken.None, true).ConfigureAwait(false); StartScanInBackground(); } From 4fa6b8874f0cc773a977b09aec249d207a1335d3 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 13 Apr 2024 14:58:29 +0800 Subject: [PATCH 2/5] fix: typo Signed-off-by: gnattu --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index baed887e3e..da6c756743 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1046,7 +1046,7 @@ namespace Emby.Server.Implementations.Library await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); - // HACK: override IsRootHere for libraries to be removed + // HACK: override IsRoot here for libraries to be removed if (removeRoot) GetUserRootFolder().IsRoot = false; await GetUserRootFolder().ValidateChildren( new Progress(), From 7befbda1a66ed0a0c0f386081e13c0b2585b8137 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 13 Apr 2024 15:02:13 +0800 Subject: [PATCH 3/5] fix: code style Signed-off-by: gnattu --- Emby.Server.Implementations/Library/LibraryManager.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index da6c756743..8f5f366883 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1047,14 +1047,21 @@ namespace Emby.Server.Implementations.Library await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); // HACK: override IsRoot here for libraries to be removed - if (removeRoot) GetUserRootFolder().IsRoot = false; + if (removeRoot) + { + GetUserRootFolder().IsRoot = false; + } + await GetUserRootFolder().ValidateChildren( new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); // HACK: restore IsRoot here after validation - if (removeRoot) GetUserRootFolder().IsRoot = true; + if (removeRoot) + { + GetUserRootFolder().IsRoot = true; + } // Quickly scan CollectionFolders for changes foreach (var folder in GetUserRootFolder().Children.OfType()) From e4d66f35fdaece836edc26cb0d2af36fdb933d7f Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Apr 2024 14:41:19 +0800 Subject: [PATCH 4/5] chore: use proper way to override remove root This is an alternate approach which is more proper, but changes all parts that uses/overrides the original ValidateChildren method Signed-off-by: gnattu --- .../Library/LibraryManager.cs | 18 ++++-------------- .../Entities/AggregateFolder.cs | 4 ++-- .../Entities/Audio/MusicArtist.cs | 4 ++-- .../Entities/CollectionFolder.cs | 3 ++- MediaBrowser.Controller/Entities/Folder.cs | 17 ++++++++++------- .../Entities/UserRootFolder.cs | 4 ++-- MediaBrowser.Controller/Entities/UserView.cs | 4 +++- MediaBrowser.Controller/Playlists/Playlist.cs | 2 +- .../Manager/ProviderManager.cs | 2 +- 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 8f5f366883..ea8b831250 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1042,26 +1042,16 @@ namespace Emby.Server.Implementations.Library new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, - cancellationToken).ConfigureAwait(false); + cancellationToken: cancellationToken).ConfigureAwait(false); await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); - // HACK: override IsRoot here for libraries to be removed - if (removeRoot) - { - GetUserRootFolder().IsRoot = false; - } - await GetUserRootFolder().ValidateChildren( new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, - cancellationToken).ConfigureAwait(false); - // HACK: restore IsRoot here after validation - if (removeRoot) - { - GetUserRootFolder().IsRoot = true; - } + allowRemoveRoot: removeRoot, + cancellationToken: cancellationToken).ConfigureAwait(false); // Quickly scan CollectionFolders for changes foreach (var folder in GetUserRootFolder().Children.OfType()) @@ -1079,7 +1069,7 @@ namespace Emby.Server.Implementations.Library var innerProgress = new Progress(pct => progress.Report(pct * 0.96)); // Validate the entire media library - await RootFolder.ValidateChildren(innerProgress, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true, cancellationToken).ConfigureAwait(false); + await RootFolder.ValidateChildren(innerProgress, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true, cancellationToken: cancellationToken).ConfigureAwait(false); progress.Report(96); diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index b225f22df0..70c33a8c4c 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -155,11 +155,11 @@ namespace MediaBrowser.Controller.Entities return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren); } - protected override async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected override async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { ClearCache(); - await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken) + await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, false, refreshOptions, directoryService, cancellationToken) .ConfigureAwait(false); ClearCache(); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 11cdf84445..1ab6c97066 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -110,7 +110,7 @@ namespace MediaBrowser.Controller.Entities.Audio return base.IsSaveLocalMetadataEnabled(); } - protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { if (IsAccessedByName) { @@ -118,7 +118,7 @@ namespace MediaBrowser.Controller.Entities.Audio return Task.CompletedTask; } - return base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken); + return base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, false, refreshOptions, directoryService, cancellationToken); } public override List GetUserDataKeys() diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 676a47c883..4ead477f83 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -316,11 +316,12 @@ namespace MediaBrowser.Controller.Entities /// The progress. /// if set to true [recursive]. /// if set to true [refresh child metadata]. + /// remove item even this folder is root. /// The refresh options. /// The directory service. /// The cancellation token. /// Task. - protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 8bfcf5deef..31ba0494ac 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -269,11 +269,12 @@ namespace MediaBrowser.Controller.Entities /// The progress. /// The metadata refresh options. /// if set to true [recursive]. + /// remove item even this folder is root. /// The cancellation token. /// Task. - public Task ValidateChildren(IProgress progress, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true, CancellationToken cancellationToken = default) + public Task ValidateChildren(IProgress progress, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true, bool allowRemoveRoot = false, CancellationToken cancellationToken = default) { - return ValidateChildrenInternal(progress, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService, cancellationToken); + return ValidateChildrenInternal(progress, recursive, true, false, metadataRefreshOptions, metadataRefreshOptions.DirectoryService, cancellationToken); } private Dictionary GetActualChildrenDictionary() @@ -307,11 +308,12 @@ namespace MediaBrowser.Controller.Entities /// The progress. /// if set to true [recursive]. /// if set to true [refresh child metadata]. + /// remove item even this folder is root. /// The refresh options. /// The directory service. /// The cancellation token. /// Task. - protected virtual async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected virtual async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { if (recursive) { @@ -320,7 +322,7 @@ namespace MediaBrowser.Controller.Entities try { - await ValidateChildrenInternal2(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken).ConfigureAwait(false); + await ValidateChildrenInternal2(progress, recursive, refreshChildMetadata, allowRemoveRoot, refreshOptions, directoryService, cancellationToken).ConfigureAwait(false); } finally { @@ -343,7 +345,7 @@ namespace MediaBrowser.Controller.Entities return true; } - private async Task ValidateChildrenInternal2(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + private async Task ValidateChildrenInternal2(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { if (!IsLibraryFolderAccessible(directoryService, this)) { @@ -414,8 +416,9 @@ namespace MediaBrowser.Controller.Entities validChildren.Add(child); } + var shouldNotRemove = IsRoot && !allowRemoveRoot; // If it's an AggregateFolder, don't remove - if (!IsRoot && currentChildren.Count != validChildren.Count) + if (shouldNotRemove && currentChildren.Count != validChildren.Count) { // That's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(validChildren).ToList(); @@ -562,7 +565,7 @@ namespace MediaBrowser.Controller.Entities private Task ValidateSubFolders(IList children, IDirectoryService directoryService, IProgress progress, CancellationToken cancellationToken) { return RunTasks( - (folder, innerProgress) => folder.ValidateChildrenInternal(innerProgress, true, false, null, directoryService, cancellationToken), + (folder, innerProgress) => folder.ValidateChildrenInternal(innerProgress, true, false, false, null, directoryService, cancellationToken), children, progress, cancellationToken); diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 69743b926c..9c413617db 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -117,11 +117,11 @@ namespace MediaBrowser.Controller.Entities return base.GetNonCachedChildren(directoryService); } - protected override async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected override async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { ClearCache(); - await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken) + await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, false, refreshOptions, directoryService, cancellationToken) .ConfigureAwait(false); ClearCache(); diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index c93488a85e..e4fb340f78 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -6,10 +6,12 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Querying; @@ -180,7 +182,7 @@ namespace MediaBrowser.Controller.Entities return _originalFolderViewTypes.Contains(viewType); } - protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 34b34e5780..b5c226a7f1 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -132,7 +132,7 @@ namespace MediaBrowser.Controller.Playlists return []; } - protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, bool allowRemoveRoot, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 0b1fed0a3d..844989a0b0 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1106,7 +1106,7 @@ namespace MediaBrowser.Providers.Manager .Where(i => i is not null) .Distinct(); - var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress(), options, true, cancellationToken)); + var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress(), options, true, false, cancellationToken)); await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false); From 1b567efeb5f4720f3d64a03359d420f83a3b781c Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Apr 2024 21:32:21 +0800 Subject: [PATCH 5/5] fix: correctly pass parameters in overrides Signed-off-by: gnattu --- MediaBrowser.Controller/Entities/Folder.cs | 6 +++--- MediaBrowser.Controller/Entities/UserRootFolder.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 31ba0494ac..45c27fdc02 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -274,7 +274,7 @@ namespace MediaBrowser.Controller.Entities /// Task. public Task ValidateChildren(IProgress progress, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true, bool allowRemoveRoot = false, CancellationToken cancellationToken = default) { - return ValidateChildrenInternal(progress, recursive, true, false, metadataRefreshOptions, metadataRefreshOptions.DirectoryService, cancellationToken); + return ValidateChildrenInternal(progress, recursive, true, allowRemoveRoot, metadataRefreshOptions, metadataRefreshOptions.DirectoryService, cancellationToken); } private Dictionary GetActualChildrenDictionary() @@ -416,9 +416,9 @@ namespace MediaBrowser.Controller.Entities validChildren.Add(child); } - var shouldNotRemove = IsRoot && !allowRemoveRoot; + var shouldRemove = !IsRoot || allowRemoveRoot; // If it's an AggregateFolder, don't remove - if (shouldNotRemove && currentChildren.Count != validChildren.Count) + if (shouldRemove && currentChildren.Count != validChildren.Count) { // That's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(validChildren).ToList(); diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 9c413617db..fc8a297639 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -121,7 +121,7 @@ namespace MediaBrowser.Controller.Entities { ClearCache(); - await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, false, refreshOptions, directoryService, cancellationToken) + await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, allowRemoveRoot, refreshOptions, directoryService, cancellationToken) .ConfigureAwait(false); ClearCache();