From 0dffb6069fe2f2d3db00e7a10ca18a86552fbf1c Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:25:38 +0530 Subject: [PATCH 1/7] Add notification for item deleted --- .../IItemDeletedManager.cs | 22 +++++ .../ItemDeletedNotifier/ItemDeletedManager.cs | 98 +++++++++++++++++++ .../ItemDeletedNotifierEntryPoint.cs | 53 ++++++++++ .../ItemDeletedScheduledTask.cs | 71 ++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/IItemDeletedManager.cs create mode 100644 Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs create mode 100644 Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedNotifierEntryPoint.cs create mode 100644 Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedScheduledTask.cs diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/IItemDeletedManager.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/IItemDeletedManager.cs new file mode 100644 index 0000000..ae95794 --- /dev/null +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/IItemDeletedManager.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier; + +/// +/// Item deleted manager interface. +/// +public interface IItemDeletedManager +{ + /// + /// Process the current queue. + /// + /// A representing the asynchronous operation. + public Task ProcessItemsAsync(); + + /// + /// Add item to process queue. + /// + /// The deleted item. + public void AddItem(BaseItem item); +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs new file mode 100644 index 0000000..1812271 --- /dev/null +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using Jellyfin.Plugin.Webhook.Destinations; +using Jellyfin.Plugin.Webhook.Helpers; +using Jellyfin.Plugin.Webhook.Models; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier; + +/// +public class ItemDeletedManager : IItemDeletedManager +{ + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly IServerApplicationHost _applicationHost; + private readonly ConcurrentDictionary _itemProcessQueue; + private readonly ConcurrentDictionary _deletedItems; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public ItemDeletedManager( + ILogger logger, + ILibraryManager libraryManager, + IServerApplicationHost applicationHost) + { + _logger = logger; + _libraryManager = libraryManager; + _applicationHost = applicationHost; + _itemProcessQueue = new ConcurrentDictionary(); + _deletedItems = new ConcurrentDictionary(); + } + + /// + public async Task ProcessItemsAsync() + { + _logger.LogDebug("ProcessItemsAsync"); + // Attempt to process all items in queue. + var currentItems = _itemProcessQueue.ToArray(); + if (currentItems.Length != 0) + { + var scope = _applicationHost.ServiceProvider!.CreateAsyncScope(); + await using (scope.ConfigureAwait(false)) + { + var webhookSender = scope.ServiceProvider.GetRequiredService(); + foreach (var (key, container) in currentItems) + { + if (_deletedItems.TryGetValue(key, out var item)) + { + if (item != null) + { + // Skip notification if item type is Studio + if (container.ItemType == "Studio") + { + _logger.LogDebug("Skipping notification for item type Studio"); + _itemProcessQueue.TryRemove(key, out _); + _deletedItems.TryRemove(key, out _); + continue; + } + + _logger.LogDebug("Notifying for {ItemName}", container.Name); + + // Send notification to each configured destination. + var dataObject = DataObjectHelpers + .GetBaseDataObject(_applicationHost, NotificationType.ItemDeleted) + .AddBaseItemData(item); + + var itemType = Type.GetType($"MediaBrowser.Controller.Entities.{container.ItemType}"); + await webhookSender.SendNotification(NotificationType.ItemDeleted, dataObject, itemType) + .ConfigureAwait(false); + + // Remove item from queue. + _itemProcessQueue.TryRemove(key, out _); + _deletedItems.TryRemove(key, out _); + } + } + } + } + } + } + + /// + public void AddItem(BaseItem item) + { + var itemType = item.GetType().Name; + _itemProcessQueue.TryAdd(item.Id, new QueuedItemContainer(item.Id, item.Name, itemType)); + _deletedItems.TryAdd(item.Id, item); + _logger.LogDebug("Queued {ItemName} for notification", item.Name); + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedNotifierEntryPoint.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedNotifierEntryPoint.cs new file mode 100644 index 0000000..9f12ac0 --- /dev/null +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedNotifierEntryPoint.cs @@ -0,0 +1,53 @@ +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using Microsoft.Extensions.Hosting; + +namespace Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier; + +/// +/// Notifier when a library item is deleted. +/// +public class ItemDeletedNotifierEntryPoint : IHostedService +{ + private readonly IItemDeletedManager _itemDeletedManager; + private readonly ILibraryManager _libraryManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + public ItemDeletedNotifierEntryPoint( + IItemDeletedManager itemDeletedManager, + ILibraryManager libraryManager) + { + _itemDeletedManager = itemDeletedManager; + _libraryManager = libraryManager; + } + + private void ItemDeletedHandler(object? sender, ItemChangeEventArgs itemChangeEventArgs) + { + // Never notify on virtual items. + if (itemChangeEventArgs.Item.IsVirtualItem) + { + return; + } + + _itemDeletedManager.AddItem(itemChangeEventArgs.Item); + } + + /// + public Task StartAsync(CancellationToken cancellationToken) + { + _libraryManager.ItemRemoved += ItemDeletedHandler; + return Task.CompletedTask; + } + + /// + public Task StopAsync(CancellationToken cancellationToken) + { + _libraryManager.ItemRemoved -= ItemDeletedHandler; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedScheduledTask.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedScheduledTask.cs new file mode 100644 index 0000000..885ce25 --- /dev/null +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedScheduledTask.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; + +namespace Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier; + +/// +/// Scheduled task that processes item deleted events. +/// +public class ItemDeletedScheduledTask : IScheduledTask, IConfigurableScheduledTask +{ + private const int RecheckIntervalSec = 30; + private readonly IItemDeletedManager _itemDeletedManager; + private readonly ILocalizationManager _localizationManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + public ItemDeletedScheduledTask( + IItemDeletedManager itemDeletedManager, + ILocalizationManager localizationManager) + { + _itemDeletedManager = itemDeletedManager; + _localizationManager = localizationManager; + } + + /// + public string Name => "Webhook Item Deleted Notifier"; + + /// + public string Key => "WebhookItemDeleted"; + + /// + public string Description => "Processes item deleted queue"; + + /// + public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory"); + + /// + public bool IsHidden => false; + + /// + public bool IsEnabled => true; + + /// + public bool IsLogged => false; + + /// + public Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + { + return _itemDeletedManager.ProcessItemsAsync(); + } + + /// + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromSeconds(RecheckIntervalSec).Ticks + } + }; + } +} \ No newline at end of file From c64a8137126d3848a50043f3ac1aad6fa0df8a19 Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:27:14 +0530 Subject: [PATCH 2/7] Add notification for item deleted --- Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs b/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs index 131f790..9550a38 100644 --- a/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs +++ b/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs @@ -13,6 +13,7 @@ using Jellyfin.Plugin.Webhook.Helpers; using Jellyfin.Plugin.Webhook.Notifiers; using Jellyfin.Plugin.Webhook.Notifiers.ItemAddedNotifier; +using Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier; using Jellyfin.Plugin.Webhook.Notifiers.UserDataSavedNotifier; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; @@ -58,6 +59,7 @@ public void RegisterServices(IServiceCollection serviceCollection, IServerApplic // Library consumers. serviceCollection.AddScoped, SubtitleDownloadFailureNotifier>(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); // Security consumers. serviceCollection.AddScoped, AuthenticationFailureNotifier>(); @@ -90,6 +92,7 @@ public void RegisterServices(IServiceCollection serviceCollection, IServerApplic serviceCollection.AddHostedService(); serviceCollection.AddHostedService(); + serviceCollection.AddHostedService(); serviceCollection.AddHostedService(); } } From 7db3b94d98f62f2d209dbcf1a5691621ef0b0dbf Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:28:34 +0530 Subject: [PATCH 3/7] Add notification for item deleted --- Jellyfin.Plugin.Webhook/Configuration/Web/config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Jellyfin.Plugin.Webhook/Configuration/Web/config.js b/Jellyfin.Plugin.Webhook/Configuration/Web/config.js index b50b6ee..f29ed49 100644 --- a/Jellyfin.Plugin.Webhook/Configuration/Web/config.js +++ b/Jellyfin.Plugin.Webhook/Configuration/Web/config.js @@ -40,6 +40,7 @@ export default function (view) { template: document.querySelector("#template-notification-type"), values: { "ItemAdded": "Item Added", + "ItemDeleted": "Item Deleted", "PlaybackStart": "Playback Start", "PlaybackProgress": "Playback Progress", "PlaybackStop": "Playback Stop", From b5d77f568ec529a07923f175b47cbfc1a2a5b7de Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:30:00 +0530 Subject: [PATCH 4/7] Add notification for item deleted --- Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs b/Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs index 27fce7f..b647b0b 100644 --- a/Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs +++ b/Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs @@ -123,5 +123,10 @@ public enum NotificationType /// /// User data saved. /// - UserDataSaved = 23 + UserDataSaved = 23, + + /// + /// Item Deleted notification. + /// + ItemDeleted = 24 } From 8cc9f5252c87a086c49ae3484fbd7a5f866c32d0 Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:32:27 +0530 Subject: [PATCH 5/7] Add notification for item deleted --- .../Models/QueuedItemContainer.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs b/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs index e3f9174..a175ebe 100644 --- a/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs +++ b/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs @@ -11,19 +11,33 @@ public class QueuedItemContainer /// Initializes a new instance of the class. /// /// The item id. - public QueuedItemContainer(Guid id) + /// The item name (nullable). + /// The item type. + public QueuedItemContainer(Guid id, string? name = null, string? itemType = null) { ItemId = id; + Name = name; + ItemType = itemType; RetryCount = 0; } /// - /// Gets or sets the current retry count. + /// Gets or sets the item name. /// - public int RetryCount { get; set; } + public string? Name { get; set; } + + /// + /// Gets or sets the item type. + /// + public string? ItemType { get; set; } /// /// Gets or sets the current item id. /// public Guid ItemId { get; set; } + + /// + /// Gets or sets the current retry count. + /// + public int RetryCount { get; set; } } From 23abedd582a2a9f0386685d8ff06687d700c36b0 Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 00:50:43 +0530 Subject: [PATCH 6/7] combined if statements --- .../ItemDeletedNotifier/ItemDeletedManager.cs | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs index 1812271..073a7ec 100644 --- a/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemDeletedNotifier/ItemDeletedManager.cs @@ -53,34 +53,31 @@ public async Task ProcessItemsAsync() var webhookSender = scope.ServiceProvider.GetRequiredService(); foreach (var (key, container) in currentItems) { - if (_deletedItems.TryGetValue(key, out var item)) + if (_deletedItems.TryGetValue(key, out var item) && item != null) { - if (item != null) + // Skip notification if item type is Studio + if (container.ItemType == "Studio") { - // Skip notification if item type is Studio - if (container.ItemType == "Studio") - { - _logger.LogDebug("Skipping notification for item type Studio"); - _itemProcessQueue.TryRemove(key, out _); - _deletedItems.TryRemove(key, out _); - continue; - } + _logger.LogDebug("Skipping notification for item type Studio"); + _itemProcessQueue.TryRemove(key, out _); + _deletedItems.TryRemove(key, out _); + continue; + } - _logger.LogDebug("Notifying for {ItemName}", container.Name); + _logger.LogDebug("Notifying for {ItemName}", container.Name); - // Send notification to each configured destination. - var dataObject = DataObjectHelpers - .GetBaseDataObject(_applicationHost, NotificationType.ItemDeleted) - .AddBaseItemData(item); + // Send notification to each configured destination. + var dataObject = DataObjectHelpers + .GetBaseDataObject(_applicationHost, NotificationType.ItemDeleted) + .AddBaseItemData(item); - var itemType = Type.GetType($"MediaBrowser.Controller.Entities.{container.ItemType}"); - await webhookSender.SendNotification(NotificationType.ItemDeleted, dataObject, itemType) - .ConfigureAwait(false); + var itemType = Type.GetType($"MediaBrowser.Controller.Entities.{container.ItemType}"); + await webhookSender.SendNotification(NotificationType.ItemDeleted, dataObject, itemType) + .ConfigureAwait(false); - // Remove item from queue. - _itemProcessQueue.TryRemove(key, out _); - _deletedItems.TryRemove(key, out _); - } + // Remove item from queue. + _itemProcessQueue.TryRemove(key, out _); + _deletedItems.TryRemove(key, out _); } } } @@ -95,4 +92,4 @@ public void AddItem(BaseItem item) _deletedItems.TryAdd(item.Id, item); _logger.LogDebug("Queued {ItemName} for notification", item.Name); } -} \ No newline at end of file +} From 1db059687be68cb199cec843de36f18d10464e7d Mon Sep 17 00:00:00 2001 From: chiragkrishna Date: Sun, 8 Sep 2024 11:06:18 +0530 Subject: [PATCH 7/7] Undo Changes --- .../Models/QueuedItemContainer.cs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs b/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs index a175ebe..0a5c314 100644 --- a/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs +++ b/Jellyfin.Plugin.Webhook/Models/QueuedItemContainer.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Jellyfin.Plugin.Webhook.Models; @@ -11,33 +11,19 @@ public class QueuedItemContainer /// Initializes a new instance of the class. /// /// The item id. - /// The item name (nullable). - /// The item type. - public QueuedItemContainer(Guid id, string? name = null, string? itemType = null) + public QueuedItemContainer(Guid id) { ItemId = id; - Name = name; - ItemType = itemType; RetryCount = 0; } /// - /// Gets or sets the item name. - /// - public string? Name { get; set; } - - /// - /// Gets or sets the item type. + /// Gets or sets the current retry count. /// - public string? ItemType { get; set; } + public int RetryCount { get; set; } /// /// Gets or sets the current item id. /// public Guid ItemId { get; set; } - - /// - /// Gets or sets the current retry count. - /// - public int RetryCount { get; set; } }