From 96d9f3418c2d7ac3b136cdce19064e80a4f91337 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 11 May 2024 12:44:41 -0600 Subject: [PATCH] 10.9 support (#219) --- .../Configuration/Web/config.js | 1 - .../Destinations/Mqtt/MqttClients.cs | 2 +- .../Helpers/DataObjectHelpers.cs | 2 +- .../Jellyfin.Plugin.Webhook.csproj | 12 ++-- .../Notifiers/GenericNotifier.cs | 52 -------------- .../ItemAddedNotifier/ItemAddedManager.cs | 67 ++++++++++--------- .../ItemAddedNotifierEntryPoint.cs | 44 +++++------- .../UserDataSavedNotifierEntryPoint.cs | 60 +++++++---------- .../PluginServiceRegistrator.cs | 5 +- .../WebhookServerEntryPoint.cs | 34 +++------- build.yaml | 6 +- 11 files changed, 102 insertions(+), 183 deletions(-) delete mode 100644 Jellyfin.Plugin.Webhook/Notifiers/GenericNotifier.cs diff --git a/Jellyfin.Plugin.Webhook/Configuration/Web/config.js b/Jellyfin.Plugin.Webhook/Configuration/Web/config.js index 976bf23..d67ac66 100644 --- a/Jellyfin.Plugin.Webhook/Configuration/Web/config.js +++ b/Jellyfin.Plugin.Webhook/Configuration/Web/config.js @@ -40,7 +40,6 @@ template: document.querySelector("#template-notification-type"), values: { "ItemAdded": "Item Added", - "Generic": "Generic", "PlaybackStart": "Playback Start", "PlaybackProgress": "Playback Progress", "PlaybackStop": "Playback Stop", diff --git a/Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs b/Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs index 6da8d51..213e3e0 100644 --- a/Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs +++ b/Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs @@ -55,7 +55,7 @@ public async Task UpdateClients(MqttOption[] options) if (option.UseTls) { - messageBuilder.WithTls(); + messageBuilder.WithTlsOptions(c => c.UseTls()); } var clientOptions = messageBuilder.Build(); diff --git a/Jellyfin.Plugin.Webhook/Helpers/DataObjectHelpers.cs b/Jellyfin.Plugin.Webhook/Helpers/DataObjectHelpers.cs index 9fca255..743e3e2 100644 --- a/Jellyfin.Plugin.Webhook/Helpers/DataObjectHelpers.cs +++ b/Jellyfin.Plugin.Webhook/Helpers/DataObjectHelpers.cs @@ -397,4 +397,4 @@ public static Dictionary AddUserItemData(this DictionaryEscaped string. private static string Escape(this string? input) => input?.Replace("\"", "\\\"", StringComparison.Ordinal) ?? string.Empty; -} \ No newline at end of file +} diff --git a/Jellyfin.Plugin.Webhook/Jellyfin.Plugin.Webhook.csproj b/Jellyfin.Plugin.Webhook/Jellyfin.Plugin.Webhook.csproj index 0c73a01..837f370 100644 --- a/Jellyfin.Plugin.Webhook/Jellyfin.Plugin.Webhook.csproj +++ b/Jellyfin.Plugin.Webhook/Jellyfin.Plugin.Webhook.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 true true enable @@ -12,15 +12,15 @@ - - - - + + + + - + diff --git a/Jellyfin.Plugin.Webhook/Notifiers/GenericNotifier.cs b/Jellyfin.Plugin.Webhook/Notifiers/GenericNotifier.cs deleted file mode 100644 index 55034a7..0000000 --- a/Jellyfin.Plugin.Webhook/Notifiers/GenericNotifier.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using Jellyfin.Plugin.Webhook.Destinations; -using Jellyfin.Plugin.Webhook.Helpers; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Notifications; - -namespace Jellyfin.Plugin.Webhook.Notifiers; - -/// -/// Generic notifier. -/// -public class GenericNotifier : INotificationService -{ - private readonly IWebhookSender _webhookSender; - private readonly IServerApplicationHost _applicationHost; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - public GenericNotifier(IWebhookSender webhookSender, IServerApplicationHost applicationHost) - { - _webhookSender = webhookSender; - _applicationHost = applicationHost; - } - - /// - public string Name => "Webhook: Generic Notifier"; - - /// - public async Task SendNotification(UserNotification request, CancellationToken cancellationToken) - { - var dataObject = DataObjectHelpers - .GetBaseDataObject(_applicationHost, NotificationType.Generic); - dataObject[nameof(request.Name)] = request.Name; - dataObject[nameof(request.Description)] = request.Description; - dataObject[nameof(request.Date)] = request.Date; - dataObject[nameof(request.Level)] = request.Level; - dataObject[nameof(request.Url)] = request.Url; - dataObject[nameof(request.User.Username)] = request.User.Username; - dataObject["UserId"] = request.User.Id; - - await _webhookSender.SendNotification(NotificationType.Generic, dataObject) - .ConfigureAwait(false); - } - - /// - public bool IsEnabledForUser(User user) => true; -} diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedManager.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedManager.cs index 2196812..c38135f 100644 --- a/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedManager.cs +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedManager.cs @@ -7,6 +7,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.Webhook.Notifiers.ItemAddedNotifier; @@ -18,7 +19,6 @@ public class ItemAddedManager : IItemAddedManager private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IServerApplicationHost _applicationHost; - private readonly IWebhookSender _webhookSender; private readonly ConcurrentDictionary _itemProcessQueue; /// @@ -27,17 +27,14 @@ public class ItemAddedManager : IItemAddedManager /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public ItemAddedManager( ILogger logger, ILibraryManager libraryManager, - IServerApplicationHost applicationHost, - IWebhookSender webhookSender) + IServerApplicationHost applicationHost) { _logger = logger; _libraryManager = libraryManager; _applicationHost = applicationHost; - _webhookSender = webhookSender; _itemProcessQueue = new ConcurrentDictionary(); } @@ -47,40 +44,48 @@ public async Task ProcessItemsAsync() _logger.LogDebug("ProcessItemsAsync"); // Attempt to process all items in queue. var currentItems = _itemProcessQueue.ToArray(); - foreach (var (key, container) in currentItems) + if (currentItems.Length != 0) { - var item = _libraryManager.GetItemById(key); - if (item is null) + var scope = _applicationHost.ServiceProvider!.CreateAsyncScope(); + await using (scope.ConfigureAwait(false)) { - // Remove item from queue. - _itemProcessQueue.TryRemove(key, out _); - return; - } + var webhookSender = scope.ServiceProvider.GetRequiredService(); + foreach (var (key, container) in currentItems) + { + var item = _libraryManager.GetItemById(key); + if (item is null) + { + // Remove item from queue. + _itemProcessQueue.TryRemove(key, out _); + return; + } - _logger.LogDebug("Item {ItemName}", item.Name); + _logger.LogDebug("Item {ItemName}", item.Name); - // Metadata not refreshed yet and under retry limit. - if (item.ProviderIds.Keys.Count == 0 && container.RetryCount < MaxRetries) - { - _logger.LogDebug("Requeue {ItemName}, no provider ids", item.Name); - container.RetryCount++; - _itemProcessQueue.AddOrUpdate(key, container, (_, _) => container); - continue; - } + // Metadata not refreshed yet and under retry limit. + if (item.ProviderIds.Keys.Count == 0 && container.RetryCount < MaxRetries) + { + _logger.LogDebug("Requeue {ItemName}, no provider ids", item.Name); + container.RetryCount++; + _itemProcessQueue.AddOrUpdate(key, container, (_, _) => container); + continue; + } - _logger.LogDebug("Notifying for {ItemName}", item.Name); + _logger.LogDebug("Notifying for {ItemName}", item.Name); - // Send notification to each configured destination. - var dataObject = DataObjectHelpers - .GetBaseDataObject(_applicationHost, NotificationType.ItemAdded) - .AddBaseItemData(item); + // Send notification to each configured destination. + var dataObject = DataObjectHelpers + .GetBaseDataObject(_applicationHost, NotificationType.ItemAdded) + .AddBaseItemData(item); - var itemType = item.GetType(); - await _webhookSender.SendNotification(NotificationType.ItemAdded, dataObject, itemType) - .ConfigureAwait(false); + var itemType = item.GetType(); + await webhookSender.SendNotification(NotificationType.ItemAdded, dataObject, itemType) + .ConfigureAwait(false); - // Remove item from queue. - _itemProcessQueue.TryRemove(key, out _); + // Remove item from queue. + _itemProcessQueue.TryRemove(key, out _); + } + } } } diff --git a/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedNotifierEntryPoint.cs b/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedNotifierEntryPoint.cs index 2dd6153..a0417c9 100644 --- a/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedNotifierEntryPoint.cs +++ b/Jellyfin.Plugin.Webhook/Notifiers/ItemAddedNotifier/ItemAddedNotifierEntryPoint.cs @@ -1,15 +1,15 @@ -using System; +using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.Webhook.Helpers; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; +using Microsoft.Extensions.Hosting; namespace Jellyfin.Plugin.Webhook.Notifiers.ItemAddedNotifier; /// /// Notifier when a library item is added. /// -public class ItemAddedNotifierEntryPoint : IServerEntryPoint +public class ItemAddedNotifierEntryPoint : IHostedService { private readonly IItemAddedManager _itemAddedManager; private readonly ILibraryManager _libraryManager; @@ -27,41 +27,29 @@ public ItemAddedNotifierEntryPoint( _libraryManager = libraryManager; } - /// - public void Dispose() + private void ItemAddedHandler(object? sender, ItemChangeEventArgs itemChangeEventArgs) { - Dispose(true); - GC.SuppressFinalize(this); + // Never notify on virtual items. + if (itemChangeEventArgs.Item.IsVirtualItem) + { + return; + } + + _itemAddedManager.AddItem(itemChangeEventArgs.Item); } /// - public Task RunAsync() + public Task StartAsync(CancellationToken cancellationToken) { _libraryManager.ItemAdded += ItemAddedHandler; HandlebarsFunctionHelpers.RegisterHelpers(); return Task.CompletedTask; } - /// - /// Dispose. - /// - /// Dispose all assets. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _libraryManager.ItemAdded -= ItemAddedHandler; - } - } - - private void ItemAddedHandler(object? sender, ItemChangeEventArgs itemChangeEventArgs) + /// + public Task StopAsync(CancellationToken cancellationToken) { - // Never notify on virtual items. - if (itemChangeEventArgs.Item.IsVirtualItem) - { - return; - } - - _itemAddedManager.AddItem(itemChangeEventArgs.Item); + _libraryManager.ItemAdded -= ItemAddedHandler; + return Task.CompletedTask; } } diff --git a/Jellyfin.Plugin.Webhook/Notifiers/UserDataSavedNotifier/UserDataSavedNotifierEntryPoint.cs b/Jellyfin.Plugin.Webhook/Notifiers/UserDataSavedNotifier/UserDataSavedNotifierEntryPoint.cs index 57910ad..8fdf7ac 100644 --- a/Jellyfin.Plugin.Webhook/Notifiers/UserDataSavedNotifier/UserDataSavedNotifierEntryPoint.cs +++ b/Jellyfin.Plugin.Webhook/Notifiers/UserDataSavedNotifier/UserDataSavedNotifierEntryPoint.cs @@ -1,10 +1,12 @@ using System; +using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.Webhook.Destinations; using Jellyfin.Plugin.Webhook.Helpers; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.Webhook.Notifiers.UserDataSavedNotifier; @@ -12,9 +14,8 @@ namespace Jellyfin.Plugin.Webhook.Notifiers.UserDataSavedNotifier; /// /// User data saved notifier. /// -public class UserDataSavedNotifierEntryPoint : IServerEntryPoint +public class UserDataSavedNotifierEntryPoint : IHostedService { - private readonly IWebhookSender _webhookSender; private readonly IServerApplicationHost _applicationHost; private readonly IUserDataManager _userDataManager; private readonly IUserManager _userManager; @@ -23,13 +24,11 @@ public class UserDataSavedNotifierEntryPoint : IServerEntryPoint /// /// Initializes a new instance of the class. /// - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public UserDataSavedNotifierEntryPoint( - IWebhookSender webhookSender, IServerApplicationHost applicationHost, IUserDataManager userDataManager, ILogger logger, @@ -39,33 +38,6 @@ public UserDataSavedNotifierEntryPoint( _logger = logger; _userManager = userManager; _applicationHost = applicationHost; - _webhookSender = webhookSender; - } - - /// - public Task RunAsync() - { - _userDataManager.UserDataSaved += UserDataSavedHandler; - return Task.CompletedTask; - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose. - /// - /// Dispose all assets. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _userDataManager.UserDataSaved -= UserDataSavedHandler; - } } private async void UserDataSavedHandler(object? sender, UserDataSaveEventArgs eventArgs) @@ -98,12 +70,32 @@ private async void UserDataSavedHandler(object? sender, UserDataSaveEventArgs ev dataObject["NotificationUsername"] = user.Username; dataObject["UserId"] = user.Id; - await _webhookSender.SendNotification(NotificationType.UserDataSaved, dataObject, eventArgs.Item.GetType()) - .ConfigureAwait(false); + var scope = _applicationHost.ServiceProvider!.CreateAsyncScope(); + await using (scope.ConfigureAwait(false)) + { + var webhookSender = scope.ServiceProvider.GetRequiredService(); + await webhookSender + .SendNotification(NotificationType.UserDataSaved, dataObject, eventArgs.Item.GetType()) + .ConfigureAwait(false); + } } catch (Exception ex) { _logger.LogWarning(ex, "Unable to send notification"); } } + + /// + public Task StartAsync(CancellationToken cancellationToken) + { + _userDataManager.UserDataSaved += UserDataSavedHandler; + return Task.CompletedTask; + } + + /// + public Task StopAsync(CancellationToken cancellationToken) + { + _userDataManager.UserDataSaved -= UserDataSavedHandler; + return Task.CompletedTask; + } } diff --git a/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs b/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs index 5e2f773..1f9efb5 100644 --- a/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs +++ b/Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs @@ -13,13 +13,14 @@ using Jellyfin.Plugin.Webhook.Destinations.Smtp; using Jellyfin.Plugin.Webhook.Notifiers; using Jellyfin.Plugin.Webhook.Notifiers.ItemAddedNotifier; -using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; +using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Session; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Tasks; @@ -33,7 +34,7 @@ namespace Jellyfin.Plugin.Webhook; public class PluginServiceRegistrator : IPluginServiceRegistrator { /// - public void RegisterServices(IServiceCollection serviceCollection) + public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost) { serviceCollection.AddScoped, DiscordClient>(); serviceCollection.AddScoped, GenericClient>(); diff --git a/Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs b/Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs index 2429b43..f858432 100644 --- a/Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs +++ b/Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs @@ -1,16 +1,16 @@ -using System; +using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.Webhook.Configuration; using Jellyfin.Plugin.Webhook.Destinations.Mqtt; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; +using Microsoft.Extensions.Hosting; namespace Jellyfin.Plugin.Webhook; /// /// WebhookServerEntryPoint. /// -public class WebhookServerEntryPoint : IServerEntryPoint +public class WebhookServerEntryPoint : IHostedService { private readonly IMqttClients _mqttClients; @@ -29,33 +29,19 @@ public WebhookServerEntryPoint(IMqttClients mqttClients) private async void ConfigurationChanged(object? sender, BasePluginConfiguration e) { - await RunAsync().ConfigureAwait(false); + await _mqttClients.UpdateClients(Configuration.MqttOptions).ConfigureAwait(false); } - /// - /// Dispose Objects. - /// - public void Dispose() + /// + public async Task StartAsync(CancellationToken cancellationToken) { - Dispose(true); - GC.SuppressFinalize(this); + await _mqttClients.UpdateClients(Configuration.MqttOptions).ConfigureAwait(false); } - /// - /// Dispose. - /// - /// Dispose of objects. - protected virtual void Dispose(bool disposeAll) + /// + public Task StopAsync(CancellationToken cancellationToken) { WebhookPlugin.Instance!.ConfigurationChanged -= ConfigurationChanged; - } - - /// - /// Run this. - /// - /// Task. - public async Task RunAsync() - { - await _mqttClients.UpdateClients(Configuration.MqttOptions).ConfigureAwait(false); + return Task.CompletedTask; } } diff --git a/build.yaml b/build.yaml index 5363cb1..3b72946 100644 --- a/build.yaml +++ b/build.yaml @@ -2,8 +2,8 @@ name: "Webhook" guid: "71552A5A-5C5C-4350-A2AE-EBE451A30173" imageUrl: "https://repo.jellyfin.org/releases/plugin/images/jellyfin-plugin-webhook.png" version: "12" -targetAbi: "10.8.0.0" -framework: "net6.0" +targetAbi: "10.9.0.0" +framework: "net8.0" owner: "jellyfin" overview: "Sends notifications." description: > @@ -15,7 +15,7 @@ artifacts: - "Handlebars.dll" - "MailKit.dll" - "MimeKit.dll" - - "BouncyCastle.Crypto.dll" + - "BouncyCastle.Cryptography.dll" - "MQTTnet.dll" - "MQTTnet.Extensions.ManagedClient.dll" changelog: |-