From 547291f04865f23090986667b5a802cd89ea003d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 25 Apr 2014 16:15:50 -0400 Subject: rework notifications infrastructure --- MediaBrowser.Api/Images/ImageService.cs | 6 +- MediaBrowser.Api/Images/RemoteImageService.cs | 6 +- MediaBrowser.Api/ItemLookupService.cs | 2 +- MediaBrowser.Api/ItemRefreshService.cs | 2 +- MediaBrowser.Api/ItemUpdateService.cs | 2 +- MediaBrowser.Api/Library/LibraryService.cs | 12 +- MediaBrowser.Api/Music/InstantMixService.cs | 8 +- MediaBrowser.Api/NotificationsService.cs | 45 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 +- MediaBrowser.Api/SimilarItemsHelper.cs | 2 +- .../UserLibrary/BaseItemsByNameService.cs | 4 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 6 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 16 +- MediaBrowser.Api/VideosService.cs | 4 +- MediaBrowser.Controller/Dto/IDtoService.cs | 9 - MediaBrowser.Controller/Library/ILibraryManager.cs | 5 + .../MediaBrowser.Controller.csproj | 2 + .../Notifications/INotificationManager.cs | 49 +++ .../Notifications/INotificationsRepository.cs | 19 +- .../Notifications/NotificationUpdateEventArgs.cs | 4 +- .../Notifications/UserNotification.cs | 21 + MediaBrowser.Dlna/Server/ContentDirectory.cs | 49 +-- MediaBrowser.Model/ApiClient/IApiClient.cs | 9 +- MediaBrowser.Model/Notifications/Notification.cs | 31 +- .../Dto/DtoService.cs | 38 -- .../EntryPoints/Notifications/Notifier.cs | 172 ++++---- .../Notifications/RemoteNotifications.cs | 32 +- .../EntryPoints/Notifications/WebSocketNotifier.cs | 4 +- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 2 +- .../MediaBrowser.Server.Implementations.csproj | 4 +- .../Notifications/InternalNotificationService.cs | 42 ++ .../Notifications/NotificationManager.cs | 94 ++++ .../Notifications/SqliteNotificationsRepository.cs | 485 ++++++++++++++++++++ .../Persistence/SqliteNotificationsRepository.cs | 490 --------------------- MediaBrowser.ServerApplication/ApplicationHost.cs | 8 + 35 files changed, 910 insertions(+), 778 deletions(-) create mode 100644 MediaBrowser.Controller/Notifications/INotificationManager.cs create mode 100644 MediaBrowser.Controller/Notifications/UserNotification.cs create mode 100644 MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs create mode 100644 MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs create mode 100644 MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs delete mode 100644 MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 562da40ee5..e85a5a6808 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -358,7 +358,7 @@ namespace MediaBrowser.Api.Images /// System.Object. public object Get(GetItemImageInfos request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var result = GetItemImageInfos(item); @@ -493,7 +493,9 @@ namespace MediaBrowser.Api.Images /// System.Object. public object Get(GetItemImage request) { - var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.RootFolder : _dtoService.GetItemByDtoId(request.Id); + var item = string.IsNullOrEmpty(request.Id) ? + _libraryManager.RootFolder : + _libraryManager.GetItemById(request.Id); return GetImage(request, item); } diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 36b509c740..c765063577 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -175,7 +175,7 @@ namespace MediaBrowser.Api.Images public object Get(GetRemoteImageProviders request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var result = GetImageProviders(item); @@ -201,7 +201,7 @@ namespace MediaBrowser.Api.Images public object Get(GetRemoteImages request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var result = GetRemoteImageResult(item, request); @@ -269,7 +269,7 @@ namespace MediaBrowser.Api.Images /// The request. public void Post(DownloadRemoteImage request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var task = DownloadRemoteImage(item, request); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index 62596287e2..b600c3b464 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -124,7 +124,7 @@ namespace MediaBrowser.Api public object Get(GetExternalIdInfos request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var infos = _providerManager.GetExternalIdInfos(item).ToList(); diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index 97e43f018c..3816332b26 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -250,7 +250,7 @@ namespace MediaBrowser.Api /// Task. private async Task RefreshItem(RefreshItem request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var options = GetRefreshOptions(request); diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 8bc06b6577..5f05fdc3f2 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Api private async Task UpdateItem(UpdateItem request) { - var item = _dtoService.GetItemByDtoId(request.ItemId); + var item = _libraryManager.GetItemById(request.ItemId); var newLockData = request.LockData ?? false; var dontFetchMetaChanged = item.IsLocked != newLockData; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 967bc1fbac..0599b45d58 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -284,7 +284,7 @@ namespace MediaBrowser.Api.Library public object Get(GetFile request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var locationType = item.LocationType; if (locationType == LocationType.Remote || locationType == LocationType.Virtual) { @@ -350,7 +350,7 @@ namespace MediaBrowser.Api.Library /// Task{BaseItemDto[]}. public List GetAncestors(GetAncestors request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var baseItemDtos = new List(); @@ -504,7 +504,7 @@ namespace MediaBrowser.Api.Library private Task DeleteItem(DeleteItem request) { - var item = _dtoService.GetItemByDtoId(request.Id); + var item = _libraryManager.GetItemById(request.Id); var session = GetSession(_sessionManager); @@ -593,7 +593,7 @@ namespace MediaBrowser.Api.Library ? (request.UserId.HasValue ? user.RootFolder : (Folder)_libraryManager.RootFolder) - : _dtoService.GetItemByDtoId(request.Id, request.UserId); + : _libraryManager.GetItemById(request.Id); var originalItem = item; @@ -661,7 +661,7 @@ namespace MediaBrowser.Api.Library ? (request.UserId.HasValue ? user.RootFolder : (Folder)_libraryManager.RootFolder) - : _dtoService.GetItemByDtoId(request.Id, request.UserId); + : _libraryManager.GetItemById(request.Id); var originalItem = item; @@ -771,7 +771,7 @@ namespace MediaBrowser.Api.Library ? (userId.HasValue ? user.RootFolder : (Folder)_libraryManager.RootFolder) - : _dtoService.GetItemByDtoId(id, userId); + : _libraryManager.GetItemById(id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)) diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index c39811bb66..f50c87f47a 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -38,18 +38,20 @@ namespace MediaBrowser.Api.Music private readonly IUserManager _userManager; private readonly IDtoService _dtoService; + private readonly ILibraryManager _libraryManager; private readonly IMusicManager _musicManager; - public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager) + public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager, ILibraryManager libraryManager) { _userManager = userManager; _dtoService = dtoService; _musicManager = musicManager; + _libraryManager = libraryManager; } public object Get(GetInstantMixFromSong request) { - var item = (Audio)_dtoService.GetItemByDtoId(request.Id); + var item = (Audio)_libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); @@ -60,7 +62,7 @@ namespace MediaBrowser.Api.Music public object Get(GetInstantMixFromAlbum request) { - var album = (MusicAlbum)_dtoService.GetItemByDtoId(request.Id); + var album = (MusicAlbum)_libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs index a71a85395a..796fcdab11 100644 --- a/MediaBrowser.Api/NotificationsService.cs +++ b/MediaBrowser.Api/NotificationsService.cs @@ -2,7 +2,7 @@ using MediaBrowser.Model.Notifications; using ServiceStack; using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -28,17 +28,17 @@ namespace MediaBrowser.Api public class GetNotificationsSummary : IReturn { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } + public string UserId { get; set; } } [Route("/Notifications/{UserId}", "POST", Summary = "Adds a notifications")] - public class AddUserNotification : IReturn + public class AddUserNotification : IReturnVoid { [ApiMember(Name = "Id", Description = "The Id of the new notification. If unspecified one will be provided.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public Guid? Id { get; set; } + public string Id { get; set; } [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid UserId { get; set; } + public string UserId { get; set; } [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string Name { get; set; } @@ -49,12 +49,6 @@ namespace MediaBrowser.Api [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string Url { get; set; } - [ApiMember(Name = "Category", Description = "The notification's category", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Category { get; set; } - - [ApiMember(Name = "RelatedId", Description = "The notification's related id (item)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string RelatedId { get; set; } - [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public NotificationLevel Level { get; set; } } @@ -63,7 +57,7 @@ namespace MediaBrowser.Api public class MarkRead : IReturnVoid { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid UserId { get; set; } + 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; } @@ -73,7 +67,7 @@ namespace MediaBrowser.Api public class MarkUnread : IReturnVoid { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid UserId { get; set; } + 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; } @@ -82,17 +76,19 @@ namespace MediaBrowser.Api public class NotificationsService : BaseApiService { private readonly INotificationsRepository _notificationsRepo; + private readonly INotificationManager _notificationManager; - public NotificationsService(INotificationsRepository notificationsRepo) + public NotificationsService(INotificationsRepository notificationsRepo, INotificationManager notificationManager) { _notificationsRepo = notificationsRepo; + _notificationManager = notificationManager; } - public object Post(AddUserNotification request) + public void Post(AddUserNotification request) { var task = AddNotification(request); - return ToOptimizedResult(task.Result); + Task.WaitAll(task); } public object Get(GetNotificationsSummary request) @@ -102,24 +98,19 @@ namespace MediaBrowser.Api return result; } - private async Task AddNotification(AddUserNotification request) + private async Task AddNotification(AddUserNotification request) { - var notification = new Notification + var notification = new NotificationRequest { - Id = request.Id ?? Guid.NewGuid(), Date = DateTime.UtcNow, Description = request.Description, Level = request.Level, Name = request.Name, Url = request.Url, - UserId = request.UserId, - Category = request.Category, - RelatedId = request.RelatedId + UserIds = new List { request.UserId } }; - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); - - return notification; + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); } public void Post(MarkRead request) @@ -136,9 +127,9 @@ namespace MediaBrowser.Api Task.WaitAll(task); } - private Task MarkRead(string idList, Guid userId, bool read) + private Task MarkRead(string idList, string userId, bool read) { - var ids = idList.Split(',').Select(i => new Guid(i)); + var ids = idList.Split(','); return _notificationsRepo.MarkRead(ids, userId, read, CancellationToken.None); } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index aafda7812d..3fd1714165 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1336,8 +1336,8 @@ namespace MediaBrowser.Api.Playback } var item = string.IsNullOrEmpty(request.MediaSourceId) ? - DtoService.GetItemByDtoId(request.Id) : - DtoService.GetItemByDtoId(request.MediaSourceId); + LibraryManager.GetItemById(request.Id) : + LibraryManager.GetItemById(request.MediaSourceId); if (user != null && item.GetPlayAccess(user) != PlayAccess.Full) { diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 1f02a63a09..664a344ce0 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Api var item = string.IsNullOrEmpty(request.Id) ? (request.UserId.HasValue ? user.RootFolder : - libraryManager.RootFolder) : dtoService.GetItemByDtoId(request.Id, request.UserId); + libraryManager.RootFolder) : libraryManager.GetItemById(request.Id); var fields = request.GetItemFields().ToList(); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index d21014dfed..c2b8069cb2 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -61,14 +61,14 @@ namespace MediaBrowser.Api.UserLibrary if (request.UserId.HasValue) { user = UserManager.GetUserById(request.UserId.Value); - item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id); + item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId); libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList(); } else { - item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId); + item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId); libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList(); } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 74ce4a0a7f..f1b8b2d52b 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -373,11 +373,9 @@ namespace MediaBrowser.Api.UserLibrary /// private IEnumerable GetItemsToSerialize(GetItems request, User user) { - var userId = user == null ? (Guid?)null : user.Id; - var item = string.IsNullOrEmpty(request.ParentId) ? user == null ? _libraryManager.RootFolder : user.RootFolder : - _dtoService.GetItemByDtoId(request.ParentId, userId); + _libraryManager.GetItemById(request.ParentId); // Default list type = children IEnumerable items; @@ -386,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary { var idList = request.Ids.Split(',').ToList(); - items = idList.Select(i => _dtoService.GetItemByDtoId(i, userId)); + items = idList.Select(i => _libraryManager.GetItemById(i)); } else if (request.Recursive) diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 649f2ae02d..d145dd054f 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -463,7 +463,9 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _dtoService.GetItemByDtoId(request.Id, user.Id); + var item = string.IsNullOrEmpty(request.Id) ? + user.RootFolder : + _libraryManager.GetItemById(request.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -529,7 +531,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _dtoService.GetItemByDtoId(request.Id, user.Id); + var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -559,7 +561,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _dtoService.GetItemByDtoId(request.Id, user.Id); + var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -597,7 +599,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _dtoService.GetItemByDtoId(request.Id, user.Id); + var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); var items = _libraryManager.GetIntros(item, user); @@ -651,7 +653,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(userId); - var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _dtoService.GetItemByDtoId(itemId, user.Id); + var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); var key = item.GetUserDataKey(); @@ -701,7 +703,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(userId); - var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _dtoService.GetItemByDtoId(itemId, user.Id); + var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); var key = item.GetUserDataKey(); @@ -871,7 +873,7 @@ namespace MediaBrowser.Api.UserLibrary /// Task. private async Task UpdatePlayedStatus(User user, string itemId, bool wasPlayed, DateTime? datePlayed) { - var item = _dtoService.GetItemByDtoId(itemId, user.Id); + var item = _libraryManager.GetItemById(itemId); if (wasPlayed) { diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index 940c82540c..f62e37f798 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Api ? (request.UserId.HasValue ? user.RootFolder : _libraryManager.RootFolder) - : _dtoService.GetItemByDtoId(request.Id, request.UserId); + : _libraryManager.GetItemById(request.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)) @@ -104,7 +104,7 @@ namespace MediaBrowser.Api private async Task RemoveAlternateVersions(DeleteAlternateSources request) { - var video = (Video)_dtoService.GetItemByDtoId(request.Id); + var video = (Video)_libraryManager.GetItemById(request.Id); foreach (var link in video.GetLinkedAlternateVersions()) { diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index d07deaf233..b2081fe6bd 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -2,7 +2,6 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; -using System; using System.Collections.Generic; namespace MediaBrowser.Controller.Dto @@ -33,14 +32,6 @@ namespace MediaBrowser.Controller.Dto /// UserItemDataDto. UserItemDataDto GetUserItemDataDto(UserItemData data); - /// - /// Gets the item by dto id. - /// - /// The id. - /// The user id. - /// BaseItem. - BaseItem GetItemByDtoId(string id, Guid? userId = null); - /// /// Attaches the primary image aspect ratio. /// diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 680317237e..9e18dc9b02 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -342,5 +342,10 @@ namespace MediaBrowser.Controller.Library DeleteFileLocation = true }); } + + public static BaseItem GetItemById(this ILibraryManager manager, string id) + { + return manager.GetItemById(new Guid(id)); + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index cc3f3d08b7..417c03f277 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -167,8 +167,10 @@ + + diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs new file mode 100644 index 0000000000..8ed62f59f7 --- /dev/null +++ b/MediaBrowser.Controller/Notifications/INotificationManager.cs @@ -0,0 +1,49 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Notifications; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Notifications +{ + public interface INotificationManager + { + /// + /// Sends the notification. + /// + /// The request. + /// The cancellation token. + /// Task. + Task SendNotification(NotificationRequest request, CancellationToken cancellationToken); + + /// + /// Adds the parts. + /// + /// The services. + void AddParts(IEnumerable services); + } + + public interface INotificationService + { + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Sends the notification. + /// + /// The request. + /// The cancellation token. + /// Task. + Task SendNotification(UserNotification request, CancellationToken cancellationToken); + + /// + /// Determines whether [is enabled for user] [the specified user identifier]. + /// + /// The user. + /// true if [is enabled for user] [the specified user identifier]; otherwise, false. + bool IsEnabledForUser(User user); + } +} diff --git a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs index 7a4b69b528..87b89e79c8 100644 --- a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs +++ b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs @@ -1,8 +1,7 @@ -using System.Threading; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Notifications; +using MediaBrowser.Model.Notifications; using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Notifications @@ -44,7 +43,7 @@ namespace MediaBrowser.Controller.Notifications /// The id. /// The user id. /// Notification. - Notification GetNotification(Guid id, Guid userId); + Notification GetNotification(string id, string userId); /// /// Adds the notification. @@ -54,14 +53,6 @@ namespace MediaBrowser.Controller.Notifications /// Task. Task AddNotification(Notification notification, CancellationToken cancellationToken); - /// - /// Updates the notification. - /// - /// The notification. - /// The cancellation token. - /// Task. - Task UpdateNotification(Notification notification, CancellationToken cancellationToken); - /// /// Marks the read. /// @@ -70,13 +61,13 @@ namespace MediaBrowser.Controller.Notifications /// if set to true [is read]. /// The cancellation token. /// Task. - Task MarkRead(IEnumerable notificationIdList, Guid userId, bool isRead, CancellationToken cancellationToken); + Task MarkRead(IEnumerable notificationIdList, string userId, bool isRead, CancellationToken cancellationToken); /// /// Gets the notifications summary. /// /// The user id. /// NotificationsSummary. - NotificationsSummary GetNotificationsSummary(Guid userId); + NotificationsSummary GetNotificationsSummary(string userId); } } diff --git a/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs b/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs index e156ca26a3..d8a6634dfc 100644 --- a/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs +++ b/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs @@ -10,8 +10,8 @@ namespace MediaBrowser.Controller.Notifications public class NotificationReadEventArgs : EventArgs { - public Guid[] IdList { get; set; } - public Guid UserId { get; set; } + public string[] IdList { get; set; } + public string UserId { get; set; } public bool IsRead { get; set; } } } diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs new file mode 100644 index 0000000000..d035a3995c --- /dev/null +++ b/MediaBrowser.Controller/Notifications/UserNotification.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Notifications; +using System; + +namespace MediaBrowser.Controller.Notifications +{ + public class UserNotification + { + public string Name { get; set; } + + public string Description { get; set; } + + public string Url { get; set; } + + public NotificationLevel Level { get; set; } + + public DateTime Date { get; set; } + + public User User { get; set; } + } +} diff --git a/MediaBrowser.Dlna/Server/ContentDirectory.cs b/MediaBrowser.Dlna/Server/ContentDirectory.cs index e657a2ff65..93c5cdec80 100644 --- a/MediaBrowser.Dlna/Server/ContentDirectory.cs +++ b/MediaBrowser.Dlna/Server/ContentDirectory.cs @@ -8,9 +8,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Threading; namespace MediaBrowser.Dlna.Server { @@ -27,9 +25,6 @@ namespace MediaBrowser.Dlna.Server private readonly IEventManager _eventManager; - private int _systemUpdateId; - private Timer _systemUpdateTimer; - public ContentDirectory(IDlnaManager dlna, IUserDataManager userDataManager, IImageProcessor imageProcessor, @@ -49,9 +44,16 @@ namespace MediaBrowser.Dlna.Server _userManager = userManager; _eventManager = eventManager; _logger = logManager.GetLogger("DlnaContentDirectory"); + } + + private int SystemUpdateId + { + get + { + var now = DateTime.UtcNow; - _systemUpdateTimer = new Timer(SystemUdpateTimerCallback, null, Timeout.Infinite, - Convert.ToInt64(TimeSpan.FromMinutes(60).TotalMilliseconds)); + return now.Year + now.DayOfYear + now.Hour; + } } public string GetContentDirectoryXml(IDictionary headers) @@ -80,7 +82,7 @@ namespace MediaBrowser.Dlna.Server _imageProcessor, _userDataManager, user, - _systemUpdateId) + SystemUpdateId) .ProcessControlRequest(request); } @@ -110,40 +112,9 @@ namespace MediaBrowser.Dlna.Server return _userManager.Users.First(); } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private async void SystemUdpateTimerCallback(object state) - { - var values = new Dictionary(); - - _systemUpdateId++; - values["SystemUpdateID"] = _systemUpdateId.ToString(_usCulture); - - try - { - await _eventManager.TriggerEvent("upnp:event", values).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error sending system update notification", ex); - } - } - - private readonly object _disposeLock = new object(); public void Dispose() { - lock (_disposeLock) - { - DisposeUpdateTimer(); - } - } - private void DisposeUpdateTimer() - { - if (_systemUpdateTimer != null) - { - _systemUpdateTimer.Dispose(); - _systemUpdateTimer = null; - } } } } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 3233731063..bbd07817c4 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -140,19 +140,12 @@ namespace MediaBrowser.Model.ApiClient /// Task. Task MarkNotificationsRead(string userId, IEnumerable notificationIdList, bool isRead); - /// - /// Updates the notification. - /// - /// The notification. - /// Task. - Task UpdateNotification(Notification notification); - /// /// Adds the notification. /// /// The notification. /// Task{Notification}. - Task AddNotification(Notification notification); + Task SendNotification(NotificationRequest notification); /// /// Gets the notifications summary. diff --git a/MediaBrowser.Model/Notifications/Notification.cs b/MediaBrowser.Model/Notifications/Notification.cs index 14f55b6e17..511227cbf5 100644 --- a/MediaBrowser.Model/Notifications/Notification.cs +++ b/MediaBrowser.Model/Notifications/Notification.cs @@ -1,12 +1,13 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Model.Notifications { public class Notification { - public Guid Id { get; set; } + public string Id { get; set; } - public Guid UserId { get; set; } + public string UserId { get; set; } public DateTime Date { get; set; } @@ -17,16 +18,32 @@ namespace MediaBrowser.Model.Notifications public string Description { get; set; } public string Url { get; set; } - - public string Category { get; set; } - - public string RelatedId { get; set; } public NotificationLevel Level { get; set; } public Notification() { - Id = Guid.NewGuid(); + Date = DateTime.UtcNow; + } + } + + public class NotificationRequest + { + public string Name { get; set; } + + public string Description { get; set; } + + public string Url { get; set; } + + public NotificationLevel Level { get; set; } + + public List UserIds { get; set; } + + public DateTime Date { get; set; } + + public NotificationRequest() + { + UserIds = new List(); Date = DateTime.UtcNow; } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index ee6bbb04f0..480cb39dba 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -556,44 +556,6 @@ namespace MediaBrowser.Server.Implementations.Dto return dto; } - - /// - /// Gets a BaseItem based upon it's client-side item id - /// - /// The id. - /// The user id. - /// BaseItem. - public BaseItem GetItemByDtoId(string id, Guid? userId = null) - { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException("id"); - } - - BaseItem item = null; - - if (userId.HasValue) - { - item = _libraryManager.GetItemById(new Guid(id)); - } - - // If we still don't find it, look within individual user views - if (item == null && !userId.HasValue) - { - foreach (var user in _userManager.Users) - { - item = GetItemByDtoId(id, user.Id); - - if (item != null) - { - break; - } - } - } - - return item; - } - /// /// Sets simple property values on a DTOBaseItem /// diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs index fcb4406abe..2a3668dfaa 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs @@ -20,20 +20,20 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications /// public class Notifications : IServerEntryPoint { - private readonly INotificationsRepository _notificationsRepo; private readonly IInstallationManager _installationManager; private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ITaskManager _taskManager; + private readonly INotificationManager _notificationManager; - public Notifications(IInstallationManager installationManager, INotificationsRepository notificationsRepo, IUserManager userManager, ILogger logger, ITaskManager taskManager) + public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager) { _installationManager = installationManager; - _notificationsRepo = notificationsRepo; _userManager = userManager; _logger = logger; _taskManager = taskManager; + _notificationManager = notificationManager; } public void Run() @@ -49,21 +49,25 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications async void _userManager_UserCreated(object sender, GenericEventArgs e) { - var notification = new Notification + var userIds = _userManager + .Users + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest { - UserId = e.Argument.Id, - Category = "UserCreated", + UserIds = userIds, Name = "Welcome to Media Browser!", Description = "Check back here for more notifications." }; try { - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error adding notification", ex); + _logger.ErrorException("Error sending notification", ex); } } @@ -73,29 +77,27 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications if (result.Status == TaskCompletionStatus.Failed) { - foreach (var user in _userManager - .Users - .Where(i => i.Configuration.IsAdministrator) - .ToList()) + var userIds = _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest + { + UserIds = userIds, + Name = result.Name + " failed", + Description = result.ErrorMessage, + Level = NotificationLevel.Error + }; + + try { - var notification = new Notification - { - UserId = user.Id, - Category = "ScheduledTaskFailed", - Name = result.Name + " failed", - RelatedId = result.Name, - Description = result.ErrorMessage, - Level = NotificationLevel.Error - }; - - try - { - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error adding notification", ex); - } + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification", ex); } } } @@ -104,27 +106,25 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var plugin = e.Argument; - foreach (var user in _userManager - .Users - .Where(i => i.Configuration.IsAdministrator) - .ToList()) + var userIds = _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest { - var notification = new Notification - { - UserId = user.Id, - Category = "PluginUninstalled", - Name = plugin.Name + " has been uninstalled", - RelatedId = plugin.Id.ToString() - }; + UserIds = userIds, + Name = plugin.Name + " has been uninstalled" + }; - try - { - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error adding notification", ex); - } + try + { + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification", ex); } } @@ -132,28 +132,26 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var installationInfo = e.InstallationInfo; - foreach (var user in _userManager - .Users - .Where(i => i.Configuration.IsAdministrator) - .ToList()) + var userIds = _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest { - var notification = new Notification - { - UserId = user.Id, - Category = "PackageInstallationCompleted", - Name = installationInfo.Name + " " + installationInfo.Version + " was installed", - RelatedId = installationInfo.Name, - Description = e.PackageVersionInfo.description - }; + UserIds = userIds, + Name = installationInfo.Name + " " + installationInfo.Version + " was installed", + Description = e.PackageVersionInfo.description + }; - try - { - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error adding notification", ex); - } + try + { + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification", ex); } } @@ -161,29 +159,27 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var installationInfo = e.InstallationInfo; - foreach (var user in _userManager + var userIds = _userManager .Users .Where(i => i.Configuration.IsAdministrator) - .ToList()) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest { - var notification = new Notification - { - UserId = user.Id, - Category = "PackageInstallationFailed", - Level = NotificationLevel.Error, - Name = installationInfo.Name + " " + installationInfo.Version + " installation failed", - RelatedId = installationInfo.Name, - Description = e.Exception.Message - }; + UserIds = userIds, + Level = NotificationLevel.Error, + Name = installationInfo.Name + " " + installationInfo.Version + " installation failed", + Description = e.Exception.Message + }; - try - { - await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error adding notification", ex); - } + try + { + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification", ex); } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs index 723e4fdd31..d5b7f5b363 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Plugins; @@ -26,22 +25,23 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; private readonly IJsonSerializer _json; - private readonly INotificationsRepository _notificationsRepo; private readonly IUserManager _userManager; private readonly IFileSystem _fileSystem; private readonly TimeSpan _frequency = TimeSpan.FromHours(6); private readonly TimeSpan _maxAge = TimeSpan.FromDays(31); - public RemoteNotifications(IApplicationPaths appPaths, ILogger logger, IHttpClient httpClient, IJsonSerializer json, INotificationsRepository notificationsRepo, IUserManager userManager, IFileSystem fileSystem) + private readonly INotificationManager _notificationManager; + + public RemoteNotifications(IApplicationPaths appPaths, ILogger logger, IHttpClient httpClient, IJsonSerializer json, IUserManager userManager, IFileSystem fileSystem, INotificationManager notificationManager) { _appPaths = appPaths; _logger = logger; _httpClient = httpClient; _json = json; - _notificationsRepo = notificationsRepo; _userManager = userManager; _fileSystem = fileSystem; + _notificationManager = notificationManager; } /// @@ -107,21 +107,19 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications .Where(i => string.Equals(i.active, "1") && i.date.ToUniversalTime() > lastRunTime && (DateTime.UtcNow - i.date.ToUniversalTime()) <= _maxAge) .ToList(); - foreach (var user in _userManager.Users.ToList()) + var userIds = _userManager.Users.Select(i => i.Id.ToString("N")).ToList(); + + foreach (var notification in notificationList) { - foreach (var notification in notificationList) + await _notificationManager.SendNotification(new NotificationRequest { - await _notificationsRepo.AddNotification(new Notification - { - Category = notification.category, - Date = notification.date, - Name = notification.name, - Description = notification.description, - Url = notification.url, - UserId = user.Id - - }, CancellationToken.None).ConfigureAwait(false); - } + Date = notification.date, + Name = notification.name, + Description = notification.description, + Url = notification.url, + UserIds = userIds + + }, CancellationToken.None).ConfigureAwait(false); } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs index c5a93720c7..42aadf62e5 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs @@ -30,9 +30,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications void _notificationsRepo_NotificationsMarkedRead(object sender, NotificationReadEventArgs e) { - var list = e.IdList.Select(i => i.ToString("N")).ToList(); + var list = e.IdList.ToList(); - list.Add(e.UserId.ToString("N")); + list.Add(e.UserId); list.Add(e.IsRead.ToString().ToLower()); var msg = string.Join("|", list.ToArray()); diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index ac1927931f..28d4769714 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -117,7 +117,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio /// /// The list. /// true if the specified list contains music; otherwise, false. - public static bool ContainsMusic(IEnumerable list) + private static bool ContainsMusic(IEnumerable list) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 505325812c..7d6ddbb6ed 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -185,11 +185,13 @@ + + - + diff --git a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs new file mode 100644 index 0000000000..56cb52f108 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs @@ -0,0 +1,42 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Notifications; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Notifications +{ + public class InternalNotificationService : INotificationService + { + 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 true; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs new file mode 100644 index 0000000000..d6caa5b68e --- /dev/null +++ b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs @@ -0,0 +1,94 @@ +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; + +namespace MediaBrowser.Server.Implementations.Notifications +{ + public class NotificationManager : INotificationManager + { + private readonly ILogger _logger; + private readonly IUserManager _userManager; + private INotificationService[] _services; + + public NotificationManager(ILogManager logManager, IUserManager userManager) + { + _userManager = userManager; + _logger = logManager.GetLogger(GetType().Name); + } + + public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) + { + var users = request.UserIds.Select(i => _userManager.GetUserById(new Guid(i))); + + var tasks = _services.Select(i => SendNotification(request, i, users, cancellationToken)); + + return Task.WhenAll(tasks); + } + + public Task SendNotification(NotificationRequest request, + INotificationService service, + IEnumerable users, + CancellationToken cancellationToken) + { + users = users.Where(i => IsEnabledForUser(service, i)) + .ToList(); + + var tasks = users.Select(i => SendNotification(request, service, i, cancellationToken)); + + return Task.WhenAll(tasks); + + } + + public async Task SendNotification(NotificationRequest request, + INotificationService service, + User user, + CancellationToken cancellationToken) + { + var notification = new UserNotification + { + Date = request.Date, + Description = request.Description, + Level = request.Level, + Name = request.Name, + 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; + } + } + + public void AddParts(IEnumerable services) + { + _services = services.ToArray(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs new file mode 100644 index 0000000000..2424a66521 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs @@ -0,0 +1,485 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using MediaBrowser.Server.Implementations.Persistence; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Notifications +{ + public class SqliteNotificationsRepository : INotificationsRepository + { + private IDbConnection _connection; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _appPaths; + + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + + public SqliteNotificationsRepository(ILogManager logManager, IServerApplicationPaths appPaths) + { + _appPaths = appPaths; + _logger = logManager.GetLogger(GetType().Name); + } + + public event EventHandler NotificationAdded; + public event EventHandler NotificationsMarkedRead; + public event EventHandler NotificationUpdated; + + private IDbCommand _replaceNotificationCommand; + private IDbCommand _markReadCommand; + + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db"); + + _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists Notifications (Id GUID NOT NULL, UserId GUID NOT NULL, Date DATETIME NOT NULL, Name TEXT NOT NULL, Description TEXT, Url TEXT, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT, PRIMARY KEY (Id, UserId))", + "create index if not exists idx_Notifications on Notifications(Id, UserId)", + + //pragmas + "pragma temp_store = memory", + + "pragma shrink_memory" + }; + + _connection.RunQueries(queries, _logger); + + PrepareStatements(); + } + + private void PrepareStatements() + { + _replaceNotificationCommand = _connection.CreateCommand(); + _replaceNotificationCommand.CommandText = "replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"; + + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Date"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Name"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Description"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Url"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Level"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Category"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@RelatedId"); + + _markReadCommand = _connection.CreateCommand(); + _markReadCommand.CommandText = "update Notifications set IsRead=@IsRead where Id=@Id and UserId=@UserId"; + + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); + } + + /// + /// Gets the notifications. + /// + /// The query. + /// NotificationResult. + public NotificationResult GetNotifications(NotificationQuery query) + { + var result = new NotificationResult(); + + using (var cmd = _connection.CreateCommand()) + { + var clauses = new List(); + + if (query.IsRead.HasValue) + { + clauses.Add("IsRead=@IsRead"); + cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = query.IsRead.Value; + } + + clauses.Add("UserId=@UserId"); + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(query.UserId); + + var whereClause = " where " + string.Join(" And ", clauses.ToArray()); + + cmd.CommandText = string.Format("select count(Id) from Notifications{0};select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause); + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + { + if (reader.Read()) + { + result.TotalRecordCount = reader.GetInt32(0); + } + + if (reader.NextResult()) + { + var notifications = GetNotifications(reader); + + if (query.StartIndex.HasValue) + { + notifications = notifications.Skip(query.StartIndex.Value); + } + + if (query.Limit.HasValue) + { + notifications = notifications.Take(query.Limit.Value); + } + + result.Notifications = notifications.ToArray(); + } + } + + return result; + } + } + + public NotificationsSummary GetNotificationsSummary(string userId) + { + var result = new NotificationsSummary(); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select Level from Notifications where UserId=@UserId and IsRead=@IsRead"; + + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(userId); + cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = false; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + { + var levels = new List(); + + while (reader.Read()) + { + levels.Add(GetLevel(reader, 0)); + } + + result.UnreadCount = levels.Count; + + if (levels.Count > 0) + { + result.MaxUnreadNotificationLevel = levels.Max(); + } + } + + return result; + } + } + + /// + /// Gets the notifications. + /// + /// The reader. + /// IEnumerable{Notification}. + private IEnumerable GetNotifications(IDataReader reader) + { + while (reader.Read()) + { + yield return GetNotification(reader); + } + } + + private Notification GetNotification(IDataReader reader) + { + var notification = new Notification + { + Id = reader.GetGuid(0).ToString("N"), + UserId = reader.GetGuid(1).ToString("N"), + Date = reader.GetDateTime(2).ToUniversalTime(), + Name = reader.GetString(3) + }; + + if (!reader.IsDBNull(4)) + { + notification.Description = reader.GetString(4); + } + + if (!reader.IsDBNull(5)) + { + notification.Url = reader.GetString(5); + } + + notification.Level = GetLevel(reader, 6); + notification.IsRead = reader.GetBoolean(7); + + return notification; + } + + /// + /// Gets the notification. + /// + /// The id. + /// The user id. + /// Notification. + /// + /// id + /// or + /// userId + /// + public Notification GetNotification(string id, string userId) + { + if (string.IsNullOrEmpty(id)) + { + throw new ArgumentNullException("id"); + } + if (string.IsNullOrEmpty(userId)) + { + throw new ArgumentNullException("userId"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId where Id=@Id And UserId = @UserId"; + + cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = new Guid(id); + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(userId); + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + return GetNotification(reader); + } + } + return null; + } + } + + /// + /// Gets the level. + /// + /// The reader. + /// The index. + /// NotificationLevel. + private NotificationLevel GetLevel(IDataReader reader, int index) + { + NotificationLevel level; + + var val = reader.GetString(index); + + Enum.TryParse(val, true, out level); + + return level; + } + + /// + /// Adds the notification. + /// + /// The notification. + /// The cancellation token. + /// Task. + public async Task AddNotification(Notification notification, CancellationToken cancellationToken) + { + await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); + + if (NotificationAdded != null) + { + try + { + NotificationAdded(this, new NotificationUpdateEventArgs + { + Notification = notification + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationAdded event handler", ex); + } + } + } + + /// + /// Updates the notification. + /// + /// The notification. + /// The cancellation token. + /// Task. + public async Task UpdateNotification(Notification notification, CancellationToken cancellationToken) + { + await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); + + if (NotificationUpdated != null) + { + try + { + NotificationUpdated(this, new NotificationUpdateEventArgs + { + Notification = notification + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationUpdated event handler", ex); + } + } + } + + /// + /// Replaces the notification. + /// + /// The notification. + /// The cancellation token. + /// Task. + private async Task ReplaceNotification(Notification notification, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(notification.Id)) + { + notification.Id = Guid.NewGuid().ToString("N"); + } + if (string.IsNullOrEmpty(notification.UserId)) + { + throw new ArgumentException("The notification must have a user id"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + _replaceNotificationCommand.GetParameter(0).Value = new Guid(notification.Id); + _replaceNotificationCommand.GetParameter(1).Value = new Guid(notification.UserId); + _replaceNotificationCommand.GetParameter(2).Value = notification.Date.ToUniversalTime(); + _replaceNotificationCommand.GetParameter(3).Value = notification.Name; + _replaceNotificationCommand.GetParameter(4).Value = notification.Description; + _replaceNotificationCommand.GetParameter(5).Value = notification.Url; + _replaceNotificationCommand.GetParameter(6).Value = notification.Level.ToString(); + _replaceNotificationCommand.GetParameter(7).Value = notification.IsRead; + _replaceNotificationCommand.GetParameter(8).Value = string.Empty; + _replaceNotificationCommand.GetParameter(9).Value = string.Empty; + + _replaceNotificationCommand.Transaction = transaction; + + _replaceNotificationCommand.ExecuteNonQuery(); + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save notification:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + + /// + /// Marks the read. + /// + /// The notification id list. + /// The user id. + /// if set to true [is read]. + /// The cancellation token. + /// Task. + public async Task MarkRead(IEnumerable notificationIdList, string userId, bool isRead, CancellationToken cancellationToken) + { + var list = notificationIdList.ToList(); + var idArray = list.Select(i => new Guid(i)).ToArray(); + + await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false); + + if (NotificationsMarkedRead != null) + { + try + { + NotificationsMarkedRead(this, new NotificationReadEventArgs + { + IdList = list.ToArray(), + IsRead = isRead, + UserId = userId + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationsMarkedRead event handler", ex); + } + } + } + + private async Task MarkReadInternal(IEnumerable notificationIdList, string userId, bool isRead, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + cancellationToken.ThrowIfCancellationRequested(); + + transaction = _connection.BeginTransaction(); + + _markReadCommand.GetParameter(0).Value = new Guid(userId); + _markReadCommand.GetParameter(1).Value = isRead; + + foreach (var id in notificationIdList) + { + _markReadCommand.GetParameter(2).Value = id; + + _markReadCommand.Transaction = transaction; + + _markReadCommand.ExecuteNonQuery(); + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save notification:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs deleted file mode 100644 index 0f9b4295ed..0000000000 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs +++ /dev/null @@ -1,490 +0,0 @@ -using System.IO; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Notifications; -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Persistence -{ - public class SqliteNotificationsRepository : INotificationsRepository - { - private IDbConnection _connection; - private readonly ILogger _logger; - private readonly IServerApplicationPaths _appPaths; - - private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - - public SqliteNotificationsRepository(ILogManager logManager, IServerApplicationPaths appPaths) - { - _appPaths = appPaths; - _logger = logManager.GetLogger(GetType().Name); - } - - public event EventHandler NotificationAdded; - public event EventHandler NotificationsMarkedRead; - public event EventHandler NotificationUpdated; - - private IDbCommand _replaceNotificationCommand; - private IDbCommand _markReadCommand; - - public async Task Initialize() - { - var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db"); - - _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists Notifications (Id GUID NOT NULL, UserId GUID NOT NULL, Date DATETIME NOT NULL, Name TEXT NOT NULL, Description TEXT, Url TEXT, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT, PRIMARY KEY (Id, UserId))", - "create index if not exists idx_Notifications on Notifications(Id, UserId)", - - //pragmas - "pragma temp_store = memory", - - "pragma shrink_memory" - }; - - _connection.RunQueries(queries, _logger); - - PrepareStatements(); - } - - private void PrepareStatements() - { - _replaceNotificationCommand = _connection.CreateCommand(); - _replaceNotificationCommand.CommandText = "replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"; - - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Date"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Name"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Description"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Url"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Level"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Category"); - _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@RelatedId"); - - _markReadCommand = _connection.CreateCommand(); - _markReadCommand.CommandText = "update Notifications set IsRead=@IsRead where Id=@Id and UserId=@UserId"; - - _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); - _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); - _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); - } - - /// - /// Gets the notifications. - /// - /// The query. - /// NotificationResult. - public NotificationResult GetNotifications(NotificationQuery query) - { - var result = new NotificationResult(); - - using (var cmd = _connection.CreateCommand()) - { - var clauses = new List(); - - if (query.IsRead.HasValue) - { - clauses.Add("IsRead=@IsRead"); - cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = query.IsRead.Value; - } - - clauses.Add("UserId=@UserId"); - cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(query.UserId); - - var whereClause = " where " + string.Join(" And ", clauses.ToArray()); - - cmd.CommandText = string.Format("select count(Id) from Notifications{0};select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause); - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) - { - if (reader.Read()) - { - result.TotalRecordCount = reader.GetInt32(0); - } - - if (reader.NextResult()) - { - var notifications = GetNotifications(reader); - - if (query.StartIndex.HasValue) - { - notifications = notifications.Skip(query.StartIndex.Value); - } - - if (query.Limit.HasValue) - { - notifications = notifications.Take(query.Limit.Value); - } - - result.Notifications = notifications.ToArray(); - } - } - - return result; - } - } - - public NotificationsSummary GetNotificationsSummary(Guid userId) - { - var result = new NotificationsSummary(); - - using (var cmd = _connection.CreateCommand()) - { - cmd.CommandText = "select Level from Notifications where UserId=@UserId and IsRead=@IsRead"; - - cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = userId; - cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = false; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) - { - var levels = new List(); - - while (reader.Read()) - { - levels.Add(GetLevel(reader, 0)); - } - - result.UnreadCount = levels.Count; - - if (levels.Count > 0) - { - result.MaxUnreadNotificationLevel = levels.Max(); - } - } - - return result; - } - } - - /// - /// Gets the notifications. - /// - /// The reader. - /// IEnumerable{Notification}. - private IEnumerable GetNotifications(IDataReader reader) - { - while (reader.Read()) - { - yield return GetNotification(reader); - } - } - - private Notification GetNotification(IDataReader reader) - { - var notification = new Notification - { - Id = reader.GetGuid(0), - UserId = reader.GetGuid(1), - Date = reader.GetDateTime(2).ToUniversalTime(), - Name = reader.GetString(3) - }; - - if (!reader.IsDBNull(4)) - { - notification.Description = reader.GetString(4); - } - - if (!reader.IsDBNull(5)) - { - notification.Url = reader.GetString(5); - } - - notification.Level = GetLevel(reader, 6); - notification.IsRead = reader.GetBoolean(7); - - notification.Category = reader.GetString(8); - - if (!reader.IsDBNull(9)) - { - notification.RelatedId = reader.GetString(9); - } - - return notification; - } - - /// - /// Gets the notification. - /// - /// The id. - /// The user id. - /// Notification. - /// - /// id - /// or - /// userId - /// - public Notification GetNotification(Guid id, Guid userId) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - if (userId == Guid.Empty) - { - throw new ArgumentNullException("userId"); - } - - using (var cmd = _connection.CreateCommand()) - { - cmd.CommandText = "select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId where Id=@Id And UserId = @UserId"; - - cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = id; - cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = userId; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - return GetNotification(reader); - } - } - return null; - } - } - - /// - /// Gets the level. - /// - /// The reader. - /// The index. - /// NotificationLevel. - private NotificationLevel GetLevel(IDataReader reader, int index) - { - NotificationLevel level; - - var val = reader.GetString(index); - - Enum.TryParse(val, true, out level); - - return level; - } - - /// - /// Adds the notification. - /// - /// The notification. - /// The cancellation token. - /// Task. - public async Task AddNotification(Notification notification, CancellationToken cancellationToken) - { - await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); - - if (NotificationAdded != null) - { - try - { - NotificationAdded(this, new NotificationUpdateEventArgs - { - Notification = notification - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in NotificationAdded event handler", ex); - } - } - } - - /// - /// Updates the notification. - /// - /// The notification. - /// The cancellation token. - /// Task. - public async Task UpdateNotification(Notification notification, CancellationToken cancellationToken) - { - await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); - - if (NotificationUpdated != null) - { - try - { - NotificationUpdated(this, new NotificationUpdateEventArgs - { - Notification = notification - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in NotificationUpdated event handler", ex); - } - } - } - - /// - /// Replaces the notification. - /// - /// The notification. - /// The cancellation token. - /// Task. - private async Task ReplaceNotification(Notification notification, CancellationToken cancellationToken) - { - if (notification.Id == Guid.Empty) - { - throw new ArgumentException("The notification must have an id"); - } - if (notification.UserId == Guid.Empty) - { - throw new ArgumentException("The notification must have a user id"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - IDbTransaction transaction = null; - - try - { - transaction = _connection.BeginTransaction(); - - _replaceNotificationCommand.GetParameter(0).Value = notification.Id; - _replaceNotificationCommand.GetParameter(1).Value = notification.UserId; - _replaceNotificationCommand.GetParameter(2).Value = notification.Date.ToUniversalTime(); - _replaceNotificationCommand.GetParameter(3).Value = notification.Name; - _replaceNotificationCommand.GetParameter(4).Value = notification.Description; - _replaceNotificationCommand.GetParameter(5).Value = notification.Url; - _replaceNotificationCommand.GetParameter(6).Value = notification.Level.ToString(); - _replaceNotificationCommand.GetParameter(7).Value = notification.IsRead; - _replaceNotificationCommand.GetParameter(8).Value = notification.Category; - _replaceNotificationCommand.GetParameter(9).Value = notification.RelatedId; - - _replaceNotificationCommand.Transaction = transaction; - - _replaceNotificationCommand.ExecuteNonQuery(); - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - _logger.ErrorException("Failed to save notification:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - - _writeLock.Release(); - } - } - - /// - /// Marks the read. - /// - /// The notification id list. - /// The user id. - /// if set to true [is read]. - /// The cancellation token. - /// Task. - public async Task MarkRead(IEnumerable notificationIdList, Guid userId, bool isRead, CancellationToken cancellationToken) - { - var idArray = notificationIdList.ToArray(); - - await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false); - - if (NotificationsMarkedRead != null) - { - try - { - NotificationsMarkedRead(this, new NotificationReadEventArgs - { - IdList = idArray.ToArray(), - IsRead = isRead, - UserId = userId - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in NotificationsMarkedRead event handler", ex); - } - } - } - - private async Task MarkReadInternal(IEnumerable notificationIdList, Guid userId, bool isRead, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - IDbTransaction transaction = null; - - try - { - cancellationToken.ThrowIfCancellationRequested(); - - transaction = _connection.BeginTransaction(); - - _markReadCommand.GetParameter(0).Value = userId; - _markReadCommand.GetParameter(1).Value = isRead; - - foreach (var id in notificationIdList) - { - _markReadCommand.GetParameter(2).Value = id; - - _markReadCommand.Transaction = transaction; - - _markReadCommand.ExecuteNonQuery(); - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - _logger.ErrorException("Failed to save notification:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - - _writeLock.Release(); - } - } - } -} diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 8ee060c3c3..2eeb075ee2 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -56,6 +56,7 @@ using MediaBrowser.Server.Implementations.Library; using MediaBrowser.Server.Implementations.LiveTv; using MediaBrowser.Server.Implementations.Localization; using MediaBrowser.Server.Implementations.MediaEncoder; +using MediaBrowser.Server.Implementations.Notifications; using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; @@ -189,6 +190,8 @@ namespace MediaBrowser.ServerApplication private IFileOrganizationRepository FileOrganizationRepository { get; set; } private IProviderRepository ProviderRepository { get; set; } + private INotificationManager NotificationManager { get; set; } + /// /// Initializes a new instance of the class. /// @@ -523,6 +526,9 @@ namespace MediaBrowser.ServerApplication LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager); RegisterSingleInstance(LiveTvManager); + NotificationManager = new NotificationManager(LogManager, UserManager); + RegisterSingleInstance(NotificationManager); + var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false)); var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false)); @@ -705,6 +711,8 @@ namespace MediaBrowser.ServerApplication SessionManager.AddParts(GetExports()); ChannelManager.AddParts(GetExports()); + + NotificationManager.AddParts(GetExports()); } /// -- cgit v1.2.3