diff options
| author | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
|---|---|---|
| committer | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
| commit | 48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch) | |
| tree | 8dae77a31670a888d733484cb17dd4077d5444e8 /Emby.Notifications | |
| parent | c32d8656382a0eacb301692e0084377fc433ae9b (diff) | |
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'Emby.Notifications')
| -rw-r--r-- | Emby.Notifications/Api/NotificationsService.cs | 182 | ||||
| -rw-r--r-- | Emby.Notifications/CoreNotificationTypes.cs | 160 | ||||
| -rw-r--r-- | Emby.Notifications/Emby.Notifications.csproj | 14 | ||||
| -rw-r--r-- | Emby.Notifications/NotificationConfigurationFactory.cs | 21 | ||||
| -rw-r--r-- | Emby.Notifications/NotificationManager.cs | 206 | ||||
| -rw-r--r-- | Emby.Notifications/Notifications.cs | 299 | ||||
| -rw-r--r-- | Emby.Notifications/Properties/AssemblyInfo.cs | 36 |
7 files changed, 918 insertions, 0 deletions
diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs new file mode 100644 index 000000000..d09552ebc --- /dev/null +++ b/Emby.Notifications/Api/NotificationsService.cs @@ -0,0 +1,182 @@ +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.Dto; + +namespace Emby.Notifications.Api +{ + [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")] + public class GetNotifications : IReturn<NotificationResult> + { + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string UserId { get; set; } + + [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsRead { get; set; } + + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + } + + public class Notification + { + public string Id { get; set; } + + public string UserId { get; set; } + + public DateTime Date { get; set; } + + public bool IsRead { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public string Url { get; set; } + + public NotificationLevel Level { get; set; } + } + + public class NotificationResult + { + public Notification[] Notifications { get; set; } + public int TotalRecordCount { get; set; } + } + + public class NotificationsSummary + { + public int UnreadCount { get; set; } + public NotificationLevel MaxUnreadNotificationLevel { get; set; } + } + + [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")] + public class GetNotificationsSummary : IReturn<NotificationsSummary> + { + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string UserId { get; set; } + } + + [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] + public class GetNotificationTypes : IReturn<List<NotificationTypeInfo>> + { + } + + [Route("/Notifications/Services", "GET", Summary = "Gets notification types")] + public class GetNotificationServices : IReturn<List<NameIdPair>> + { + } + + [Route("/Notifications/Admin", "POST", Summary = "Sends a notification to all admin users")] + public class AddAdminNotification : IReturnVoid + { + [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Name { get; set; } + + [ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Description { get; set; } + + [ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string ImageUrl { get; set; } + + [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Url { get; set; } + + [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public NotificationLevel Level { get; set; } + } + + [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")] + public class MarkRead : IReturnVoid + { + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string UserId { get; set; } + + [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; } + } + + [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; } + + [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; } + } + + [Authenticated] + public class NotificationsService : IService + { + private readonly INotificationManager _notificationManager; + private readonly IUserManager _userManager; + + public NotificationsService(INotificationManager notificationManager, IUserManager userManager) + { + _notificationManager = notificationManager; + _userManager = userManager; + } + + public object Get(GetNotificationTypes request) + { + return _notificationManager.GetNotificationTypes(); + } + + public object Get(GetNotificationServices request) + { + return _notificationManager.GetNotificationServices().ToList(); + } + + public object Get(GetNotificationsSummary request) + { + return new NotificationsSummary + { + + }; + } + + public Task Post(AddAdminNotification request) + { + // This endpoint really just exists as post of a real with sickbeard + return AddNotification(request); + } + + private Task AddNotification(AddAdminNotification request) + { + var notification = new NotificationRequest + { + Date = DateTime.UtcNow, + Description = request.Description, + Level = request.Level, + Name = request.Name, + Url = request.Url, + UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray() + }; + + return _notificationManager.SendNotification(notification, CancellationToken.None); + } + + public void Post(MarkRead request) + { + } + + public void Post(MarkUnread request) + { + } + + public object Get(GetNotifications request) + { + return new NotificationResult(); + } + } +} diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs new file mode 100644 index 000000000..b45a75b1e --- /dev/null +++ b/Emby.Notifications/CoreNotificationTypes.cs @@ -0,0 +1,160 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Globalization; + +namespace Emby.Notifications +{ + public class CoreNotificationTypes : INotificationTypeFactory + { + private readonly ILocalizationManager _localization; + private readonly IServerApplicationHost _appHost; + + public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost) + { + _localization = localization; + _appHost = appHost; + } + + public IEnumerable<NotificationTypeInfo> GetNotificationTypes() + { + var knownTypes = new List<NotificationTypeInfo> + { + new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateInstalled.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.InstallationFailed.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginInstalled.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginError.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginUninstalled.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginUpdateInstalled.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.ServerRestartRequired.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.TaskFailed.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.NewLibraryContent.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.AudioPlayback.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.GamePlayback.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.VideoPlayback.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.AudioPlaybackStopped.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.GamePlaybackStopped.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.VideoPlaybackStopped.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.CameraImageUploaded.ToString() + }, + + new NotificationTypeInfo + { + Type = NotificationType.UserLockedOut.ToString() + } + }; + + if (!_appHost.CanSelfUpdate) + { + knownTypes.Add(new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateAvailable.ToString() + }); + } + + foreach (var type in knownTypes) + { + Update(type); + } + + var systemName = _localization.GetLocalizedString("System"); + + return knownTypes.OrderByDescending(i => string.Equals(i.Category, systemName, StringComparison.OrdinalIgnoreCase)) + .ThenBy(i => i.Category) + .ThenBy(i => i.Name); + } + + private void Update(NotificationTypeInfo note) + { + note.Name = _localization.GetLocalizedString("NotificationOption" + note.Type) ?? note.Type; + + note.IsBasedOnUserEvent = note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1; + + if (note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("User"); + } + else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("Plugin"); + } + else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("Sync"); + } + else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("User"); + } + else + { + note.Category = _localization.GetLocalizedString("System"); + } + } + } +} diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj new file mode 100644 index 000000000..0a07c419b --- /dev/null +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp2.1</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> + <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/Emby.Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs new file mode 100644 index 000000000..2d464910e --- /dev/null +++ b/Emby.Notifications/NotificationConfigurationFactory.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Notifications; +using System.Collections.Generic; + +namespace Emby.Notifications +{ + public class NotificationConfigurationFactory : IConfigurationFactory + { + public IEnumerable<ConfigurationStore> GetConfigurations() + { + return new ConfigurationStore[] + { + new ConfigurationStore + { + Key = "notifications", + ConfigurationType = typeof (NotificationOptions) + } + }; + } + } +} diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs new file mode 100644 index 000000000..bb348d267 --- /dev/null +++ b/Emby.Notifications/NotificationManager.cs @@ -0,0 +1,206 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Dto; + +namespace Emby.Notifications +{ + public class NotificationManager : INotificationManager + { + private readonly ILogger _logger; + private readonly IUserManager _userManager; + private readonly IServerConfigurationManager _config; + + private INotificationService[] _services; + private INotificationTypeFactory[] _typeFactories; + + public NotificationManager(ILogManager logManager, IUserManager userManager, IServerConfigurationManager config) + { + _userManager = userManager; + _config = config; + _logger = logManager.GetLogger(GetType().Name); + } + + private NotificationOptions GetConfiguration() + { + return _config.GetConfiguration<NotificationOptions>("notifications"); + } + + public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) + { + return SendNotification(request, null, cancellationToken); + } + + public Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken) + { + var notificationType = request.NotificationType; + + var options = string.IsNullOrEmpty(notificationType) ? + null : + GetConfiguration().GetOptions(notificationType); + + var users = GetUserIds(request, options) + .Select(i => _userManager.GetUserById(i)) + .Where(i => relatedItem == null || relatedItem.IsVisibleStandalone(i)) + .ToArray(); + + var title = request.Name; + var description = request.Description; + + var tasks = _services.Where(i => IsEnabled(i, notificationType)) + .Select(i => SendNotification(request, i, users, title, description, cancellationToken)); + + return Task.WhenAll(tasks); + } + + private Task SendNotification(NotificationRequest request, + INotificationService service, + IEnumerable<User> users, + string title, + string description, + CancellationToken cancellationToken) + { + users = users.Where(i => IsEnabledForUser(service, i)) + .ToList(); + + var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken)); + + return Task.WhenAll(tasks); + } + + private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption options) + { + if (request.SendToUserMode.HasValue) + { + switch (request.SendToUserMode.Value) + { + case SendToUserType.Admins: + return _userManager.Users.Where(i => i.Policy.IsAdministrator) + .Select(i => i.Id); + case SendToUserType.All: + return _userManager.Users.Select(i => i.Id); + case SendToUserType.Custom: + return request.UserIds; + default: + throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value); + } + } + + if (options != null && !string.IsNullOrEmpty(request.NotificationType)) + { + var config = GetConfiguration(); + + return _userManager.Users + .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy)) + .Select(i => i.Id); + } + + return request.UserIds; + } + + private async Task SendNotification(NotificationRequest request, + INotificationService service, + string title, + string description, + User user, + CancellationToken cancellationToken) + { + var notification = new UserNotification + { + Date = request.Date, + Description = description, + Level = request.Level, + Name = title, + Url = request.Url, + User = user + }; + + _logger.Debug("Sending notification via {0} to user {1}", service.Name, user.Name); + + try + { + await service.SendNotification(notification, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification to {0}", ex, service.Name); + } + } + + private bool IsEnabledForUser(INotificationService service, User user) + { + try + { + return service.IsEnabledForUser(user); + } + catch (Exception ex) + { + _logger.ErrorException("Error in IsEnabledForUser", ex); + return false; + } + } + + private bool IsEnabled(INotificationService service, string notificationType) + { + if (string.IsNullOrEmpty(notificationType)) + { + return true; + } + + return GetConfiguration().IsServiceEnabled(service.Name, notificationType); + } + + public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories) + { + _services = services.ToArray(); + _typeFactories = notificationTypeFactories.ToArray(); + } + + public List<NotificationTypeInfo> GetNotificationTypes() + { + var list = _typeFactories.Select(i => + { + try + { + return i.GetNotificationTypes().ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error in GetNotificationTypes", ex); + return new List<NotificationTypeInfo>(); + } + + }).SelectMany(i => i).ToList(); + + var config = GetConfiguration(); + + foreach (var i in list) + { + i.Enabled = config.IsEnabled(i.Type); + } + + return list; + } + + public IEnumerable<NameIdPair> GetNotificationServices() + { + return _services.Select(i => new NameIdPair + { + Name = i.Name, + Id = i.Name.GetMD5().ToString("N") + + }).OrderBy(i => i.Name); + } + } +} diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs new file mode 100644 index 000000000..64863eb39 --- /dev/null +++ b/Emby.Notifications/Notifications.cs @@ -0,0 +1,299 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Updates; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using MediaBrowser.Model.Tasks; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Model.Threading; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Events; + +namespace Emby.Notifications +{ + /// <summary> + /// Creates notifications for various system events + /// </summary> + public class Notifications : IServerEntryPoint + { + private readonly IInstallationManager _installationManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + private readonly ITaskManager _taskManager; + private readonly INotificationManager _notificationManager; + + private readonly ILibraryManager _libraryManager; + private readonly ISessionManager _sessionManager; + private readonly IServerApplicationHost _appHost; + private readonly ITimerFactory _timerFactory; + + private ITimer LibraryUpdateTimer { get; set; } + private readonly object _libraryChangedSyncLock = new object(); + + private readonly IConfigurationManager _config; + private readonly IDeviceManager _deviceManager; + private readonly ILocalizationManager _localization; + private readonly IActivityManager _activityManager; + + private string[] _coreNotificationTypes; + + public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory) + { + _installationManager = installationManager; + _userManager = userManager; + _logger = logger; + _taskManager = taskManager; + _notificationManager = notificationManager; + _libraryManager = libraryManager; + _sessionManager = sessionManager; + _appHost = appHost; + _config = config; + _deviceManager = deviceManager; + _timerFactory = timerFactory; + _localization = localization; + _activityManager = activityManager; + + _coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray(); + } + + public void Run() + { + _libraryManager.ItemAdded += _libraryManager_ItemAdded; + _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; + _activityManager.EntryCreated += _activityManager_EntryCreated; + } + + private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) + { + var type = NotificationType.ServerRestartRequired.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type, + Name = string.Format(_localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), _appHost.Name) + }; + + await SendNotification(notification, null).ConfigureAwait(false); + } + + private async void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> 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<NotificationOptions>("notifications"); + } + + async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e) + { + // This notification is for users who can't auto-update (aka running as service) + if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate) + { + return; + } + + var type = NotificationType.ApplicationUpdateAvailable.ToString(); + + var notification = new NotificationRequest + { + Description = "Please see emby.media for details.", + NotificationType = type, + Name = _localization.GetLocalizedString("NewVersionIsAvailable") + }; + + await SendNotification(notification, null).ConfigureAwait(false); + } + + private readonly List<BaseItem> _itemsAdded = new List<BaseItem>(); + void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) + { + if (!FilterItem(e.Item)) + { + return; + } + + lock (_libraryChangedSyncLock) + { + if (LibraryUpdateTimer == null) + { + LibraryUpdateTimer = _timerFactory.Create(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<BaseItem> items; + + lock (_libraryChangedSyncLock) + { + items = _itemsAdded.ToList(); + _itemsAdded.Clear(); + DisposeLibraryUpdateTimer(); + } + + items = items.Take(10).ToList(); + + foreach (var item in items) + { + var notification = new NotificationRequest + { + NotificationType = NotificationType.NewLibraryContent.ToString(), + Name = string.Format(_localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), GetItemName(item)), + Description = item.Overview + }; + + await SendNotification(notification, item).ConfigureAwait(false); + } + } + + public static string GetItemName(BaseItem item) + { + var name = item.Name; + var episode = item as Episode; + if (episode != null) + { + if (episode.IndexNumber.HasValue) + { + name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + } + if (episode.ParentIndexNumber.HasValue) + { + name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + } + } + + var hasSeries = item as IHasSeriesName; + + if (hasSeries != null) + { + name = hasSeries.SeriesName + " - " + name; + } + + var hasAlbumArtist = item as IHasAlbumArtist; + if (hasAlbumArtist != null) + { + var artists = hasAlbumArtist.AlbumArtists; + + if (artists.Length > 0) + { + name = artists[0] + " - " + name; + } + } + else + { + var hasArtist = item as IHasArtist; + if (hasArtist != null) + { + var artists = hasArtist.Artists; + + if (artists.Length > 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.ErrorException("Error sending notification", ex); + } + } + + public void Dispose() + { + DisposeLibraryUpdateTimer(); + + _libraryManager.ItemAdded -= _libraryManager_ItemAdded; + _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged; + _activityManager.EntryCreated -= _activityManager_EntryCreated; + } + + private void DisposeLibraryUpdateTimer() + { + if (LibraryUpdateTimer != null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; + } + } + } +} diff --git a/Emby.Notifications/Properties/AssemblyInfo.cs b/Emby.Notifications/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d35c8b289 --- /dev/null +++ b/Emby.Notifications/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Emby.Notifications")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Emby.Notifications")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4d1d313b-60bb-4e11-acf9-cda6745266ef")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] |
