diff options
71 files changed, 1656 insertions, 1256 deletions
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e85a5a680..da21342ac 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -37,23 +37,6 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } } - [Route("/Artists/{Name}/Images", "GET")] - [Route("/Genres/{Name}/Images", "GET")] - [Route("/GameGenres/{Name}/Images", "GET")] - [Route("/MusicGenres/{Name}/Images", "GET")] - [Route("/Persons/{Name}/Images", "GET")] - [Route("/Studios/{Name}/Images", "GET")] - [Api(Description = "Gets information about an item's images")] - public class GetItemByNameImageInfos : IReturn<List<ImageInfo>> - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - [Route("/Items/{Id}/Images/{Type}", "GET")] [Route("/Items/{Id}/Images/{Type}/{Index}", "GET")] [Api(Description = "Gets an item image")] @@ -103,45 +86,6 @@ namespace MediaBrowser.Api.Images public int NewIndex { get; set; } } - [Route("/Artists/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/Genres/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/GameGenres/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/MusicGenres/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/Persons/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/Studios/{Name}/Images/{Type}/{Index}/Index", "POST")] - [Route("/Years/{Year}/Images/{Type}/{Index}/Index", "POST")] - [Api(Description = "Updates the index for an item image")] - public class UpdateItemByNameImageIndex : IReturnVoid - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Name", Description = "Item name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - - /// <summary> - /// Gets or sets the type of the image. - /// </summary> - /// <value>The type of the image.</value> - [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public ImageType Type { get; set; } - - /// <summary> - /// Gets or sets the index. - /// </summary> - /// <value>The index.</value> - [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int Index { get; set; } - - /// <summary> - /// Gets or sets the new index. - /// </summary> - /// <value>The new index.</value> - [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public int NewIndex { get; set; } - } - /// <summary> /// Class GetPersonImage /// </summary> @@ -202,31 +146,6 @@ namespace MediaBrowser.Api.Images public Guid Id { get; set; } } - [Route("/Artists/{Name}/Images/{Type}", "DELETE")] - [Route("/Artists/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/Genres/{Name}/Images/{Type}", "DELETE")] - [Route("/Genres/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/GameGenres/{Name}/Images/{Type}", "DELETE")] - [Route("/GameGenres/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/MusicGenres/{Name}/Images/{Type}", "DELETE")] - [Route("/MusicGenres/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/Persons/{Name}/Images/{Type}", "DELETE")] - [Route("/Persons/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/Studios/{Name}/Images/{Type}", "DELETE")] - [Route("/Studios/{Name}/Images/{Type}/{Index}", "DELETE")] - [Route("/Years/{Year}/Images/{Type}", "DELETE")] - [Route("/Years/{Year}/Images/{Type}/{Index}", "DELETE")] - [Api(Description = "Deletes an item image")] - public class DeleteItemByNameImage : DeleteImageRequest, IReturnVoid - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Name", Description = "Item name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Name { get; set; } - } - /// <summary> /// Class DeleteUserImage /// </summary> @@ -287,37 +206,6 @@ namespace MediaBrowser.Api.Images public Stream RequestStream { get; set; } } - [Route("/Artists/{Name}/Images/{Type}", "POST")] - [Route("/Artists/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/Genres/{Name}/Images/{Type}", "POST")] - [Route("/Genres/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/GameGenres/{Name}/Images/{Type}", "POST")] - [Route("/GameGenres/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/MusicGenres/{Name}/Images/{Type}", "POST")] - [Route("/MusicGenres/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/Persons/{Name}/Images/{Type}", "POST")] - [Route("/Persons/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/Studios/{Name}/Images/{Type}", "POST")] - [Route("/Studios/{Name}/Images/{Type}/{Index}", "POST")] - [Route("/Years/{Year}/Images/{Type}", "POST")] - [Route("/Years/{Year}/Images/{Type}/{Index}", "POST")] - [Api(Description = "Posts an item image")] - public class PostItemByNameImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Name", Description = "Item name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - - /// <summary> - /// The raw Http Request Input Stream - /// </summary> - /// <value>The request stream.</value> - public Stream RequestStream { get; set; } - } - /// <summary> /// Class ImageService /// </summary> @@ -327,26 +215,21 @@ namespace MediaBrowser.Api.Images private readonly ILibraryManager _libraryManager; - private readonly IApplicationPaths _appPaths; - private readonly IProviderManager _providerManager; private readonly IItemRepository _itemRepo; - private readonly IDtoService _dtoService; private readonly IImageProcessor _imageProcessor; private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="ImageService" /> class. /// </summary> - public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor, IFileSystem fileSystem) + public ImageService(IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IItemRepository itemRepo, IImageProcessor imageProcessor, IFileSystem fileSystem) { _userManager = userManager; _libraryManager = libraryManager; - _appPaths = appPaths; _providerManager = providerManager; _itemRepo = itemRepo; - _dtoService = dtoService; _imageProcessor = imageProcessor; _fileSystem = fileSystem; } @@ -365,28 +248,6 @@ namespace MediaBrowser.Api.Images return ToOptimizedSerializedResultUsingCache(result); } - public object Get(GetItemByNameImageInfos request) - { - var result = GetItemByNameImageInfos(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - - /// <summary> - /// Gets the item by name image infos. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{List{ImageInfo}}.</returns> - private List<ImageInfo> GetItemByNameImageInfos(GetItemByNameImageInfos request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue<string>(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - return GetItemImageInfos(item); - } - /// <summary> /// Gets the item image infos. /// </summary> @@ -540,21 +401,6 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } - public void Post(PostItemByNameImage request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue<string>(0); - var name = pathInfo.GetArgumentValue<string>(1); - - request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true); - - var item = GetItemByName(name, type, _libraryManager); - - var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType); - - Task.WaitAll(task); - } - /// <summary> /// Posts the specified request. /// </summary> @@ -600,22 +446,6 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Deletes the specified request. - /// </summary> - /// <param name="request">The request.</param> - public void Delete(DeleteItemByNameImage request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue<string>(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - var task = item.DeleteImage(request.Type, request.Index ?? 0); - - Task.WaitAll(task); - } - - /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> @@ -629,22 +459,6 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public void Post(UpdateItemByNameImageIndex request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue<string>(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - var task = UpdateItemIndex(item, request.Type, request.Index, request.NewIndex); - - Task.WaitAll(task); - } - - /// <summary> /// Updates the index of the item. /// </summary> /// <param name="item">The item.</param> diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index 3816332b2..4345fbc5c 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -28,76 +27,17 @@ namespace MediaBrowser.Api public string Id { get; set; } } - [Route("/Artists/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for an artist")] - public class RefreshArtist : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/Genres/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for a genre")] - public class RefreshGenre : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/MusicGenres/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for a music genre")] - public class RefreshMusicGenre : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/GameGenres/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for a game genre")] - public class RefreshGameGenre : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/Persons/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for a person")] - public class RefreshPerson : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/Studios/{Name}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for a studio")] - public class RefreshStudio : BaseRefreshRequest - { - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - public class ItemRefreshService : BaseApiService { private readonly ILibraryManager _libraryManager; - private readonly IDtoService _dtoService; - public ItemRefreshService(ILibraryManager libraryManager, IDtoService dtoService) + public ItemRefreshService(ILibraryManager libraryManager) { _libraryManager = libraryManager; - _dtoService = dtoService; } - public void Post(RefreshArtist request) + private async Task RefreshArtist(RefreshItem request, MusicArtist item) { - var task = RefreshArtist(request); - - Task.WaitAll(task); - } - - private async Task RefreshArtist(RefreshArtist request) - { - var item = GetArtist(request.Name, _libraryManager); - var cancellationToken = CancellationToken.None; var albums = _libraryManager.RootFolder @@ -127,118 +67,15 @@ namespace MediaBrowser.Api } } - public void Post(RefreshGenre request) - { - var task = RefreshGenre(request); - - Task.WaitAll(task); - } - - private async Task RefreshGenre(RefreshGenre request) - { - var item = GetGenre(request.Name, _libraryManager); - - try - { - await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error refreshing library", ex); - } - } - - public void Post(RefreshMusicGenre request) - { - var task = RefreshMusicGenre(request); - - Task.WaitAll(task); - } - - private async Task RefreshMusicGenre(RefreshMusicGenre request) - { - var item = GetMusicGenre(request.Name, _libraryManager); - - try - { - await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error refreshing library", ex); - } - } - - public void Post(RefreshGameGenre request) - { - var task = RefreshGameGenre(request); - - Task.WaitAll(task); - } - - private async Task RefreshGameGenre(RefreshGameGenre request) - { - var item = GetGameGenre(request.Name, _libraryManager); - - try - { - await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error refreshing library", ex); - } - } - - public void Post(RefreshPerson request) - { - var task = RefreshPerson(request); - - Task.WaitAll(task); - } - - private async Task RefreshPerson(RefreshPerson request) - { - var item = GetPerson(request.Name, _libraryManager); - - try - { - await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error refreshing library", ex); - } - } - - public void Post(RefreshStudio request) - { - var task = RefreshStudio(request); - - Task.WaitAll(task); - } - - private async Task RefreshStudio(RefreshStudio request) - { - var item = GetStudio(request.Name, _libraryManager); - - try - { - await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error refreshing library", ex); - } - } - /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> public void Post(RefreshItem request) { - var task = RefreshItem(request); + var item = _libraryManager.GetItemById(request.Id); + + var task = item is MusicArtist ? RefreshArtist(request, (MusicArtist)item) : RefreshItem(request, item); Task.WaitAll(task); } @@ -248,10 +85,8 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>Task.</returns> - private async Task RefreshItem(RefreshItem request) + private async Task RefreshItem(RefreshItem request, BaseItem item) { - var item = _libraryManager.GetItemById(request.Id); - var options = GetRefreshOptions(request); try diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 5f05fdc3f..92e6a098d 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -1,9 +1,7 @@ -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; using ServiceStack; using System; @@ -13,73 +11,20 @@ using System.Threading.Tasks; namespace MediaBrowser.Api { - [Route("/Items/{ItemId}", "POST")] - [Api(("Updates an item"))] + [Route("/Items/{ItemId}", "POST", Summary = "Updates an item")] public class UpdateItem : BaseItemDto, IReturnVoid { [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string ItemId { get; set; } } - [Route("/Artists/{ArtistName}", "POST")] - [Api(("Updates an artist"))] - public class UpdateArtist : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "ArtistName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string ArtistName { get; set; } - } - - [Route("/Studios/{StudioName}", "POST")] - [Api(("Updates a studio"))] - public class UpdateStudio : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "StudioName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string StudioName { get; set; } - } - - [Route("/Persons/{PersonName}", "POST")] - [Api(("Updates a person"))] - public class UpdatePerson : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "PersonName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string PersonName { get; set; } - } - - [Route("/MusicGenres/{GenreName}", "POST")] - [Api(("Updates a music genre"))] - public class UpdateMusicGenre : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "GenreName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string GenreName { get; set; } - } - - [Route("/GameGenres/{GenreName}", "POST")] - [Api(("Updates a game genre"))] - public class UpdateGameGenre : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "GenreName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string GenreName { get; set; } - } - - [Route("/Genres/{GenreName}", "POST")] - [Api(("Updates a genre"))] - public class UpdateGenre : BaseItemDto, IReturnVoid - { - [ApiMember(Name = "GenreName", Description = "The name of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string GenreName { get; set; } - } - public class ItemUpdateService : BaseApiService { private readonly ILibraryManager _libraryManager; - private readonly IDtoService _dtoService; - private readonly ILiveTvManager _liveTv; - public ItemUpdateService(ILibraryManager libraryManager, IDtoService dtoService, ILiveTvManager liveTv) + public ItemUpdateService(ILibraryManager libraryManager) { _libraryManager = libraryManager; - _dtoService = dtoService; - _liveTv = liveTv; } public void Post(UpdateItem request) @@ -94,120 +39,29 @@ namespace MediaBrowser.Api var item = _libraryManager.GetItemById(request.ItemId); var newLockData = request.LockData ?? false; - var dontFetchMetaChanged = item.IsLocked != newLockData; + var isLockedChanged = item.IsLocked != newLockData; UpdateItem(request, item); + if (isLockedChanged && item.IsLocked) + { + item.IsUnidentified = false; + } + await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - if (dontFetchMetaChanged && item.IsFolder) + if (isLockedChanged && item.IsFolder) { var folder = (Folder)item; foreach (var child in folder.RecursiveChildren.ToList()) { - child.DontFetchMeta = newLockData; + child.IsLocked = newLockData; await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } } - public void Post(UpdatePerson request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdatePerson request) - { - var item = GetPerson(request.PersonName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(UpdateArtist request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdateArtist request) - { - var item = GetArtist(request.ArtistName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(UpdateStudio request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdateStudio request) - { - var item = GetStudio(request.StudioName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(UpdateMusicGenre request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdateMusicGenre request) - { - var item = GetMusicGenre(request.GenreName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(UpdateGameGenre request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdateGameGenre request) - { - var item = GetGameGenre(request.GenreName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(UpdateGenre request) - { - var task = UpdateItem(request); - - Task.WaitAll(task); - } - - private async Task UpdateItem(UpdateGenre request) - { - var item = GetGenre(request.GenreName, _libraryManager); - - UpdateItem(request, item); - - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - } - private void UpdateItem(BaseItemDto request, BaseItem item) { item.Name = request.Name; @@ -246,7 +100,7 @@ namespace MediaBrowser.Api episode.AirsBeforeSeasonNumber = request.AirsBeforeSeasonNumber; episode.AbsoluteEpisodeNumber = request.AbsoluteEpisodeNumber; } - + var hasTags = item as IHasTags; if (hasTags != null) { @@ -295,14 +149,14 @@ namespace MediaBrowser.Api { hasDisplayOrder.DisplayOrder = request.DisplayOrder; } - + var hasAspectRatio = item as IHasAspectRatio; if (hasAspectRatio != null) { hasAspectRatio.AspectRatio = request.AspectRatio; } - item.DontFetchMeta = (request.LockData ?? false); + item.IsLocked = (request.LockData ?? false); if (request.LockedFields != null) { diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 290073652..2b5b27804 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -231,6 +231,14 @@ namespace MediaBrowser.Api.Library } + [Route("/Library/Series/Updated", "POST")] + [Api(Description = "Reports that new episodes of a series have been added by an external source")] + public class PostUpdatedSeries : IReturnVoid + { + [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string TvdbId { get; set; } + } + /// <summary> /// Class LibraryService /// </summary> diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index a80dd796a..d8abf81ba 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -10,8 +10,7 @@ using System.Linq; namespace MediaBrowser.Api.Music { - [Route("/Albums/{Id}/Similar", "GET")] - [Api(Description = "Finds albums similar to a given album.")] + [Route("/Albums/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")] public class GetSimilarAlbums : BaseGetSimilarItemsFromItem { } diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs index 796fcdab1..d17b3c368 100644 --- a/MediaBrowser.Api/NotificationsService.cs +++ b/MediaBrowser.Api/NotificationsService.cs @@ -1,8 +1,10 @@ -using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Notifications; using ServiceStack; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -31,12 +33,38 @@ namespace MediaBrowser.Api public string UserId { get; set; } } - [Route("/Notifications/{UserId}", "POST", Summary = "Adds a notifications")] - public class AddUserNotification : IReturnVoid + [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] + public class GetNotificationTypes : IReturn<List<NotificationTypeInfo>> + { + } + + [Route("/Notifications/Services", "GET", Summary = "Gets notification types")] + public class GetNotificationServices : IReturn<List<NotificationServiceInfo>> + { + } + + [Route("/Notifications", "POST", Summary = "Sends a notification to all admin users")] + public class AddAdminNotification : 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 string Id { get; set; } + [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Name { get; set; } + + [ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Description { get; set; } + + [ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string ImageUrl { get; set; } + [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Url { get; set; } + + [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public NotificationLevel Level { get; set; } + } + + [Route("/Notifications/{UserId}", "POST", Summary = "Sends a notification to a user")] + public class AddUserNotification : IReturnVoid + { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string UserId { get; set; } @@ -77,11 +105,13 @@ namespace MediaBrowser.Api { private readonly INotificationsRepository _notificationsRepo; private readonly INotificationManager _notificationManager; + private readonly IUserManager _userManager; - public NotificationsService(INotificationsRepository notificationsRepo, INotificationManager notificationManager) + public NotificationsService(INotificationsRepository notificationsRepo, INotificationManager notificationManager, IUserManager userManager) { _notificationsRepo = notificationsRepo; _notificationManager = notificationManager; + _userManager = userManager; } public void Post(AddUserNotification request) @@ -91,11 +121,25 @@ namespace MediaBrowser.Api Task.WaitAll(task); } + public object Get(GetNotificationTypes request) + { + var result = _notificationManager.GetNotificationTypes().ToList(); + + return ToOptimizedResult(result); + } + + public object Get(GetNotificationServices request) + { + var result = _notificationManager.GetNotificationServices().ToList(); + + return ToOptimizedResult(result); + } + public object Get(GetNotificationsSummary request) { var result = _notificationsRepo.GetNotificationsSummary(request.UserId); - return result; + return ToOptimizedResult(result); } private async Task AddNotification(AddUserNotification request) @@ -113,6 +157,29 @@ namespace MediaBrowser.Api await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); } + public void Post(AddAdminNotification request) + { + // This endpoint really just exists as post of a real with sickbeard + var task = AddNotification(request); + + Task.WaitAll(task); + } + + private async Task AddNotification(AddAdminNotification request) + { + var notification = new NotificationRequest + { + Date = DateTime.UtcNow, + Description = request.Description, + Level = request.Level, + Name = request.Name, + Url = request.Url, + UserIds = _userManager.Users.Where(i => i.Configuration.IsAdministrator).Select(i => i.Id.ToString("N")).ToList() + }; + + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + public void Post(MarkRead request) { var task = MarkRead(request.Ids, request.UserId, true); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f1b8b2d52..1682afb10 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -301,9 +301,10 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>Task{ItemsResult}.</returns> private ItemsResult GetItems(GetItems request) { + var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId); var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; - var items = GetItemsToSerialize(request, user); + var items = GetItemsToSerialize(request, user, parentItem); items = items.AsParallel(); @@ -320,7 +321,7 @@ namespace MediaBrowser.Api.UserLibrary items = items.AsEnumerable(); - if (CollapseBoxSetItems(request)) + if (CollapseBoxSetItems(request, parentItem)) { items = _collectionManager.CollapseItemsWithinBoxSets(items, user); } @@ -348,8 +349,14 @@ namespace MediaBrowser.Api.UserLibrary }; } - private bool CollapseBoxSetItems(GetItems request) + private bool CollapseBoxSetItems(GetItems request, BaseItem parentItem) { + // Could end up stuck in a loop like this + if (parentItem is BoxSet) + { + return false; + } + var param = request.CollapseBoxSetItems; if (!param.HasValue) @@ -369,13 +376,14 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <param name="request">The request.</param> /// <param name="user">The user.</param> + /// <param name="parentItem">The parent item.</param> /// <returns>IEnumerable{BaseItem}.</returns> /// <exception cref="System.InvalidOperationException"></exception> - private IEnumerable<BaseItem> GetItemsToSerialize(GetItems request, User user) + private IEnumerable<BaseItem> GetItemsToSerialize(GetItems request, User user, BaseItem parentItem) { var item = string.IsNullOrEmpty(request.ParentId) ? user == null ? _libraryManager.RootFolder : user.RootFolder : - _libraryManager.GetItemById(request.ParentId); + parentItem; // Default list type = children IEnumerable<BaseItem> items; @@ -1382,7 +1390,7 @@ namespace MediaBrowser.Api.UserLibrary { return false; } - + return true; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index be64d20c3..80fa3b869 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -236,6 +236,10 @@ namespace MediaBrowser.Controller.Entities { return DontFetchMeta; } + set + { + DontFetchMeta = value; + } } public bool IsUnidentified { get; set; } diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs index 8387881cf..7482607ff 100644 --- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs @@ -16,6 +16,9 @@ namespace MediaBrowser.Controller.Library public BaseItemInfo MediaInfo { get; set; } public string MediaSourceId { get; set; } + public string DeviceName { get; set; } + public string ClientName { get; set; } + public PlaybackProgressEventArgs() { Users = new List<User>(); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 417c03f27..b54ad2272 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -168,7 +168,9 @@ <Compile Include="Net\IRestfulService.cs" /> <Compile Include="News\INewsService.cs" /> <Compile Include="Notifications\INotificationManager.cs" /> + <Compile Include="Notifications\INotificationService.cs" /> <Compile Include="Notifications\INotificationsRepository.cs" /> + <Compile Include="Notifications\INotificationTypeFactory.cs" /> <Compile Include="Notifications\NotificationUpdateEventArgs.cs" /> <Compile Include="Notifications\UserNotification.cs" /> <Compile Include="Persistence\IFileOrganizationRepository.cs" /> diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs index 8ed62f59f..cb1e3da90 100644 --- a/MediaBrowser.Controller/Notifications/INotificationManager.cs +++ b/MediaBrowser.Controller/Notifications/INotificationManager.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Notifications; +using MediaBrowser.Model.Notifications; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -20,30 +19,19 @@ namespace MediaBrowser.Controller.Notifications /// Adds the parts. /// </summary> /// <param name="services">The services.</param> - void AddParts(IEnumerable<INotificationService> services); - } + /// <param name="notificationTypeFactories">The notification type factories.</param> + void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories); - public interface INotificationService - { /// <summary> - /// Gets the name. + /// Gets the notification types. /// </summary> - /// <value>The name.</value> - string Name { get; } - - /// <summary> - /// Sends the notification. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SendNotification(UserNotification request, CancellationToken cancellationToken); + /// <returns>IEnumerable{NotificationTypeInfo}.</returns> + IEnumerable<NotificationTypeInfo> GetNotificationTypes(); /// <summary> - /// Determines whether [is enabled for user] [the specified user identifier]. + /// Gets the notification services. /// </summary> - /// <param name="user">The user.</param> - /// <returns><c>true</c> if [is enabled for user] [the specified user identifier]; otherwise, <c>false</c>.</returns> - bool IsEnabledForUser(User user); + /// <returns>IEnumerable{NotificationServiceInfo}.</returns> + IEnumerable<NotificationServiceInfo> GetNotificationServices(); } } diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs new file mode 100644 index 000000000..b1e313b87 --- /dev/null +++ b/MediaBrowser.Controller/Notifications/INotificationService.cs @@ -0,0 +1,30 @@ +using MediaBrowser.Controller.Entities; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Notifications +{ + public interface INotificationService + { + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Sends the notification. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendNotification(UserNotification request, CancellationToken cancellationToken); + + /// <summary> + /// Determines whether [is enabled for user] [the specified user identifier]. + /// </summary> + /// <param name="user">The user.</param> + /// <returns><c>true</c> if [is enabled for user] [the specified user identifier]; otherwise, <c>false</c>.</returns> + bool IsEnabledForUser(User user); + } +} diff --git a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs new file mode 100644 index 000000000..bf92aae2d --- /dev/null +++ b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs @@ -0,0 +1,14 @@ +using MediaBrowser.Model.Notifications; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Notifications +{ + public interface INotificationTypeFactory + { + /// <summary> + /// Gets the notification types. + /// </summary> + /// <returns>IEnumerable{NotificationTypeInfo}.</returns> + IEnumerable<NotificationTypeInfo> GetNotificationTypes(); + } +} diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 70b49efec..e24daf853 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -429,7 +429,7 @@ namespace MediaBrowser.Controller.Providers if (!string.IsNullOrWhiteSpace(val)) { - item.DontFetchMeta = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); + item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; } diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 3746630be..2952773a4 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -1,8 +1,15 @@ -using MediaBrowser.Common; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Session; +using MediaBrowser.Dlna.PlayTo; using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Logging; using System; @@ -15,19 +22,39 @@ namespace MediaBrowser.Dlna.Main { private readonly IServerConfigurationManager _config; private readonly ILogger _logger; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; private readonly INetworkManager _network; + private PlayToManager _manager; + private readonly ISessionManager _sessionManager; + private readonly IHttpClient _httpClient; + private readonly IItemRepository _itemRepo; + private readonly ILibraryManager _libraryManager; + private readonly INetworkManager _networkManager; + private readonly IUserManager _userManager; + private readonly IDlnaManager _dlnaManager; + private readonly IDtoService _dtoService; + private readonly IImageProcessor _imageProcessor; + private SsdpHandler _ssdpHandler; private readonly List<Guid> _registeredServerIds = new List<Guid>(); private bool _dlnaServerStarted; - public DlnaEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network) + public DlnaEntryPoint(IServerConfigurationManager config, ILogManager logManager, IServerApplicationHost appHost, INetworkManager network, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IDtoService dtoService, IImageProcessor imageProcessor) { _config = config; _appHost = appHost; _network = network; + _sessionManager = sessionManager; + _httpClient = httpClient; + _itemRepo = itemRepo; + _libraryManager = libraryManager; + _networkManager = networkManager; + _userManager = userManager; + _dlnaManager = dlnaManager; + _dtoService = dtoService; + _imageProcessor = imageProcessor; _logger = logManager.GetLogger("Dlna"); } @@ -46,16 +73,27 @@ namespace MediaBrowser.Dlna.Main private void ReloadComponents() { - var isStarted = _dlnaServerStarted; + var isServerStarted = _dlnaServerStarted; - if (_config.Configuration.DlnaOptions.EnableServer && !isStarted) + if (_config.Configuration.DlnaOptions.EnableServer && !isServerStarted) { StartDlnaServer(); } - else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted) + else if (!_config.Configuration.DlnaOptions.EnableServer && isServerStarted) { DisposeDlnaServer(); } + + var isPlayToStarted = _manager != null; + + if (_config.Configuration.DlnaOptions.EnablePlayTo && !isPlayToStarted) + { + StartPlayToManager(); + } + else if (!_config.Configuration.DlnaOptions.EnablePlayTo && isPlayToStarted) + { + DisposePlayToManager(); + } } private void StartSsdpHandler() @@ -145,9 +183,59 @@ namespace MediaBrowser.Dlna.Main ); } + private readonly object _syncLock = new object(); + private void StartPlayToManager() + { + lock (_syncLock) + { + try + { + _manager = new PlayToManager(_logger, + _config, + _sessionManager, + _httpClient, + _itemRepo, + _libraryManager, + _networkManager, + _userManager, + _dlnaManager, + _appHost, + _dtoService, + _imageProcessor, + _ssdpHandler); + + _manager.Start(); + } + catch (Exception ex) + { + _logger.ErrorException("Error starting PlayTo manager", ex); + } + } + } + + private void DisposePlayToManager() + { + lock (_syncLock) + { + if (_manager != null) + { + try + { + _manager.Dispose(); + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing PlayTo manager", ex); + } + _manager = null; + } + } + } + public void Dispose() { DisposeDlnaServer(); + DisposePlayToManager(); DisposeSsdpHandler(); } diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 97da7b697..fa609843b 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -70,7 +70,6 @@ </Compile> <Compile Include="PlayTo\PlaylistItemFactory.cs" /> <Compile Include="PlayTo\PlayToManager.cs" /> - <Compile Include="PlayTo\PlayToServerEntryPoint.cs" /> <Compile Include="Common\ServiceAction.cs" /> <Compile Include="Profiles\Foobar2000Profile.cs" /> <Compile Include="Profiles\Windows81Profile.cs" /> diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 9a35897ee..70e2d6630 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -409,30 +409,30 @@ namespace MediaBrowser.Dlna.PlayTo UpdateMediaInfo(currentObject, transportState.Value); } } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error updating device info", ex); - } - if (_disposed) - return; + if (_disposed) + return; - // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive - if (TransportState == TRANSPORTSTATE.STOPPED) - { - _successiveStopCount++; + // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive + if (transportState.Value == TRANSPORTSTATE.STOPPED) + { + _successiveStopCount++; - if (_successiveStopCount >= 10) - { - RestartTimerInactive(); + if (_successiveStopCount >= 10) + { + RestartTimerInactive(); + } + } + else + { + _successiveStopCount = 0; + RestartTimer(); + } } } - else + catch (Exception ex) { - _successiveStopCount = 0; - RestartTimer(); + _logger.ErrorException("Error updating device info", ex); } } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index 64eb04946..0a8da4744 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Session; using MediaBrowser.Dlna.Didl; +using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -38,6 +39,8 @@ namespace MediaBrowser.Dlna.PlayTo private readonly IDtoService _dtoService; private readonly IImageProcessor _imageProcessor; + private readonly SsdpHandler _ssdpHandler; + public bool SupportsMediaRemoteControl { get { return true; } @@ -51,7 +54,7 @@ namespace MediaBrowser.Dlna.PlayTo } } - public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor) + public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor, SsdpHandler ssdpHandler) { _session = session; _itemRepository = itemRepository; @@ -63,6 +66,7 @@ namespace MediaBrowser.Dlna.PlayTo _appHost = appHost; _dtoService = dtoService; _imageProcessor = imageProcessor; + _ssdpHandler = ssdpHandler; _logger = logger; } @@ -74,7 +78,35 @@ namespace MediaBrowser.Dlna.PlayTo _device.PlaybackStopped += _device_PlaybackStopped; _device.Start(); - _updateTimer = new Timer(updateTimer_Elapsed, null, 30000, 30000); + _ssdpHandler.MessageReceived += _SsdpHandler_MessageReceived; + } + + async void _SsdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e) + { + string nts; + e.Headers.TryGetValue("NTS", out nts); + + string usn; + if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty; + + string nt; + if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty; + + if (string.Equals(e.Method, "NOTIFY", StringComparison.OrdinalIgnoreCase) && + string.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase)) + { + if (usn.IndexOf(_device.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1) + { + if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1 || + nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1) + { + if (!_disposed) + { + await _sessionManager.ReportSessionEnded(_session.Id).ConfigureAwait(false); + } + } + } + } } async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e) @@ -165,34 +197,6 @@ namespace MediaBrowser.Dlna.PlayTo }; } - #region Device EventHandlers & Update Timer - - Timer _updateTimer; - - /// <summary> - /// Handles the Elapsed event of the updateTimer control. - /// </summary> - /// <param name="state">The state.</param> - private async void updateTimer_Elapsed(object state) - { - if (!IsSessionActive) - { - _updateTimer.Change(Timeout.Infinite, Timeout.Infinite); - - try - { - // Session is inactive, mark it for Disposal and don't start the elapsed timer. - await _sessionManager.ReportSessionEnded(_session.Id); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ReportSessionEnded", ex); - } - } - } - - #endregion - #region SendCommands public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken) @@ -571,8 +575,8 @@ namespace MediaBrowser.Dlna.PlayTo _device.PlaybackStart -= _device_PlaybackStart; _device.PlaybackProgress -= _device_PlaybackProgress; _device.PlaybackStopped -= _device_PlaybackStopped; + _ssdpHandler.MessageReceived -= _SsdpHandler_MessageReceived; - _updateTimer.Dispose(); _device.Dispose(); } } diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 1ba0ded1f..889501e66 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -1,5 +1,4 @@ -using System.Text; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -12,12 +11,12 @@ using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -30,7 +29,6 @@ namespace MediaBrowser.Dlna.PlayTo private readonly ISessionManager _sessionManager; private readonly IHttpClient _httpClient; private readonly CancellationTokenSource _tokenSource; - private ConcurrentDictionary<string, DateTime> _locations; private readonly IItemRepository _itemRepository; private readonly ILibraryManager _libraryManager; @@ -42,9 +40,10 @@ namespace MediaBrowser.Dlna.PlayTo private readonly IDtoService _dtoService; private readonly IImageProcessor _imageProcessor; - public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor) + private readonly SsdpHandler _ssdpHandler; + + public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor, SsdpHandler ssdpHandler) { - _locations = new ConcurrentDictionary<string, DateTime>(); _tokenSource = new CancellationTokenSource(); _logger = logger; @@ -58,13 +57,12 @@ namespace MediaBrowser.Dlna.PlayTo _appHost = appHost; _dtoService = dtoService; _imageProcessor = imageProcessor; + _ssdpHandler = ssdpHandler; _config = config; } public void Start() { - _locations = new ConcurrentDictionary<string, DateTime>(); - foreach (var network in GetNetworkInterfaces()) { _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus); @@ -121,7 +119,9 @@ namespace MediaBrowser.Dlna.PlayTo { var socket = GetMulticastSocket(networkInterfaceIndex); - socket.Bind(new IPEndPoint(localIp, 1900)); + var endPoint = new IPEndPoint(localIp, 1900); + + socket.Bind(endPoint); _logger.Info("Creating SSDP listener"); @@ -135,9 +135,9 @@ namespace MediaBrowser.Dlna.PlayTo if (receivedBytes > 0) { - var headers = SsdpHelper.ParseSsdpResponse(receiveBuffer); + var args = SsdpHelper.ParseSsdpResponse(receiveBuffer, endPoint); - TryCreateController(headers); + TryCreateController(args); } } @@ -154,11 +154,47 @@ namespace MediaBrowser.Dlna.PlayTo }, _tokenSource.Token, TaskCreationOptions.LongRunning); } - private void TryCreateController(IDictionary<string, string> headers) + private void TryCreateController(SsdpMessageEventArgs args) { + string nts; + args.Headers.TryGetValue("NTS", out nts); + + string usn; + if (!args.Headers.TryGetValue("USN", out usn)) usn = string.Empty; + + string nt; + if (!args.Headers.TryGetValue("NT", out nt)) nt = string.Empty; + + // Don't create a new controller when a device is indicating it's shutting down + if (string.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + // It has to report that it's a media renderer + if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 && + nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1) + { + return; + } + + // Need to be able to download device description string location; + if (!args.Headers.TryGetValue("Location", out location) || + string.IsNullOrEmpty(location)) + { + return; + } - if (!headers.TryGetValue("Location", out location)) + if (_config.Configuration.DlnaOptions.EnableDebugLogging) + { + var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); + var headerText = string.Join(",", headerTexts.ToArray()); + + _logger.Debug("{0} PlayTo message received from {1}. Headers: {2}", args.Method, args.EndPoint, headerText); + } + + if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1)) { return; } @@ -230,12 +266,9 @@ namespace MediaBrowser.Dlna.PlayTo /// <returns></returns> private async Task CreateController(Uri uri) { - if (!IsUriValid(uri)) - return; - var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false); - if (device != null && device.RendererCommands != null && !_sessionManager.Sessions.Any(s => string.Equals(s.DeviceId, device.Properties.UUID) && s.IsActive)) + if (device != null && device.RendererCommands != null) { var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null) .ConfigureAwait(false); @@ -244,7 +277,7 @@ namespace MediaBrowser.Dlna.PlayTo if (controller == null) { - sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost, _dtoService, _imageProcessor); + sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost, _dtoService, _imageProcessor, _ssdpHandler); controller.Init(device); @@ -271,33 +304,6 @@ namespace MediaBrowser.Dlna.PlayTo } } - /// <summary> - /// Determines if the Uri is valid for further inspection or not. - /// (the limit for reinspection is 5 minutes) - /// </summary> - /// <param name="uri">The URI.</param> - /// <returns>Returns <b>True</b> if the Uri is valid for further inspection</returns> - private bool IsUriValid(Uri uri) - { - if (uri == null) - return false; - - if (!_locations.ContainsKey(uri.OriginalString)) - { - _locations.AddOrUpdate(uri.OriginalString, DateTime.UtcNow, (key, existingVal) => existingVal); - - return true; - } - - var time = _locations[uri.OriginalString]; - - if ((DateTime.UtcNow - time).TotalMinutes <= 5) - { - return false; - } - return _locations.TryUpdate(uri.OriginalString, DateTime.UtcNow, time); - } - public void Dispose() { if (!_disposed) diff --git a/MediaBrowser.Dlna/PlayTo/PlayToServerEntryPoint.cs b/MediaBrowser.Dlna/PlayTo/PlayToServerEntryPoint.cs deleted file mode 100644 index 15bebd996..000000000 --- a/MediaBrowser.Dlna/PlayTo/PlayToServerEntryPoint.cs +++ /dev/null @@ -1,127 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Logging; -using System; - -namespace MediaBrowser.Dlna.PlayTo -{ - public class PlayToServerEntryPoint : IServerEntryPoint - { - private PlayToManager _manager; - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - private readonly ISessionManager _sessionManager; - private readonly IHttpClient _httpClient; - private readonly IItemRepository _itemRepo; - private readonly ILibraryManager _libraryManager; - private readonly INetworkManager _networkManager; - private readonly IUserManager _userManager; - private readonly IDlnaManager _dlnaManager; - private readonly IServerApplicationHost _appHost; - private readonly IDtoService _dtoService; - private readonly IImageProcessor _imageProcessor; - - public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor) - { - _config = config; - _sessionManager = sessionManager; - _httpClient = httpClient; - _itemRepo = itemRepo; - _libraryManager = libraryManager; - _networkManager = networkManager; - _userManager = userManager; - _dlnaManager = dlnaManager; - _appHost = appHost; - _dtoService = dtoService; - _imageProcessor = imageProcessor; - _logger = logManager.GetLogger("PlayTo"); - } - - public void Run() - { - _config.ConfigurationUpdated += ConfigurationUpdated; - - ReloadPlayToManager(); - } - - void ConfigurationUpdated(object sender, EventArgs e) - { - ReloadPlayToManager(); - } - - private void ReloadPlayToManager() - { - var isStarted = _manager != null; - - if (_config.Configuration.DlnaOptions.EnablePlayTo && !isStarted) - { - StartPlayToManager(); - } - else if (!_config.Configuration.DlnaOptions.EnablePlayTo && isStarted) - { - DisposePlayToManager(); - } - } - - private readonly object _syncLock = new object(); - private void StartPlayToManager() - { - lock (_syncLock) - { - try - { - _manager = new PlayToManager(_logger, - _config, - _sessionManager, - _httpClient, - _itemRepo, - _libraryManager, - _networkManager, - _userManager, - _dlnaManager, - _appHost, - _dtoService, - _imageProcessor); - - _manager.Start(); - } - catch (Exception ex) - { - _logger.ErrorException("Error starting PlayTo manager", ex); - } - } - } - - private void DisposePlayToManager() - { - lock (_syncLock) - { - if (_manager != null) - { - try - { - _manager.Dispose(); - } - catch (Exception ex) - { - _logger.ErrorException("Error disposing PlayTo manager", ex); - } - _manager = null; - } - } - } - - public void Dispose() - { - DisposePlayToManager(); - } - } -} diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 01393c6ce..ad7626f32 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -1,14 +1,13 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.Events; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Dlna.Server; using MediaBrowser.Model.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; namespace MediaBrowser.Dlna.Ssdp @@ -29,13 +28,13 @@ namespace MediaBrowser.Dlna.Ssdp private Timer _queueTimer; private Timer _notificationTimer; - + private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false); private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>(); private bool _isDisposed; private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>(); - + public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature) { _logger = logger; @@ -51,6 +50,8 @@ namespace MediaBrowser.Dlna.Ssdp { RespondToSearch(args.EndPoint, args.Headers["st"]); } + + EventHelper.FireEventIfNotNull(MessageReceived, this, args, _logger); } public IEnumerable<UpnpDevice> RegisteredDevices @@ -60,7 +61,7 @@ namespace MediaBrowser.Dlna.Ssdp return _devices.Values.SelectMany(i => i).ToList(); } } - + public void Start() { _socket = CreateMulticastSocket(); @@ -79,8 +80,8 @@ namespace MediaBrowser.Dlna.Ssdp SendDatagram(header, values, _ssdpEndp, localAddress, sendCount); } - public void SendDatagram(string header, - Dictionary<string, string> values, + public void SendDatagram(string header, + Dictionary<string, string> values, IPEndPoint endpoint, IPAddress localAddress, int sendCount = 1) @@ -105,7 +106,7 @@ namespace MediaBrowser.Dlna.Ssdp { foreach (var d in RegisteredDevices) { - if (string.Equals(deviceType, "ssdp:all", StringComparison.OrdinalIgnoreCase) || + if (string.Equals(deviceType, "ssdp:all", StringComparison.OrdinalIgnoreCase) || string.Equals(deviceType, d.Type, StringComparison.OrdinalIgnoreCase)) { SendDatagram(header, values, endpoint, d.Address); @@ -141,7 +142,7 @@ namespace MediaBrowser.Dlna.Ssdp _logger.Info("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString()); } - } + } } private readonly object _queueTimerSyncLock = new object(); @@ -223,43 +224,17 @@ namespace MediaBrowser.Dlna.Ssdp var receivedCount = _socket.EndReceiveFrom(result, ref endpoint); var received = (byte[])result.AsyncState; - if (_config.Configuration.DlnaOptions.EnableDebugLogging) - { - _logger.Debug("{0} - SSDP Received a datagram", endpoint); - } + var args = SsdpHelper.ParseSsdpResponse(received, (IPEndPoint)endpoint); - using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII)) + if (_config.Configuration.DlnaOptions.EnableDebugLogging) { - var proto = (reader.ReadLine() ?? string.Empty).Trim(); - var method = proto.Split(new[] { ' ' }, 2)[0]; - var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) - { - line = line.Trim(); - if (string.IsNullOrEmpty(line)) - { - break; - } - var parts = line.Split(new[] { ':' }, 2); - - if (parts.Length >= 2) - { - headers[parts[0]] = parts[1].Trim(); - } - } + var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); + var headerText = string.Join(",", headerTexts.ToArray()); - if (_config.Configuration.DlnaOptions.EnableDebugLogging) - { - _logger.Debug("{0} - Datagram method: {1}", endpoint, method); - } - - OnMessageReceived(new SsdpMessageEventArgs - { - Method = method, - Headers = headers, - EndPoint = (IPEndPoint)endpoint - }); + _logger.Debug("{0} message received from {1}. Headers: {2}", args.Method, args.EndPoint, headerText); } + + OnMessageReceived(args); } catch (Exception ex) { diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs index 2b5f38622..b666140d2 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs @@ -1,40 +1,45 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Text; namespace MediaBrowser.Dlna.Ssdp { public class SsdpHelper { - /// <summary> - /// Parses the socket response into a location Uri for the DeviceDescription.xml. - /// </summary> - /// <param name="data">The data.</param> - /// <returns></returns> - public static Dictionary<string,string> ParseSsdpResponse(byte[] data) + public static SsdpMessageEventArgs ParseSsdpResponse(byte[] data, IPEndPoint endpoint) { - var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - - using (var reader = new StreamReader(new MemoryStream(data), Encoding.ASCII)) + using (var ms = new MemoryStream(data)) { - for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) + using (var reader = new StreamReader(ms, Encoding.ASCII)) { - line = line.Trim(); - if (string.IsNullOrEmpty(line)) + var proto = (reader.ReadLine() ?? string.Empty).Trim(); + var method = proto.Split(new[] { ' ' }, 2)[0]; + var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) { - break; + line = line.Trim(); + if (string.IsNullOrEmpty(line)) + { + break; + } + var parts = line.Split(new[] { ':' }, 2); + + if (parts.Length >= 2) + { + headers[parts[0]] = parts[1].Trim(); + } } - var parts = line.Split(new[] { ':' }, 2); - if (parts.Length == 2) + return new SsdpMessageEventArgs { - headers[parts[0]] = parts[1].Trim(); - } + Method = method, + Headers = headers, + EndPoint = endpoint + }; } } - - return headers; } } } diff --git a/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs b/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs index 4e60cfe2e..5f8ca1111 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Dlna.Ssdp var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); values["HOST"] = "239.255.255.250:1900"; - values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/0.6.9.1"; + values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2"; values["ST"] = deviceSearchType; values["MAN"] = "\"ssdp:discover\""; values["MX"] = mx; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index e129468d3..b9a8323fb 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -80,13 +80,7 @@ <None Include="packages.config" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> - <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> - <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> - <PropertyGroup> - <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> - </PropertyGroup> - <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" /> - </Target> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 2ccd364d5..f6c8f6135 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -92,6 +92,9 @@ <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPlugin.cs"> <Link>Configuration\MetadataPlugin.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Configuration\NotificationOptions.cs"> + <Link>Configuration\NotificationOptions.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs"> <Link>Configuration\ServerConfiguration.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index c3eb6b11d..9845cb6b2 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -79,6 +79,9 @@ <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPlugin.cs"> <Link>Configuration\MetadataPlugin.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Configuration\NotificationOptions.cs"> + <Link>Configuration\NotificationOptions.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs"> <Link>Configuration\ServerConfiguration.cs</Link> </Compile> diff --git a/MediaBrowser.Model/Configuration/NotificationOptions.cs b/MediaBrowser.Model/Configuration/NotificationOptions.cs new file mode 100644 index 000000000..4c0c092fe --- /dev/null +++ b/MediaBrowser.Model/Configuration/NotificationOptions.cs @@ -0,0 +1,149 @@ +using System; +using System.Linq; + +namespace MediaBrowser.Model.Configuration +{ + public class NotificationOptions + { + public NotificationOption[] Options { get; set; } + + public NotificationOptions() + { + Options = new[] + { + new NotificationOption + { + Type = NotificationType.TaskFailed.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.ServerRestartRequired.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.ApplicationUpdateAvailable.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.ApplicationUpdateInstalled.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.PluginUpdateInstalled.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.PluginUninstalled.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.InstallationFailed.ToString(), + Enabled = true + }, + new NotificationOption + { + Type = NotificationType.PluginInstalled.ToString(), + Enabled = true + } + }; + } + + public NotificationOption GetOptions(string type) + { + return Options.FirstOrDefault(i => string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)); + } + + public bool IsEnabled(string type) + { + var opt = GetOptions(type); + + return opt != null && opt.Enabled; + } + + public bool IsServiceEnabled(string service, string notificationType) + { + var opt = GetOptions(notificationType); + + return opt == null || + !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase); + } + + public bool IsEnabledToMonitorUser(string type, string userId) + { + var opt = GetOptions(type); + + return opt != null && opt.Enabled && + !opt.DisabledMonitorUsers.Contains(userId, StringComparer.OrdinalIgnoreCase); + } + + public bool IsEnabledToSendToUser(string type, string userId) + { + var opt = GetOptions(type); + + return opt != null && opt.Enabled && + !opt.DisabledSendToUsers.Contains(userId, StringComparer.OrdinalIgnoreCase); + } + } + + public class NotificationOption + { + public string Type { get; set; } + + /// <summary> + /// User Ids to not monitor (it's opt out) + /// </summary> + public string[] DisabledMonitorUsers { get; set; } + + /// <summary> + /// User Ids to not send to (it's opt out) + /// </summary> + public string[] DisabledSendToUsers { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="NotificationOption"/> is enabled. + /// </summary> + /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value> + public bool Enabled { get; set; } + + /// <summary> + /// Gets or sets the title format string. + /// </summary> + /// <value>The title format string.</value> + public string Title { get; set; } + + /// <summary> + /// Gets or sets the disabled services. + /// </summary> + /// <value>The disabled services.</value> + public string[] DisabledServices { get; set; } + + public NotificationOption() + { + DisabledServices = new string[] { }; + DisabledMonitorUsers = new string[] { }; + DisabledSendToUsers = new string[] { }; + } + } + + public enum NotificationType + { + TaskFailed, + InstallationFailed, + NewLibraryContent, + ServerRestartRequired, + ApplicationUpdateAvailable, + ApplicationUpdateInstalled, + PluginInstalled, + PluginUpdateInstalled, + PluginUninstalled, + AudioPlayback, + GamePlayback, + VideoPlayback + } +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 997f90187..03857d8b9 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -319,22 +319,4 @@ namespace MediaBrowser.Model.Configuration public string From { get; set; } public string To { get; set; } } - - public class NotificationOptions - { - public bool SendOnUpdates { get; set; } - public bool SendOnVideoPlayback { get; set; } - public bool SendOnAudioPlayback { get; set; } - public bool SendOnGamePlayback { get; set; } - public bool SendOnFailedTasks { get; set; } - public bool SendOnNewLibraryContent { get; set; } - public bool SendOnServerRestartRequired { get; set; } - - public NotificationOptions() - { - SendOnUpdates = true; - SendOnFailedTasks = true; - SendOnServerRestartRequired = true; - } - } } diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 70285078a..b23ad876c 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -1,9 +1,8 @@ -using System; +using MediaBrowser.Model.MediaInfo; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.Serialization; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index c13e1d2d0..a4bb0646a 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -113,6 +113,8 @@ namespace MediaBrowser.Model.Dto /// <value>The critic rating summary.</value> public string CriticRatingSummary { get; set; } + public List<string> MultiPartGameFiles { get; set; } + /// <summary> /// Gets or sets the path. /// </summary> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index c22688b33..710e5f6b4 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -64,6 +64,7 @@ <Compile Include="Configuration\DlnaOptions.cs" /> <Compile Include="Configuration\MetadataPlugin.cs" /> <Compile Include="Configuration\MetadataOptions.cs" /> + <Compile Include="Configuration\NotificationOptions.cs" /> <Compile Include="Configuration\ServerConfiguration.cs" /> <Compile Include="Dlna\CodecProfile.cs" /> <Compile Include="Dlna\ConditionProcessor.cs" /> diff --git a/MediaBrowser.Model/Notifications/Notification.cs b/MediaBrowser.Model/Notifications/Notification.cs index 511227cbf..9b42d8a74 100644 --- a/MediaBrowser.Model/Notifications/Notification.cs +++ b/MediaBrowser.Model/Notifications/Notification.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Model.Notifications public string Description { get; set; } public string Url { get; set; } - + public NotificationLevel Level { get; set; } public Notification() @@ -40,11 +40,48 @@ namespace MediaBrowser.Model.Notifications public List<string> UserIds { get; set; } public DateTime Date { get; set; } - + + /// <summary> + /// The corresponding type name used in configuration. Not for display. + /// </summary> + public string NotificationType { get; set; } + + public Dictionary<string, string> Variables { get; set; } + public NotificationRequest() { UserIds = new List<string>(); Date = DateTime.UtcNow; + + Variables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } } + + public class NotificationTypeInfo + { + public string Type { get; set; } + + public string Name { get; set; } + + public bool Enabled { get; set; } + + public string Category { get; set; } + + public bool IsBasedOnUserEvent { get; set; } + + public string DefaultTitle { get; set; } + + public List<string> Variables { get; set; } + + public NotificationTypeInfo() + { + Variables = new List<string>(); + } + } + + public class NotificationServiceInfo + { + public string Name { get; set; } + public string Id { get; set; } + } } diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 8da4919a2..fafcc5fad 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -183,7 +183,7 @@ namespace MediaBrowser.Providers.Manager { target.ForcedSortName = source.ForcedSortName; target.LockedFields = source.LockedFields; - target.DontFetchMeta = source.DontFetchMeta; + target.IsLocked = source.IsLocked; target.DisplayMediaType = source.DisplayMediaType; var sourceHasLanguageSettings = source as IHasPreferredMetadataLanguage; diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index bbf692536..653cbacb6 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -56,7 +56,7 @@ namespace MediaBrowser.Server.Implementations.Collections Parent = parentFolder, DisplayMediaType = "Collection", Path = path, - DontFetchMeta = options.IsLocked, + IsLocked = options.IsLocked, ProviderIds = options.ProviderIds }; diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 480cb39db..5854cf28a 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -313,6 +313,7 @@ namespace MediaBrowser.Server.Implementations.Dto { dto.Players = item.PlayersSupported; dto.GameSystem = item.GameSystem; + dto.MultiPartGameFiles = item.MultiPartGameFiles; } private void SetGameSystemProperties(BaseItemDto dto, GameSystem item) diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs index fa02e5f76..dd2c5bf85 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs @@ -9,14 +9,17 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Tasks; using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Updates; namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { @@ -52,7 +55,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications public void Run() { - _installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted; + _installationManager.PluginInstalled += _installationManager_PluginInstalled; + _installationManager.PluginUpdated += _installationManager_PluginUpdated; _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; _installationManager.PluginUninstalled += _installationManager_PluginUninstalled; @@ -65,98 +69,192 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; } - async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e) + async void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e) { - // This notification is for users who can't auto-update (aka running as service) - if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate || !_config.Configuration.NotificationOptions.SendOnUpdates) + var type = NotificationType.PluginUpdateInstalled.ToString(); + + var installationInfo = e.Argument.Item1; + + var userIds = _userManager + .Users + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var notification = new NotificationRequest { - return; - } + UserIds = userIds, + Description = installationInfo.Description, + NotificationType = type + }; + + notification.Variables["Name"] = installationInfo.Name; + notification.Variables["Version"] = installationInfo.Version.ToString(); + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e) + { + var type = NotificationType.PluginInstalled.ToString(); + + var installationInfo = e.Argument; var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); var notification = new NotificationRequest { UserIds = userIds, - Name = "A new version of Media Browser is available.", - Description = "Please see mediabrowser3.com for details." + Description = installationInfo.description, + NotificationType = type }; + notification.Variables["Name"] = installationInfo.name; + notification.Variables["Version"] = installationInfo.versionStr; + await SendNotification(notification).ConfigureAwait(false); } - async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) + async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e) { - if (!_appHost.HasPendingRestart || !_config.Configuration.NotificationOptions.SendOnUpdates) + // This notification is for users who can't auto-update (aka running as service) + if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate) { return; } + var type = NotificationType.ApplicationUpdateAvailable.ToString(); + var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); var notification = new NotificationRequest { UserIds = userIds, - Name = "Please restart Media Browser to finish updating" + Description = "Please see mediabrowser3.com for details.", + NotificationType = type }; await SendNotification(notification).ConfigureAwait(false); } - async void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) { - if (!NotifyOnPlayback(e.MediaInfo.MediaType)) + if (!_appHost.HasPendingRestart) { return; } + var type = NotificationType.ServerRestartRequired.ToString(); + var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); - var item = e.MediaInfo; + var notification = new NotificationRequest + { + UserIds = userIds, + NotificationType = type + }; - var msgName = "playing " + item.Name; + await SendNotification(notification).ConfigureAwait(false); + } + async void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + { var user = e.Users.FirstOrDefault(); - if (user != null) - { - msgName = user.Name + " " + msgName; - } + var userIds = _userManager + .Users + .Where(i => NotifyOnPlayback(e.MediaInfo.MediaType, user, i)) + .Select(i => i.Id.ToString("N")) + .ToList(); + + var item = e.MediaInfo; var notification = new NotificationRequest { - UserIds = userIds, - Name = msgName + UserIds = userIds }; + notification.Variables["ItemName"] = item.Name; + notification.Variables["UserName"] = user == null ? "Unknown user" : user.Name; + notification.Variables["AppName"] = e.ClientName; + notification.Variables["DeviceName"] = e.DeviceName; + await SendNotification(notification).ConfigureAwait(false); } - private bool NotifyOnPlayback(string mediaType) + private bool NotifyOnPlayback(string mediaType, User playingUser, User notifiedUser) { if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { - return _config.Configuration.NotificationOptions.SendOnAudioPlayback; + var type = NotificationType.AudioPlayback.ToString(); + + if (playingUser != null) + { + if (!_config.Configuration.NotificationOptions.IsEnabledToMonitorUser( + type, playingUser.Id.ToString("N"))) + { + return false; + } + + if (playingUser.Id == notifiedUser.Id) + { + return false; + } + } + + return _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, notifiedUser.Id.ToString("N")); } if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase)) { - return _config.Configuration.NotificationOptions.SendOnGamePlayback; + var type = NotificationType.GamePlayback.ToString(); + + if (playingUser != null) + { + if (!_config.Configuration.NotificationOptions.IsEnabledToMonitorUser( + type, playingUser.Id.ToString("N"))) + { + return false; + } + + if (playingUser.Id == notifiedUser.Id) + { + return false; + } + } + + return _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, notifiedUser.Id.ToString("N")); } if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { - return _config.Configuration.NotificationOptions.SendOnVideoPlayback; + var type = NotificationType.VideoPlayback.ToString(); + + if (playingUser != null) + { + if (!_config.Configuration.NotificationOptions.IsEnabledToMonitorUser( + type, playingUser.Id.ToString("N"))) + { + return false; + } + + if (playingUser.Id == notifiedUser.Id) + { + return false; + } + } + + return _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, notifiedUser.Id.ToString("N")); } return false; @@ -164,12 +262,13 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications async void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) { - if (_config.Configuration.NotificationOptions.SendOnNewLibraryContent && - e.Item.LocationType == LocationType.FileSystem) + if (e.Item.LocationType == LocationType.FileSystem) { + var type = NotificationType.NewLibraryContent.ToString(); + var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); @@ -178,23 +277,20 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications var notification = new NotificationRequest { UserIds = userIds, - Name = item.Name + " added to library." + NotificationType = type }; + notification.Variables["Name"] = item.Name; + await SendNotification(notification).ConfigureAwait(false); } } async void _userManager_UserCreated(object sender, GenericEventArgs<User> e) { - var userIds = _userManager - .Users - .Select(i => i.Id.ToString("N")) - .ToList(); - var notification = new NotificationRequest { - UserIds = userIds, + UserIds = new List<string> { e.Argument.Id.ToString("N") }, Name = "Welcome to Media Browser!", Description = "Check back here for more notifications." }; @@ -206,68 +302,51 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var result = e.Argument; - if (result.Status == TaskCompletionStatus.Failed && - _config.Configuration.NotificationOptions.SendOnFailedTasks) + if (result.Status == TaskCompletionStatus.Failed) { + var type = NotificationType.TaskFailed.ToString(); + var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); var notification = new NotificationRequest { UserIds = userIds, - Name = result.Name + " failed", Description = result.ErrorMessage, - Level = NotificationLevel.Error + Level = NotificationLevel.Error, + NotificationType = type }; + notification.Variables["Name"] = e.Argument.Name; + await SendNotification(notification).ConfigureAwait(false); } } async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e) { + var type = NotificationType.PluginUninstalled.ToString(); + var plugin = e.Argument; var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) - .Select(i => i.Id.ToString("N")) - .ToList(); - - var notification = new NotificationRequest - { - UserIds = userIds, - Name = plugin.Name + " has been uninstalled" - }; - - await SendNotification(notification).ConfigureAwait(false); - } - - async void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e) - { - if (!_config.Configuration.NotificationOptions.SendOnUpdates) - { - return; - } - - var installationInfo = e.InstallationInfo; - - var userIds = _userManager - .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); var notification = new NotificationRequest { UserIds = userIds, - Name = installationInfo.Name + " " + installationInfo.Version + " was installed", - Description = e.PackageVersionInfo.description + NotificationType = type }; + notification.Variables["Name"] = plugin.Name; + notification.Variables["Version"] = plugin.Version.ToString(); + await SendNotification(notification).ConfigureAwait(false); } @@ -275,9 +354,11 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var installationInfo = e.InstallationInfo; + var type = NotificationType.InstallationFailed.ToString(); + var userIds = _userManager .Users - .Where(i => i.Configuration.IsAdministrator) + .Where(i => i.Configuration.IsAdministrator && _config.Configuration.NotificationOptions.IsEnabledToSendToUser(type, i.Id.ToString("N"))) .Select(i => i.Id.ToString("N")) .ToList(); @@ -285,10 +366,13 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { UserIds = userIds, Level = NotificationLevel.Error, - Name = installationInfo.Name + " " + installationInfo.Version + " installation failed", - Description = e.Exception.Message + Description = e.Exception.Message, + NotificationType = type }; + notification.Variables["Name"] = installationInfo.Name; + notification.Variables["Version"] = installationInfo.Version; + await SendNotification(notification).ConfigureAwait(false); } @@ -306,7 +390,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications public void Dispose() { - _installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted; + _installationManager.PluginInstalled -= _installationManager_PluginInstalled; + _installationManager.PluginUpdated -= _installationManager_PluginUpdated; _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled; diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 3097495e3..10c9cf733 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Implementations.Library // Make sure the item has a name EnsureName(item, args); - item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || + item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || item.Parents.Any(i => i.IsLocked); // Make sure DateCreated and DateModified have values diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ar.json b/MediaBrowser.Server.Implementations/Localization/Server/ar.json index 9bdfa4b7e..2730f5e3b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ar.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ar.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ca.json b/MediaBrowser.Server.Implementations/Localization/Server/ca.json index 363277cde..f6609df7c 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ca.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ca.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/cs.json b/MediaBrowser.Server.Implementations/Localization/Server/cs.json index aa572205f..7ae06b232 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/cs.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/cs.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/de.json b/MediaBrowser.Server.Implementations/Localization/Server/de.json index 4c989aaa2..c3821d84a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/de.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/de.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/el.json b/MediaBrowser.Server.Implementations/Localization/Server/el.json index bc19958ca..f258e00e1 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/el.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/el.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json b/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json index 3e4461bf3..86c850a25 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json index ae27769ab..5dd5027e9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/es.json b/MediaBrowser.Server.Implementations/Localization/Server/es.json index 3f28acd71..a896fcfdd 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/es.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/es.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json index 6e697e563..6785e5f2a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "Cuando se encuentra deshabilitado los clientes podr\u00edan mostrar una pantalla de inicio de sesi\u00f3n con una selecci\u00f3n visual de los usuarios.", "OptionOtherApps": "Otras applicaciones", "OptionMobileApps": "Apps m\u00f3viles", - "HeaderEnableNotificationForEvents": "Enviar notificaciones para los siguientes eventos:", - "OptionNotifyOnUpdates": "Cuando se encuentren disponibles actualizaciones", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Cuando las tareas programadas fallen", - "OptionNotifyOnNewLibraryContent": "Cuando se a\u00f1adan nuevos contenidos a la biblioteca", + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", "SendNotificationHelp": "Las notificaciones son enviadas a la bandeja de entrada del panel de control. Navegue el cat\u00e1logo de complementos para instalar opciones de notificaci\u00f3n adiconales.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/fr.json b/MediaBrowser.Server.Implementations/Localization/Server/fr.json index f43479409..8001cdbb6 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json @@ -60,7 +60,7 @@ "LabelDisplayMissingEpisodesWithinSeasons": "Afficher les \u00e9pisodes manquants dans les saisons", "LabelUnairedMissingEpisodesWithinSeasons": "Afficher les \u00e9pisodes non diffus\u00e9s dans les saisons", "HeaderVideoPlaybackSettings": "Param\u00e8tres de lecture video", - "HeaderPlaybackSettings": "Playback Settings", + "HeaderPlaybackSettings": "Param\u00e8tres de lecture", "LabelAudioLanguagePreference": "Param\u00e8tres de langue audio:", "LabelSubtitleLanguagePreference": "Param\u00e8tres de langue de sous-titre", "LabelDisplayForcedSubtitlesOnly": "Afficher seulement les sous-titres forc\u00e9s", @@ -529,7 +529,7 @@ "ButtonRetrieveKey": "obtenir la cl\u00e9", "LabelSupporterKey": "Cl\u00e9 de supporteur (coller du courriel)", "LabelSupporterKeyHelp": "Enter your supporter key to start enjoying additional benefits the community has developed for Media Browser.", - "MessageInvalidKey": "MB3 Key Missing or Invalid", + "MessageInvalidKey": "Cl\u00e9 MB3 manquante ou invalide", "ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an MB3 Supporter. Please donate and support the continued development of the core product. Thank you.", "HeaderDisplaySettings": "Param\u00e8tres d'affichage", "TabPlayTo": "Lire sur", @@ -542,24 +542,37 @@ "LabelDefaultUser": "Utilisateur par d\u00e9faut:", "LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles.", "TitleDlna": "DLNA", - "HeaderServerSettings": "Server Settings", - "LabelWeatherDisplayLocation": "Weather display location:", - "LabelWeatherDisplayLocationHelp": "US zip code \/ City, State, Country \/ City, Country", - "LabelWeatherDisplayUnit": "Weather display unit:", + "HeaderServerSettings": "Param\u00e8tres du serveur", + "LabelWeatherDisplayLocation": "Emplacement de l'affichage de la m\u00e9t\u00e9o:", + "LabelWeatherDisplayLocationHelp": "Code ZIP US \/ Ville, \u00c9tat, Pays \/ Ville, Pays", + "LabelWeatherDisplayUnit": "Unit\u00e9 d'affichage de la m\u00e9t\u00e9o:", "OptionCelsius": "Celsius", "OptionFahrenheit": "Fahrenheit", - "HeaderRequireManualLogin": "Require manual username entry for:", - "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", - "OptionOtherApps": "Other apps", - "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderRequireManualLogin": "Exiger l'entr\u00e9e manuelle du nom d'utilisateur pour:", + "HeaderRequireManualLoginHelp": "Lorsque d\u00e9sactiv\u00e9, le clients auront un \u00e9cran de connexion avec une s\u00e9lection visuelle des utilisateurs.", + "OptionOtherApps": "Autres applications", + "OptionMobileApps": "Applications mobiles", + "HeaderNotificationList": "Cliquez sur une notification pour configurer ses options d'envois", + "NotificationOptionApplicationUpdateAvailable": "Mise \u00e0 jour d'application disponible", + "NotificationOptionApplicationUpdateInstalled": "Mise \u00e0 jour d'application mis \u00e0 jour", + "NotificationOptionPluginUpdateInstalled": "Mise \u00e0 jour de plugin install\u00e9e", + "NotificationOptionPluginInstalled": "Plugin install\u00e9", + "NotificationOptionPluginUninstalled": "Plugin d\u00e9sinstall\u00e9", + "NotificationOptionVideoPlayback": "Lecture vid\u00e9o", + "NotificationOptionAudioPlayback": "Lecture audio", + "NotificationOptionGamePlayback": "Lecture des jeux", + "NotificationOptionTaskFailed": "\u00c9chec de t\u00e2che programm\u00e9e", + "NotificationOptionInstallationFailed": "\u00c9chec d'installation", + "NotificationOptionNewLibraryContent": "Nouveau contenu ajout\u00e9", + "SendNotificationHelp": "Par d\u00e9faut, les notifications sont d\u00e9livr\u00e9es dans la bo\u00eete de r\u00e9ception du tableau de bord. Consultez le catalogue de plugins pour installer des options de notifications suppl\u00e9mentaires.", + "NotificationOptionServerRestartRequired": "Un red\u00e9marrage du serveur est requis", + "LabelNotificationEnabled": "Activer cette notification", + "LabelMonitorUsers": "Surveiller les activit\u00e9s de:", + "LabelSendNotificationToUsers": "Envoyer la notification \u00e0:", + "UsersNotNotifiedAboutSelfActivity": "Les utilisateurs ne seront pas notifi\u00e9s de leurs propres activit\u00e9s.", + "LabelUseNotificationServices": "Utiliser les services suivants:", + "CategoryUser": "Utilisateur", + "CategorySystem": "Syst\u00e8me", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/he.json b/MediaBrowser.Server.Implementations/Localization/Server/he.json index 0aaae2719..2a9f3b62c 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/he.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/he.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "\u05db\u05d0\u05e9\u05e8 \u05de\u05d1\u05d5\u05d8\u05dc, \u05d9\u05d5\u05e6\u05d2 \u05dc\u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd \u05dc\u05d5\u05d7 \u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e2\u05dd \u05d1\u05d7\u05d9\u05e8\u05ea \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd.", "OptionOtherApps": "\u05ea\u05d5\u05db\u05e0\u05d5\u05ea \u05d0\u05d7\u05e8\u05d5\u05ea", "OptionMobileApps": "\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d5\u05ea \u05dc\u05e0\u05d9\u05d9\u05d3", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/it.json b/MediaBrowser.Server.Implementations/Localization/Server/it.json index 1c7b07328..d232f853a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/it.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/it.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/kk.json b/MediaBrowser.Server.Implementations/Localization/Server/kk.json index 2bf294aa0..0c8f38b48 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/kk.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/kk.json @@ -85,7 +85,7 @@ "MessagePleaseEnsureInternetMetadata": "\u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0442\u0435\u043d \u043c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440\u0434\u0456 \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443\u044b \u049b\u043e\u0441\u044b\u043b\u0493\u0430\u043d\u044b\u043d \u0442\u0435\u043a\u0441\u0435\u0440\u0456\u04a3\u0456\u0437.", "TabSuggested": "\u04b0\u0441\u044b\u043d\u044b\u043b\u0493\u0430\u043d", "TabLatest": "\u0415\u04a3 \u043a\u0435\u0439\u0456\u043d\u0433\u0456", - "TabUpcoming": "\u0410\u043b\u0434\u0430\u0493\u044b", + "TabUpcoming": "\u041a\u04af\u0442\u0456\u043b\u0433\u0435\u043d", "TabShows": "\u0421\u0435\u0440\u0438\u0430\u043b\u0434\u0430\u0440", "TabEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u0442\u0430\u0440", "TabGenres": "\u0416\u0430\u043d\u0440\u043b\u0430\u0440", @@ -103,7 +103,7 @@ "OptionWriters": "\u0416\u0430\u0437\u0443\u0448\u044b\u043b\u0430\u0440", "OptionProducers": "\u041f\u0440\u043e\u0434\u044e\u0441\u0435\u0440\u043b\u0435\u0440", "HeaderResume": "\u0416\u0430\u043b\u0493\u0430\u0441\u0442\u044b\u0440\u0443", - "HeaderNextUp": "\u0416\u0430\u043b\u0493\u0430\u0441\u044b \u0431\u0430\u0440", + "HeaderNextUp": "\u0410\u043b\u0434\u0430\u0493\u044b", "NoNextUpItemsMessage": "\u0415\u0448\u0442\u0435\u04a3\u0435 \u0442\u0430\u0431\u044b\u043b\u043c\u0430\u0434\u044b. \u041a\u04e9\u0440\u0441\u0435\u0442\u0456\u043c\u0434\u0435\u0440\u0456\u04a3\u0456\u0437\u0434\u0456 \u049b\u0430\u0440\u0430\u0439 \u0431\u0430\u0441\u0442\u0430\u04a3\u044b\u0437!", "HeaderLatestEpisodes": "\u0415\u04a3 \u043a\u0435\u0439\u0456\u043d\u0433\u0456 \u044d\u043f\u0438\u0437\u043e\u0434\u0442\u0430\u0440", "HeaderPersonTypes": "\u0410\u0434\u0430\u043c \u0442\u04af\u0440\u043b\u0435\u0440\u0456:", @@ -291,8 +291,8 @@ "OptionPrePaddingRequired": "\u0410\u043b\u0493\u0430 \u0448\u0435\u0433\u0456\u043d\u0456\u0441 \u0436\u0430\u0437\u0443 \u04af\u0448\u0456\u043d \u043a\u0435\u0440\u0435\u043a.", "LabelPostPaddingMinutes": "\u0410\u0440\u0442\u049b\u0430 \u0448\u0435\u0433\u0456\u043d\u0456\u0441, \u043c\u0438\u043d:", "OptionPostPaddingRequired": "\u0410\u0440\u0442\u049b\u0430 \u0448\u0435\u0433\u0456\u043d\u0456\u0441 \u0436\u0430\u0437\u0443 \u04af\u0448\u0456\u043d \u043a\u0435\u0440\u0435\u043a.", - "HeaderWhatsOnTV": "\u0410\u0493\u044b\u043c\u0434\u0430\u0493\u044b", - "HeaderUpcomingTV": "\u0410\u043b\u0434\u0430\u0493\u044b \u0422\u0414", + "HeaderWhatsOnTV": "\u042d\u0444\u0438\u0440\u0434\u0435", + "HeaderUpcomingTV": "\u041a\u04af\u0442\u0456\u043b\u0433\u0435\u043d \u0422\u0414", "TabStatus": "\u041a\u04af\u0439", "TabSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440", "ButtonRefreshGuideData": "\u0422\u0414 \u043a\u0435\u0441\u0442\u0435\u0441\u0456 \u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440\u0456\u043d \u0436\u0430\u04a3\u0430\u0440\u0442\u0443", @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "\u0410\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u0493\u0430\u043d\u0434\u0430, \u043a\u043b\u0438\u0435\u043d\u0442\u0442\u0435\u0440 \u043f\u0430\u0439\u0434\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440\u0434\u044b \u043a\u04e9\u0440\u043d\u0435\u043a\u0456 \u0442\u0430\u04a3\u0434\u0430\u0443\u044b \u0431\u0430\u0440 \u043a\u0456\u0440\u0443 \u044d\u043a\u0440\u0430\u043d\u044b\u043d \u043a\u04e9\u0440\u0441\u0435\u0442\u0435 \u0430\u043b\u0430\u0434\u044b.", "OptionOtherApps": "\u0411\u0430\u0441\u049b\u0430 \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u043b\u0430\u0440", "OptionMobileApps": "\u04b0\u0442\u049b\u044b\u0440 \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u043b\u0430\u0440", - "HeaderEnableNotificationForEvents": "\u041a\u0435\u043b\u0435\u0441\u0456 \u043e\u049b\u0438\u0493\u0430\u043b\u0430\u0440 \u04af\u0448\u0456\u043d \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u044b\u0440\u0443\u043b\u0430\u0440\u0434\u044b \u0436\u0456\u0431\u0435\u0440\u0443:", - "OptionNotifyOnUpdates": "\u0416\u0430\u04a3\u0430\u0440\u0442\u0443\u043b\u0430\u0440 \u049b\u043e\u043b \u0436\u0435\u0442\u0456\u043c\u0434\u0456 \u0431\u043e\u043b\u0493\u0430\u043d\u0434\u0430", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "\u0416\u043e\u0441\u043f\u0430\u0440\u043b\u0430\u0493\u0430\u043d \u0442\u0430\u043f\u0441\u044b\u0440\u043c\u0430\u043b\u0430\u0440 \u0430\u049b\u0430\u0443\u043b\u044b\u049b\u0442\u0430\u0440\u044b\u043d\u0434\u0430", - "OptionNotifyOnNewLibraryContent": "\u0422\u0430\u0441\u0443\u0448\u044b\u0445\u0430\u043d\u0430\u0493\u0430 \u0436\u0430\u04a3\u0430 \u043c\u0430\u0437\u043c\u04b1\u043d \u04af\u0441\u0442\u0435\u043b\u0433\u0435\u043d\u0434\u0435", - "SendNotificationHelp": "\u0425\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443\u043b\u0430\u0440 \u0431\u0430\u049b\u044b\u043b\u0430\u0443 \u0442\u0430\u049b\u0442\u0430\u0441\u044b\u043d\u0434\u0430\u0493\u044b \u043a\u0456\u0440\u0456\u0441 \u0436\u04d9\u0448\u0456\u0433\u0456\u043d\u0435 \u0436\u0435\u0442\u043a\u0456\u0437\u0456\u043b\u0435\u0434\u0456. \u049a\u043e\u0441\u044b\u043c\u0448\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440\u0434\u0456 \u043e\u0440\u043d\u0430\u0442\u0443 \u04af\u0448\u0456\u043d \u043f\u043b\u0430\u0433\u0438\u043d\u0434\u0435\u0440 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0456\u043d \u0448\u0430\u0440\u043b\u0430\u04a3\u044b\u0437.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "\u0416\u0456\u0431\u0435\u0440\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440\u0456\u043d \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043b\u0430\u0443 \u04af\u0448\u0456\u043d \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443\u0493\u0430 \u0431\u0430\u0441\u044b\u04a3\u044b\u0437.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "\u0411\u0435\u0439\u043d\u0435 \u043e\u0439\u043d\u0430\u0442\u0443", + "NotificationOptionAudioPlayback": "\u0414\u044b\u0431\u044b\u0441 \u043e\u0439\u043d\u0430\u0442\u0443", + "NotificationOptionGamePlayback": "\u041e\u0439\u044b\u043d \u043e\u0439\u043d\u0430\u0442\u0443", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "\u0422\u0430\u0441\u0443\u0448\u044b\u0445\u0430\u043d\u0430\u0493\u0430 \u0436\u0430\u04a3\u0430 \u043c\u0430\u0437\u043c\u04b1\u043d \u04af\u0441\u0442\u0435\u043b\u0433\u0435\u043d", + "SendNotificationHelp": "\u04d8\u0434\u0435\u043f\u043a\u0456\u0434\u0435, \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443\u043b\u0430\u0440 \u0431\u0430\u049b\u044b\u043b\u0430\u0443 \u0442\u0430\u049b\u0442\u0430\u0441\u044b\u043d\u0434\u0430\u0493\u044b \u043a\u0456\u0440\u0456\u0441 \u0436\u04d9\u0448\u0456\u0433\u0456\u043d\u0435 \u0436\u0435\u0442\u043a\u0456\u0437\u0456\u043b\u0435\u0434\u0456. \u049a\u043e\u0441\u044b\u043c\u0448\u0430 \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443 \u049b\u04b1\u0440\u0430\u043b\u0434\u0430\u0440\u044b\u043d \u043e\u0440\u043d\u0430\u0442\u0443 \u04af\u0448\u0456\u043d \u043f\u043b\u0430\u0433\u0438\u043d\u0434\u0435\u0440 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0456\u043d \u0448\u0430\u0440\u043b\u0430\u04a3\u044b\u0437.", + "NotificationOptionServerRestartRequired": "\u0421\u0435\u0440\u0432\u0435\u0440\u0434\u0456 \u049b\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443 \u049b\u0430\u0436\u0435\u0442", + "LabelNotificationEnabled": "\u0411\u04b1\u043b \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443\u0434\u044b \u049b\u043e\u0441\u0443", + "LabelMonitorUsers": "\u041c\u044b\u043d\u0430 \u0436\u0435\u0440\u0434\u0435\u043d \u04d9\u0440\u0435\u043a\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u0431\u0430\u049b\u044b\u043b\u0430\u0443:", + "LabelSendNotificationToUsers": "\u0425\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u0443\u0434\u044b \u043c\u044b\u043d\u0430 \u0436\u0435\u0440\u0433\u0435 \u0436\u0456\u0431\u0435\u0440\u0443:", + "UsersNotNotifiedAboutSelfActivity": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440\u0493\u0430 \u04e9\u0437\u0456\u043d\u0456\u04a3 \u04d9\u0440\u0435\u043a\u0435\u0442\u0442\u0435\u0440\u0456 \u0442\u0443\u0440\u0430\u043b\u044b \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u043d\u0434\u044b\u0440\u044b\u043b\u043c\u0430\u0439\u0434\u044b.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ms.json b/MediaBrowser.Server.Implementations/Localization/Server/ms.json index 3929f1702..cb2c7f80d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ms.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ms.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/nb.json b/MediaBrowser.Server.Implementations/Localization/Server/nb.json index 5e12320cf..6b39c850e 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/nb.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/nb.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/nl.json b/MediaBrowser.Server.Implementations/Localization/Server/nl.json index 5d4b18899..a2c20ab97 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/nl.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/nl.json @@ -37,7 +37,7 @@ "ButtonCancel": "Annuleren", "ButtonNew": "Nieuw", "HeaderSetupLibrary": "Stel uw mediabibliotheek in", - "ButtonAddMediaFolder": "Voeg mediamap toe", + "ButtonAddMediaFolder": "Mediamap toevoegen", "LabelFolderType": "Maptype:", "MediaFolderHelpPluginRequired": "* Hiervoor is het gebruik van een plug-in vereist, bijvoorbeeld GameBrowser of MB Bookshelf.", "ReferToMediaLibraryWiki": "Raadpleeg de mediabibliotheek wiki.", @@ -45,9 +45,9 @@ "LabelLanguage": "Taal:", "HeaderPreferredMetadataLanguage": "Gewenste metagegevens taal:", "LabelSaveLocalMetadata": "Sla afbeeldingen en metagegevens op in de mediamappen", - "LabelSaveLocalMetadataHelp": "Afbeeldingen en metagegevens opslaan in mediamappen zorgt er voor dat ze makkelijk vindbaar zijn en gemakkelijk kunnen worden bewerkt.", + "LabelSaveLocalMetadataHelp": "Door afbeeldingen en metagegevens op te slaan in de mediamappen kunnen ze makkelijker worden gevonden en bewerkt.", "LabelDownloadInternetMetadata": "Download afbeeldingen en metagegevens van het internet", - "LabelDownloadInternetMetadataHelp": "Media Browser kan informatie downloaden over uw media om mooie rijkelijke weergave mogelijk te maken.", + "LabelDownloadInternetMetadataHelp": "Media Browser kan informatie en afbeeldingen van uw media downloaden, om zo een mooie en uitgebreide weergave mogelijk te maken.", "TabPreferences": "Voorkeuren", "TabPassword": "Wachtwoord", "TabLibraryAccess": "Bibliotheek toegang", @@ -55,7 +55,7 @@ "TabProfile": "Profiel", "TabMetadata": "Metagegevens", "TabImages": "Afbeeldingen", - "TabNotifications": "Notifications", + "TabNotifications": "Meldingen", "TabCollectionTitles": "Titels", "LabelDisplayMissingEpisodesWithinSeasons": "Toon ontbrekende afleveringen binnen een seizoen", "LabelUnairedMissingEpisodesWithinSeasons": "Toon toekomstige afleveringen binnen een seizoen", @@ -75,7 +75,7 @@ "LabelCurrentPassword": "Huidig wachtwoord", "LabelMaxParentalRating": "Leeftijdsgrens", "MaxParentalRatingHelp": "Media met een hogere classificatie wordt niet weergegeven", - "LibraryAccessHelp": "Selecteer de media mappen om met deze gebruiker te delen. Beheerders kunnen alle mappen bewerken via de metagegevens manager.", + "LibraryAccessHelp": "Selecteer de mediamappen om met deze gebruiker te delen. Beheerders kunnen alle mappen bewerken via de metagegevens manager.", "ButtonDeleteImage": "Verwijder afbeelding", "ButtonUpload": "Uploaden", "HeaderUploadNewImage": "Nieuwe afbeelding uploaden", @@ -191,10 +191,10 @@ "OptionMissingImdbId": "IMDb Id ontbreekt", "OptionMissingTvdbId": "TheTVDB Id ontbreekt", "OptionMissingOverview": "Overzicht ontbreekt", - "OptionFileMetadataYearMismatch": "Bestands\/metagegevens Jaren komen niet overeen", + "OptionFileMetadataYearMismatch": "Het jaartal in de Bestands\/metagegevens komt niet overeen", "TabGeneral": "Algemeen", "TitleSupport": "Ondersteuning", - "TabLog": "Log", + "TabLog": "Logboek", "TabAbout": "Over", "TabSupporterKey": "Supporter Sleutel", "TabBecomeSupporter": "Word Supporter", @@ -236,7 +236,7 @@ "OptionDev": "Dev (Instabiel)", "LabelAllowServerAutoRestart": "Sta de server toe automatisch te herstarten om updates toe te passen", "LabelAllowServerAutoRestartHelp": "De server zal alleen opnieuw op tijdens inactieve perioden, wanneer er geen gebruikers actief zijn.", - "LabelEnableDebugLogging": "Foutopsporing log inschakelen", + "LabelEnableDebugLogging": "Foutopsporings loboekg inschakelen", "LabelRunServerAtStartup": "Start server bij het aanmelden", "LabelRunServerAtStartupHelp": "Dit zal de applicatie starten als pictogram in het systeemvak tijdens het aanmelden van Windows. Om de Windows-service te starten, schakelt u deze uit en start u de service via het Configuratiescherm van Windows. Houd er rekening mee dat u de service en de applicatie niet tegelijk kunt uitvoeren, u moet dus eerst de applicatie sluiten alvorens u de service start.", "ButtonSelectDirectory": "Selecteer map", @@ -356,7 +356,7 @@ "TabGameSystems": "Game Systemen", "TitleMediaLibrary": "Media Bibliotheek", "TabFolders": "Mappen", - "TabPathSubstitution": "Pad Vervanging", + "TabPathSubstitution": "Pad Vervangen", "LabelSeasonZeroDisplayName": "Seizoen 0 weergave naam:", "LabelEnableRealtimeMonitor": "Real time monitoring inschakelen", "LabelEnableRealtimeMonitorHelp": "Wijzigingen worden direct verwerkt, op ondersteunde bestandssystemen.", @@ -381,7 +381,7 @@ "ButtonPlayTrailer": "Trailer", "LabelMissing": "Ontbreekt", "LabelOffline": "Offline", - "PathSubstitutionHelp": "Pad vervangingen worden gebruikt voor het in kaart brengen van een pad op de server naar een pad dat de Cli\u00ebnts in staat stellen om toegang te krijgen. Doordat Cli\u00ebnts directe toegang tot de media op de server hebben zijn zij in staat om ze direct af te spelen via het netwerk en daardoor het gebruik van server resources om te streamen en te transcoderen te vermijden.", + "PathSubstitutionHelp": "Pad vervangen worden gebruikt voor het in kaart brengen van een pad op de server naar een pad dat de Cli\u00ebnt in staat stelt om toegang te krijgen. Doordat de Cli\u00ebnt directe toegang tot de media op de server heeft is deze in staat om ze direct af te spelen via het netwerk. Daardoor wordt het gebruik van server resources om te streamen en te transcoderen vermeden.", "HeaderFrom": "Van", "HeaderTo": "Naar", "LabelFrom": "Van:", @@ -403,7 +403,7 @@ "OptionHighSpeedTranscoding": "Hogere snelheid", "OptionHighQualityTranscoding": "Hogere kwaliteit", "OptionMaxQualityTranscoding": "Max kwaliteit", - "OptionEnableDebugTranscodingLogging": "Transcodeer Foutopsporings log inschakelen", + "OptionEnableDebugTranscodingLogging": "Transcodeer Foutopsporings logboek inschakelen", "OptionEnableDebugTranscodingLoggingHelp": "Dit zal zeer grote logbestanden maken en wordt alleen aanbevolen wanneer het nodig is voor het oplossen van problemen.", "OptionUpscaling": "Cli\u00ebnts kunnen opgeschaalde video aanvragen", "OptionUpscalingHelp": "In sommige gevallen zal dit resulteren in een betere videokwaliteit, maar zal het CPU-gebruik verhogen.", @@ -451,7 +451,7 @@ "LabelMaxResumePercentageHelp": "Titels worden ingesteld als volledig afgespeeld als gestopt na deze tijd", "LabelMinResumeDurationHelp": "Titels korter deze tijd dit zullen niet hervatbaar zijn", "TitleAutoOrganize": "Automatisch Organiseren", - "TabActivityLog": "Activiteiten Log", + "TabActivityLog": "Activiteiten Logboek", "HeaderName": "Naam", "HeaderDate": "Datum", "HeaderSource": "Bron", @@ -545,21 +545,34 @@ "HeaderServerSettings": "Server Instellingen", "LabelWeatherDisplayLocation": "Weersbericht locatie:", "LabelWeatherDisplayLocationHelp": "US postcode \/ plaats, staat, land \/ Stad, Land \/ Weer ID", - "LabelWeatherDisplayUnit": "Temeratuur eenheid:", + "LabelWeatherDisplayUnit": "Temperatuurs eenheid:", "OptionCelsius": "Celsius", "OptionFahrenheit": "Fahrenheit", "HeaderRequireManualLogin": "Vereist handmatig aanmelden met gebruikersnaam voor:", - "HeaderRequireManualLoginHelp": "Indien uitgeschaleld dan tonen de cli\u00ebnts een visueel login-scherm met een selectie van gebruikers.", + "HeaderRequireManualLoginHelp": "Indien uitgeschaleld dan toont de cli\u00ebnt een login-scherm met een visuele selectie van gebruikers.", "OptionOtherApps": "Overige apps", "OptionMobileApps": "Mobiele apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Klik op een melding om de opties voor het versturen ervan te configureren .", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "Meldingen worden geplaatst in de dashboard inbox. Blader door de Plug-ins catalogus om aanvullende opties voor meldingen te installeren.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Deze melding inschakelen", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json index 7b20c11be..d763a636c 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json @@ -55,7 +55,7 @@ "TabProfile": "Perfil", "TabMetadata": "Metadados", "TabImages": "Imagens", - "TabNotifications": "Notifications", + "TabNotifications": "Notifica\u00e7\u00f5es", "TabCollectionTitles": "T\u00edtulos", "LabelDisplayMissingEpisodesWithinSeasons": "Exibir epis\u00f3dios ausentes dentro das temporadas", "LabelUnairedMissingEpisodesWithinSeasons": "Exibir epis\u00f3dios por estrear dentro das temporadas", @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "Quando desativados, os clientes podem mostrar a tela de login com uma sele\u00e7\u00e3o visual de usu\u00e1rios.", "OptionOtherApps": "Outras apps", "OptionMobileApps": "Apps m\u00f3veis", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "Notifica\u00e7\u00f5es s\u00e3o entregues \u00e0 caixa de entrada do painel. Navegue pelo cat\u00e1logo de plugins para instalar op\u00e7\u00f5es adicionais de notifica\u00e7\u00f5es.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json index fa79ecfde..d191c9988 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json @@ -60,7 +60,7 @@ "LabelDisplayMissingEpisodesWithinSeasons": "Mostrar epis\u00f3dios em falta dentro das temporadas", "LabelUnairedMissingEpisodesWithinSeasons": "Mostrar epis\u00f3dios por estrear dentro das temporadas", "HeaderVideoPlaybackSettings": "Configura\u00e7\u00f5es de Reprodu\u00e7\u00e3o de V\u00eddeo", - "HeaderPlaybackSettings": "Playback Settings", + "HeaderPlaybackSettings": "Op\u00e7\u00f5es de Reprodu\u00e7\u00e3o", "LabelAudioLanguagePreference": "Prefer\u00eancias de Idioma de Audio:", "LabelSubtitleLanguagePreference": "Prefer\u00eancia de Idioma de Legenda:", "LabelDisplayForcedSubtitlesOnly": "Mostrar apenas legendas for\u00e7adas", @@ -534,7 +534,7 @@ "HeaderDisplaySettings": "Apresentar Configura\u00e7\u00f5es", "TabPlayTo": "Play To", "LabelEnableDlnaServer": "Ativar servidor DLNA", - "LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play Media Browser content.", + "LabelEnableDlnaServerHelp": "Permite aos dispositivos UPnP da sua rede, navegar e reproduzir conte\u00fado do Media Browser.", "LabelEnableBlastAliveMessages": "Blast alive messages", "LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.", "LabelBlastMessageInterval": "Alive message interval (seconds)", @@ -542,24 +542,37 @@ "LabelDefaultUser": "Utilizador padr\u00e3o:", "LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles.", "TitleDlna": "DLNA", - "HeaderServerSettings": "Server Settings", + "HeaderServerSettings": "Op\u00e7\u00f5es do Servidor", "LabelWeatherDisplayLocation": "Weather display location:", - "LabelWeatherDisplayLocationHelp": "US zip code \/ City, State, Country \/ City, Country", + "LabelWeatherDisplayLocationHelp": "C\u00f3digo postal dos EUA \/ Cidade, Estado, Pa\u00eds \/ Cidade, Pa\u00eds", "LabelWeatherDisplayUnit": "Weather display unit:", "OptionCelsius": "Celsius", "OptionFahrenheit": "Fahrenheit", - "HeaderRequireManualLogin": "Require manual username entry for:", - "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", + "HeaderRequireManualLogin": "Necessita a inser\u00e7\u00e3o manual de um nome de utilizador para:", + "HeaderRequireManualLoginHelp": "Quando desativados, os clientes podem mostrar a tela de login com uma sele\u00e7\u00e3o visual de utilizadores.", "OptionOtherApps": "Outras apps", - "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Quando est\u00e3o dispon\u00edveis atualiza\u00e7\u00f5es", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "OptionMobileApps": "Apps m\u00f3veis", + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ru.json b/MediaBrowser.Server.Implementations/Localization/Server/ru.json index bae2b7056..e7944467f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ru.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json @@ -103,7 +103,7 @@ "OptionWriters": "\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0441\u0442\u044b", "OptionProducers": "\u041f\u0440\u043e\u0434\u044e\u0441\u0435\u0440\u044b", "HeaderResume": "\u0412\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435", - "HeaderNextUp": "\u0427\u0442\u043e \u043f\u043e\u0442\u043e\u043c", + "HeaderNextUp": "\u041d\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0435", "NoNextUpItemsMessage": "\u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e. \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u044b \u0441 \u043d\u0430\u0447\u0430\u043b\u0430!", "HeaderLatestEpisodes": "\u0421\u0432\u0435\u0436\u0438\u0435 \u044d\u043f\u0438\u0437\u043e\u0434\u044b", "HeaderPersonTypes": "\u0422\u0438\u043f\u044b \u043b\u044e\u0434\u0435\u0439:", @@ -291,8 +291,8 @@ "OptionPrePaddingRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0442\u0431\u0438\u0432\u043a\u0430 \u0434\u043e \u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u0435\u0451 \u0446\u0435\u043b\u043e\u0441\u0442\u043d\u043e\u0441\u0442\u0438.", "LabelPostPaddingMinutes": "\u041e\u0442\u0431\u0438\u0432\u043a\u0430 \u043f\u043e\u0441\u043b\u0435, \u043c\u0438\u043d:", "OptionPostPaddingRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0442\u0431\u0438\u0432\u043a\u0430 \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u043d\u0446\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u0435\u0451 \u0446\u0435\u043b\u043e\u0441\u0442\u043d\u043e\u0441\u0442\u0438.", - "HeaderWhatsOnTV": "\u0427\u0442\u043e \u0438\u0434\u0451\u0442", - "HeaderUpcomingTV": "\u0421\u043a\u043e\u0440\u043e \u0432 \u044d\u0444\u0438\u0440\u0435", + "HeaderWhatsOnTV": "\u0412 \u044d\u0444\u0438\u0440\u0435", + "HeaderUpcomingTV": "\u0421\u043a\u043e\u0440\u043e \u043d\u0430 \u0422\u0412", "TabStatus": "\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435:", "TabSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b", "ButtonRefreshGuideData": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u0435\u043b\u0435\u0433\u0438\u0434", @@ -396,7 +396,7 @@ "OptionSeriesSortName": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0438\u0430\u043b\u0430", "OptionTvdbRating": "\u041e\u0446\u0435\u043d\u043a\u0430 TVDb", "HeaderTranscodingQualityPreference": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u0440\u0430\u043d\u0441\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:", - "OptionAutomaticTranscodingHelp": "\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c", + "OptionAutomaticTranscodingHelp": "\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c", "OptionHighSpeedTranscodingHelp": "\u0411\u043e\u043b\u0435\u0435 \u043d\u0438\u0437\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e, \u043d\u043e \u043f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u0431\u044b\u0441\u0442\u0440\u043e\u043c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438", "OptionHighQualityTranscodingHelp": "\u0411\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e, \u043d\u043e \u043f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438", "OptionMaxQualityTranscodingHelp": "\u041d\u0430\u0438\u043b\u0443\u0447\u0448\u0435\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e, \u043f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0438 \u043f\u0440\u0438 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440", @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "\u041f\u0440\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438, \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u044d\u043a\u0440\u0430\u043d \u0432\u0445\u043e\u0434\u0430 \u0441 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u044b\u0431\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.", "OptionOtherApps": "\u0414\u0440\u0443\u0433\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f", "OptionMobileApps": "\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f", - "HeaderEnableNotificationForEvents": "\u041f\u043e\u0441\u044b\u043b\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445:", - "OptionNotifyOnUpdates": "\u041a\u043e\u0433\u0434\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "\u041a\u043e\u0433\u0434\u0430 \u0441\u0431\u043e\u0439 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u043d\u0438\u0439", - "OptionNotifyOnNewLibraryContent": "\u041a\u043e\u0433\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0432 \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0443", - "SendNotificationHelp": "\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "\u0429\u0451\u043b\u043a\u043d\u0438\u0442\u0435 \u043f\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044e, \u0447\u0442\u043e\u0431\u044b \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0442\u0441\u044b\u043b\u043a\u0438.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432\u0438\u0434\u0435\u043e", + "NotificationOptionAudioPlayback": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0430\u0443\u0434\u0438\u043e", + "NotificationOptionGamePlayback": "\u0417\u0430\u043f\u0443\u0441\u043a \u0438\u0433\u0440\u044b", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f", + "SendNotificationHelp": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.", + "NotificationOptionServerRestartRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430", + "LabelNotificationEnabled": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435", + "LabelMonitorUsers": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043e\u0442:", + "LabelSendNotificationToUsers": "\u041f\u043e\u0441\u044b\u043b\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043d\u0430:", + "UsersNotNotifiedAboutSelfActivity": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0442\u044c\u0441\u044f \u043e \u0441\u0432\u043e\u0438\u0445 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u044f\u0445.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 9c3a72aee..cda614761 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -556,14 +556,28 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:", + "AdditionalNotificationServices": "Browse the plugin catalog to install additional notification services." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/sv.json b/MediaBrowser.Server.Implementations/Localization/Server/sv.json index 8a8a60eb7..d4a1f382b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/sv.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/sv.json @@ -55,12 +55,12 @@ "TabProfile": "Profil", "TabMetadata": "Metadata", "TabImages": "Bilder", - "TabNotifications": "Notifications", + "TabNotifications": "Meddelanden", "TabCollectionTitles": "Titlar", "LabelDisplayMissingEpisodesWithinSeasons": "Visa saknade avsnitt i s\u00e4songer", "LabelUnairedMissingEpisodesWithinSeasons": "Visa \u00e4nnu ej s\u00e4nda avsnitt i s\u00e4songer", "HeaderVideoPlaybackSettings": "Inst\u00e4llningar f\u00f6r videouppspelning", - "HeaderPlaybackSettings": "Playback Settings", + "HeaderPlaybackSettings": "Uppspelningsinst\u00e4llningar", "LabelAudioLanguagePreference": "\u00d6nskat spr\u00e5k f\u00f6r ljudsp\u00e5r", "LabelSubtitleLanguagePreference": "\u00d6nskat spr\u00e5k f\u00f6r undertexter", "LabelDisplayForcedSubtitlesOnly": "Visa endast tvingande undertexter", @@ -196,8 +196,8 @@ "TitleSupport": "Support", "TabLog": "H\u00e4ndelselogg", "TabAbout": "Om", - "TabSupporterKey": "Sponsorkod", - "TabBecomeSupporter": "Bli sponsor", + "TabSupporterKey": "Donationskod", + "TabBecomeSupporter": "Bidra med en donation", "MediaBrowserHasCommunity": "Media Browser har en livaktig grupp av anv\u00e4ndare och medverkande.", "CheckoutKnowledgeBase": "Ta en titt i v\u00e5r kunskapsdatabas s\u00e5 att du f\u00e5r ut mesta m\u00f6jliga av Media Browser.", "SearchKnowledgeBase": "S\u00f6k i kunskapsdatabasen", @@ -470,8 +470,8 @@ "HeaderSupportTheTeam": "St\u00f6d Media Browser", "LabelSupportAmount": "M\u00e4ngd (USD)", "HeaderSupportTheTeamHelp": "Bidra till fortsatt utveckling av projektet genom att donera. En del av alla donationer kommer att ges till andra kostnadsfria verktyg som vi \u00e4r beroende av.", - "ButtonEnterSupporterKey": "Skriv in donations kod", - "DonationNextStep": "N\u00e4r du \u00e4r klar, g\u00e5 tillbaka och ange din donations kod, som du kommer att f\u00e5 via e-post.", + "ButtonEnterSupporterKey": "Ange din donationskod", + "DonationNextStep": "N\u00e4r du \u00e4r klar, g\u00e5 tillbaka och ange din donationskod, som du kommer att f\u00e5 via e-post.", "AutoOrganizeHelp": "Auto-Organisera \u00f6vervakar din nedladdnings mapp efter nya filer och kommer att flytta dessa till dina medie mappar.", "AutoOrganizeTvHelp": "TV-fil organisering kommer bara l\u00e4gga episoder till befintliga serier. Den kommer inte att skapa nya serie mappar.", "OptionEnableEpisodeOrganization": "Aktivera ny avsnitts organisering", @@ -517,18 +517,18 @@ "LabelDownMixAudioScale": "Skala vid volymh\u00f6jning av nedmixat ljud", "LabelDownMixAudioScaleHelp": "H\u00f6j volymen vid nedmixning. S\u00e4tt v\u00e4rdet till 1 f\u00f6r att beh\u00e5lla den ursprungliga niv\u00e5n.", "ButtonLinkKeys": "Koppla koder", - "LabelOldSupporterKey": "Gammal supporterkod", - "LabelNewSupporterKey": "Ny supporterkod", + "LabelOldSupporterKey": "Tidigare donationskod", + "LabelNewSupporterKey": "Ny donationskod", "HeaderMultipleKeyLinking": "Koppla ihop flera koder", - "MultipleKeyLinkingHelp": "Om du har flera supporterkoder, anv\u00e4nd det h\u00e4r formul\u00e4ret f\u00f6r att koppla deras registreringar till den nya koden.", + "MultipleKeyLinkingHelp": "Om du har flera donationskoder, anv\u00e4nd det h\u00e4r formul\u00e4ret f\u00f6r att koppla deras registreringar till den nya koden.", "LabelCurrentEmailAddress": "Nuvarande e-postadress", "LabelCurrentEmailAddressHelp": "Den e-postadress den nya koden skickades till.", "HeaderForgotKey": "Gl\u00f6mt koden", "LabelEmailAddress": "E-postadress", "LabelSupporterEmailAddress": "Den e-postadress du angav vid k\u00f6pet av den nya koden.", - "ButtonRetrieveKey": "H\u00e4mta supporterkod", - "LabelSupporterKey": "Supporterkod (klistra in fr\u00e5n e-postmeddelandet)", - "LabelSupporterKeyHelp": "Ange din supporterkod s\u00e5 att du kan b\u00f6rja anv\u00e4nda de extrafunktioner som har utvecklats inom Media Browsers anv\u00e4ndargrupper.", + "ButtonRetrieveKey": "H\u00e4mta donationskod", + "LabelSupporterKey": "Donationskod (klistra in fr\u00e5n e-postmeddelandet)", + "LabelSupporterKeyHelp": "Ange din donationskod s\u00e5 att du kan b\u00f6rja anv\u00e4nda de extrafunktioner som har utvecklats inom Media Browsers anv\u00e4ndargrupper.", "MessageInvalidKey": "MB3-kod ogiltig eller saknas", "ErrorMessageInvalidKey": "F\u00f6r att kunna registrera premiumfunktioner m\u00e5ste du \u00e4ven vara MB3-supporter. V\u00e4nligen g\u00f6r en donation s\u00e5 att du st\u00f6ttar den fortsatta utvecklingen av huvudprodukten. Tack!", "HeaderDisplaySettings": "Bildsk\u00e4rmsinst\u00e4llningar", @@ -542,24 +542,37 @@ "LabelDefaultUser": "F\u00f6rvald anv\u00e4ndare:", "LabelDefaultUserHelp": "Anger vilket anv\u00e4ndarbibliotek som skall visas p\u00e5 anslutna enheter. Denna inst\u00e4llning kan \u00e4ndras med hj\u00e4lp av en enhetsprofil.", "TitleDlna": "DLNA", - "HeaderServerSettings": "Server Settings", - "LabelWeatherDisplayLocation": "Weather display location:", - "LabelWeatherDisplayLocationHelp": "US zip code \/ City, State, Country \/ City, Country", - "LabelWeatherDisplayUnit": "Weather display unit:", + "HeaderServerSettings": "Serverinst\u00e4llningar", + "LabelWeatherDisplayLocation": "Geografisk plats f\u00f6r v\u00e4derdata:", + "LabelWeatherDisplayLocationHelp": "U.S. zip code \/ Stad, stat, land \/ Stad, land", + "LabelWeatherDisplayUnit": "Enhet f\u00f6r v\u00e4derdata:", "OptionCelsius": "Celsius", "OptionFahrenheit": "Fahrenheit", - "HeaderRequireManualLogin": "Require manual username entry for:", - "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", - "OptionOtherApps": "Other apps", - "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderRequireManualLogin": "Kr\u00e4v att anv\u00e4ndarnamn anges manuellt f\u00f6r:", + "HeaderRequireManualLoginHelp": "Om avaktiverat kan klienterna visa en inloggningsbild f\u00f6r visuellt val av anv\u00e4ndare.", + "OptionOtherApps": "Andra appar", + "OptionMobileApps": "Mobilappar", + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "Meddelanden visas i kontrollpanelens inkorg. S\u00f6k efter fler meddelandetill\u00e4gg i pluginkatalogen.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json b/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json index 67bb512fd..096dac02b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json @@ -552,14 +552,27 @@ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "OptionOtherApps": "Other apps", "OptionMobileApps": "Mobile apps", - "HeaderEnableNotificationForEvents": "Nofity administrative users when:", - "OptionNotifyOnUpdates": "Updates are available", - "OptionNotifyOnVideoPlayback": "Video", - "OptionNotifyOnAudioPlayback": "Audio", - "OptionNotifyOnGamePlayback": "Games", - "OptionNotifyOnFailedTasks": "Scheduled tasks fail", - "OptionNotifyOnNewLibraryContent": "New library content is added", - "SendNotificationHelp": "Notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", - "HeaderEnableNotificationForPlayback": "Notify when users play:", - "OptionNotifyOnServerRestartRequired": "The server needs to be restarted" + "HeaderNotificationList": "Click on a notification to configure it's sending options.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback", + "NotificationOptionAudioPlayback": "Audio playback", + "NotificationOptionGamePlayback": "Game playback", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", + "NotificationOptionServerRestartRequired": "Server restart required", + "LabelNotificationEnabled": "Enable this notification", + "LabelMonitorUsers": "Monitor activity from:", + "LabelSendNotificationToUsers": "Send the notification to:", + "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.", + "LabelUseNotificationServices": "Use the following services:", + "CategoryUser": "User", + "CategorySystem": "System", + "LabelMessageTitle": "Message title:", + "LabelAvailableTokens": "Available tokens:" }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 7d6ddbb6e..d86ff8aac 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -185,6 +185,7 @@ <Compile Include="MediaEncoder\EncodingManager.cs" /> <Compile Include="News\NewsEntryPoint.cs" /> <Compile Include="News\NewsService.cs" /> + <Compile Include="Notifications\CoreNotificationTypes.cs" /> <Compile Include="Notifications\InternalNotificationService.cs" /> <Compile Include="Notifications\NotificationManager.cs" /> <Compile Include="Persistence\SqliteChapterRepository.cs" /> diff --git a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs index bb6ae5a90..9a4783cba 100644 --- a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs @@ -2,9 +2,12 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Logging; using MediaBrowser.Model.News; +using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; @@ -25,15 +28,20 @@ namespace MediaBrowser.Server.Implementations.News private readonly ILogger _logger; private readonly IJsonSerializer _json; + private readonly INotificationManager _notifications; + private readonly IUserManager _userManager; + private readonly TimeSpan _frequency = TimeSpan.FromHours(24); - public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json) + public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json, INotificationManager notifications, IUserManager userManager) { _httpClient = httpClient; _appPaths = appPaths; _fileSystem = fileSystem; _logger = logger; _json = json; + _notifications = notifications; + _userManager = userManager; } public void Run() @@ -61,6 +69,13 @@ namespace MediaBrowser.Server.Implementations.News private async Task DownloadNews(string path) { + DateTime? lastUpdate = null; + + if (File.Exists(path)) + { + lastUpdate = _fileSystem.GetLastWriteTimeUtc(path); + } + var requestOptions = new HttpRequestOptions { Url = "http://mediabrowser3.com/community/index.php?/blog/rss/1-media-browser-developers-blog", @@ -75,9 +90,31 @@ namespace MediaBrowser.Server.Implementations.News var news = ParseRssItems(doc).ToList(); _json.SerializeToFile(news, path); + + await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false); } } + private Task CreateNotifications(List<NewsItem> items, DateTime? lastUpdate, CancellationToken cancellationToken) + { + if (lastUpdate.HasValue) + { + items = items.Where(i => i.Date.ToUniversalTime() > lastUpdate.Value) + .ToList(); + } + + var tasks = items.Select(i => _notifications.SendNotification(new NotificationRequest + { + Date = i.Date, + Name = i.Title, + Description = i.Link, + UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList() + + }, cancellationToken)); + + return Task.WhenAll(tasks); + } + private IEnumerable<NewsItem> ParseRssItems(XmlDocument xmlDoc) { var nodes = xmlDoc.SelectNodes("rss/channel/item"); diff --git a/MediaBrowser.Server.Implementations/News/NewsService.cs b/MediaBrowser.Server.Implementations/News/NewsService.cs index 34da857dd..9eeadfab7 100644 --- a/MediaBrowser.Server.Implementations/News/NewsService.cs +++ b/MediaBrowser.Server.Implementations/News/NewsService.cs @@ -2,10 +2,10 @@ using MediaBrowser.Controller.News; using MediaBrowser.Model.News; using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Serialization; using System.Collections.Generic; using System.IO; using System.Linq; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.News { diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs new file mode 100644 index 000000000..bf5228dd9 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs @@ -0,0 +1,131 @@ +using MediaBrowser.Controller.Localization; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.Notifications +{ + public class CoreNotificationTypes : INotificationTypeFactory + { + private readonly ILocalizationManager _localization; + + public CoreNotificationTypes(ILocalizationManager localization) + { + _localization = localization; + } + + public IEnumerable<NotificationTypeInfo> GetNotificationTypes() + { + var knownTypes = new List<NotificationTypeInfo> + { + new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateAvailable.ToString(), + DefaultTitle = "A new version of Media Browser Server is available for download." + }, + + new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateInstalled.ToString(), + DefaultTitle = "A new version of Media Browser 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.PluginUninstalled.ToString(), + DefaultTitle = "{Name} was uninstalled.", + Variables = new List<string>{"Name", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginUpdateInstalled.ToString(), + DefaultTitle = "{Name} was updated.", + Variables = new List<string>{"Name", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.ServerRestartRequired.ToString(), + DefaultTitle = "Please restart Media Browser Server to finish updating." + }, + + new NotificationTypeInfo + { + Type = NotificationType.TaskFailed.ToString(), + DefaultTitle = "{Name} failed.", + Variables = new List<string>{"Name"} + }, + + 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"} + } + }; + + foreach (var type in knownTypes) + { + Update(type); + } + + return knownTypes.OrderBy(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 + { + note.Category = _localization.GetLocalizedString("CategorySystem"); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs index d6caa5b68..ff8d35e28 100644 --- a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs +++ b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Logging; @@ -15,11 +17,15 @@ namespace MediaBrowser.Server.Implementations.Notifications { private readonly ILogger _logger; private readonly IUserManager _userManager; + private readonly IServerConfigurationManager _config; + private INotificationService[] _services; + private INotificationTypeFactory[] _typeFactories; - public NotificationManager(ILogManager logManager, IUserManager userManager) + public NotificationManager(ILogManager logManager, IUserManager userManager, IServerConfigurationManager config) { _userManager = userManager; + _config = config; _logger = logManager.GetLogger(GetType().Name); } @@ -27,27 +33,34 @@ namespace MediaBrowser.Server.Implementations.Notifications { var users = request.UserIds.Select(i => _userManager.GetUserById(new Guid(i))); - var tasks = _services.Select(i => SendNotification(request, i, users, cancellationToken)); + var notificationType = request.NotificationType; + + var title = GetTitle(request); + + var tasks = _services.Where(i => IsEnabled(i, notificationType)) + .Select(i => SendNotification(request, i, users, title, cancellationToken)); return Task.WhenAll(tasks); } - public Task SendNotification(NotificationRequest request, + private Task SendNotification(NotificationRequest request, INotificationService service, IEnumerable<User> users, + string title, CancellationToken cancellationToken) { users = users.Where(i => IsEnabledForUser(service, i)) .ToList(); - var tasks = users.Select(i => SendNotification(request, service, i, cancellationToken)); + var tasks = users.Select(i => SendNotification(request, service, title, i, cancellationToken)); return Task.WhenAll(tasks); } - public async Task SendNotification(NotificationRequest request, + private async Task SendNotification(NotificationRequest request, INotificationService service, + string title, User user, CancellationToken cancellationToken) { @@ -56,13 +69,13 @@ namespace MediaBrowser.Server.Implementations.Notifications Date = request.Date, Description = request.Description, Level = request.Level, - Name = request.Name, + 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); @@ -73,6 +86,50 @@ namespace MediaBrowser.Server.Implementations.Notifications } } + private string GetTitle(NotificationRequest request) + { + var title = request.Name; + + // If empty, grab from options + if (string.IsNullOrEmpty(title)) + { + if (!string.IsNullOrEmpty(request.NotificationType)) + { + var options = _config.Configuration.NotificationOptions.GetOptions(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 bool IsEnabledForUser(INotificationService service, User user) { try @@ -86,9 +143,50 @@ namespace MediaBrowser.Server.Implementations.Notifications } } - public void AddParts(IEnumerable<INotificationService> services) + private bool IsEnabled(INotificationService service, string notificationType) + { + return string.IsNullOrEmpty(notificationType) || + _config.Configuration.NotificationOptions.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(); + + foreach (var i in list) + { + i.Enabled = _config.Configuration.NotificationOptions.IsEnabled(i.Type); + } + + return list; + } + + public IEnumerable<NotificationServiceInfo> GetNotificationServices() + { + return _services.Select(i => new NotificationServiceInfo + { + Name = i.Name, + Id = i.Name.GetMD5().ToString("N") + + }).OrderBy(i => i.Name); } } } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index f266a6492..30d9976f0 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -438,7 +438,9 @@ namespace MediaBrowser.Server.Implementations.Session Item = libraryItem, Users = users, MediaSourceId = info.MediaSourceId, - MediaInfo = info.Item + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client }, _logger); @@ -507,7 +509,9 @@ namespace MediaBrowser.Server.Implementations.Session Users = users, PlaybackPositionTicks = session.PlayState.PositionTicks, MediaSourceId = session.PlayState.MediaSourceId, - MediaInfo = info.Item + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client }, _logger); } @@ -577,7 +581,9 @@ namespace MediaBrowser.Server.Implementations.Session PlaybackPositionTicks = info.PositionTicks, PlayedToCompletion = playedToCompletion, MediaSourceId = info.MediaSourceId, - MediaInfo = info.Item + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client }, _logger); diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c3f1afa33..157f39755 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -34,6 +34,7 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; using MediaBrowser.Dlna.Eventing; +using MediaBrowser.Dlna.Main; using MediaBrowser.Dlna.PlayTo; using MediaBrowser.Dlna.Server; using MediaBrowser.MediaEncoding.BdInfo; @@ -527,7 +528,7 @@ namespace MediaBrowser.ServerApplication LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager); RegisterSingleInstance(LiveTvManager); - NotificationManager = new NotificationManager(LogManager, UserManager); + NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); @@ -713,7 +714,7 @@ namespace MediaBrowser.ServerApplication ChannelManager.AddParts(GetExports<IChannel>()); - NotificationManager.AddParts(GetExports<INotificationService>()); + NotificationManager.AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>()); } /// <summary> @@ -846,7 +847,7 @@ namespace MediaBrowser.ServerApplication list.Add(typeof(MediaEncoder).Assembly); // Dlna - list.Add(typeof(PlayToServerEntryPoint).Assembly); + list.Add(typeof(DlnaEntryPoint).Assembly); list.AddRange(Assemblies.GetAssembliesWithParts()); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 0fc095998..c357e1e46 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -597,6 +597,7 @@ namespace MediaBrowser.WebDashboard.Api "musicrecommended.js", "musicvideos.js", "notifications.js", + "notificationsetting.js", "notificationsettings.js", "playlist.js", "plugincatalogpage.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 64e25242b..cc542cd26 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -508,6 +508,9 @@ <Content Include="dashboard-ui\livetvrecordings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\notificationsetting.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\notificationsettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -634,6 +637,9 @@ <Content Include="dashboard-ui\scripts\livetvrecordings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\notificationsetting.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\notificationsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index db580ceda..1d7144765 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.353</version> + <version>3.0.354</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.353" /> + <dependency id="MediaBrowser.Common" version="3.0.354" /> <dependency id="NLog" version="2.1.0" /> <dependency id="SimpleInjector" version="2.4.1" /> <dependency id="sharpcompress" version="0.10.2" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 8fd6c7e12..743ebbf21 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.353</version> + <version>3.0.354</version> <title>MediaBrowser.Common</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index cd60ffb60..a203b8d56 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.353</version> + <version>3.0.354</version> <title>Media Browser.Server.Core</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Media Browser Server.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.353" /> + <dependency id="MediaBrowser.Common" version="3.0.354" /> </dependencies> </metadata> <files> |
