From 8eb2fa53b5ca0e0bc03b70029e3669e36d54d10c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:23:51 -0500 Subject: [PATCH 1/8] Use pattern matching for EnableRefreshMessage --- .../EntryPoints/LibraryChangedNotifier.cs | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index be36bbd2c1..6654f48672 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -147,34 +147,8 @@ namespace Emby.Server.Implementations.EntryPoints } private static bool EnableRefreshMessage(BaseItem item) - { - if (item is not Folder folder) - { - return false; - } - - if (folder.IsRoot) - { - return false; - } - - if (folder is AggregateFolder || folder is UserRootFolder) - { - return false; - } - - if (folder is UserView || folder is Channel) - { - return false; - } - - if (!folder.IsTopParent) - { - return false; - } - - return true; - } + => item is Folder { IsRoot: false, IsTopParent: true } + and not (AggregateFolder or UserRootFolder or UserView or Channel); /// /// Handles the ItemAdded event of the libraryManager control. From 98f8cb2ad04d23edb9a6dfdf5d0b6e577d7ebb85 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:28:35 -0500 Subject: [PATCH 2/8] Use target-typed new for fields --- .../EntryPoints/LibraryChangedNotifier.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 6654f48672..7a8686ec84 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -34,17 +34,13 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IUserManager _userManager; private readonly ILogger _logger; - /// - /// The library changed sync lock. - /// - private readonly object _libraryChangedSyncLock = new object(); - - private readonly List _foldersAddedTo = new List(); - private readonly List _foldersRemovedFrom = new List(); - private readonly List _itemsAdded = new List(); - private readonly List _itemsRemoved = new List(); - private readonly List _itemsUpdated = new List(); - private readonly ConcurrentDictionary _lastProgressMessageTimes = new ConcurrentDictionary(); + private readonly object _libraryChangedSyncLock = new(); + private readonly List _foldersAddedTo = new(); + private readonly List _foldersRemovedFrom = new(); + private readonly List _itemsAdded = new(); + private readonly List _itemsRemoved = new(); + private readonly List _itemsUpdated = new(); + private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); public LibraryChangedNotifier( ILibraryManager libraryManager, From eb4d8e13dfa10123c84b1676e9f20983b6abcfee Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:35:01 -0500 Subject: [PATCH 3/8] Break up long lines --- .../EntryPoints/LibraryChangedNotifier.cs | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 7a8686ec84..5a2f8a6dd4 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -296,7 +296,13 @@ namespace Emby.Server.Implementations.EntryPoints /// The folders added to. /// The folders removed from. /// The cancellation token. - private async Task SendChangeNotifications(List itemsAdded, List itemsUpdated, List itemsRemoved, List foldersAddedTo, List foldersRemovedFrom, CancellationToken cancellationToken) + private async Task SendChangeNotifications( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + CancellationToken cancellationToken) { var userIds = _sessionManager.Sessions .Select(i => i.UserId) @@ -325,7 +331,12 @@ namespace Emby.Server.Implementations.EntryPoints try { - await _sessionManager.SendMessageToUserSessions(new List { userId }, SessionMessageType.LibraryChanged, info, cancellationToken).ConfigureAwait(false); + await _sessionManager.SendMessageToUserSessions( + new List { userId }, + SessionMessageType.LibraryChanged, + info, + cancellationToken) + .ConfigureAwait(false); } catch (Exception ex) { @@ -344,7 +355,13 @@ namespace Emby.Server.Implementations.EntryPoints /// The folders removed from. /// The user id. /// LibraryUpdateInfo. - private LibraryUpdateInfo GetLibraryUpdateInfo(List itemsAdded, List itemsUpdated, List itemsRemoved, List foldersAddedTo, List foldersRemovedFrom, Guid userId) + private LibraryUpdateInfo GetLibraryUpdateInfo( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + Guid userId) { var user = _userManager.GetUserById(userId); @@ -352,20 +369,33 @@ namespace Emby.Server.Implementations.EntryPoints newAndRemoved.AddRange(foldersAddedTo); newAndRemoved.AddRange(foldersRemovedFrom); - var allUserRootChildren = _libraryManager.GetUserRootFolder().GetChildren(user, true).OfType().ToList(); + var allUserRootChildren = _libraryManager.GetUserRootFolder() + .GetChildren(user, true) + .OfType() + .ToList(); return new LibraryUpdateInfo { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() }; } From 8f5f0a0310ca7dd55b64623782c05bbb810f946f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:49:25 -0500 Subject: [PATCH 4/8] Combine library item event handlers --- .../EntryPoints/LibraryChangedNotifier.cs | 83 +++---------------- 1 file changed, 13 insertions(+), 70 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 5a2f8a6dd4..4d5065b927 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -146,98 +146,41 @@ namespace Emby.Server.Implementations.EntryPoints => item is Folder { IsRoot: false, IsTopParent: true } and not (AggregateFolder or UserRootFolder or UserView or Channel); - /// - /// Handles the ItemAdded event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } - - lock (_libraryChangedSyncLock) - { - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer( - LibraryUpdateTimerCallback, - null, - TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), - Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } + => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - if (e.Item.GetParent() is Folder parent) - { - _foldersAddedTo.Add(parent); - } - - _itemsAdded.Add(e.Item); - } - } - - /// - /// Handles the ItemUpdated event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } + => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - lock (_libraryChangedSyncLock) - { - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } - - _itemsUpdated.Add(e.Item); - } - } - - /// - /// Handles the ItemRemoved event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); + + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) { - if (!FilterItem(e.Item)) + if (!FilterItem(item)) { return; } lock (_libraryChangedSyncLock) { + var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); + if (LibraryUpdateTimer is null) { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); + LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } else { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); + LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } - if (e.Parent is Folder parent) + if (foldersList is not null && parent is Folder folder) { - _foldersRemovedFrom.Add(parent); + foldersList.Add(folder); } - _itemsRemoved.Add(e.Item); + itemsList.Add(item); } } From 7e645dcfc0809ed2bfa9d31235839bf649bc76d2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 19:48:50 -0500 Subject: [PATCH 5/8] Make ILibraryChangedNotifier sealed --- .../EntryPoints/LibraryChangedNotifier.cs | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 4d5065b927..0df8c2a5a5 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -25,7 +25,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class LibraryChangedNotifier : IServerEntryPoint + public sealed class LibraryChangedNotifier : IServerEntryPoint { private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _configurationManager; @@ -405,36 +405,21 @@ namespace Emby.Server.Implementations.EntryPoints return Array.Empty(); } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } + _libraryManager.ItemAdded -= OnLibraryItemAdded; + _libraryManager.ItemUpdated -= OnLibraryItemUpdated; + _libraryManager.ItemRemoved -= OnLibraryItemRemoved; - _libraryManager.ItemAdded -= OnLibraryItemAdded; - _libraryManager.ItemUpdated -= OnLibraryItemUpdated; - _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; + _providerManager.RefreshStarted -= OnProviderRefreshStarted; + _providerManager.RefreshProgress -= OnProviderRefreshProgress; - _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; - _providerManager.RefreshStarted -= OnProviderRefreshStarted; - _providerManager.RefreshProgress -= OnProviderRefreshProgress; + if (LibraryUpdateTimer is not null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } } } From c38bfd281c9616a626eceb22ad5f5e2a4a120b86 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 19:49:15 -0500 Subject: [PATCH 6/8] Use file-scoped namespace --- .../EntryPoints/LibraryChangedNotifier.cs | 635 +++++++++--------- 1 file changed, 317 insertions(+), 318 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 0df8c2a5a5..40dc00e1af 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -23,404 +23,403 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.EntryPoints +namespace Emby.Server.Implementations.EntryPoints; + +public sealed class LibraryChangedNotifier : IServerEntryPoint { - public sealed class LibraryChangedNotifier : IServerEntryPoint + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; + private readonly IProviderManager _providerManager; + private readonly ISessionManager _sessionManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + private readonly object _libraryChangedSyncLock = new(); + private readonly List _foldersAddedTo = new(); + private readonly List _foldersRemovedFrom = new(); + private readonly List _itemsAdded = new(); + private readonly List _itemsRemoved = new(); + private readonly List _itemsUpdated = new(); + private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + + public LibraryChangedNotifier( + ILibraryManager libraryManager, + IServerConfigurationManager configurationManager, + ISessionManager sessionManager, + IUserManager userManager, + ILogger logger, + IProviderManager providerManager) { - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _configurationManager; - private readonly IProviderManager _providerManager; - private readonly ISessionManager _sessionManager; - private readonly IUserManager _userManager; - private readonly ILogger _logger; - - private readonly object _libraryChangedSyncLock = new(); - private readonly List _foldersAddedTo = new(); - private readonly List _foldersRemovedFrom = new(); - private readonly List _itemsAdded = new(); - private readonly List _itemsRemoved = new(); - private readonly List _itemsUpdated = new(); - private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); - - public LibraryChangedNotifier( - ILibraryManager libraryManager, - IServerConfigurationManager configurationManager, - ISessionManager sessionManager, - IUserManager userManager, - ILogger logger, - IProviderManager providerManager) - { - _libraryManager = libraryManager; - _configurationManager = configurationManager; - _sessionManager = sessionManager; - _userManager = userManager; - _logger = logger; - _providerManager = providerManager; - } + _libraryManager = libraryManager; + _configurationManager = configurationManager; + _sessionManager = sessionManager; + _userManager = userManager; + _logger = logger; + _providerManager = providerManager; + } - /// - /// Gets or sets the library update timer. - /// - /// The library update timer. - private Timer LibraryUpdateTimer { get; set; } + /// + /// Gets or sets the library update timer. + /// + /// The library update timer. + private Timer LibraryUpdateTimer { get; set; } - public Task RunAsync() - { - _libraryManager.ItemAdded += OnLibraryItemAdded; - _libraryManager.ItemUpdated += OnLibraryItemUpdated; - _libraryManager.ItemRemoved += OnLibraryItemRemoved; + public Task RunAsync() + { + _libraryManager.ItemAdded += OnLibraryItemAdded; + _libraryManager.ItemUpdated += OnLibraryItemUpdated; + _libraryManager.ItemRemoved += OnLibraryItemRemoved; - _providerManager.RefreshCompleted += OnProviderRefreshCompleted; - _providerManager.RefreshStarted += OnProviderRefreshStarted; - _providerManager.RefreshProgress += OnProviderRefreshProgress; + _providerManager.RefreshCompleted += OnProviderRefreshCompleted; + _providerManager.RefreshStarted += OnProviderRefreshStarted; + _providerManager.RefreshProgress += OnProviderRefreshProgress; - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + { + var item = e.Argument.Item1; + + if (!EnableRefreshMessage(item)) { - var item = e.Argument.Item1; + return; + } + + var progress = e.Argument.Item2; - if (!EnableRefreshMessage(item)) + if (_lastProgressMessageTimes.TryGetValue(item.Id, out var lastMessageSendTime)) + { + if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000) { return; } + } - var progress = e.Argument.Item2; + _lastProgressMessageTimes.AddOrUpdate(item.Id, _ => DateTime.UtcNow, (_, _) => DateTime.UtcNow); - if (_lastProgressMessageTimes.TryGetValue(item.Id, out var lastMessageSendTime)) - { - if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000) - { - return; - } - } + var dict = new Dictionary(); + dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); + dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); - _lastProgressMessageTimes.AddOrUpdate(item.Id, _ => DateTime.UtcNow, (_, _) => DateTime.UtcNow); + try + { + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None); + } + catch + { + } + + var collectionFolders = _libraryManager.GetCollectionFolders(item); - var dict = new Dictionary(); - dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); - dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); + foreach (var collectionFolder in collectionFolders) + { + var collectionFolderDict = new Dictionary + { + ["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), + ["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture) + }; try { - _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None); + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None); } catch { } - - var collectionFolders = _libraryManager.GetCollectionFolders(item); - - foreach (var collectionFolder in collectionFolders) - { - var collectionFolderDict = new Dictionary - { - ["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), - ["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture) - }; - - try - { - _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None); - } - catch - { - } - } } + } - private void OnProviderRefreshStarted(object sender, GenericEventArgs e) - { - OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); - } + private void OnProviderRefreshStarted(object sender, GenericEventArgs e) + { + OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); + } - private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) - { - OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); + private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) + { + OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); - _lastProgressMessageTimes.TryRemove(e.Argument.Id, out _); - } + _lastProgressMessageTimes.TryRemove(e.Argument.Id, out _); + } - private static bool EnableRefreshMessage(BaseItem item) - => item is Folder { IsRoot: false, IsTopParent: true } - and not (AggregateFolder or UserRootFolder or UserView or Channel); + private static bool EnableRefreshMessage(BaseItem item) + => item is Folder { IsRoot: false, IsTopParent: true } + and not (AggregateFolder or UserRootFolder or UserView or Channel); - private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); + private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); + private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); + private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); - private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + { + if (!FilterItem(item)) { - if (!FilterItem(item)) + return; + } + + lock (_libraryChangedSyncLock) + { + var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); + + if (LibraryUpdateTimer is null) { - return; + LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } - - lock (_libraryChangedSyncLock) + else { - var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); - - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); - } - - if (foldersList is not null && parent is Folder folder) - { - foldersList.Add(folder); - } - - itemsList.Add(item); + LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } - } - /// - /// Libraries the update timer callback. - /// - /// The state. - private async void LibraryUpdateTimerCallback(object state) - { - List foldersAddedTo; - List foldersRemovedFrom; - List itemsUpdated; - List itemsAdded; - List itemsRemoved; - lock (_libraryChangedSyncLock) + if (foldersList is not null && parent is Folder folder) { - // Remove dupes in case some were saved multiple times - foldersAddedTo = _foldersAddedTo - .DistinctBy(x => x.Id) - .ToList(); - - foldersRemovedFrom = _foldersRemovedFrom - .DistinctBy(x => x.Id) - .ToList(); - - itemsUpdated = _itemsUpdated - .Where(i => !_itemsAdded.Contains(i)) - .DistinctBy(x => x.Id) - .ToList(); - - itemsAdded = _itemsAdded.ToList(); - itemsRemoved = _itemsRemoved.ToList(); - - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } - - _itemsAdded.Clear(); - _itemsRemoved.Clear(); - _itemsUpdated.Clear(); - _foldersAddedTo.Clear(); - _foldersRemovedFrom.Clear(); + foldersList.Add(folder); } - await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); + itemsList.Add(item); } + } - /// - /// Sends the change notifications. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The cancellation token. - private async Task SendChangeNotifications( - List itemsAdded, - List itemsUpdated, - List itemsRemoved, - List foldersAddedTo, - List foldersRemovedFrom, - CancellationToken cancellationToken) + /// + /// Libraries the update timer callback. + /// + /// The state. + private async void LibraryUpdateTimerCallback(object state) + { + List foldersAddedTo; + List foldersRemovedFrom; + List itemsUpdated; + List itemsAdded; + List itemsRemoved; + lock (_libraryChangedSyncLock) { - var userIds = _sessionManager.Sessions - .Select(i => i.UserId) - .Where(i => !i.Equals(default)) - .Distinct() - .ToArray(); + // Remove dupes in case some were saved multiple times + foldersAddedTo = _foldersAddedTo + .DistinctBy(x => x.Id) + .ToList(); - foreach (var userId in userIds) + foldersRemovedFrom = _foldersRemovedFrom + .DistinctBy(x => x.Id) + .ToList(); + + itemsUpdated = _itemsUpdated + .Where(i => !_itemsAdded.Contains(i)) + .DistinctBy(x => x.Id) + .ToList(); + + itemsAdded = _itemsAdded.ToList(); + itemsRemoved = _itemsRemoved.ToList(); + + if (LibraryUpdateTimer is not null) { - LibraryUpdateInfo info; - - try - { - info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, userId); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in GetLibraryUpdateInfo"); - return; - } - - if (info.IsEmpty) - { - continue; - } - - try - { - await _sessionManager.SendMessageToUserSessions( - new List { userId }, - SessionMessageType.LibraryChanged, - info, - cancellationToken) - .ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending LibraryChanged message"); - } + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } + + _itemsAdded.Clear(); + _itemsRemoved.Clear(); + _itemsUpdated.Clear(); + _foldersAddedTo.Clear(); + _foldersRemovedFrom.Clear(); } - /// - /// Gets the library update info. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The user id. - /// LibraryUpdateInfo. - private LibraryUpdateInfo GetLibraryUpdateInfo( - List itemsAdded, - List itemsUpdated, - List itemsRemoved, - List foldersAddedTo, - List foldersRemovedFrom, - Guid userId) - { - var user = _userManager.GetUserById(userId); + await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); + } - var newAndRemoved = new List(); - newAndRemoved.AddRange(foldersAddedTo); - newAndRemoved.AddRange(foldersRemovedFrom); + /// + /// Sends the change notifications. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The cancellation token. + private async Task SendChangeNotifications( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + CancellationToken cancellationToken) + { + var userIds = _sessionManager.Sessions + .Select(i => i.UserId) + .Where(i => !i.Equals(default)) + .Distinct() + .ToArray(); - var allUserRootChildren = _libraryManager.GetUserRootFolder() - .GetChildren(user, true) - .OfType() - .ToList(); + foreach (var userId in userIds) + { + LibraryUpdateInfo info; - return new LibraryUpdateInfo + try { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() - }; - } - - private static bool FilterItem(BaseItem item) - { - if (!item.IsFolder && !item.HasPathProtocol) + info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, userId); + } + catch (Exception ex) { - return false; + _logger.LogError(ex, "Error in GetLibraryUpdateInfo"); + return; } - if (item is IItemByName && item is not MusicArtist) + if (info.IsEmpty) { - return false; + continue; } - return item.SourceType == SourceType.Library; + try + { + await _sessionManager.SendMessageToUserSessions( + new List { userId }, + SessionMessageType.LibraryChanged, + info, + cancellationToken) + .ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending LibraryChanged message"); + } } + } + + /// + /// Gets the library update info. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The user id. + /// LibraryUpdateInfo. + private LibraryUpdateInfo GetLibraryUpdateInfo( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + Guid userId) + { + var user = _userManager.GetUserById(userId); + + var newAndRemoved = new List(); + newAndRemoved.AddRange(foldersAddedTo); + newAndRemoved.AddRange(foldersRemovedFrom); - private IEnumerable GetTopParentIds(List items, List allUserRootChildren) + var allUserRootChildren = _libraryManager.GetUserRootFolder() + .GetChildren(user, true) + .OfType() + .ToList(); + + return new LibraryUpdateInfo { - var list = new List(); + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() + }; + } - foreach (var item in items) - { - // If the physical root changed, return the user root - if (item is AggregateFolder) - { - continue; - } - - foreach (var folder in allUserRootChildren) - { - list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); - } - } + private static bool FilterItem(BaseItem item) + { + if (!item.IsFolder && !item.HasPathProtocol) + { + return false; + } - return list.Distinct(StringComparer.Ordinal); + if (item is IItemByName && item is not MusicArtist) + { + return false; } - /// - /// Translates the physical item to user library. - /// - /// The type of item. - /// The item. - /// The user. - /// if set to true [include if not found]. - /// IEnumerable{``0}. - private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) - where T : BaseItem + return item.SourceType == SourceType.Library; + } + + private IEnumerable GetTopParentIds(List items, List allUserRootChildren) + { + var list = new List(); + + foreach (var item in items) { // If the physical root changed, return the user root if (item is AggregateFolder) { - return new[] { _libraryManager.GetUserRootFolder() as T }; + continue; } - // Return it only if it's in the user's library - if (includeIfNotFound || item.IsVisibleStandalone(user)) + foreach (var folder in allUserRootChildren) { - return new[] { item }; + list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); } + } + + return list.Distinct(StringComparer.Ordinal); + } - return Array.Empty(); + /// + /// Translates the physical item to user library. + /// + /// The type of item. + /// The item. + /// The user. + /// if set to true [include if not found]. + /// IEnumerable{``0}. + private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) + where T : BaseItem + { + // If the physical root changed, return the user root + if (item is AggregateFolder) + { + return new[] { _libraryManager.GetUserRootFolder() as T }; } - /// - public void Dispose() + // Return it only if it's in the user's library + if (includeIfNotFound || item.IsVisibleStandalone(user)) { - _libraryManager.ItemAdded -= OnLibraryItemAdded; - _libraryManager.ItemUpdated -= OnLibraryItemUpdated; - _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + return new[] { item }; + } - _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; - _providerManager.RefreshStarted -= OnProviderRefreshStarted; - _providerManager.RefreshProgress -= OnProviderRefreshProgress; + return Array.Empty(); + } - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } + /// + public void Dispose() + { + _libraryManager.ItemAdded -= OnLibraryItemAdded; + _libraryManager.ItemUpdated -= OnLibraryItemUpdated; + _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + + _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; + _providerManager.RefreshStarted -= OnProviderRefreshStarted; + _providerManager.RefreshProgress -= OnProviderRefreshProgress; + + if (LibraryUpdateTimer is not null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } } } From 0ea9f713f48e433f66a95bad7bf765cdd2589fa3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 20:07:07 -0500 Subject: [PATCH 7/8] Document LibraryChangedNotifier --- .../EntryPoints/LibraryChangedNotifier.cs | 50 +++++-------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 40dc00e1af..8e0f37d895 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -25,6 +23,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints; +/// +/// A that notifies users when libraries are updated. +/// public sealed class LibraryChangedNotifier : IServerEntryPoint { private readonly ILibraryManager _libraryManager; @@ -42,6 +43,15 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint private readonly List _itemsUpdated = new(); private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . public LibraryChangedNotifier( ILibraryManager libraryManager, IServerConfigurationManager configurationManager, @@ -58,12 +68,9 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager = providerManager; } - /// - /// Gets or sets the library update timer. - /// - /// The library update timer. private Timer LibraryUpdateTimer { get; set; } + /// public Task RunAsync() { _libraryManager.ItemAdded += OnLibraryItemAdded; @@ -184,10 +191,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - /// - /// Libraries the update timer callback. - /// - /// The state. private async void LibraryUpdateTimerCallback(object state) { List foldersAddedTo; @@ -230,15 +233,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); } - /// - /// Sends the change notifications. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The cancellation token. private async Task SendChangeNotifications( List itemsAdded, List itemsUpdated, @@ -288,16 +282,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - /// - /// Gets the library update info. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The user id. - /// LibraryUpdateInfo. private LibraryUpdateInfo GetLibraryUpdateInfo( List itemsAdded, List itemsUpdated, @@ -379,14 +363,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return list.Distinct(StringComparer.Ordinal); } - /// - /// Translates the physical item to user library. - /// - /// The type of item. - /// The item. - /// The user. - /// if set to true [include if not found]. - /// IEnumerable{``0}. private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) where T : BaseItem { From 4e61c2b4ec7ff1a25ac7b8e71bcf6a2833f78e75 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 20:31:07 -0500 Subject: [PATCH 8/8] Enable nullable in LibraryChangedNotifier --- .../EntryPoints/LibraryChangedNotifier.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 8e0f37d895..a83d7a4105 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -43,6 +41,8 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint private readonly List _itemsUpdated = new(); private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + private Timer? _libraryUpdateTimer; + /// /// Initializes a new instance of the class. /// @@ -68,8 +68,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager = providerManager; } - private Timer LibraryUpdateTimer { get; set; } - /// public Task RunAsync() { @@ -84,7 +82,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return Task.CompletedTask; } - private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + private void OnProviderRefreshProgress(object? sender, GenericEventArgs> e) { var item = e.Argument.Item1; @@ -137,12 +135,12 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - private void OnProviderRefreshStarted(object sender, GenericEventArgs e) + private void OnProviderRefreshStarted(object? sender, GenericEventArgs e) { OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); } - private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) + private void OnProviderRefreshCompleted(object? sender, GenericEventArgs e) { OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); @@ -153,16 +151,16 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint => item is Folder { IsRoot: false, IsTopParent: true } and not (AggregateFolder or UserRootFolder or UserView or Channel); - private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) + private void OnLibraryItemAdded(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) + private void OnLibraryItemUpdated(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + private void OnLibraryItemRemoved(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); - private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List? foldersList) { if (!FilterItem(item)) { @@ -173,13 +171,13 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint { var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); - if (LibraryUpdateTimer is null) + if (_libraryUpdateTimer is null) { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); + _libraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } else { - LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); + _libraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } if (foldersList is not null && parent is Folder folder) @@ -191,7 +189,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - private async void LibraryUpdateTimerCallback(object state) + private async void LibraryUpdateTimerCallback(object? state) { List foldersAddedTo; List foldersRemovedFrom; @@ -217,10 +215,10 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint itemsAdded = _itemsAdded.ToList(); itemsRemoved = _itemsRemoved.ToList(); - if (LibraryUpdateTimer is not null) + if (_libraryUpdateTimer is not null) { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; + _libraryUpdateTimer.Dispose(); + _libraryUpdateTimer = null; } _itemsAdded.Clear(); @@ -291,6 +289,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint Guid userId) { var user = _userManager.GetUserById(userId); + ArgumentNullException.ThrowIfNull(user); var newAndRemoved = new List(); newAndRemoved.AddRange(foldersAddedTo); @@ -369,7 +368,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint // If the physical root changed, return the user root if (item is AggregateFolder) { - return new[] { _libraryManager.GetUserRootFolder() as T }; + return _libraryManager.GetUserRootFolder() is T t ? new[] { t } : Array.Empty(); } // Return it only if it's in the user's library @@ -392,10 +391,10 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager.RefreshStarted -= OnProviderRefreshStarted; _providerManager.RefreshProgress -= OnProviderRefreshProgress; - if (LibraryUpdateTimer is not null) + if (_libraryUpdateTimer is not null) { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; + _libraryUpdateTimer.Dispose(); + _libraryUpdateTimer = null; } } }