From 5dc3874ebdeac26d7be885b9a44ca1321b4b25cf Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 20 Dec 2019 21:30:51 +0100 Subject: Enable nullable reference types for Emby.Photos and Emby.Notifications * Enable TreatWarningsAsErrors for Emby.Notifications * Add analyzers to Emby.Notifications --- Emby.Notifications/NotificationEntryPoint.cs | 326 +++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 Emby.Notifications/NotificationEntryPoint.cs (limited to 'Emby.Notifications/NotificationEntryPoint.cs') diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs new file mode 100644 index 000000000..ab92822fa --- /dev/null +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Notifications; +using Microsoft.Extensions.Logging; + +namespace Emby.Notifications +{ + /// + /// Creates notifications for various system events. + /// + public class NotificationEntryPoint : IServerEntryPoint + { + private readonly ILogger _logger; + private readonly IActivityManager _activityManager; + private readonly ILocalizationManager _localization; + private readonly INotificationManager _notificationManager; + private readonly ILibraryManager _libraryManager; + private readonly IServerApplicationHost _appHost; + private readonly IConfigurationManager _config; + + private readonly object _libraryChangedSyncLock = new object(); + private readonly List _itemsAdded = new List(); + + private Timer? _libraryUpdateTimer; + + private string[] _coreNotificationTypes; + + private bool _disposed = false; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The activity manager. + /// The localization manager. + /// The notification manager. + /// The library manager. + /// The application host. + /// The configuration manager. + public NotificationEntryPoint( + ILogger logger, + IActivityManager activityManager, + ILocalizationManager localization, + INotificationManager notificationManager, + ILibraryManager libraryManager, + IServerApplicationHost appHost, + IConfigurationManager config) + { + _logger = logger; + _activityManager = activityManager; + _localization = localization; + _notificationManager = notificationManager; + _libraryManager = libraryManager; + _appHost = appHost; + _config = config; + + _coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray(); + } + + /// + public Task RunAsync() + { + _libraryManager.ItemAdded += OnLibraryManagerItemAdded; + _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged; + _activityManager.EntryCreated += OnActivityManagerEntryCreated; + + return Task.CompletedTask; + } + + private async void OnAppHostHasPendingRestartChanged(object sender, EventArgs e) + { + var type = NotificationType.ServerRestartRequired.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type, + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), + _appHost.Name) + }; + + await SendNotification(notification, null).ConfigureAwait(false); + } + + private async void OnActivityManagerEntryCreated(object sender, GenericEventArgs e) + { + var entry = e.Argument; + + var type = entry.Type; + + if (string.IsNullOrEmpty(type) || !_coreNotificationTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) + { + return; + } + + var userId = e.Argument.UserId; + + if (!userId.Equals(Guid.Empty) && !GetOptions().IsEnabledToMonitorUser(type, userId)) + { + return; + } + + var notification = new NotificationRequest + { + NotificationType = type, + Name = entry.Name, + Description = entry.Overview + }; + + await SendNotification(notification, null).ConfigureAwait(false); + } + + private NotificationOptions GetOptions() + { + return _config.GetConfiguration("notifications"); + } + + private async void OnAppHostHasUpdateAvailableChanged(object sender, EventArgs e) + { + if (!_appHost.HasUpdateAvailable) + { + return; + } + + var type = NotificationType.ApplicationUpdateAvailable.ToString(); + + var notification = new NotificationRequest + { + Description = "Please see jellyfin.media for details.", + NotificationType = type, + Name = _localization.GetLocalizedString("NewVersionIsAvailable") + }; + + await SendNotification(notification, null).ConfigureAwait(false); + } + + private void OnLibraryManagerItemAdded(object sender, ItemChangeEventArgs e) + { + if (!FilterItem(e.Item)) + { + return; + } + + lock (_libraryChangedSyncLock) + { + if (_libraryUpdateTimer == null) + { + _libraryUpdateTimer = new Timer( + LibraryUpdateTimerCallback, + null, + 5000, + Timeout.Infinite); + } + else + { + _libraryUpdateTimer.Change(5000, Timeout.Infinite); + } + + _itemsAdded.Add(e.Item); + } + } + + private bool FilterItem(BaseItem item) + { + if (item.IsFolder) + { + return false; + } + + if (!item.HasPathProtocol) + { + return false; + } + + if (item is IItemByName) + { + return false; + } + + return item.SourceType == SourceType.Library; + } + + private async void LibraryUpdateTimerCallback(object state) + { + List items; + + lock (_libraryChangedSyncLock) + { + items = _itemsAdded.ToList(); + _itemsAdded.Clear(); + _libraryUpdateTimer!.Dispose(); // Shouldn't be null as it just set off this callback + _libraryUpdateTimer = null; + } + + items = items.Take(10).ToList(); + + foreach (var item in items) + { + var notification = new NotificationRequest + { + NotificationType = NotificationType.NewLibraryContent.ToString(), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), + GetItemName(item)), + Description = item.Overview + }; + + await SendNotification(notification, item).ConfigureAwait(false); + } + } + + private static string GetItemName(BaseItem item) + { + var name = item.Name; + if (item is Episode episode) + { + if (episode.IndexNumber.HasValue) + { + name = string.Format( + CultureInfo.InvariantCulture, + "Ep{0} - {1}", + episode.IndexNumber.Value, + name); + } + + if (episode.ParentIndexNumber.HasValue) + { + name = string.Format( + CultureInfo.InvariantCulture, + "S{0}, {1}", + episode.ParentIndexNumber.Value, + name); + } + } + + if (item is IHasSeries hasSeries) + { + name = hasSeries.SeriesName + " - " + name; + } + + if (item is IHasAlbumArtist hasAlbumArtist) + { + var artists = hasAlbumArtist.AlbumArtists; + + if (artists.Count > 0) + { + name = artists[0] + " - " + name; + } + } + else if (item is IHasArtist hasArtist) + { + var artists = hasArtist.Artists; + + if (artists.Count > 0) + { + name = artists[0] + " - " + name; + } + } + + return name; + } + + private async Task SendNotification(NotificationRequest notification, BaseItem? relatedItem) + { + try + { + await _notificationManager.SendNotification(notification, relatedItem, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending notification"); + } + } + + /// + 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 disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _libraryUpdateTimer?.Dispose(); + } + + _libraryUpdateTimer = null; + + _libraryManager.ItemAdded -= OnLibraryManagerItemAdded; + _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged; + _activityManager.EntryCreated -= OnActivityManagerEntryCreated; + + _disposed = true; + } + } +} -- cgit v1.2.3 From 867835a474d16bfe6ab6a27214de49478ed2a05b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 8 Feb 2020 22:25:44 +0100 Subject: Fix build --- Emby.Notifications/Api/NotificationsService.cs | 24 +++++++++++----------- Emby.Notifications/NotificationEntryPoint.cs | 9 ++++++-- Emby.Notifications/NotificationManager.cs | 2 +- .../Activity/ActivityLogEntryPoint.cs | 10 ++++----- Emby.Server.Implementations/ApplicationHost.cs | 5 ++++- 5 files changed, 28 insertions(+), 22 deletions(-) (limited to 'Emby.Notifications/NotificationEntryPoint.cs') diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs index f9e923d10..f2f381838 100644 --- a/Emby.Notifications/Api/NotificationsService.cs +++ b/Emby.Notifications/Api/NotificationsService.cs @@ -22,7 +22,7 @@ namespace Emby.Notifications.Api public class GetNotifications : IReturn { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsRead { get; set; } @@ -36,26 +36,26 @@ namespace Emby.Notifications.Api public class Notification { - public string Id { get; set; } + public string Id { get; set; } = string.Empty; - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; public DateTime Date { get; set; } public bool IsRead { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string Url { get; set; } + public string Url { get; set; } = string.Empty; public NotificationLevel Level { get; set; } } public class NotificationResult { - public IReadOnlyList Notifications { get; set; } + public IReadOnlyList Notifications { get; set; } = Array.Empty(); public int TotalRecordCount { get; set; } } @@ -71,7 +71,7 @@ namespace Emby.Notifications.Api public class GetNotificationsSummary : IReturn { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; } [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] @@ -107,20 +107,20 @@ namespace Emby.Notifications.Api public class MarkRead : IReturnVoid { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } + public string Ids { get; set; } = string.Empty; } [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")] public class MarkUnread : IReturnVoid { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } + public string Ids { get; set; } = string.Empty; } [Authenticated] diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index ab92822fa..befecc570 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -227,7 +227,12 @@ namespace Emby.Notifications } } - private static string GetItemName(BaseItem item) + /// + /// Creates a human readable name for the item. + /// + /// The item. + /// A human readable name for the item. + public static string GetItemName(BaseItem item) { var name = item.Name; if (item is Episode episode) @@ -298,7 +303,7 @@ namespace Emby.Notifications } /// - /// Releases unmanaged and - optionally - managed resources. + /// 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 disposing) diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index 836aa3e72..639a5e1aa 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -32,7 +32,7 @@ namespace Emby.Notifications /// Initializes a new instance of the class. /// /// The logger. - /// the user manager. + /// The user manager. /// The server configuration manager. public NotificationManager( ILogger logger, diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index ac8af66a2..4664eadd3 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -29,7 +29,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { - public class ActivityLogEntryPoint : IServerEntryPoint + public sealed class ActivityLogEntryPoint : IServerEntryPoint { private readonly ILogger _logger; private readonly IInstallationManager _installationManager; @@ -39,7 +39,6 @@ namespace Emby.Server.Implementations.Activity private readonly ILocalizationManager _localization; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; - private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; /// @@ -64,8 +63,7 @@ namespace Emby.Server.Implementations.Activity ILocalizationManager localization, IInstallationManager installationManager, ISubtitleManager subManager, - IUserManager userManager, - IServerApplicationHost appHost) + IUserManager userManager) { _logger = logger; _sessionManager = sessionManager; @@ -76,7 +74,6 @@ namespace Emby.Server.Implementations.Activity _installationManager = installationManager; _subManager = subManager; _userManager = userManager; - _appHost = appHost; } public Task RunAsync() @@ -141,7 +138,7 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, - Notifications.Notifications.GetItemName(e.Item)), + Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -533,6 +530,7 @@ namespace Emby.Server.Implementations.Activity private void CreateLogEntry(ActivityLogEntry entry) => _activityManager.Create(entry); + /// public void Dispose() { _taskManager.TaskCompleted -= OnTaskCompleted; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e2df8877a..91e635b46 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -825,7 +825,10 @@ namespace Emby.Server.Implementations UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); - NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager); + NotificationManager = new NotificationManager( + LoggerFactory.CreateLogger(), + UserManager, + ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager)); -- cgit v1.2.3