aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj5
-rw-r--r--Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs198
-rw-r--r--Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs8
-rw-r--r--Emby.Server.Implementations/Notifications/InternalNotificationService.cs61
-rw-r--r--Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs21
-rw-r--r--Emby.Server.Implementations/Notifications/NotificationManager.cs296
6 files changed, 589 insertions, 0 deletions
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 567c9b99e..18e357679 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -115,6 +115,11 @@
<Compile Include="Library\Validators\YearsPostScanTask.cs" />
<Compile Include="Logging\PatternsLogger.cs" />
<Compile Include="News\NewsService.cs" />
+ <Compile Include="Notifications\CoreNotificationTypes.cs" />
+ <Compile Include="Notifications\IConfigurableNotificationService.cs" />
+ <Compile Include="Notifications\InternalNotificationService.cs" />
+ <Compile Include="Notifications\NotificationConfigurationFactory.cs" />
+ <Compile Include="Notifications\NotificationManager.cs" />
<Compile Include="Notifications\Notifications.cs" />
<Compile Include="Notifications\WebSocketNotifier.cs" />
<Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
diff --git a/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
new file mode 100644
index 000000000..f9fb98f85
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
@@ -0,0 +1,198 @@
+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.Server.Implementations.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(),
+ DefaultDescription = "{ReleaseNotes}",
+ DefaultTitle = "A new version of Emby Server has been installed.",
+ Variables = new List<string>{"Version"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.InstallationFailed.ToString(),
+ DefaultTitle = "{Name} installation failed.",
+ Variables = new List<string>{"Name", "Version"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.PluginInstalled.ToString(),
+ DefaultTitle = "{Name} was installed.",
+ Variables = new List<string>{"Name", "Version"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.PluginError.ToString(),
+ DefaultTitle = "{Name} has encountered an error.",
+ DefaultDescription = "{ErrorMessage}",
+ Variables = new List<string>{"Name", "ErrorMessage"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.PluginUninstalled.ToString(),
+ DefaultTitle = "{Name} was uninstalled.",
+ Variables = new List<string>{"Name", "Version"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.PluginUpdateInstalled.ToString(),
+ DefaultTitle = "{Name} was updated.",
+ DefaultDescription = "{ReleaseNotes}",
+ Variables = new List<string>{"Name", "ReleaseNotes", "Version"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.ServerRestartRequired.ToString(),
+ DefaultTitle = "Please restart Emby Server to finish updating."
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.TaskFailed.ToString(),
+ DefaultTitle = "{Name} failed.",
+ DefaultDescription = "{ErrorMessage}",
+ Variables = new List<string>{"Name", "ErrorMessage"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.NewLibraryContent.ToString(),
+ DefaultTitle = "{Name} has been added to your media library.",
+ Variables = new List<string>{"Name"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.AudioPlayback.ToString(),
+ DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.GamePlayback.ToString(),
+ DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.VideoPlayback.ToString(),
+ DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.AudioPlaybackStopped.ToString(),
+ DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.GamePlaybackStopped.ToString(),
+ DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.VideoPlaybackStopped.ToString(),
+ DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
+ Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.CameraImageUploaded.ToString(),
+ DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
+ Variables = new List<string>{"DeviceName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.UserLockedOut.ToString(),
+ DefaultTitle = "{UserName} has been locked out.",
+ Variables = new List<string>{"UserName"}
+ }
+ };
+
+ if (!_appHost.CanSelfUpdate)
+ {
+ knownTypes.Add(new NotificationTypeInfo
+ {
+ Type = NotificationType.ApplicationUpdateAvailable.ToString(),
+ DefaultTitle = "A new version of Emby Server is available for download."
+ });
+ }
+
+ foreach (var type in knownTypes)
+ {
+ Update(type);
+ }
+
+ var systemName = _localization.GetLocalizedString("CategorySystem");
+
+ 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("CategoryUser");
+ }
+ else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ note.Category = _localization.GetLocalizedString("CategoryPlugin");
+ }
+ else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ note.Category = _localization.GetLocalizedString("CategorySync");
+ }
+ else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ note.Category = _localization.GetLocalizedString("CategoryUser");
+ }
+ else
+ {
+ note.Category = _localization.GetLocalizedString("CategorySystem");
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
new file mode 100644
index 000000000..d74667c48
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
@@ -0,0 +1,8 @@
+namespace Emby.Server.Implementations.Notifications
+{
+ public interface IConfigurableNotificationService
+ {
+ bool IsHidden { get; }
+ bool IsEnabled(string notificationType);
+ }
+}
diff --git a/Emby.Server.Implementations/Notifications/InternalNotificationService.cs b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
new file mode 100644
index 000000000..61c564f18
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
@@ -0,0 +1,61 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Model.Notifications;
+using System.Threading;
+using System.Threading.Tasks;
+using System;
+
+namespace Emby.Server.Implementations.Notifications
+{
+ public class InternalNotificationService : INotificationService, IConfigurableNotificationService
+ {
+ private readonly INotificationsRepository _repo;
+
+ public InternalNotificationService(INotificationsRepository repo)
+ {
+ _repo = repo;
+ }
+
+ public string Name
+ {
+ get { return "Dashboard Notifications"; }
+ }
+
+ public Task SendNotification(UserNotification request, CancellationToken cancellationToken)
+ {
+ return _repo.AddNotification(new Notification
+ {
+ Date = request.Date,
+ Description = request.Description,
+ Level = request.Level,
+ Name = request.Name,
+ Url = request.Url,
+ UserId = request.User.Id.ToString("N")
+
+ }, cancellationToken);
+ }
+
+ public bool IsEnabledForUser(User user)
+ {
+ return user.Policy.IsAdministrator;
+ }
+
+ public bool IsHidden
+ {
+ get { return true; }
+ }
+
+ public bool IsEnabled(string notificationType)
+ {
+ if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+ if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
new file mode 100644
index 000000000..a7c5b1233
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
@@ -0,0 +1,21 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Notifications;
+using System.Collections.Generic;
+
+namespace Emby.Server.Implementations.Notifications
+{
+ public class NotificationConfigurationFactory : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new List<ConfigurationStore>
+ {
+ new ConfigurationStore
+ {
+ Key = "notifications",
+ ConfigurationType = typeof (NotificationOptions)
+ }
+ };
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Notifications/NotificationManager.cs b/Emby.Server.Implementations/Notifications/NotificationManager.cs
new file mode 100644
index 000000000..db7980497
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/NotificationManager.cs
@@ -0,0 +1,296 @@
+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;
+
+namespace Emby.Server.Implementations.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)
+ {
+ var notificationType = request.NotificationType;
+
+ var options = string.IsNullOrWhiteSpace(notificationType) ?
+ null :
+ GetConfiguration().GetOptions(notificationType);
+
+ var users = GetUserIds(request, options)
+ .Select(i => _userManager.GetUserById(i));
+
+ var title = GetTitle(request, options);
+ var description = GetDescription(request, options);
+
+ 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<string> 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.ToString("N"));
+ case SendToUserType.All:
+ return _userManager.Users.Select(i => i.Id.ToString("N"));
+ case SendToUserType.Custom:
+ return request.UserIds;
+ default:
+ throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value);
+ }
+ }
+
+ if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType))
+ {
+ var config = GetConfiguration();
+
+ return _userManager.Users
+ .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy))
+ .Select(i => i.Id.ToString("N"));
+ }
+
+ 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 string GetTitle(NotificationRequest request, NotificationOption options)
+ {
+ var title = request.Name;
+
+ // If empty, grab from options
+ if (string.IsNullOrEmpty(title))
+ {
+ if (!string.IsNullOrEmpty(request.NotificationType))
+ {
+ if (options != null)
+ {
+ title = options.Title;
+ }
+ }
+ }
+
+ // If still empty, grab default
+ if (string.IsNullOrEmpty(title))
+ {
+ if (!string.IsNullOrEmpty(request.NotificationType))
+ {
+ var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
+
+ if (info != null)
+ {
+ title = info.DefaultTitle;
+ }
+ }
+ }
+
+ title = title ?? string.Empty;
+
+ foreach (var pair in request.Variables)
+ {
+ var token = "{" + pair.Key + "}";
+
+ title = title.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return title;
+ }
+
+ private string GetDescription(NotificationRequest request, NotificationOption options)
+ {
+ var text = request.Description;
+
+ // If empty, grab from options
+ if (string.IsNullOrEmpty(text))
+ {
+ if (!string.IsNullOrEmpty(request.NotificationType))
+ {
+ if (options != null)
+ {
+ text = options.Description;
+ }
+ }
+ }
+
+ // If still empty, grab default
+ if (string.IsNullOrEmpty(text))
+ {
+ if (!string.IsNullOrEmpty(request.NotificationType))
+ {
+ var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
+
+ if (info != null)
+ {
+ text = info.DefaultDescription;
+ }
+ }
+ }
+
+ text = text ?? string.Empty;
+
+ foreach (var pair in request.Variables)
+ {
+ var token = "{" + pair.Key + "}";
+
+ text = text.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return text;
+ }
+
+ 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;
+ }
+
+ var configurable = service as IConfigurableNotificationService;
+
+ if (configurable != null)
+ {
+ return configurable.IsEnabled(notificationType);
+ }
+
+ return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
+ }
+
+ public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories)
+ {
+ _services = services.ToArray();
+ _typeFactories = notificationTypeFactories.ToArray();
+ }
+
+ public IEnumerable<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<NotificationServiceInfo> GetNotificationServices()
+ {
+ return _services.Where(i =>
+ {
+ var configurable = i as IConfigurableNotificationService;
+
+ return configurable == null || !configurable.IsHidden;
+
+ }).Select(i => new NotificationServiceInfo
+ {
+ Name = i.Name,
+ Id = i.Name.GetMD5().ToString("N")
+
+ }).OrderBy(i => i.Name);
+ }
+ }
+}