Skip to content

Commit

Permalink
Add notification for item deleted (#285)
Browse files Browse the repository at this point in the history
  • Loading branch information
chiragkrishna committed Sep 9, 2024
1 parent 1927ff2 commit 5a1f418
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 1 deletion.
1 change: 1 addition & 0 deletions Jellyfin.Plugin.Webhook/Configuration/Web/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 6 additions & 1 deletion Jellyfin.Plugin.Webhook/Destinations/NotificationType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,10 @@ public enum NotificationType
/// <summary>
/// User data saved.
/// </summary>
UserDataSaved = 23
UserDataSaved = 23,

/// <summary>
/// Item Deleted notification.
/// </summary>
ItemDeleted = 24
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;

namespace Jellyfin.Plugin.Webhook.Notifiers.ItemDeletedNotifier;

/// <summary>
/// Item deleted manager interface.
/// </summary>
public interface IItemDeletedManager
{
/// <summary>
/// Process the current queue.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task ProcessItemsAsync();

/// <summary>
/// Add item to process queue.
/// </summary>
/// <param name="item">The deleted item.</param>
public void AddItem(BaseItem item);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Jellyfin.Plugin.Webhook.Destinations;
using Jellyfin.Plugin.Webhook.Helpers;
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;

/// <inheritdoc />
public class ItemDeletedManager : IItemDeletedManager
{
private readonly ILogger<ItemDeletedManager> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IServerApplicationHost _applicationHost;
private readonly ConcurrentDictionary<Guid, BaseItem> _itemProcessQueue;

/// <summary>
/// Initializes a new instance of the <see cref="ItemDeletedManager"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{ItemDeletedManager}"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="applicationHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
public ItemDeletedManager(
ILogger<ItemDeletedManager> logger,
ILibraryManager libraryManager,
IServerApplicationHost applicationHost)
{
_logger = logger;
_libraryManager = libraryManager;
_applicationHost = applicationHost;
_itemProcessQueue = new ConcurrentDictionary<Guid, BaseItem>();
}

/// <inheritdoc />
public async Task ProcessItemsAsync()
{
_logger.LogDebug("ProcessItemsAsync");
// Attempt to process all items in queue.
if (!_itemProcessQueue.IsEmpty)
{
var scope = _applicationHost.ServiceProvider!.CreateAsyncScope();
await using (scope.ConfigureAwait(false))
{
var webhookSender = scope.ServiceProvider.GetRequiredService<IWebhookSender>();
foreach (var (key, item) in _itemProcessQueue)
{
if (item != null)
{
_logger.LogDebug("Item {ItemName}", item.Name);

// Skip notification if item type is Studio
if (item.GetType().Name == "Studio")
{
_logger.LogDebug("Skipping notification for item type Studio");
_itemProcessQueue.TryRemove(key, out _);
continue;
}

_logger.LogDebug("Notifying for {ItemName}", item.Name);

// Send notification to each configured destination.
var dataObject = DataObjectHelpers
.GetBaseDataObject(_applicationHost, NotificationType.ItemDeleted)
.AddBaseItemData(item);

var itemType = item.GetType();
await webhookSender.SendNotification(NotificationType.ItemDeleted, dataObject, itemType)
.ConfigureAwait(false);

// Remove item from queue.
_itemProcessQueue.TryRemove(key, out _);
}
}
}
}
}

/// <inheritdoc />
public void AddItem(BaseItem item)
{
_itemProcessQueue.TryAdd(item.Id, item);
_logger.LogDebug("Queued {ItemName} for notification", item.Name);
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Notifier when a library item is deleted.
/// </summary>
public class ItemDeletedNotifierEntryPoint : IHostedService
{
private readonly IItemDeletedManager _itemDeletedManager;
private readonly ILibraryManager _libraryManager;

/// <summary>
/// Initializes a new instance of the <see cref="ItemDeletedNotifierEntryPoint"/> class.
/// </summary>
/// <param name="itemDeletedManager">Instance of the <see cref="IItemDeletedManager"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
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);
}

/// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken)
{
_libraryManager.ItemRemoved += ItemDeletedHandler;
return Task.CompletedTask;
}

/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken)
{
_libraryManager.ItemRemoved -= ItemDeletedHandler;
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Scheduled task that processes item deleted events.
/// </summary>
public class ItemDeletedScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private const int RecheckIntervalSec = 30;
private readonly IItemDeletedManager _itemDeletedManager;
private readonly ILocalizationManager _localizationManager;

/// <summary>
/// Initializes a new instance of the <see cref="ItemDeletedScheduledTask"/> class.
/// </summary>
/// <param name="itemDeletedManager">Instance of the <see cref="IItemDeletedManager"/> interface.</param>
/// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public ItemDeletedScheduledTask(
IItemDeletedManager itemDeletedManager,
ILocalizationManager localizationManager)
{
_itemDeletedManager = itemDeletedManager;
_localizationManager = localizationManager;
}

/// <inheritdoc />
public string Name => "Webhook Item Deleted Notifier";

/// <inheritdoc />
public string Key => "WebhookItemDeleted";

/// <inheritdoc />
public string Description => "Processes item deleted queue";

/// <inheritdoc />
public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");

/// <inheritdoc />
public bool IsHidden => false;

/// <inheritdoc />
public bool IsEnabled => true;

/// <inheritdoc />
public bool IsLogged => false;

/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
return _itemDeletedManager.ProcessItemsAsync();
}

/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[]
{
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerInterval,
IntervalTicks = TimeSpan.FromSeconds(RecheckIntervalSec).Ticks
}
};
}
}
3 changes: 3 additions & 0 deletions Jellyfin.Plugin.Webhook/PluginServiceRegistrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,6 +59,7 @@ public void RegisterServices(IServiceCollection serviceCollection, IServerApplic
// Library consumers.
serviceCollection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureNotifier>();
serviceCollection.AddSingleton<IItemAddedManager, ItemAddedManager>();
serviceCollection.AddSingleton<IItemDeletedManager, ItemDeletedManager>();

// Security consumers.
serviceCollection.AddScoped<IEventConsumer<AuthenticationRequestEventArgs>, AuthenticationFailureNotifier>();
Expand Down Expand Up @@ -90,6 +92,7 @@ public void RegisterServices(IServiceCollection serviceCollection, IServerApplic

serviceCollection.AddHostedService<WebhookServerEntryPoint>();
serviceCollection.AddHostedService<ItemAddedNotifierEntryPoint>();
serviceCollection.AddHostedService<ItemDeletedNotifierEntryPoint>();
serviceCollection.AddHostedService<UserDataSavedNotifierEntryPoint>();
}
}

0 comments on commit 5a1f418

Please sign in to comment.